1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. 4: * 5: * Redistribution and use in source and binary forms are permitted 6: * provided that: (1) source distributions retain this entire copyright 7: * notice and comment, and (2) distributions including binaries display 8: * the following acknowledgement: ``This product includes software 9: * developed by the University of California, Berkeley and its contributors'' 10: * in the documentation or other materials provided with the distribution 11: * and in all advertising materials mentioning features or use of this 12: * software. Neither the name of the University nor the names of its 13: * contributors may be used to endorse or promote products derived 14: * from this software without specific prior written permission. 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 16: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 17: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18: */ 19: 20: #if defined(DOSCCS) && !defined(lint) 21: static char sccsid[] = "@(#)tftp.c 5.9.1 (2.11BSD GTE) 1/1/94"; 22: #endif 23: 24: /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 25: 26: /* 27: * TFTP User Program -- Protocol Machines 28: */ 29: #include <sys/types.h> 30: #include <sys/socket.h> 31: #include <sys/time.h> 32: 33: #include <netinet/in.h> 34: 35: #include <arpa/tftp.h> 36: 37: #include <signal.h> 38: #include <stdio.h> 39: #include <errno.h> 40: #include <setjmp.h> 41: 42: extern int errno; 43: 44: extern struct sockaddr_in sin; /* filled in by main */ 45: extern int f; /* the opened socket */ 46: extern int trace; 47: extern int verbose; 48: extern int rexmtval; 49: extern int maxtimeout; 50: 51: #define PKTSIZE SEGSIZE+4 52: char ackbuf[PKTSIZE]; 53: int timeout; 54: jmp_buf toplevel; 55: jmp_buf timeoutbuf; 56: 57: timer() 58: { 59: 60: timeout += rexmtval; 61: if (timeout >= maxtimeout) { 62: printf("Transfer timed out.\n"); 63: longjmp(toplevel, -1); 64: } 65: longjmp(timeoutbuf, 1); 66: } 67: 68: /* 69: * Send the requested file. 70: */ 71: sendfile(fd, name, mode) 72: int fd; 73: char *name; 74: char *mode; 75: { 76: register struct tftphdr *ap; /* data and ack packets */ 77: struct tftphdr *r_init(), *dp; 78: register int block = 0, size, n; 79: u_long amount = 0; 80: struct sockaddr_in from; 81: int fromlen; 82: int convert; /* true if doing nl->crlf conversion */ 83: FILE *file; 84: 85: startclock(); /* start stat's clock */ 86: dp = r_init(); /* reset fillbuf/read-ahead code */ 87: ap = (struct tftphdr *)ackbuf; 88: file = fdopen(fd, "r"); 89: convert = !strcmp(mode, "netascii"); 90: 91: signal(SIGALRM, timer); 92: do { 93: if (block == 0) 94: size = makerequest(WRQ, name, dp, mode) - 4; 95: else { 96: /* size = read(fd, dp->th_data, SEGSIZE); */ 97: size = readit(file, &dp, convert); 98: if (size < 0) { 99: nak(errno + 100); 100: break; 101: } 102: dp->th_opcode = htons((u_short)DATA); 103: dp->th_block = htons((u_short)block); 104: } 105: timeout = 0; 106: (void) setjmp(timeoutbuf); 107: send_data: 108: if (trace) 109: tpacket("sent", dp, size + 4); 110: n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 111: if (n != size + 4) { 112: perror("tftp: sendto"); 113: goto abort; 114: } 115: read_ahead(file, convert); 116: for ( ; ; ) { 117: alarm(rexmtval); 118: do { 119: fromlen = sizeof (from); 120: n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 121: (caddr_t)&from, &fromlen); 122: } while (n <= 0); 123: alarm(0); 124: if (n < 0) { 125: perror("tftp: recvfrom"); 126: goto abort; 127: } 128: sin.sin_port = from.sin_port; /* added */ 129: if (trace) 130: tpacket("received", ap, n); 131: /* should verify packet came from server */ 132: ap->th_opcode = ntohs(ap->th_opcode); 133: ap->th_block = ntohs(ap->th_block); 134: if (ap->th_opcode == ERROR) { 135: printf("Error code %d: %s\n", ap->th_code, 136: ap->th_msg); 137: goto abort; 138: } 139: if (ap->th_opcode == ACK) { 140: int j; 141: 142: if (ap->th_block == block) { 143: break; 144: } 145: /* On an error, try to synchronize 146: * both sides. 147: */ 148: j = synchnet(f); 149: if (j && trace) { 150: printf("discarded %d packets\n", 151: j); 152: } 153: if (ap->th_block == (block-1)) { 154: goto send_data; 155: } 156: } 157: } 158: if (block > 0) 159: amount += size; 160: block++; 161: } while (size == SEGSIZE || block == 1); 162: abort: 163: fclose(file); 164: stopclock(); 165: if (amount > 0) 166: printstats("Sent", amount); 167: } 168: 169: /* 170: * Receive a file. 171: */ 172: recvfile(fd, name, mode) 173: int fd; 174: char *name; 175: char *mode; 176: { 177: register struct tftphdr *ap; 178: struct tftphdr *dp, *w_init(); 179: register int block = 1, n, size; 180: u_long amount = 0; 181: struct sockaddr_in from; 182: int fromlen, firsttrip = 1; 183: FILE *file; 184: int convert; /* true if converting crlf -> lf */ 185: 186: startclock(); 187: dp = w_init(); 188: ap = (struct tftphdr *)ackbuf; 189: file = fdopen(fd, "w"); 190: convert = !strcmp(mode, "netascii"); 191: 192: signal(SIGALRM, timer); 193: do { 194: if (firsttrip) { 195: size = makerequest(RRQ, name, ap, mode); 196: firsttrip = 0; 197: } else { 198: ap->th_opcode = htons((u_short)ACK); 199: ap->th_block = htons((u_short)(block)); 200: size = 4; 201: block++; 202: } 203: timeout = 0; 204: (void) setjmp(timeoutbuf); 205: send_ack: 206: if (trace) 207: tpacket("sent", ap, size); 208: if (sendto(f, ackbuf, size, 0, (caddr_t)&sin, 209: sizeof (sin)) != size) { 210: alarm(0); 211: perror("tftp: sendto"); 212: goto abort; 213: } 214: write_behind(file, convert); 215: for ( ; ; ) { 216: alarm(rexmtval); 217: do { 218: fromlen = sizeof (from); 219: n = recvfrom(f, dp, PKTSIZE, 0, 220: (caddr_t)&from, &fromlen); 221: } while (n <= 0); 222: alarm(0); 223: if (n < 0) { 224: perror("tftp: recvfrom"); 225: goto abort; 226: } 227: sin.sin_port = from.sin_port; /* added */ 228: if (trace) 229: tpacket("received", dp, n); 230: /* should verify client address */ 231: dp->th_opcode = ntohs(dp->th_opcode); 232: dp->th_block = ntohs(dp->th_block); 233: if (dp->th_opcode == ERROR) { 234: printf("Error code %d: %s\n", dp->th_code, 235: dp->th_msg); 236: goto abort; 237: } 238: if (dp->th_opcode == DATA) { 239: int j; 240: 241: if (dp->th_block == block) { 242: break; /* have next packet */ 243: } 244: /* On an error, try to synchronize 245: * both sides. 246: */ 247: j = synchnet(f); 248: if (j && trace) { 249: printf("discarded %d packets\n", j); 250: } 251: if (dp->th_block == (block-1)) { 252: goto send_ack; /* resend ack */ 253: } 254: } 255: } 256: /* size = write(fd, dp->th_data, n - 4); */ 257: size = writeit(file, &dp, n - 4, convert); 258: if (size < 0) { 259: nak(errno + 100); 260: break; 261: } 262: amount += size; 263: } while (size == SEGSIZE); 264: abort: /* ok to ack, since user */ 265: ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 266: ap->th_block = htons((u_short)block); 267: (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin)); 268: write_behind(file, convert); /* flush last buffer */ 269: fclose(file); 270: stopclock(); 271: if (amount > 0) 272: printstats("Received", amount); 273: } 274: 275: makerequest(request, name, tp, mode) 276: int request; 277: char *name, *mode; 278: struct tftphdr *tp; 279: { 280: register char *cp; 281: 282: tp->th_opcode = htons((u_short)request); 283: cp = tp->th_stuff; 284: strcpy(cp, name); 285: cp += strlen(name); 286: *cp++ = '\0'; 287: strcpy(cp, mode); 288: cp += strlen(mode); 289: *cp++ = '\0'; 290: return (cp - (char *)tp); 291: } 292: 293: struct errmsg { 294: int e_code; 295: char *e_msg; 296: } errmsgs[] = { 297: { EUNDEF, "Undefined error code" }, 298: { ENOTFOUND, "File not found" }, 299: { EACCESS, "Access violation" }, 300: { ENOSPACE, "Disk full or allocation exceeded" }, 301: { EBADOP, "Illegal TFTP operation" }, 302: { EBADID, "Unknown transfer ID" }, 303: { EEXISTS, "File already exists" }, 304: { ENOUSER, "No such user" }, 305: { -1, 0 } 306: }; 307: 308: /* 309: * Send a nak packet (error message). 310: * Error code passed in is one of the 311: * standard TFTP codes, or a UNIX errno 312: * offset by 100. 313: */ 314: nak(error) 315: int error; 316: { 317: register struct errmsg *pe; 318: register struct tftphdr *tp; 319: int length; 320: char *strerror(); 321: 322: tp = (struct tftphdr *)ackbuf; 323: tp->th_opcode = htons((u_short)ERROR); 324: tp->th_code = htons((u_short)error); 325: for (pe = errmsgs; pe->e_code >= 0; pe++) 326: if (pe->e_code == error) 327: break; 328: if (pe->e_code < 0) { 329: pe->e_msg = strerror(error - 100); 330: tp->th_code = EUNDEF; 331: } 332: strcpy(tp->th_msg, pe->e_msg); 333: length = strlen(pe->e_msg) + 4; 334: if (trace) 335: tpacket("sent", tp, length); 336: if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length) 337: perror("nak"); 338: } 339: 340: tpacket(s, tp, n) 341: char *s; 342: struct tftphdr *tp; 343: int n; 344: { 345: static char *opcodes[] = 346: { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 347: register char *cp, *file; 348: u_short op = ntohs(tp->th_opcode); 349: char *index(); 350: 351: if (op < RRQ || op > ERROR) 352: printf("%s opcode=%x ", s, op); 353: else 354: printf("%s %s ", s, opcodes[op]); 355: switch (op) { 356: 357: case RRQ: 358: case WRQ: 359: n -= 2; 360: file = cp = tp->th_stuff; 361: cp = index(cp, '\0'); 362: printf("<file=%s, mode=%s>\n", file, cp + 1); 363: break; 364: 365: case DATA: 366: printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 367: break; 368: 369: case ACK: 370: printf("<block=%d>\n", ntohs(tp->th_block)); 371: break; 372: 373: case ERROR: 374: printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 375: break; 376: } 377: } 378: 379: struct timeval tstart; 380: struct timeval tstop; 381: struct timezone zone; 382: 383: startclock() { 384: gettimeofday(&tstart, &zone); 385: } 386: 387: stopclock() { 388: gettimeofday(&tstop, &zone); 389: } 390: 391: printstats(direction, amount) 392: char *direction; 393: u_long amount; 394: { 395: double delta; 396: /* compute delta in 1/10's second units */ 397: delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000L)) - 398: ((tstart.tv_sec*10.)+(tstart.tv_usec/100000L)); 399: delta = delta/10.; /* back to seconds */ 400: printf("%s %ld bytes in %.1f seconds", direction, amount, delta); 401: if (verbose) 402: printf(" [%.0f bits/sec]", (amount*8.)/delta); 403: putchar('\n'); 404: }