/* net.c * * This file is part of ftp. * * * 01/25/96 Initial Release Michael Temari, */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ftp.h" #include "file.h" #include "net.h" _PROTOTYPE(void donothing, (int sig)); static int ftpcomm_fd; static ipaddr_t myip; static ipaddr_t hostip; static char host[256]; static int lpid; void NETinit() { int s; char *tcp_device; int tcp_fd; nwio_tcpconf_t nwio_tcpconf; /* All this just to get our ip address */ if((tcp_device = getenv("TCP_DEVICE")) == (char *)NULL) tcp_device = TCP_DEVICE; tcp_fd = open(tcp_device, O_RDWR); if(tcp_fd < 0) { perror("ftp: Could not open tcp_device"); exit(-1); } s = ioctl(tcp_fd, NWIOGTCPCONF, &nwio_tcpconf); if(s < 0) { perror("ftp: Could not get tcp configuration"); exit(-1); } myip = nwio_tcpconf.nwtc_locaddr; close(tcp_fd); } int DOopen() { nwio_tcpconf_t tcpconf; nwio_tcpcl_t tcpcopt; char *tcp_device; tcpport_t port; int s; struct hostent *hp; struct servent *servent; if(linkopen) { printf("Use \"CLOSE\" to close the connection first.\n"); return(0); } if(cmdargc < 2) readline("Host: ", host, sizeof(host)); else strncpy(host, cmdargv[1], sizeof(host)); if((servent = getservbyname("ftp", "tcp")) == (struct servent *)NULL) { fprintf(stderr, "ftp: Could not find ftp tcp service\n"); return(-1); } port = (tcpport_t)servent->s_port; hp = gethostbyname(host); if (hp == (struct hostent *)NULL) { hostip = (ipaddr_t)0; printf("Unresolved host %s\n", host); return(0); } else memcpy((char *) &hostip, (char *) hp->h_addr, hp->h_length); /* This HACK allows the server to establish data connections correctly */ /* when using the loopback device to talk to ourselves */ if(hostip == inet_addr("127.0.0.1")) hostip = myip; if((tcp_device = getenv("TCP_DEVICE")) == NULL) tcp_device = "/dev/tcp"; if((ftpcomm_fd = open(tcp_device, O_RDWR)) < 0) { perror("ftp: open error on tcp device"); return(-1); } tcpconf.nwtc_flags = NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; tcpconf.nwtc_remaddr = hostip; tcpconf.nwtc_remport = port; s = ioctl(ftpcomm_fd, NWIOSTCPCONF, &tcpconf); if(s < 0) { perror("ftp: ioctl error on NWIOSTCPCONF"); close(ftpcomm_fd); return(s); } tcpcopt.nwtcl_flags = 0; s = ioctl(ftpcomm_fd, NWIOTCPCONN, &tcpcopt); if(s < 0) { perror("ftp: ioctl error on NWIOTCPCONN"); close(ftpcomm_fd); return(s); } s = ioctl(ftpcomm_fd, NWIOGTCPCONF, &tcpconf); if(s < 0) { perror("ftp: ioctl error on NWIOGTCPCONF"); close(ftpcomm_fd); return(s); } fpcommin = fdopen(ftpcomm_fd, "r"); fpcommout = fdopen(ftpcomm_fd, "w"); s = DOgetreply(); if(s < 0) { fclose(fpcommin); fclose(fpcommout); close(ftpcomm_fd); return(s); } if(s != 220) { fclose(fpcommin); fclose(fpcommout); close(ftpcomm_fd); return(0); } linkopen = 1; return(s); } int DOclose() { if(!linkopen) { printf("You can't close a connection that isn't open.\n"); return(0); } fclose(fpcommin); fclose(fpcommout); close(ftpcomm_fd); linkopen = 0; loggedin = 0; return(0); } int DOquit() { int s; if(linkopen) { s = DOcommand("QUIT", ""); s = DOclose(); } printf("FTP done.\n"); exit(0); } void donothing(sig) int sig; { } int DOdata(datacom, file, direction, fd) char *datacom; char *file; int direction; /* RETR or STOR */ int fd; { nwio_tcpconf_t tcpconf; nwio_tcpcl_t tcplopt, tcpcopt; char *tcp_device; int ftpdata_fd; char *buff; ipaddr_t ripaddr; tcpport_t rport; static tcpport_t lport = HTONS(0xF000); int s; int i; int cs; int pfd[2]; char dummy; char port[32]; ripaddr = hostip; rport = HTONS(20); /* here we set up a connection to listen on if not passive mode */ /* otherwise we use this to connect for passive mode */ if((tcp_device = getenv("TCP_DEVICE")) == NULL) tcp_device = "/dev/tcp"; if((ftpdata_fd = open(tcp_device, O_RDWR)) < 0) { perror("ftp: open error on tcp device"); return(-1); } if(passive) { s = DOcommand("PASV", ""); if(s != 227) { close(ftpdata_fd); return(s); } /* decode host and port */ buff = reply; while(*buff && (*buff != '(')) buff++; buff++; ripaddr = (ipaddr_t)0; for(i = 0; i < 4; i++) { ripaddr = (ripaddr << 8) + (ipaddr_t)atoi(buff); if((buff = strchr(buff, ',')) == (char *)0) { printf("Could not parse PASV reply\n"); return(-1); } buff++; } rport = (tcpport_t)atoi(buff); if((buff = strchr(buff, ',')) == (char *)0) { printf("Could not parse PASV reply\n"); return(-1); } buff++; rport = (rport << 8) + (tcpport_t)atoi(buff); ripaddr = ntohl(ripaddr); rport = ntohs(rport); } for (;;) { tcpconf.nwtc_flags = NWTC_SET_RA | NWTC_SET_RP; if (passive || ntohs(lport) >= 0xF000) { tcpconf.nwtc_flags |= NWTC_LP_SEL; } else { /* For no good reason Sun hosts don't like it if they have to * connect to the same port twice in a short time... */ lport = htons(ntohs(lport) + 1); tcpconf.nwtc_flags |= NWTC_LP_SET; tcpconf.nwtc_locport = lport; } tcpconf.nwtc_remaddr = ripaddr; tcpconf.nwtc_remport = rport; s = ioctl(ftpdata_fd, NWIOSTCPCONF, &tcpconf); if(s < 0) { if (errno == EADDRINUSE) continue; perror("ftp: ioctl error on NWIOSTCPCONF"); close(ftpdata_fd); return(s); } break; } s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); if(s < 0) { perror("ftp: ioctl error on NWIOGTCPCONF"); close(ftpdata_fd); return(s); } lport = tcpconf.nwtc_locport; if(passive) { tcplopt.nwtcl_flags = 0; s = ioctl(ftpdata_fd, NWIOTCPCONN, &tcpcopt); if(s < 0) { perror("ftp: error on ioctl NWIOTCPCONN"); close(ftpdata_fd); return(0); } s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); if(s < 0) { perror("ftp: error on ioctl NWIOGTCPCONF"); close(ftpdata_fd); return(0); } } else { tcplopt.nwtcl_flags = 0; if (pipe(pfd) < 0) { perror("ftp: could not create a pipe"); return(s); } lpid = fork(); if(lpid < 0) { perror("ftp: could not fork listener"); close(ftpdata_fd); close(pfd[0]); close(pfd[1]); return(s); } else if(lpid == 0) { close(pfd[0]); signal(SIGALRM, donothing); alarm(15); close(pfd[1]); s = ioctl(ftpdata_fd, NWIOTCPLISTEN, &tcplopt); alarm(0); if(s < 0) if(errno == EINTR) exit(1); /* timed out */ else exit(-1); /* error */ else exit(0); /* connection made */ } /* Wait for the pipe to close, then the listener is ready (almost). */ close(pfd[1]); (void) read(pfd[0], &dummy, 1); close(pfd[0]); while(1) { signal(SIGALRM, donothing); alarm(1); s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); alarm(0); if(s == -1) break; } } #define hiword(x) ((u16_t)((x) >> 16)) #define loword(x) ((u16_t)(x & 0xffff)) #define hibyte(x) (((x) >> 8) & 0xff) #define lobyte(x) ((x) & 0xff) if(!passive) { sprintf(port, "%u,%u,%u,%u,%u,%u", hibyte(hiword(ntohl(myip))), lobyte(hiword(ntohl(myip))), hibyte(loword(ntohl(myip))), lobyte(loword(ntohl(myip))), hibyte(ntohs(lport)), lobyte(ntohs(lport))); s = DOcommand("PORT", port); if(s != 200) { close(ftpdata_fd); kill(lpid, SIGKILL); return(s); } } s = DOcommand(datacom, file); if(s == 125 || s == 150) { if(!passive) { while(1) { s = wait(&cs); if(s < 0 || s == lpid) break; } if(s < 0) { perror("wait error:"); close(ftpdata_fd); kill(lpid, SIGKILL); return(s); } if((cs & 0x00ff)) { printf("Child listener failed %04x\n", cs); close(ftpdata_fd); return(-1); } cs = (cs >> 8) & 0x00ff; if(cs == 1) { printf("Child listener timed out\n"); return(DOgetreply()); } else if(cs) { printf("Child listener returned %02x\n", cs); close(ftpdata_fd); return(-1); } } switch(direction) { case RETR: s = recvfile(fd, ftpdata_fd); break; case STOR: s = sendfile(fd, ftpdata_fd); break; } close(ftpdata_fd); s = DOgetreply(); } else { if(!passive) kill(lpid, SIGKILL); close(ftpdata_fd); } return(s); }