1*10296Ssam #ifndef lint 2*10296Ssam static char sccsid[] = "@(#)ftp.c 4.1 (Berkeley) 01/15/83"; 3*10296Ssam #endif 4*10296Ssam 5*10296Ssam #include <sys/param.h> 6*10296Ssam #include <sys/stat.h> 7*10296Ssam #include <sys/ioctl.h> 8*10296Ssam #include <sys/socket.h> 9*10296Ssam 10*10296Ssam #include <netinet/in.h> 11*10296Ssam 12*10296Ssam #include <stdio.h> 13*10296Ssam #include <signal.h> 14*10296Ssam #include <time.h> 15*10296Ssam #include <errno.h> 16*10296Ssam #include <netdb.h> 17*10296Ssam 18*10296Ssam #include "ftp.h" 19*10296Ssam #include "ftp_var.h" 20*10296Ssam 21*10296Ssam struct sockaddr_in hisctladdr; 22*10296Ssam struct sockaddr_in data_addr; 23*10296Ssam int data = -1; 24*10296Ssam int connected; 25*10296Ssam struct sockaddr_in myctladdr; 26*10296Ssam 27*10296Ssam FILE *cin, *cout; 28*10296Ssam FILE *dataconn(); 29*10296Ssam 30*10296Ssam struct hostent * 31*10296Ssam hookup(host, port) 32*10296Ssam char *host; 33*10296Ssam int port; 34*10296Ssam { 35*10296Ssam register struct hostent *hp; 36*10296Ssam int s; 37*10296Ssam 38*10296Ssam bzero((char *)&hisctladdr, sizeof (hisctladdr)); 39*10296Ssam hp = gethostbyname(host); 40*10296Ssam if (hp) { 41*10296Ssam hisctladdr.sin_family = hp->h_addrtype; 42*10296Ssam hostname = hp->h_name; 43*10296Ssam } else { 44*10296Ssam static struct hostent def; 45*10296Ssam static struct in_addr defaddr; 46*10296Ssam static char namebuf[128]; 47*10296Ssam int inet_addr(); 48*10296Ssam 49*10296Ssam defaddr.s_addr = inet_addr(host); 50*10296Ssam if (defaddr.s_addr == -1) { 51*10296Ssam fprintf(stderr, "%s: Unknown host.\n", host); 52*10296Ssam return (0); 53*10296Ssam } 54*10296Ssam strcpy(namebuf, host); 55*10296Ssam def.h_name = namebuf; 56*10296Ssam hostname = namebuf; 57*10296Ssam def.h_addr = (char *)&defaddr; 58*10296Ssam def.h_length = sizeof (struct in_addr); 59*10296Ssam def.h_addrtype = AF_INET; 60*10296Ssam def.h_aliases = 0; 61*10296Ssam hp = &def; 62*10296Ssam } 63*10296Ssam s = socket(hp->h_addrtype, SOCK_STREAM, 0, 0); 64*10296Ssam if (s < 0) { 65*10296Ssam perror("ftp: socket"); 66*10296Ssam return (0); 67*10296Ssam } 68*10296Ssam if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 69*10296Ssam perror("ftp: bind"); 70*10296Ssam goto bad; 71*10296Ssam } 72*10296Ssam bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length); 73*10296Ssam hisctladdr.sin_port = port; 74*10296Ssam if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) { 75*10296Ssam perror("ftp: connect"); 76*10296Ssam goto bad; 77*10296Ssam } 78*10296Ssam if (socketaddr(s, &myctladdr) < 0) { 79*10296Ssam perror("ftp: socketaddr"); 80*10296Ssam goto bad; 81*10296Ssam } 82*10296Ssam cin = fdopen(s, "r"); 83*10296Ssam cout = fdopen(s, "w"); 84*10296Ssam if (cin == NULL | cout == NULL) { 85*10296Ssam fprintf(stderr, "ftp: fdopen failed.\n"); 86*10296Ssam if (cin) 87*10296Ssam fclose(cin); 88*10296Ssam if (cout) 89*10296Ssam fclose(cout); 90*10296Ssam goto bad; 91*10296Ssam } 92*10296Ssam if (verbose) 93*10296Ssam printf("Connected to %s.\n", hp->h_name); 94*10296Ssam (void) getreply(0); /* read startup message from server */ 95*10296Ssam return (hp); 96*10296Ssam bad: 97*10296Ssam close(s); 98*10296Ssam return ((struct hostent *)0); 99*10296Ssam } 100*10296Ssam 101*10296Ssam login(hp) 102*10296Ssam struct hostent *hp; 103*10296Ssam { 104*10296Ssam char acct[80]; 105*10296Ssam char *user, *pass; 106*10296Ssam int n; 107*10296Ssam 108*10296Ssam user = pass = 0; 109*10296Ssam ruserpass(hp->h_name, &user, &pass); 110*10296Ssam n = command("USER %s", user); 111*10296Ssam if (n == CONTINUE) 112*10296Ssam n = command("PASS %s", pass); 113*10296Ssam if (n == CONTINUE) { 114*10296Ssam printf("Account: "); (void) fflush(stdout); 115*10296Ssam (void) fgets(acct, sizeof(acct) - 1, stdin); 116*10296Ssam acct[strlen(acct) - 1] = '\0'; 117*10296Ssam n = command("ACCT %s", acct); 118*10296Ssam } 119*10296Ssam if (n != COMPLETE) { 120*10296Ssam fprintf(stderr, "Login failed.\n"); 121*10296Ssam return (0); 122*10296Ssam } 123*10296Ssam return (1); 124*10296Ssam } 125*10296Ssam 126*10296Ssam /*VARARGS 1*/ 127*10296Ssam command(fmt, args) 128*10296Ssam char *fmt; 129*10296Ssam { 130*10296Ssam 131*10296Ssam if (debug) { 132*10296Ssam printf("---> "); 133*10296Ssam _doprnt(fmt, &args, stdout); 134*10296Ssam printf("\n"); 135*10296Ssam (void) fflush(stdout); 136*10296Ssam } 137*10296Ssam _doprnt(fmt, &args, cout); 138*10296Ssam fprintf(cout, "\r\n"); 139*10296Ssam (void) fflush(cout); 140*10296Ssam return (getreply(!strcmp(fmt, "QUIT"))); 141*10296Ssam } 142*10296Ssam 143*10296Ssam #include <ctype.h> 144*10296Ssam 145*10296Ssam getreply(expecteof) 146*10296Ssam int expecteof; 147*10296Ssam { 148*10296Ssam register char c, n; 149*10296Ssam register int code, dig; 150*10296Ssam int originalcode = 0, continuation = 0; 151*10296Ssam 152*10296Ssam for (;;) { 153*10296Ssam dig = n = code = 0; 154*10296Ssam while ((c = getc(cin)) != '\n') { 155*10296Ssam dig++; 156*10296Ssam if (c == EOF) { 157*10296Ssam if (expecteof) 158*10296Ssam return (0); 159*10296Ssam lostpeer(); 160*10296Ssam exit(1); 161*10296Ssam } 162*10296Ssam if (verbose && c != '\r' || 163*10296Ssam (n == '5' && dig > 4)) 164*10296Ssam putchar(c); 165*10296Ssam if (dig < 4 && isdigit(c)) 166*10296Ssam code = code * 10 + (c - '0'); 167*10296Ssam if (dig == 4 && c == '-') 168*10296Ssam continuation++; 169*10296Ssam if (n == 0) 170*10296Ssam n = c; 171*10296Ssam } 172*10296Ssam if (verbose || n == '5') 173*10296Ssam putchar(c); 174*10296Ssam if (continuation && code != originalcode) { 175*10296Ssam if (originalcode == 0) 176*10296Ssam originalcode = code; 177*10296Ssam continue; 178*10296Ssam } 179*10296Ssam if (empty(cin)) 180*10296Ssam return (n - '0'); 181*10296Ssam } 182*10296Ssam } 183*10296Ssam 184*10296Ssam empty(f) 185*10296Ssam FILE *f; 186*10296Ssam { 187*10296Ssam int mask; 188*10296Ssam struct timeval t; 189*10296Ssam 190*10296Ssam if (f->_cnt > 0) 191*10296Ssam return (0); 192*10296Ssam mask = (1 << fileno(f)); 193*10296Ssam t.tv_sec = t.tv_usec = 0; 194*10296Ssam (void) select(20, &mask, 0, 0, &t); 195*10296Ssam return (mask == 0); 196*10296Ssam } 197*10296Ssam 198*10296Ssam jmp_buf sendabort; 199*10296Ssam 200*10296Ssam abortsend() 201*10296Ssam { 202*10296Ssam 203*10296Ssam longjmp(sendabort, 1); 204*10296Ssam } 205*10296Ssam 206*10296Ssam sendrequest(cmd, local, remote) 207*10296Ssam char *cmd, *local, *remote; 208*10296Ssam { 209*10296Ssam FILE *fin, *dout, *popen(); 210*10296Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 211*10296Ssam register int bytes = 0; 212*10296Ssam register char c; 213*10296Ssam struct stat st; 214*10296Ssam struct timeval start, stop; 215*10296Ssam 216*10296Ssam closefunc = NULL; 217*10296Ssam if (setjmp(sendabort)) 218*10296Ssam goto bad; 219*10296Ssam oldintr = signal(SIGINT, abortsend); 220*10296Ssam if (strcmp(local, "-") == 0) 221*10296Ssam fin = stdin; 222*10296Ssam else if (*local == '|') { 223*10296Ssam fin = popen(local + 1, "r"); 224*10296Ssam if (fin == NULL) { 225*10296Ssam perror(local + 1); 226*10296Ssam goto bad; 227*10296Ssam } 228*10296Ssam closefunc = pclose; 229*10296Ssam } else { 230*10296Ssam fin = fopen(local, "r"); 231*10296Ssam if (fin == NULL) { 232*10296Ssam perror(local); 233*10296Ssam goto bad; 234*10296Ssam } 235*10296Ssam closefunc = fclose; 236*10296Ssam if (fstat(fileno(fin), &st) < 0 || 237*10296Ssam (st.st_mode&S_IFMT) != S_IFREG) { 238*10296Ssam fprintf(stderr, "%s: not a plain file.", local); 239*10296Ssam goto bad; 240*10296Ssam } 241*10296Ssam } 242*10296Ssam if (initconn()) 243*10296Ssam goto bad; 244*10296Ssam if (remote) { 245*10296Ssam if (command("%s %s", cmd, remote) != PRELIM) 246*10296Ssam goto bad; 247*10296Ssam } else 248*10296Ssam if (command("%s", cmd) != PRELIM) 249*10296Ssam goto bad; 250*10296Ssam dout = dataconn("w"); 251*10296Ssam if (dout == NULL) 252*10296Ssam goto bad; 253*10296Ssam gettimeofday(&start, (struct timezone *)0); 254*10296Ssam while ((c = getc(fin)) != EOF) { 255*10296Ssam if (type == TYPE_A && c == '\n') 256*10296Ssam putc('\r', dout); 257*10296Ssam putc(c, dout); 258*10296Ssam bytes++; 259*10296Ssam } 260*10296Ssam gettimeofday(&stop, (struct timezone *)0); 261*10296Ssam if (closefunc != NULL) 262*10296Ssam (*closefunc)(fin); 263*10296Ssam (void) fclose(dout); 264*10296Ssam if (ferror(fin)) { 265*10296Ssam perror(local); 266*10296Ssam goto done; 267*10296Ssam } 268*10296Ssam (void) getreply(0); 269*10296Ssam done: 270*10296Ssam signal(SIGINT, oldintr); 271*10296Ssam if (bytes > 0 && verbose) 272*10296Ssam ptransfer("sent", bytes, &start, &stop); 273*10296Ssam return; 274*10296Ssam bad: 275*10296Ssam if (data >= 0) 276*10296Ssam (void) close(data), data = -1; 277*10296Ssam if (closefunc != NULL && fin != NULL) 278*10296Ssam (*closefunc)(fin); 279*10296Ssam goto done; 280*10296Ssam } 281*10296Ssam 282*10296Ssam jmp_buf recvabort; 283*10296Ssam 284*10296Ssam abortrecv() 285*10296Ssam { 286*10296Ssam 287*10296Ssam longjmp(recvabort, 1); 288*10296Ssam } 289*10296Ssam 290*10296Ssam recvrequest(cmd, local, remote) 291*10296Ssam char *cmd, *local, *remote; 292*10296Ssam { 293*10296Ssam FILE *fout, *din, *popen(); 294*10296Ssam int (*closefunc)(), pclose(), fclose(), (*oldintr)(); 295*10296Ssam register char c; 296*10296Ssam register int bytes = 0; 297*10296Ssam struct timeval start, stop; 298*10296Ssam 299*10296Ssam closefunc = NULL; 300*10296Ssam if (setjmp(recvabort)) 301*10296Ssam goto bad; 302*10296Ssam oldintr = signal(SIGINT, abortrecv); 303*10296Ssam if (strcmp(local, "-") && *local != '|') 304*10296Ssam if (access(local, 2) < 0) { 305*10296Ssam char *dir = rindex(local, '/'); 306*10296Ssam 307*10296Ssam if (dir != NULL) 308*10296Ssam *dir = 0; 309*10296Ssam if (access(dir ? dir : ".", 2) < 0) { 310*10296Ssam perror(local); 311*10296Ssam goto bad; 312*10296Ssam } 313*10296Ssam if (dir != NULL) 314*10296Ssam *dir = '/'; 315*10296Ssam } 316*10296Ssam if (initconn()) 317*10296Ssam goto bad; 318*10296Ssam if (remote) { 319*10296Ssam if (command("%s %s", cmd, remote) != PRELIM) 320*10296Ssam goto bad; 321*10296Ssam } else 322*10296Ssam if (command("%s", cmd) != PRELIM) 323*10296Ssam goto bad; 324*10296Ssam if (strcmp(local, "-") == 0) 325*10296Ssam fout = stdout; 326*10296Ssam else if (*local == '|') { 327*10296Ssam fout = popen(local + 1, "w"); 328*10296Ssam closefunc = pclose; 329*10296Ssam } else { 330*10296Ssam fout = fopen(local, "w"); 331*10296Ssam closefunc = fclose; 332*10296Ssam } 333*10296Ssam if (fout == NULL) { 334*10296Ssam perror(local + 1); 335*10296Ssam goto bad; 336*10296Ssam } 337*10296Ssam din = dataconn("r"); 338*10296Ssam if (din == NULL) 339*10296Ssam goto bad; 340*10296Ssam gettimeofday(&start, (struct timezone *)0); 341*10296Ssam while ((c = getc(din)) != EOF) { 342*10296Ssam if (c != '\r' || type != TYPE_A) 343*10296Ssam putc(c, fout); 344*10296Ssam bytes++; 345*10296Ssam if (ferror(fout)) { 346*10296Ssam perror(local); 347*10296Ssam while (c = getc(din) != EOF) 348*10296Ssam bytes++; 349*10296Ssam break; 350*10296Ssam } 351*10296Ssam } 352*10296Ssam gettimeofday(&stop, (struct timezone *)0); 353*10296Ssam (void) fclose(din); 354*10296Ssam if (closefunc != NULL) 355*10296Ssam (*closefunc)(fout); 356*10296Ssam (void) getreply(0); 357*10296Ssam done: 358*10296Ssam signal(SIGINT, oldintr); 359*10296Ssam if (bytes > 0 && verbose) 360*10296Ssam ptransfer("received", bytes, &start, &stop); 361*10296Ssam return; 362*10296Ssam bad: 363*10296Ssam if (data >= 0) 364*10296Ssam (void) close(data), data = -1; 365*10296Ssam if (closefunc != NULL && fout != NULL) 366*10296Ssam (*closefunc)(fout); 367*10296Ssam goto done; 368*10296Ssam } 369*10296Ssam 370*10296Ssam /* 371*10296Ssam * Need to start a listen on the data channel 372*10296Ssam * before we send the command, otherwise the 373*10296Ssam * server's connect may fail. 374*10296Ssam */ 375*10296Ssam initconn() 376*10296Ssam { 377*10296Ssam register char *p, *a; 378*10296Ssam int result; 379*10296Ssam 380*10296Ssam data_addr = myctladdr; 381*10296Ssam data_addr.sin_port = 0; /* let system pick one */ 382*10296Ssam data = socket(AF_INET, SOCK_STREAM, 0, 0); 383*10296Ssam if (data < 0) { 384*10296Ssam perror("ftp: socket"); 385*10296Ssam return (1); 386*10296Ssam } 387*10296Ssam if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) { 388*10296Ssam perror("ftp: bind"); 389*10296Ssam goto bad; 390*10296Ssam } 391*10296Ssam #ifdef notdef 392*10296Ssam if (options & SO_DEBUG && 393*10296Ssam setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 394*10296Ssam perror("ftp: setsockopt (ignored)"); 395*10296Ssam #endif 396*10296Ssam if (socketaddr(data, &data_addr) < 0) { 397*10296Ssam perror("ftp: socketaddr"); 398*10296Ssam goto bad; 399*10296Ssam } 400*10296Ssam if (listen(data, 1) < 0) { 401*10296Ssam perror("ftp: listen"); 402*10296Ssam goto bad; 403*10296Ssam } 404*10296Ssam a = (char *)&data_addr.sin_addr; 405*10296Ssam p = (char *)&data_addr.sin_port; 406*10296Ssam #define UC(b) (((int)b)&0xff) 407*10296Ssam result = 408*10296Ssam command("PORT %d,%d,%d,%d,%d,%d", 409*10296Ssam UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 410*10296Ssam UC(p[0]), UC(p[1])); 411*10296Ssam return (result != COMPLETE); 412*10296Ssam bad: 413*10296Ssam (void) close(data), data = -1; 414*10296Ssam return (1); 415*10296Ssam } 416*10296Ssam 417*10296Ssam FILE * 418*10296Ssam dataconn(mode) 419*10296Ssam char *mode; 420*10296Ssam { 421*10296Ssam struct sockaddr_in from; 422*10296Ssam int s, fromlen = sizeof (from); 423*10296Ssam 424*10296Ssam s = accept(data, &from, &fromlen, 0); 425*10296Ssam if (s < 0) { 426*10296Ssam perror("ftp: accept"); 427*10296Ssam (void) close(data), data = -1; 428*10296Ssam return (NULL); 429*10296Ssam } 430*10296Ssam (void) close(data); 431*10296Ssam data = s; 432*10296Ssam return (fdopen(data, mode)); 433*10296Ssam } 434*10296Ssam 435*10296Ssam ptransfer(direction, bytes, t0, t1) 436*10296Ssam char *direction; 437*10296Ssam int bytes; 438*10296Ssam struct timeval *t0, *t1; 439*10296Ssam { 440*10296Ssam struct timeval td; 441*10296Ssam int ms, bs; 442*10296Ssam 443*10296Ssam tvsub(&td, t1, t0); 444*10296Ssam ms = (td.tv_sec * 1000) + (td.tv_usec / 1000); 445*10296Ssam #define nz(x) ((x) == 0 ? 1 : (x)) 446*10296Ssam bs = ((bytes * NBBY * 1000) / nz(ms)) / NBBY; 447*10296Ssam printf("%d bytes %s in %d.%02d seconds (%d.%01d Kbytes/s)\n", 448*10296Ssam bytes, direction, td.tv_sec, td.tv_usec / 10000, 449*10296Ssam bs / 1024, (((bs % 1024) * 10) + 1023) / 1024); 450*10296Ssam } 451*10296Ssam 452*10296Ssam tvadd(tsum, t0) 453*10296Ssam struct timeval *tsum, *t0; 454*10296Ssam { 455*10296Ssam 456*10296Ssam tsum->tv_sec += t0->tv_sec; 457*10296Ssam tsum->tv_usec += t0->tv_usec; 458*10296Ssam if (tsum->tv_usec > 1000000) 459*10296Ssam tsum->tv_sec++, tsum->tv_usec -= 1000000; 460*10296Ssam } 461*10296Ssam 462*10296Ssam tvsub(tdiff, t1, t0) 463*10296Ssam struct timeval *tdiff, *t1, *t0; 464*10296Ssam { 465*10296Ssam 466*10296Ssam tdiff->tv_sec = t1->tv_sec - t0->tv_sec; 467*10296Ssam tdiff->tv_usec = t1->tv_usec - t0->tv_usec; 468*10296Ssam if (tdiff->tv_usec < 0) 469*10296Ssam tdiff->tv_sec--, tdiff->tv_usec += 1000000; 470*10296Ssam } 471