release.sh: allow REPO and GITBRANCH env override
[minix.git] / commands / ftp101 / net.c
blob9e6cab7b4cc66de20c5983e95dd76dbb4b5e7da6
1 /* net.c Copyright 1992-2000 by Michael Temari All Rights Reserved
3 * This file is part of ftp.
6 * 01/25/96 Initial Release Michael Temari, <Michael@TemWare.Com>
7 */
9 #include <sys/types.h>
10 #include <sys/ioctl.h>
11 #include <sys/wait.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <fcntl.h>
18 #include <signal.h>
19 #include <errno.h>
20 #include <net/netlib.h>
21 #include <net/hton.h>
22 #include <net/gen/netdb.h>
23 #include <net/gen/in.h>
24 #include <net/gen/inet.h>
25 #include <net/gen/tcp.h>
26 #include <net/gen/tcp_io.h>
28 #include "ftp.h"
29 #include "xfer.h"
30 #include "net.h"
32 void donothing(int sig);
34 int ftpcomm_fd;
35 static ipaddr_t myip;
36 static ipaddr_t hostip;
37 static char host[256];
38 static int lpid;
40 int NETinit()
42 int s;
43 char *tcp_device;
44 int tcp_fd;
45 nwio_tcpconf_t nwio_tcpconf;
47 /* All this just to get our ip address */
49 if((tcp_device = getenv("TCP_DEVICE")) == (char *)NULL)
50 tcp_device = TCP_DEVICE;
52 tcp_fd = open(tcp_device, O_RDWR);
53 if(tcp_fd < 0) {
54 perror("ftp: Could not open tcp_device");
55 return(-1);
57 s = ioctl(tcp_fd, NWIOGTCPCONF, &nwio_tcpconf);
58 if(s < 0) {
59 perror("ftp: Could not get tcp configuration");
60 return(-1);
63 myip = nwio_tcpconf.nwtc_locaddr;
65 close(tcp_fd);
67 return(0);
70 int DOopen()
72 nwio_tcpconf_t tcpconf;
73 nwio_tcpcl_t tcpcopt;
74 char *tcp_device;
75 tcpport_t port;
76 int s;
77 struct hostent *hp;
78 struct servent *servent;
80 if(linkopen) {
81 printf("Use \"CLOSE\" to close the connection first.\n");
82 return(0);
85 if(cmdargc < 2) {
86 if(readline("Host: ", host, sizeof(host)) < 0)
87 return(-1);
88 } else
89 strncpy(host, cmdargv[1], sizeof(host));
91 if((servent = getservbyname("ftp", "tcp")) == (struct servent *)NULL) {
92 fprintf(stderr, "ftp: Could not find ftp tcp service\n");
93 port = htons(21);
94 } else
95 port = (tcpport_t)servent->s_port;
97 hp = gethostbyname(host);
98 if(hp == (struct hostent *)NULL) {
99 hostip = (ipaddr_t)0;
100 printf("Unresolved host %s\n", host);
101 return(0);
102 } else
103 memcpy((char *) &hostip, (char *) hp->h_addr, hp->h_length);
105 /* This HACK allows the server to establish data connections correctly */
106 /* when using the loopback device to talk to ourselves */
107 #ifdef __NBSD_LIBC
108 if((hostip & ntohl(0xFF000000)) == inet_addr("127.0.0.0"))
109 #else
110 if((hostip & NTOHL(0xFF000000)) == inet_addr("127.0.0.0"))
111 #endif
112 hostip = myip;
114 if((tcp_device = getenv("TCP_DEVICE")) == NULL)
115 tcp_device = "/dev/tcp";
117 if((ftpcomm_fd = open(tcp_device, O_RDWR)) < 0) {
118 perror("ftp: open error on tcp device");
119 return(-1);
122 tcpconf.nwtc_flags = NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP;
123 tcpconf.nwtc_remaddr = hostip;
124 tcpconf.nwtc_remport = port;
126 s = ioctl(ftpcomm_fd, NWIOSTCPCONF, &tcpconf);
127 if(s < 0) {
128 perror("ftp: ioctl error on NWIOSTCPCONF");
129 close(ftpcomm_fd);
130 return(-1);
133 tcpcopt.nwtcl_flags = 0;
135 s = ioctl(ftpcomm_fd, NWIOTCPCONN, &tcpcopt);
136 if(s < 0) {
137 perror("ftp: ioctl error on NWIOTCPCONN");
138 close(ftpcomm_fd);
139 return(-1);
142 s = ioctl(ftpcomm_fd, NWIOGTCPCONF, &tcpconf);
143 if(s < 0) {
144 perror("ftp: ioctl error on NWIOGTCPCONF");
145 close(ftpcomm_fd);
146 return(-1);
149 s = DOgetreply();
151 if(s < 0) {
152 close(ftpcomm_fd);
153 return(s);
156 if(s != 220) {
157 close(ftpcomm_fd);
158 return(0);
161 linkopen = 1;
163 return(s);
166 int DOclose()
168 if(!linkopen) {
169 printf("You can't close a connection that isn't open.\n");
170 return(0);
173 close(ftpcomm_fd);
175 linkopen = 0;
176 loggedin = 0;
178 return(0);
181 int DOquit()
183 int s;
185 if(linkopen) {
186 s = DOcommand("QUIT", "");
187 s = DOclose();
190 printf("FTP done.\n");
192 exit(0);
195 void donothing(sig)
196 int sig;
200 int DOdata(datacom, file, direction, fd)
201 char *datacom;
202 char *file;
203 int direction; /* RETR or STOR */
204 int fd;
206 nwio_tcpconf_t tcpconf;
207 nwio_tcpcl_t tcplopt, tcpcopt;
208 char *tcp_device;
209 static int ftpdata_fd = -1;
210 char *buff;
211 ipaddr_t ripaddr;
212 tcpport_t rport;
213 static tcpport_t lport;
214 int s;
215 int i;
216 int wpid;
217 int cs;
218 int pfd[2];
219 char dummy;
220 char port[32];
221 int wasopen;
223 lport = htons(0xF000);
225 #ifdef DEBUG
226 printf("DOdata %s %s %d %d\n", datacom, file, direction, fd);
227 #endif
229 ripaddr = hostip;
230 #ifdef __NBSD_LIBC
231 rport = htons(2);
232 #else
233 rport = HTONS(20);
234 #endif
236 /* here we set up a connection to listen on if not passive mode */
237 /* otherwise we use this to connect for passive mode */
239 if((tcp_device = getenv("TCP_DEVICE")) == NULL)
240 tcp_device = "/dev/tcp";
242 if(ftpdata_fd >= 0 && mode != MODE_B) {
243 close(ftpdata_fd);
244 ftpdata_fd = -1;
247 wasopen = (ftpdata_fd >= 0);
249 #ifdef DEBUG
250 printf("wasopen = %d\n", wasopen);
251 #endif
253 if(wasopen)
254 goto WASOPEN;
256 #ifdef DEBUG
257 printf("b4 open = %d\n", ftpdata_fd);
258 #endif
260 if(ftpdata_fd == -1)
261 if((ftpdata_fd = open(tcp_device, O_RDWR)) < 0) {
262 perror("ftp: open error on tcp device");
263 return(0);
266 #ifdef DEBUG
267 printf("at open = %d\n", ftpdata_fd);
268 #endif
270 if(passive) {
271 #ifdef DEBUG
272 printf("b4 PASV command\n");
273 #endif
274 s = DOcommand("PASV", "");
275 #ifdef DEBUG
276 printf("PASV command returned %d\n", s);
277 #endif
278 if(s != 227) {
279 close(ftpdata_fd);
280 ftpdata_fd = -1;
281 return(s);
283 /* decode host and port */
284 buff = reply;
285 while(*buff && (*buff != '(')) buff++;
286 buff++;
287 ripaddr = (ipaddr_t)0;
288 for(i = 0; i < 4; i++) {
289 ripaddr = (ripaddr << 8) + (ipaddr_t)atoi(buff);
290 if((buff = strchr(buff, ',')) == (char *)0) {
291 printf("Could not parse PASV reply\n");
292 return(0);
294 buff++;
296 rport = (tcpport_t)atoi(buff);
297 if((buff = strchr(buff, ',')) == (char *)0) {
298 printf("Could not parse PASV reply\n");
299 return(0);
301 buff++;
302 rport = (rport << 8) + (tcpport_t)atoi(buff);
303 ripaddr = ntohl(ripaddr);
304 rport = ntohs(rport);
305 #ifdef DEBUG
306 printf("PASV %08x %04x\n", ripaddr, rport);
307 #endif
310 while(1) {
311 tcpconf.nwtc_flags = NWTC_SET_RA | NWTC_SET_RP;
312 if(passive || ntohs(lport) >= 0xF000) {
313 tcpconf.nwtc_flags |= NWTC_LP_SEL;
314 } else {
315 /* For no good reason Sun hosts don't like it if they have to
316 * connect to the same port twice in a short time...
318 lport = htons(ntohs(lport) + 1);
319 tcpconf.nwtc_flags |= NWTC_LP_SET;
320 tcpconf.nwtc_locport = lport;
323 tcpconf.nwtc_remaddr = ripaddr;
324 tcpconf.nwtc_remport = rport;
326 #ifdef DEBUG
327 printf("b4 STCPCONF locport = %d\n", lport);
328 #endif
330 s = ioctl(ftpdata_fd, NWIOSTCPCONF, &tcpconf);
331 #ifdef DEBUG
332 printf("at STCPCONF %d %d\n", s, errno);
333 #endif
334 if(s < 0) {
335 if(errno == EADDRINUSE) continue;
336 perror("ftp: ioctl error on NWIOSTCPCONF");
337 close(ftpdata_fd);
338 ftpdata_fd = -1;
339 return(0);
341 break;
344 #ifdef DEBUG
345 printf("b4 GTCPCONF\n");
346 #endif
347 s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf);
348 #ifdef DEBUG
349 printf("at GTCPCONF %d %d\n", s, errno);
350 #endif
351 if(s < 0) {
352 perror("ftp: ioctl error on NWIOGTCPCONF");
353 close(ftpdata_fd);
354 ftpdata_fd = -1;
355 return(0);
357 lport = tcpconf.nwtc_locport;
359 #ifdef DEBUG
360 printf("lport = %04x\n", lport);
361 #endif
363 if(passive) {
364 /* passive mode we connect to them */
365 tcpcopt.nwtcl_flags = 0;
366 #ifdef DEBUG
367 printf("Doing TCPCONN\n");
368 #endif
369 s = ioctl(ftpdata_fd, NWIOTCPCONN, &tcpcopt);
370 #ifdef DEBUG
371 printf("TCPCONN %d %d\n", s, errno);
372 #endif
373 if(s < 0) {
374 perror("ftp: error on ioctl NWIOTCPCONN");
375 close(ftpdata_fd);
376 ftpdata_fd = -1;
377 return(0);
379 #ifdef DEBUG
380 printf("GTCPCONF\n");
381 #endif
382 s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf);
383 #ifdef DEBUG
384 printf("GTCPCONF %d %d\n", s, errno);
385 #endif
386 if(s < 0) {
387 perror("ftp: error on ioctl NWIOGTCPCONF");
388 close(ftpdata_fd);
389 ftpdata_fd = -1;
390 return(0);
392 } else {
393 /* we listen for them */
394 tcplopt.nwtcl_flags = 0;
395 #ifdef DEBUG
396 printf("Listen\n");
397 #endif
399 if (pipe(pfd) < 0) {
400 perror("ftp: could not create a pipe");
401 close(ftpdata_fd);
402 ftpdata_fd = -1;
403 return(0);
405 lpid = fork();
406 if(lpid < 0) {
407 perror("ftp: could not fork listener");
408 close(ftpdata_fd);
409 ftpdata_fd = -1;
410 close(pfd[0]);
411 close(pfd[1]);
412 return(0);
413 } else if(lpid == 0) {
414 #ifdef DEBUG
415 printf("Child here\n");
416 #endif
417 close(pfd[0]);
418 signal(SIGALRM, donothing);
419 alarm(15);
420 close(pfd[1]);
421 #ifdef DEBUG
422 printf("child TCPLISTEN\n");
423 #endif
424 s = ioctl(ftpdata_fd, NWIOTCPLISTEN, &tcplopt);
425 alarm(0);
426 #ifdef DEBUG
427 printf("listen %d %d\n", s, errno);
428 #endif
429 if(s < 0)
430 exit(errno); /* error */
431 else
432 exit(0); /* connection made */
434 #ifdef DEBUG
435 printf("Fork = %d\n", lpid);
436 #endif
437 /* Wait for the pipe to close, then the listener is ready (almost). */
438 close(pfd[1]);
439 (void) read(pfd[0], &dummy, 1);
440 close(pfd[0]);
441 while(1) {
442 wpid = waitpid(lpid, &cs, WNOHANG);
443 #ifdef DEBUG
444 printf("waitpid %d %d\n", wpid, cs);
445 printf("GTCPCONF loop\n");
446 #endif
447 if(wpid != 0) break;
448 signal(SIGALRM, donothing);
449 alarm(1);
450 s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf);
451 alarm(0);
452 #ifdef DEBUG
453 printf("GTCPCONF loop %d %d\n", s, errno);
454 #endif
455 if(s == -1) break;
456 sleep(1);
458 #ifdef DEBUG
459 printf("GTCPCONF = %d\n", s);
460 #endif
463 #define hiword(x) ((u16_t)((x) >> 16))
464 #define loword(x) ((u16_t)(x & 0xffff))
465 #define hibyte(x) (((x) >> 8) & 0xff)
466 #define lobyte(x) ((x) & 0xff)
468 if(!passive) {
469 if(wpid != 0) {
470 close(ftpdata_fd);
471 ftpdata_fd = -1;
472 cs = (cs >> 8) & 0x00ff;
473 printf("Child listener error %s\n", strerror(cs));
474 return(0);
476 sprintf(port, "%u,%u,%u,%u,%u,%u",
477 hibyte(hiword(ntohl(myip))), lobyte(hiword(ntohl(myip))),
478 hibyte(loword(ntohl(myip))), lobyte(loword(ntohl(myip))),
479 hibyte(ntohs(lport)), lobyte(ntohs(lport)));
480 #ifdef DEBUG
481 printf("sending port command %s\n", port);
482 #endif
483 s = DOcommand("PORT", port);
484 #ifdef DEBUG
485 printf("port command = %d\n", s);
486 #endif
487 if(s != 200) {
488 close(ftpdata_fd);
489 ftpdata_fd = -1;
490 kill(lpid, SIGKILL);
491 (void) wait(&cs);
492 return(s);
496 WASOPEN:
498 #ifdef DEBUG
499 printf("doing data command %s %s\n", datacom, file);
500 #endif
501 s = DOcommand(datacom, file);
502 #ifdef DEBUG
503 printf("do command reply %d\n", s);
504 #endif
505 if(s == 125 || s == 150) {
506 if(!passive && !wasopen) {
507 while(1) {
508 #ifdef DEBUG
509 printf("Waiting for child %d\n", lpid);
510 #endif
511 s = wait(&cs);
512 #ifdef DEBUG
513 printf("Wait returned %d cs=%d errno=%d\n", s, cs, errno);
514 #endif
515 if(s < 0 || s == lpid)
516 break;
518 if(s < 0) {
519 perror("wait error:");
520 close(ftpdata_fd);
521 ftpdata_fd = -1;
522 kill(lpid, SIGKILL);
523 (void) wait(&cs);
524 return(s);
526 if((cs & 0x00ff)) {
527 printf("Child listener failed %04x\n", cs);
528 close(ftpdata_fd);
529 ftpdata_fd = -1;
530 return(-1);
532 cs = (cs >> 8) & 0x00ff;
533 if(cs) {
534 printf("Child listener error %s\n", strerror(cs));
535 close(ftpdata_fd);
536 ftpdata_fd = -1;
537 return(DOgetreply());
540 #ifdef DEBUG
541 printf("Before recvfile/sendfile call\n");
542 #endif
543 switch(direction) {
544 case RETR:
545 s = recvfile(fd, ftpdata_fd);
546 break;
547 case STOR:
548 s = sendfile(fd, ftpdata_fd);
549 break;
551 #ifdef DEBUG
552 printf("send/recieve %d\n", s);
553 #endif
554 if(mode != MODE_B) {
555 close(ftpdata_fd);
556 ftpdata_fd = -1;
559 s = DOgetreply();
560 #ifdef DEBUG
561 printf("send/recieve reply %d\n", s);
562 #endif
563 if(mode == MODE_B && s == 226) {
564 close(ftpdata_fd);
565 ftpdata_fd = -1;
567 } else {
568 if(!passive) {
569 kill(lpid, SIGKILL);
570 (void) wait(&cs);
572 close(ftpdata_fd);
573 ftpdata_fd = -1;
576 return(s);