122404Sdist /* 2*26094Sminshall * Copyright (c) 1985 Regents of the University of California. 322404Sdist * All rights reserved. The Berkeley software License Agreement 422404Sdist * specifies the terms and conditions for redistribution. 522404Sdist */ 622404Sdist 714554Ssam #ifndef lint 8*26094Sminshall static char sccsid[] = "@(#)tftp.c 5.2 (Berkeley) 02/06/86"; 922404Sdist #endif not lint 107770Ssam 11*26094Sminshall /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 12*26094Sminshall 137770Ssam /* 147770Ssam * TFTP User Program -- Protocol Machines 157770Ssam */ 167770Ssam #include <sys/types.h> 177770Ssam #include <sys/socket.h> 18*26094Sminshall #include <sys/time.h> 199220Ssam 209220Ssam #include <netinet/in.h> 21*26094Sminshall 2212399Ssam #include <arpa/tftp.h> 2312399Ssam 247770Ssam #include <signal.h> 257770Ssam #include <stdio.h> 267770Ssam #include <errno.h> 277770Ssam #include <setjmp.h> 289220Ssam 297770Ssam extern int errno; 30*26094Sminshall 31*26094Sminshall extern struct sockaddr_in sin; /* filled in by main */ 32*26094Sminshall extern int f; /* the opened socket */ 33*26094Sminshall extern int trace; 34*26094Sminshall extern int verbose; 35*26094Sminshall extern int rexmtval; 36*26094Sminshall extern int maxtimeout; 37*26094Sminshall 38*26094Sminshall #define PKTSIZE SEGSIZE+4 39*26094Sminshall char ackbuf[PKTSIZE]; 407770Ssam int timeout; 417770Ssam jmp_buf toplevel; 4213018Ssam jmp_buf timeoutbuf; 437770Ssam 447770Ssam timer() 457770Ssam { 4613018Ssam 4713018Ssam timeout += rexmtval; 4813018Ssam if (timeout >= maxtimeout) { 497770Ssam printf("Transfer timed out.\n"); 507770Ssam longjmp(toplevel, -1); 517770Ssam } 5213018Ssam longjmp(timeoutbuf, 1); 537770Ssam } 547770Ssam 557770Ssam /* 567770Ssam * Send the requested file. 577770Ssam */ 58*26094Sminshall sendfile(fd, name, mode) 597770Ssam int fd; 607770Ssam char *name; 61*26094Sminshall char *mode; 627770Ssam { 63*26094Sminshall register struct tftphdr *ap; /* data and ack packets */ 64*26094Sminshall struct tftphdr *r_init(), *dp; 65*26094Sminshall register int block = 0, size, n; 66*26094Sminshall register unsigned long amount = 0; 67*26094Sminshall struct sockaddr_in from; 68*26094Sminshall int fromlen; 69*26094Sminshall int convert; /* true if doing nl->crlf conversion */ 70*26094Sminshall FILE *file; 717770Ssam 72*26094Sminshall startclock(); /* start stat's clock */ 73*26094Sminshall dp = r_init(); /* reset fillbuf/read-ahead code */ 74*26094Sminshall ap = (struct tftphdr *)ackbuf; 75*26094Sminshall file = fdopen(fd, "r"); 76*26094Sminshall convert = !strcmp(mode, "netascii"); 77*26094Sminshall 7813018Ssam signal(SIGALRM, timer); 797770Ssam do { 8013018Ssam if (block == 0) 81*26094Sminshall size = makerequest(WRQ, name, dp, mode) - 4; 8213018Ssam else { 83*26094Sminshall /* size = read(fd, dp->th_data, SEGSIZE); */ 84*26094Sminshall size = readit(file, &dp, convert); 857770Ssam if (size < 0) { 86*26094Sminshall nak(errno + 100); 877770Ssam break; 887770Ssam } 89*26094Sminshall dp->th_opcode = htons((u_short)DATA); 90*26094Sminshall dp->th_block = htons((u_short)block); 917770Ssam } 927770Ssam timeout = 0; 9313018Ssam (void) setjmp(timeoutbuf); 947770Ssam if (trace) 95*26094Sminshall tpacket("sent", dp, size + 4); 96*26094Sminshall n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 979220Ssam if (n != size + 4) { 9813018Ssam perror("tftp: sendto"); 99*26094Sminshall goto abort; 1007770Ssam } 101*26094Sminshall read_ahead(file, convert); 10213018Ssam do { 10313018Ssam alarm(rexmtval); 10413018Ssam do { 10513018Ssam fromlen = sizeof (from); 106*26094Sminshall n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 10713018Ssam (caddr_t)&from, &fromlen); 10813018Ssam } while (n <= 0); 1097770Ssam alarm(0); 11013018Ssam if (n < 0) { 11113018Ssam perror("tftp: recvfrom"); 112*26094Sminshall goto abort; 11313018Ssam } 114*26094Sminshall sin.sin_port = from.sin_port; /* added */ 11513018Ssam if (trace) 116*26094Sminshall tpacket("received", ap, n); 11713018Ssam /* should verify packet came from server */ 118*26094Sminshall ap->th_opcode = ntohs(ap->th_opcode); 119*26094Sminshall ap->th_block = ntohs(ap->th_block); 120*26094Sminshall if (ap->th_opcode == ERROR) { 121*26094Sminshall printf("Error code %d: %s\n", ap->th_code, 122*26094Sminshall ap->th_msg); 123*26094Sminshall goto abort; 12413018Ssam } 125*26094Sminshall } while (ap->th_opcode != ACK || block != ap->th_block); 1267770Ssam if (block > 0) 1277770Ssam amount += size; 1287770Ssam block++; 1297770Ssam } while (size == SEGSIZE || block == 1); 130*26094Sminshall abort: 131*26094Sminshall fclose(file); 132*26094Sminshall stopclock(); 133*26094Sminshall if (amount > 0) 134*26094Sminshall printstats("Sent", amount); 1357770Ssam } 1367770Ssam 1377770Ssam /* 1387770Ssam * Receive a file. 1397770Ssam */ 140*26094Sminshall recvfile(fd, name, mode) 1417770Ssam int fd; 1427770Ssam char *name; 143*26094Sminshall char *mode; 1447770Ssam { 145*26094Sminshall register struct tftphdr *ap; 146*26094Sminshall struct tftphdr *dp, *w_init(); 147*26094Sminshall register int block = 1, n, size; 148*26094Sminshall unsigned long amount = 0; 149*26094Sminshall struct sockaddr_in from; 150*26094Sminshall int fromlen, firsttrip = 1; 151*26094Sminshall FILE *file; 152*26094Sminshall int convert; /* true if converting crlf -> lf */ 1537770Ssam 154*26094Sminshall startclock(); 155*26094Sminshall dp = w_init(); 156*26094Sminshall ap = (struct tftphdr *)ackbuf; 157*26094Sminshall file = fdopen(fd, "w"); 158*26094Sminshall convert = !strcmp(mode, "netascii"); 159*26094Sminshall 16013018Ssam signal(SIGALRM, timer); 1617770Ssam do { 16213018Ssam if (firsttrip) { 163*26094Sminshall size = makerequest(RRQ, name, ap, mode); 16413018Ssam firsttrip = 0; 16513018Ssam } else { 166*26094Sminshall ap->th_opcode = htons((u_short)ACK); 167*26094Sminshall ap->th_block = htons((u_short)(block)); 16813018Ssam size = 4; 16913018Ssam block++; 17013018Ssam } 1717770Ssam timeout = 0; 17213018Ssam (void) setjmp(timeoutbuf); 173*26094Sminshall send_ack: 1747770Ssam if (trace) 175*26094Sminshall tpacket("sent", ap, size); 176*26094Sminshall if (sendto(f, ackbuf, size, 0, (caddr_t)&sin, 177*26094Sminshall sizeof (sin)) != size) { 17813018Ssam alarm(0); 17913018Ssam perror("tftp: sendto"); 180*26094Sminshall goto abort; 1817770Ssam } 182*26094Sminshall write_behind(file, convert); 183*26094Sminshall for ( ; ; ) { 18413018Ssam alarm(rexmtval); 185*26094Sminshall do { 18616384Ssam fromlen = sizeof (from); 187*26094Sminshall n = recvfrom(f, dp, PKTSIZE, 0, 18813018Ssam (caddr_t)&from, &fromlen); 18916384Ssam } while (n <= 0); 1907770Ssam alarm(0); 19113018Ssam if (n < 0) { 19213018Ssam perror("tftp: recvfrom"); 193*26094Sminshall goto abort; 19413018Ssam } 195*26094Sminshall sin.sin_port = from.sin_port; /* added */ 196*26094Sminshall if (trace) 197*26094Sminshall tpacket("received", dp, n); 198*26094Sminshall /* should verify client address */ 199*26094Sminshall dp->th_opcode = ntohs(dp->th_opcode); 200*26094Sminshall dp->th_block = ntohs(dp->th_block); 201*26094Sminshall if (dp->th_opcode == ERROR) { 202*26094Sminshall printf("Error code %d: %s\n", dp->th_code, 203*26094Sminshall dp->th_msg); 204*26094Sminshall goto abort; 20516384Ssam } 206*26094Sminshall if (dp->th_opcode == DATA) { 207*26094Sminshall if (dp->th_block == block) 208*26094Sminshall break; /* have next packet */ 209*26094Sminshall if (dp->th_block == (block-1)) 210*26094Sminshall goto send_ack; /* resend ack */ 21116384Ssam } 212*26094Sminshall } 213*26094Sminshall /* size = write(fd, dp->th_data, n - 4); */ 214*26094Sminshall size = writeit(file, &dp, n - 4, convert); 2157770Ssam if (size < 0) { 216*26094Sminshall nak(errno + 100); 217*26094Sminshall break; 2187770Ssam } 2197770Ssam amount += size; 2207770Ssam } while (size == SEGSIZE); 221*26094Sminshall abort: /* ok to ack, since user */ 222*26094Sminshall ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 223*26094Sminshall ap->th_block = htons((u_short)block); 224*26094Sminshall (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin)); 225*26094Sminshall write_behind(file, convert); /* flush last buffer */ 226*26094Sminshall fclose(file); 227*26094Sminshall stopclock(); 228*26094Sminshall if (amount > 0) 229*26094Sminshall printstats("Received", amount); 2307770Ssam } 2317770Ssam 232*26094Sminshall makerequest(request, name, tp, mode) 2337770Ssam int request; 234*26094Sminshall char *name, *mode; 235*26094Sminshall struct tftphdr *tp; 2367770Ssam { 2377770Ssam register char *cp; 2387770Ssam 239*26094Sminshall tp->th_opcode = htons((u_short)request); 240*26094Sminshall cp = tp->th_stuff; 241*26094Sminshall strcpy(cp, name); 242*26094Sminshall cp += strlen(name); 2437770Ssam *cp++ = '\0'; 2447770Ssam strcpy(cp, mode); 245*26094Sminshall cp += strlen(mode); 2467770Ssam *cp++ = '\0'; 247*26094Sminshall return (cp - (char *)tp); 2487770Ssam } 2497770Ssam 2507770Ssam struct errmsg { 2517770Ssam int e_code; 2527770Ssam char *e_msg; 2537770Ssam } errmsgs[] = { 2547770Ssam { EUNDEF, "Undefined error code" }, 2557770Ssam { ENOTFOUND, "File not found" }, 2567770Ssam { EACCESS, "Access violation" }, 2577770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2587770Ssam { EBADOP, "Illegal TFTP operation" }, 2597770Ssam { EBADID, "Unknown transfer ID" }, 2607770Ssam { EEXISTS, "File already exists" }, 2617770Ssam { ENOUSER, "No such user" }, 2627770Ssam { -1, 0 } 2637770Ssam }; 2647770Ssam 2657770Ssam /* 2667770Ssam * Send a nak packet (error message). 2677770Ssam * Error code passed in is one of the 2687770Ssam * standard TFTP codes, or a UNIX errno 2697770Ssam * offset by 100. 2707770Ssam */ 271*26094Sminshall nak(error) 2727770Ssam int error; 2737770Ssam { 274*26094Sminshall register struct tftphdr *tp; 2757770Ssam int length; 2767770Ssam register struct errmsg *pe; 2777770Ssam extern char *sys_errlist[]; 2787770Ssam 279*26094Sminshall tp = (struct tftphdr *)ackbuf; 280*26094Sminshall tp->th_opcode = htons((u_short)ERROR); 281*26094Sminshall tp->th_code = htons((u_short)error); 2827770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 2837770Ssam if (pe->e_code == error) 2847770Ssam break; 285*26094Sminshall if (pe->e_code < 0) { 2867770Ssam pe->e_msg = sys_errlist[error - 100]; 287*26094Sminshall tp->th_code = EUNDEF; 288*26094Sminshall } 289*26094Sminshall strcpy(tp->th_msg, pe->e_msg); 2907770Ssam length = strlen(pe->e_msg) + 4; 2917770Ssam if (trace) 292*26094Sminshall tpacket("sent", tp, length); 293*26094Sminshall if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length) 294*26094Sminshall perror("nak"); 2957770Ssam } 2967770Ssam 297*26094Sminshall tpacket(s, tp, n) 2987770Ssam struct tftphdr *tp; 2997770Ssam int n; 3007770Ssam { 3017770Ssam static char *opcodes[] = 3027770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3037770Ssam register char *cp, *file; 3047770Ssam u_short op = ntohs(tp->th_opcode); 3057770Ssam char *index(); 3067770Ssam 3077770Ssam if (op < RRQ || op > ERROR) 308*26094Sminshall printf("%s opcode=%x ", s, op); 3097770Ssam else 310*26094Sminshall printf("%s %s ", s, opcodes[op]); 3117770Ssam switch (op) { 3127770Ssam 3137770Ssam case RRQ: 3147770Ssam case WRQ: 3157770Ssam n -= 2; 3167770Ssam file = cp = tp->th_stuff; 3177770Ssam cp = index(cp, '\0'); 3187770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3197770Ssam break; 3207770Ssam 3217770Ssam case DATA: 3227770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3237770Ssam break; 3247770Ssam 3257770Ssam case ACK: 3267770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3277770Ssam break; 3287770Ssam 3297770Ssam case ERROR: 3307770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3317770Ssam break; 3327770Ssam } 3337770Ssam } 334*26094Sminshall 335*26094Sminshall struct timeval tstart; 336*26094Sminshall struct timeval tstop; 337*26094Sminshall struct timezone zone; 338*26094Sminshall 339*26094Sminshall startclock() { 340*26094Sminshall gettimeofday(&tstart, &zone); 341*26094Sminshall } 342*26094Sminshall 343*26094Sminshall stopclock() { 344*26094Sminshall gettimeofday(&tstop, &zone); 345*26094Sminshall } 346*26094Sminshall 347*26094Sminshall printstats(direction, amount) 348*26094Sminshall char *direction; 349*26094Sminshall unsigned long amount; 350*26094Sminshall { 351*26094Sminshall double delta; 352*26094Sminshall /* compute delta in 1/10's second units */ 353*26094Sminshall delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 354*26094Sminshall ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 355*26094Sminshall delta = delta/10.; /* back to seconds */ 356*26094Sminshall printf("%s %d bytes in %.1f seconds", direction, amount, delta); 357*26094Sminshall if (verbose) 358*26094Sminshall printf(" [%.0f bits/sec]", (amount*8.)/delta); 359*26094Sminshall putchar('\n'); 360*26094Sminshall } 361*26094Sminshall 362