122404Sdist /* 2*33821Sbostic * Copyright (c) 1983 Regents of the University of California. 3*33821Sbostic * All rights reserved. 4*33821Sbostic * 5*33821Sbostic * Redistribution and use in source and binary forms are permitted 6*33821Sbostic * provided that this notice is preserved and that due credit is given 7*33821Sbostic * to the University of California at Berkeley. The name of the University 8*33821Sbostic * may not be used to endorse or promote products derived from this 9*33821Sbostic * software without specific prior written permission. This software 10*33821Sbostic * is provided ``as is'' without express or implied warranty. 1122404Sdist */ 1222404Sdist 1314554Ssam #ifndef lint 14*33821Sbostic static char sccsid[] = "@(#)tftp.c 5.6 (Berkeley) 03/28/88"; 15*33821Sbostic #endif /* not lint */ 167770Ssam 1726094Sminshall /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 1826094Sminshall 197770Ssam /* 207770Ssam * TFTP User Program -- Protocol Machines 217770Ssam */ 227770Ssam #include <sys/types.h> 237770Ssam #include <sys/socket.h> 2426094Sminshall #include <sys/time.h> 259220Ssam 269220Ssam #include <netinet/in.h> 2726094Sminshall 2812399Ssam #include <arpa/tftp.h> 2912399Ssam 307770Ssam #include <signal.h> 317770Ssam #include <stdio.h> 327770Ssam #include <errno.h> 337770Ssam #include <setjmp.h> 349220Ssam 357770Ssam extern int errno; 3626094Sminshall 3726094Sminshall extern struct sockaddr_in sin; /* filled in by main */ 3826094Sminshall extern int f; /* the opened socket */ 3926094Sminshall extern int trace; 4026094Sminshall extern int verbose; 4126094Sminshall extern int rexmtval; 4226094Sminshall extern int maxtimeout; 4326094Sminshall 4426094Sminshall #define PKTSIZE SEGSIZE+4 4526094Sminshall char ackbuf[PKTSIZE]; 467770Ssam int timeout; 477770Ssam jmp_buf toplevel; 4813018Ssam jmp_buf timeoutbuf; 497770Ssam 507770Ssam timer() 517770Ssam { 5213018Ssam 5313018Ssam timeout += rexmtval; 5413018Ssam if (timeout >= maxtimeout) { 557770Ssam printf("Transfer timed out.\n"); 567770Ssam longjmp(toplevel, -1); 577770Ssam } 5813018Ssam longjmp(timeoutbuf, 1); 597770Ssam } 607770Ssam 617770Ssam /* 627770Ssam * Send the requested file. 637770Ssam */ 6426094Sminshall sendfile(fd, name, mode) 657770Ssam int fd; 667770Ssam char *name; 6726094Sminshall char *mode; 687770Ssam { 6926094Sminshall register struct tftphdr *ap; /* data and ack packets */ 7026094Sminshall struct tftphdr *r_init(), *dp; 7126094Sminshall register int block = 0, size, n; 7226094Sminshall register unsigned long amount = 0; 7326094Sminshall struct sockaddr_in from; 7426094Sminshall int fromlen; 7526094Sminshall int convert; /* true if doing nl->crlf conversion */ 7626094Sminshall FILE *file; 777770Ssam 7826094Sminshall startclock(); /* start stat's clock */ 7926094Sminshall dp = r_init(); /* reset fillbuf/read-ahead code */ 8026094Sminshall ap = (struct tftphdr *)ackbuf; 8126094Sminshall file = fdopen(fd, "r"); 8226094Sminshall convert = !strcmp(mode, "netascii"); 8326094Sminshall 8413018Ssam signal(SIGALRM, timer); 857770Ssam do { 8613018Ssam if (block == 0) 8726094Sminshall size = makerequest(WRQ, name, dp, mode) - 4; 8813018Ssam else { 8926094Sminshall /* size = read(fd, dp->th_data, SEGSIZE); */ 9026094Sminshall size = readit(file, &dp, convert); 917770Ssam if (size < 0) { 9226094Sminshall nak(errno + 100); 937770Ssam break; 947770Ssam } 9526094Sminshall dp->th_opcode = htons((u_short)DATA); 9626094Sminshall dp->th_block = htons((u_short)block); 977770Ssam } 987770Ssam timeout = 0; 9913018Ssam (void) setjmp(timeoutbuf); 10026104Sminshall send_data: 1017770Ssam if (trace) 10226094Sminshall tpacket("sent", dp, size + 4); 10326094Sminshall n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 1049220Ssam if (n != size + 4) { 10513018Ssam perror("tftp: sendto"); 10626094Sminshall goto abort; 1077770Ssam } 10826094Sminshall read_ahead(file, convert); 10926104Sminshall for ( ; ; ) { 11013018Ssam alarm(rexmtval); 11113018Ssam do { 11213018Ssam fromlen = sizeof (from); 11326094Sminshall n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 11413018Ssam (caddr_t)&from, &fromlen); 11513018Ssam } while (n <= 0); 1167770Ssam alarm(0); 11713018Ssam if (n < 0) { 11813018Ssam perror("tftp: recvfrom"); 11926094Sminshall goto abort; 12013018Ssam } 12126094Sminshall sin.sin_port = from.sin_port; /* added */ 12213018Ssam if (trace) 12326094Sminshall tpacket("received", ap, n); 12413018Ssam /* should verify packet came from server */ 12526094Sminshall ap->th_opcode = ntohs(ap->th_opcode); 12626094Sminshall ap->th_block = ntohs(ap->th_block); 12726094Sminshall if (ap->th_opcode == ERROR) { 12826094Sminshall printf("Error code %d: %s\n", ap->th_code, 12926094Sminshall ap->th_msg); 13026094Sminshall goto abort; 13113018Ssam } 13226104Sminshall if (ap->th_opcode == ACK) { 13326113Sminshall int j; 13426113Sminshall 13526104Sminshall if (ap->th_block == block) { 13626104Sminshall break; 13726104Sminshall } 13826113Sminshall /* On an error, try to synchronize 13926113Sminshall * both sides. 14026113Sminshall */ 14126113Sminshall j = synchnet(f); 14226113Sminshall if (j && trace) { 14326113Sminshall printf("discarded %d packets\n", 14426113Sminshall j); 14526113Sminshall } 14626113Sminshall if (ap->th_block == (block-1)) { 14726113Sminshall goto send_data; 14826113Sminshall } 14926104Sminshall } 15026104Sminshall } 1517770Ssam if (block > 0) 1527770Ssam amount += size; 1537770Ssam block++; 1547770Ssam } while (size == SEGSIZE || block == 1); 15526094Sminshall abort: 15626094Sminshall fclose(file); 15726094Sminshall stopclock(); 15826094Sminshall if (amount > 0) 15926094Sminshall printstats("Sent", amount); 1607770Ssam } 1617770Ssam 1627770Ssam /* 1637770Ssam * Receive a file. 1647770Ssam */ 16526094Sminshall recvfile(fd, name, mode) 1667770Ssam int fd; 1677770Ssam char *name; 16826094Sminshall char *mode; 1697770Ssam { 17026094Sminshall register struct tftphdr *ap; 17126094Sminshall struct tftphdr *dp, *w_init(); 17226094Sminshall register int block = 1, n, size; 17326094Sminshall unsigned long amount = 0; 17426094Sminshall struct sockaddr_in from; 17526094Sminshall int fromlen, firsttrip = 1; 17626094Sminshall FILE *file; 17726094Sminshall int convert; /* true if converting crlf -> lf */ 1787770Ssam 17926094Sminshall startclock(); 18026094Sminshall dp = w_init(); 18126094Sminshall ap = (struct tftphdr *)ackbuf; 18226094Sminshall file = fdopen(fd, "w"); 18326094Sminshall convert = !strcmp(mode, "netascii"); 18426094Sminshall 18513018Ssam signal(SIGALRM, timer); 1867770Ssam do { 18713018Ssam if (firsttrip) { 18826094Sminshall size = makerequest(RRQ, name, ap, mode); 18913018Ssam firsttrip = 0; 19013018Ssam } else { 19126094Sminshall ap->th_opcode = htons((u_short)ACK); 19226094Sminshall ap->th_block = htons((u_short)(block)); 19313018Ssam size = 4; 19413018Ssam block++; 19513018Ssam } 1967770Ssam timeout = 0; 19713018Ssam (void) setjmp(timeoutbuf); 19826094Sminshall send_ack: 1997770Ssam if (trace) 20026094Sminshall tpacket("sent", ap, size); 20126094Sminshall if (sendto(f, ackbuf, size, 0, (caddr_t)&sin, 20226094Sminshall sizeof (sin)) != size) { 20313018Ssam alarm(0); 20413018Ssam perror("tftp: sendto"); 20526094Sminshall goto abort; 2067770Ssam } 20726094Sminshall write_behind(file, convert); 20826094Sminshall for ( ; ; ) { 20913018Ssam alarm(rexmtval); 21026094Sminshall do { 21116384Ssam fromlen = sizeof (from); 21226094Sminshall n = recvfrom(f, dp, PKTSIZE, 0, 21313018Ssam (caddr_t)&from, &fromlen); 21416384Ssam } while (n <= 0); 2157770Ssam alarm(0); 21613018Ssam if (n < 0) { 21713018Ssam perror("tftp: recvfrom"); 21826094Sminshall goto abort; 21913018Ssam } 22026094Sminshall sin.sin_port = from.sin_port; /* added */ 22126094Sminshall if (trace) 22226094Sminshall tpacket("received", dp, n); 22326094Sminshall /* should verify client address */ 22426094Sminshall dp->th_opcode = ntohs(dp->th_opcode); 22526094Sminshall dp->th_block = ntohs(dp->th_block); 22626094Sminshall if (dp->th_opcode == ERROR) { 22726094Sminshall printf("Error code %d: %s\n", dp->th_code, 22826094Sminshall dp->th_msg); 22926094Sminshall goto abort; 23016384Ssam } 23126094Sminshall if (dp->th_opcode == DATA) { 23226113Sminshall int j; 23326113Sminshall 23426113Sminshall if (dp->th_block == block) { 23526094Sminshall break; /* have next packet */ 23626113Sminshall } 23726113Sminshall /* On an error, try to synchronize 23826113Sminshall * both sides. 23926113Sminshall */ 24026113Sminshall j = synchnet(f); 24126113Sminshall if (j && trace) { 24226113Sminshall printf("discarded %d packets\n", j); 24326113Sminshall } 24426113Sminshall if (dp->th_block == (block-1)) { 24526094Sminshall goto send_ack; /* resend ack */ 24626113Sminshall } 24716384Ssam } 24826094Sminshall } 24926094Sminshall /* size = write(fd, dp->th_data, n - 4); */ 25026094Sminshall size = writeit(file, &dp, n - 4, convert); 2517770Ssam if (size < 0) { 25226094Sminshall nak(errno + 100); 25326094Sminshall break; 2547770Ssam } 2557770Ssam amount += size; 2567770Ssam } while (size == SEGSIZE); 25726094Sminshall abort: /* ok to ack, since user */ 25826094Sminshall ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 25926094Sminshall ap->th_block = htons((u_short)block); 26026094Sminshall (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin)); 26126094Sminshall write_behind(file, convert); /* flush last buffer */ 26226094Sminshall fclose(file); 26326094Sminshall stopclock(); 26426094Sminshall if (amount > 0) 26526094Sminshall printstats("Received", amount); 2667770Ssam } 2677770Ssam 26826094Sminshall makerequest(request, name, tp, mode) 2697770Ssam int request; 27026094Sminshall char *name, *mode; 27126094Sminshall struct tftphdr *tp; 2727770Ssam { 2737770Ssam register char *cp; 2747770Ssam 27526094Sminshall tp->th_opcode = htons((u_short)request); 27626094Sminshall cp = tp->th_stuff; 27726094Sminshall strcpy(cp, name); 27826094Sminshall cp += strlen(name); 2797770Ssam *cp++ = '\0'; 2807770Ssam strcpy(cp, mode); 28126094Sminshall cp += strlen(mode); 2827770Ssam *cp++ = '\0'; 28326094Sminshall return (cp - (char *)tp); 2847770Ssam } 2857770Ssam 2867770Ssam struct errmsg { 2877770Ssam int e_code; 2887770Ssam char *e_msg; 2897770Ssam } errmsgs[] = { 2907770Ssam { EUNDEF, "Undefined error code" }, 2917770Ssam { ENOTFOUND, "File not found" }, 2927770Ssam { EACCESS, "Access violation" }, 2937770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2947770Ssam { EBADOP, "Illegal TFTP operation" }, 2957770Ssam { EBADID, "Unknown transfer ID" }, 2967770Ssam { EEXISTS, "File already exists" }, 2977770Ssam { ENOUSER, "No such user" }, 2987770Ssam { -1, 0 } 2997770Ssam }; 3007770Ssam 3017770Ssam /* 3027770Ssam * Send a nak packet (error message). 3037770Ssam * Error code passed in is one of the 3047770Ssam * standard TFTP codes, or a UNIX errno 3057770Ssam * offset by 100. 3067770Ssam */ 30726094Sminshall nak(error) 3087770Ssam int error; 3097770Ssam { 31026094Sminshall register struct tftphdr *tp; 3117770Ssam int length; 3127770Ssam register struct errmsg *pe; 3137770Ssam extern char *sys_errlist[]; 3147770Ssam 31526094Sminshall tp = (struct tftphdr *)ackbuf; 31626094Sminshall tp->th_opcode = htons((u_short)ERROR); 31726094Sminshall tp->th_code = htons((u_short)error); 3187770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 3197770Ssam if (pe->e_code == error) 3207770Ssam break; 32126094Sminshall if (pe->e_code < 0) { 3227770Ssam pe->e_msg = sys_errlist[error - 100]; 32326094Sminshall tp->th_code = EUNDEF; 32426094Sminshall } 32526094Sminshall strcpy(tp->th_msg, pe->e_msg); 3267770Ssam length = strlen(pe->e_msg) + 4; 3277770Ssam if (trace) 32826094Sminshall tpacket("sent", tp, length); 32926094Sminshall if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length) 33026094Sminshall perror("nak"); 3317770Ssam } 3327770Ssam 33326094Sminshall tpacket(s, tp, n) 33426111Sminshall char *s; 3357770Ssam struct tftphdr *tp; 3367770Ssam int n; 3377770Ssam { 3387770Ssam static char *opcodes[] = 3397770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3407770Ssam register char *cp, *file; 3417770Ssam u_short op = ntohs(tp->th_opcode); 3427770Ssam char *index(); 3437770Ssam 3447770Ssam if (op < RRQ || op > ERROR) 34526094Sminshall printf("%s opcode=%x ", s, op); 3467770Ssam else 34726094Sminshall printf("%s %s ", s, opcodes[op]); 3487770Ssam switch (op) { 3497770Ssam 3507770Ssam case RRQ: 3517770Ssam case WRQ: 3527770Ssam n -= 2; 3537770Ssam file = cp = tp->th_stuff; 3547770Ssam cp = index(cp, '\0'); 3557770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3567770Ssam break; 3577770Ssam 3587770Ssam case DATA: 3597770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3607770Ssam break; 3617770Ssam 3627770Ssam case ACK: 3637770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3647770Ssam break; 3657770Ssam 3667770Ssam case ERROR: 3677770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3687770Ssam break; 3697770Ssam } 3707770Ssam } 37126094Sminshall 37226094Sminshall struct timeval tstart; 37326094Sminshall struct timeval tstop; 37426094Sminshall struct timezone zone; 37526094Sminshall 37626094Sminshall startclock() { 37726094Sminshall gettimeofday(&tstart, &zone); 37826094Sminshall } 37926094Sminshall 38026094Sminshall stopclock() { 38126094Sminshall gettimeofday(&tstop, &zone); 38226094Sminshall } 38326094Sminshall 38426094Sminshall printstats(direction, amount) 38526094Sminshall char *direction; 38626094Sminshall unsigned long amount; 38726094Sminshall { 38826094Sminshall double delta; 38926094Sminshall /* compute delta in 1/10's second units */ 39026094Sminshall delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 39126094Sminshall ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 39226094Sminshall delta = delta/10.; /* back to seconds */ 39326094Sminshall printf("%s %d bytes in %.1f seconds", direction, amount, delta); 39426094Sminshall if (verbose) 39526094Sminshall printf(" [%.0f bits/sec]", (amount*8.)/delta); 39626094Sminshall putchar('\n'); 39726094Sminshall } 39826094Sminshall 399