122404Sdist /* 233821Sbostic * Copyright (c) 1983 Regents of the University of California. 333821Sbostic * All rights reserved. 433821Sbostic * 533821Sbostic * Redistribution and use in source and binary forms are permitted 6*34903Sbostic * provided that the above copyright notice and this paragraph are 7*34903Sbostic * duplicated in all such forms and that any documentation, 8*34903Sbostic * advertising materials, and other materials related to such 9*34903Sbostic * distribution and use acknowledge that the software was developed 10*34903Sbostic * by the University of California, Berkeley. The name of the 11*34903Sbostic * University may not be used to endorse or promote products derived 12*34903Sbostic * from this software without specific prior written permission. 13*34903Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34903Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34903Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622404Sdist */ 1722404Sdist 1814554Ssam #ifndef lint 19*34903Sbostic static char sccsid[] = "@(#)tftp.c 5.7 (Berkeley) 06/29/88"; 2033821Sbostic #endif /* not lint */ 217770Ssam 2226094Sminshall /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 2326094Sminshall 247770Ssam /* 257770Ssam * TFTP User Program -- Protocol Machines 267770Ssam */ 277770Ssam #include <sys/types.h> 287770Ssam #include <sys/socket.h> 2926094Sminshall #include <sys/time.h> 309220Ssam 319220Ssam #include <netinet/in.h> 3226094Sminshall 3312399Ssam #include <arpa/tftp.h> 3412399Ssam 357770Ssam #include <signal.h> 367770Ssam #include <stdio.h> 377770Ssam #include <errno.h> 387770Ssam #include <setjmp.h> 399220Ssam 407770Ssam extern int errno; 4126094Sminshall 4226094Sminshall extern struct sockaddr_in sin; /* filled in by main */ 4326094Sminshall extern int f; /* the opened socket */ 4426094Sminshall extern int trace; 4526094Sminshall extern int verbose; 4626094Sminshall extern int rexmtval; 4726094Sminshall extern int maxtimeout; 4826094Sminshall 4926094Sminshall #define PKTSIZE SEGSIZE+4 5026094Sminshall char ackbuf[PKTSIZE]; 517770Ssam int timeout; 527770Ssam jmp_buf toplevel; 5313018Ssam jmp_buf timeoutbuf; 547770Ssam 557770Ssam timer() 567770Ssam { 5713018Ssam 5813018Ssam timeout += rexmtval; 5913018Ssam if (timeout >= maxtimeout) { 607770Ssam printf("Transfer timed out.\n"); 617770Ssam longjmp(toplevel, -1); 627770Ssam } 6313018Ssam longjmp(timeoutbuf, 1); 647770Ssam } 657770Ssam 667770Ssam /* 677770Ssam * Send the requested file. 687770Ssam */ 6926094Sminshall sendfile(fd, name, mode) 707770Ssam int fd; 717770Ssam char *name; 7226094Sminshall char *mode; 737770Ssam { 7426094Sminshall register struct tftphdr *ap; /* data and ack packets */ 7526094Sminshall struct tftphdr *r_init(), *dp; 7626094Sminshall register int block = 0, size, n; 7726094Sminshall register unsigned long amount = 0; 7826094Sminshall struct sockaddr_in from; 7926094Sminshall int fromlen; 8026094Sminshall int convert; /* true if doing nl->crlf conversion */ 8126094Sminshall FILE *file; 827770Ssam 8326094Sminshall startclock(); /* start stat's clock */ 8426094Sminshall dp = r_init(); /* reset fillbuf/read-ahead code */ 8526094Sminshall ap = (struct tftphdr *)ackbuf; 8626094Sminshall file = fdopen(fd, "r"); 8726094Sminshall convert = !strcmp(mode, "netascii"); 8826094Sminshall 8913018Ssam signal(SIGALRM, timer); 907770Ssam do { 9113018Ssam if (block == 0) 9226094Sminshall size = makerequest(WRQ, name, dp, mode) - 4; 9313018Ssam else { 9426094Sminshall /* size = read(fd, dp->th_data, SEGSIZE); */ 9526094Sminshall size = readit(file, &dp, convert); 967770Ssam if (size < 0) { 9726094Sminshall nak(errno + 100); 987770Ssam break; 997770Ssam } 10026094Sminshall dp->th_opcode = htons((u_short)DATA); 10126094Sminshall dp->th_block = htons((u_short)block); 1027770Ssam } 1037770Ssam timeout = 0; 10413018Ssam (void) setjmp(timeoutbuf); 10526104Sminshall send_data: 1067770Ssam if (trace) 10726094Sminshall tpacket("sent", dp, size + 4); 10826094Sminshall n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 1099220Ssam if (n != size + 4) { 11013018Ssam perror("tftp: sendto"); 11126094Sminshall goto abort; 1127770Ssam } 11326094Sminshall read_ahead(file, convert); 11426104Sminshall for ( ; ; ) { 11513018Ssam alarm(rexmtval); 11613018Ssam do { 11713018Ssam fromlen = sizeof (from); 11826094Sminshall n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 11913018Ssam (caddr_t)&from, &fromlen); 12013018Ssam } while (n <= 0); 1217770Ssam alarm(0); 12213018Ssam if (n < 0) { 12313018Ssam perror("tftp: recvfrom"); 12426094Sminshall goto abort; 12513018Ssam } 12626094Sminshall sin.sin_port = from.sin_port; /* added */ 12713018Ssam if (trace) 12826094Sminshall tpacket("received", ap, n); 12913018Ssam /* should verify packet came from server */ 13026094Sminshall ap->th_opcode = ntohs(ap->th_opcode); 13126094Sminshall ap->th_block = ntohs(ap->th_block); 13226094Sminshall if (ap->th_opcode == ERROR) { 13326094Sminshall printf("Error code %d: %s\n", ap->th_code, 13426094Sminshall ap->th_msg); 13526094Sminshall goto abort; 13613018Ssam } 13726104Sminshall if (ap->th_opcode == ACK) { 13826113Sminshall int j; 13926113Sminshall 14026104Sminshall if (ap->th_block == block) { 14126104Sminshall break; 14226104Sminshall } 14326113Sminshall /* On an error, try to synchronize 14426113Sminshall * both sides. 14526113Sminshall */ 14626113Sminshall j = synchnet(f); 14726113Sminshall if (j && trace) { 14826113Sminshall printf("discarded %d packets\n", 14926113Sminshall j); 15026113Sminshall } 15126113Sminshall if (ap->th_block == (block-1)) { 15226113Sminshall goto send_data; 15326113Sminshall } 15426104Sminshall } 15526104Sminshall } 1567770Ssam if (block > 0) 1577770Ssam amount += size; 1587770Ssam block++; 1597770Ssam } while (size == SEGSIZE || block == 1); 16026094Sminshall abort: 16126094Sminshall fclose(file); 16226094Sminshall stopclock(); 16326094Sminshall if (amount > 0) 16426094Sminshall printstats("Sent", amount); 1657770Ssam } 1667770Ssam 1677770Ssam /* 1687770Ssam * Receive a file. 1697770Ssam */ 17026094Sminshall recvfile(fd, name, mode) 1717770Ssam int fd; 1727770Ssam char *name; 17326094Sminshall char *mode; 1747770Ssam { 17526094Sminshall register struct tftphdr *ap; 17626094Sminshall struct tftphdr *dp, *w_init(); 17726094Sminshall register int block = 1, n, size; 17826094Sminshall unsigned long amount = 0; 17926094Sminshall struct sockaddr_in from; 18026094Sminshall int fromlen, firsttrip = 1; 18126094Sminshall FILE *file; 18226094Sminshall int convert; /* true if converting crlf -> lf */ 1837770Ssam 18426094Sminshall startclock(); 18526094Sminshall dp = w_init(); 18626094Sminshall ap = (struct tftphdr *)ackbuf; 18726094Sminshall file = fdopen(fd, "w"); 18826094Sminshall convert = !strcmp(mode, "netascii"); 18926094Sminshall 19013018Ssam signal(SIGALRM, timer); 1917770Ssam do { 19213018Ssam if (firsttrip) { 19326094Sminshall size = makerequest(RRQ, name, ap, mode); 19413018Ssam firsttrip = 0; 19513018Ssam } else { 19626094Sminshall ap->th_opcode = htons((u_short)ACK); 19726094Sminshall ap->th_block = htons((u_short)(block)); 19813018Ssam size = 4; 19913018Ssam block++; 20013018Ssam } 2017770Ssam timeout = 0; 20213018Ssam (void) setjmp(timeoutbuf); 20326094Sminshall send_ack: 2047770Ssam if (trace) 20526094Sminshall tpacket("sent", ap, size); 20626094Sminshall if (sendto(f, ackbuf, size, 0, (caddr_t)&sin, 20726094Sminshall sizeof (sin)) != size) { 20813018Ssam alarm(0); 20913018Ssam perror("tftp: sendto"); 21026094Sminshall goto abort; 2117770Ssam } 21226094Sminshall write_behind(file, convert); 21326094Sminshall for ( ; ; ) { 21413018Ssam alarm(rexmtval); 21526094Sminshall do { 21616384Ssam fromlen = sizeof (from); 21726094Sminshall n = recvfrom(f, dp, PKTSIZE, 0, 21813018Ssam (caddr_t)&from, &fromlen); 21916384Ssam } while (n <= 0); 2207770Ssam alarm(0); 22113018Ssam if (n < 0) { 22213018Ssam perror("tftp: recvfrom"); 22326094Sminshall goto abort; 22413018Ssam } 22526094Sminshall sin.sin_port = from.sin_port; /* added */ 22626094Sminshall if (trace) 22726094Sminshall tpacket("received", dp, n); 22826094Sminshall /* should verify client address */ 22926094Sminshall dp->th_opcode = ntohs(dp->th_opcode); 23026094Sminshall dp->th_block = ntohs(dp->th_block); 23126094Sminshall if (dp->th_opcode == ERROR) { 23226094Sminshall printf("Error code %d: %s\n", dp->th_code, 23326094Sminshall dp->th_msg); 23426094Sminshall goto abort; 23516384Ssam } 23626094Sminshall if (dp->th_opcode == DATA) { 23726113Sminshall int j; 23826113Sminshall 23926113Sminshall if (dp->th_block == block) { 24026094Sminshall break; /* have next packet */ 24126113Sminshall } 24226113Sminshall /* On an error, try to synchronize 24326113Sminshall * both sides. 24426113Sminshall */ 24526113Sminshall j = synchnet(f); 24626113Sminshall if (j && trace) { 24726113Sminshall printf("discarded %d packets\n", j); 24826113Sminshall } 24926113Sminshall if (dp->th_block == (block-1)) { 25026094Sminshall goto send_ack; /* resend ack */ 25126113Sminshall } 25216384Ssam } 25326094Sminshall } 25426094Sminshall /* size = write(fd, dp->th_data, n - 4); */ 25526094Sminshall size = writeit(file, &dp, n - 4, convert); 2567770Ssam if (size < 0) { 25726094Sminshall nak(errno + 100); 25826094Sminshall break; 2597770Ssam } 2607770Ssam amount += size; 2617770Ssam } while (size == SEGSIZE); 26226094Sminshall abort: /* ok to ack, since user */ 26326094Sminshall ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 26426094Sminshall ap->th_block = htons((u_short)block); 26526094Sminshall (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin)); 26626094Sminshall write_behind(file, convert); /* flush last buffer */ 26726094Sminshall fclose(file); 26826094Sminshall stopclock(); 26926094Sminshall if (amount > 0) 27026094Sminshall printstats("Received", amount); 2717770Ssam } 2727770Ssam 27326094Sminshall makerequest(request, name, tp, mode) 2747770Ssam int request; 27526094Sminshall char *name, *mode; 27626094Sminshall struct tftphdr *tp; 2777770Ssam { 2787770Ssam register char *cp; 2797770Ssam 28026094Sminshall tp->th_opcode = htons((u_short)request); 28126094Sminshall cp = tp->th_stuff; 28226094Sminshall strcpy(cp, name); 28326094Sminshall cp += strlen(name); 2847770Ssam *cp++ = '\0'; 2857770Ssam strcpy(cp, mode); 28626094Sminshall cp += strlen(mode); 2877770Ssam *cp++ = '\0'; 28826094Sminshall return (cp - (char *)tp); 2897770Ssam } 2907770Ssam 2917770Ssam struct errmsg { 2927770Ssam int e_code; 2937770Ssam char *e_msg; 2947770Ssam } errmsgs[] = { 2957770Ssam { EUNDEF, "Undefined error code" }, 2967770Ssam { ENOTFOUND, "File not found" }, 2977770Ssam { EACCESS, "Access violation" }, 2987770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2997770Ssam { EBADOP, "Illegal TFTP operation" }, 3007770Ssam { EBADID, "Unknown transfer ID" }, 3017770Ssam { EEXISTS, "File already exists" }, 3027770Ssam { ENOUSER, "No such user" }, 3037770Ssam { -1, 0 } 3047770Ssam }; 3057770Ssam 3067770Ssam /* 3077770Ssam * Send a nak packet (error message). 3087770Ssam * Error code passed in is one of the 3097770Ssam * standard TFTP codes, or a UNIX errno 3107770Ssam * offset by 100. 3117770Ssam */ 31226094Sminshall nak(error) 3137770Ssam int error; 3147770Ssam { 31526094Sminshall register struct tftphdr *tp; 3167770Ssam int length; 3177770Ssam register struct errmsg *pe; 3187770Ssam extern char *sys_errlist[]; 3197770Ssam 32026094Sminshall tp = (struct tftphdr *)ackbuf; 32126094Sminshall tp->th_opcode = htons((u_short)ERROR); 32226094Sminshall tp->th_code = htons((u_short)error); 3237770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 3247770Ssam if (pe->e_code == error) 3257770Ssam break; 32626094Sminshall if (pe->e_code < 0) { 3277770Ssam pe->e_msg = sys_errlist[error - 100]; 32826094Sminshall tp->th_code = EUNDEF; 32926094Sminshall } 33026094Sminshall strcpy(tp->th_msg, pe->e_msg); 3317770Ssam length = strlen(pe->e_msg) + 4; 3327770Ssam if (trace) 33326094Sminshall tpacket("sent", tp, length); 33426094Sminshall if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length) 33526094Sminshall perror("nak"); 3367770Ssam } 3377770Ssam 33826094Sminshall tpacket(s, tp, n) 33926111Sminshall char *s; 3407770Ssam struct tftphdr *tp; 3417770Ssam int n; 3427770Ssam { 3437770Ssam static char *opcodes[] = 3447770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3457770Ssam register char *cp, *file; 3467770Ssam u_short op = ntohs(tp->th_opcode); 3477770Ssam char *index(); 3487770Ssam 3497770Ssam if (op < RRQ || op > ERROR) 35026094Sminshall printf("%s opcode=%x ", s, op); 3517770Ssam else 35226094Sminshall printf("%s %s ", s, opcodes[op]); 3537770Ssam switch (op) { 3547770Ssam 3557770Ssam case RRQ: 3567770Ssam case WRQ: 3577770Ssam n -= 2; 3587770Ssam file = cp = tp->th_stuff; 3597770Ssam cp = index(cp, '\0'); 3607770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3617770Ssam break; 3627770Ssam 3637770Ssam case DATA: 3647770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3657770Ssam break; 3667770Ssam 3677770Ssam case ACK: 3687770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3697770Ssam break; 3707770Ssam 3717770Ssam case ERROR: 3727770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3737770Ssam break; 3747770Ssam } 3757770Ssam } 37626094Sminshall 37726094Sminshall struct timeval tstart; 37826094Sminshall struct timeval tstop; 37926094Sminshall struct timezone zone; 38026094Sminshall 38126094Sminshall startclock() { 38226094Sminshall gettimeofday(&tstart, &zone); 38326094Sminshall } 38426094Sminshall 38526094Sminshall stopclock() { 38626094Sminshall gettimeofday(&tstop, &zone); 38726094Sminshall } 38826094Sminshall 38926094Sminshall printstats(direction, amount) 39026094Sminshall char *direction; 39126094Sminshall unsigned long amount; 39226094Sminshall { 39326094Sminshall double delta; 39426094Sminshall /* compute delta in 1/10's second units */ 39526094Sminshall delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 39626094Sminshall ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 39726094Sminshall delta = delta/10.; /* back to seconds */ 39826094Sminshall printf("%s %d bytes in %.1f seconds", direction, amount, delta); 39926094Sminshall if (verbose) 40026094Sminshall printf(" [%.0f bits/sec]", (amount*8.)/delta); 40126094Sminshall putchar('\n'); 40226094Sminshall } 40326094Sminshall 404