1*22404Sdist /* 2*22404Sdist * Copyright (c) 1983 Regents of the University of California. 3*22404Sdist * All rights reserved. The Berkeley software License Agreement 4*22404Sdist * specifies the terms and conditions for redistribution. 5*22404Sdist */ 6*22404Sdist 714554Ssam #ifndef lint 8*22404Sdist static char sccsid[] = "@(#)tftp.c 5.1 (Berkeley) 06/06/85"; 9*22404Sdist #endif not lint 107770Ssam 117770Ssam /* 127770Ssam * TFTP User Program -- Protocol Machines 137770Ssam */ 147770Ssam #include <sys/types.h> 157770Ssam #include <sys/socket.h> 169220Ssam 179220Ssam #include <netinet/in.h> 1816384Ssam #include <arpa/inet.h> 1912399Ssam #include <arpa/tftp.h> 2012399Ssam 217770Ssam #include <signal.h> 227770Ssam #include <stdio.h> 237770Ssam #include <errno.h> 247770Ssam #include <setjmp.h> 2516384Ssam #include <netdb.h> 269220Ssam 277770Ssam extern int errno; 287770Ssam extern struct sockaddr_in sin; 297770Ssam extern char mode[]; 307770Ssam int f; 317770Ssam int trace; 327770Ssam int connected; 3317269Ssam char sbuf[BUFSIZ]; /* send buffer */ 3417269Ssam char rbuf[BUFSIZ]; /* receive buffer */ 3513018Ssam int rexmtval; 3613018Ssam int maxtimeout; 377770Ssam int timeout; 387770Ssam jmp_buf toplevel; 3913018Ssam jmp_buf timeoutbuf; 407770Ssam 417770Ssam timer() 427770Ssam { 4313018Ssam 4413018Ssam timeout += rexmtval; 4513018Ssam if (timeout >= maxtimeout) { 467770Ssam printf("Transfer timed out.\n"); 477770Ssam longjmp(toplevel, -1); 487770Ssam } 4913018Ssam longjmp(timeoutbuf, 1); 507770Ssam } 517770Ssam 527770Ssam /* 537770Ssam * Send the requested file. 547770Ssam */ 557770Ssam sendfile(fd, name) 567770Ssam int fd; 577770Ssam char *name; 587770Ssam { 5917269Ssam register struct tftphdr *stp = (struct tftphdr *)sbuf; 6017269Ssam register struct tftphdr *rtp = (struct tftphdr *)rbuf; 617770Ssam register int block = 0, size, n, amount = 0; 6216384Ssam struct sockaddr_in from, to; 637770Ssam time_t start = time(0), delta; 6416384Ssam int fromlen, aborted = 0; 657770Ssam 6616384Ssam to = sin; 6713018Ssam signal(SIGALRM, timer); 687770Ssam do { 6913018Ssam if (block == 0) 7013018Ssam size = makerequest(WRQ, name) - 4; 7113018Ssam else { 7217269Ssam size = read(fd, stp->th_data, SEGSIZE); 737770Ssam if (size < 0) { 7416384Ssam nak(&to, errno + 100); 757770Ssam break; 767770Ssam } 7717269Ssam stp->th_opcode = htons((u_short)DATA); 7817269Ssam stp->th_block = htons((u_short)block); 797770Ssam } 807770Ssam timeout = 0; 8113018Ssam (void) setjmp(timeoutbuf); 827770Ssam if (trace) 8317269Ssam tpacket("sent", &to, stp, size + 4); 8417269Ssam n = sendto(f, sbuf, size + 4, 0, (caddr_t)&to, sizeof (to)); 859220Ssam if (n != size + 4) { 8613018Ssam perror("tftp: sendto"); 8716384Ssam aborted = 1; 8816384Ssam goto done; 897770Ssam } 9013018Ssam do { 9116384Ssam again: 9213018Ssam alarm(rexmtval); 9313018Ssam do { 9413018Ssam fromlen = sizeof (from); 9517269Ssam n = recvfrom(f, rbuf, sizeof (rbuf), 0, 9613018Ssam (caddr_t)&from, &fromlen); 9713018Ssam } while (n <= 0); 987770Ssam alarm(0); 9913018Ssam if (n < 0) { 10013018Ssam perror("tftp: recvfrom"); 10116384Ssam aborted = 1; 10216384Ssam goto done; 10313018Ssam } 10416384Ssam if (to.sin_addr.s_addr != from.sin_addr.s_addr) { 10517269Ssam tpacket("discarded (wrong host)", 10617269Ssam &from, rtp, n); 10716384Ssam goto again; 10816384Ssam } 10916384Ssam if (to.sin_port = sin.sin_port) 11016384Ssam to.sin_port = from.sin_port; 11116384Ssam if (to.sin_port != from.sin_port) { 11217269Ssam tpacket("discarded (wrong port)", 11317269Ssam &from, rtp, n); 11416384Ssam goto again; 11516384Ssam } 11613018Ssam if (trace) 11717269Ssam tpacket("received", &from, rtp, n); 11813018Ssam /* should verify packet came from server */ 11917269Ssam rtp->th_opcode = ntohs(rtp->th_opcode); 12017269Ssam rtp->th_block = ntohs(rtp->th_block); 12117269Ssam if (rtp->th_opcode == ERROR) { 12217269Ssam printf("Error code %d: %s\n", rtp->th_code, 12317269Ssam rtp->th_msg); 12416384Ssam aborted = 1; 12516384Ssam goto done; 12613018Ssam } 12717269Ssam } while (rtp->th_opcode != ACK && block != rtp->th_block); 1287770Ssam if (block > 0) 1297770Ssam amount += size; 1307770Ssam block++; 1317770Ssam } while (size == SEGSIZE || block == 1); 13216384Ssam if (!aborted && amount > 0) { 1337770Ssam delta = time(0) - start; 1347770Ssam printf("Sent %d bytes in %d seconds.\n", amount, delta); 1357770Ssam } 13616384Ssam done: 13716384Ssam (void) close(fd); 13816384Ssam return (aborted); 1397770Ssam } 1407770Ssam 1417770Ssam /* 1427770Ssam * Receive a file. 1437770Ssam */ 1447770Ssam recvfile(fd, name) 1457770Ssam int fd; 1467770Ssam char *name; 1477770Ssam { 14817269Ssam register struct tftphdr *stp = (struct tftphdr *)sbuf; 14917269Ssam register struct tftphdr *rtp = (struct tftphdr *)rbuf; 1507770Ssam register int block = 1, n, size, amount = 0; 15116384Ssam struct sockaddr_in from, to; 1527770Ssam time_t start = time(0), delta; 15316384Ssam int fromlen, firsttrip = 1, aborted = 0; 1547770Ssam 15516384Ssam to = sin; 15613018Ssam signal(SIGALRM, timer); 1577770Ssam do { 15813018Ssam if (firsttrip) { 15913018Ssam size = makerequest(RRQ, name); 16013018Ssam firsttrip = 0; 16113018Ssam } else { 16217269Ssam stp->th_opcode = htons((u_short)ACK); 16317269Ssam stp->th_block = htons((u_short)(block)); 16413018Ssam size = 4; 16513018Ssam block++; 16613018Ssam } 1677770Ssam timeout = 0; 16813018Ssam (void) setjmp(timeoutbuf); 1697770Ssam if (trace) 17017269Ssam tpacket("sent", &to, stp, size); 17117269Ssam if (sendto(f, sbuf, size, 0, (caddr_t)&to, 17216384Ssam sizeof (to)) != size) { 17313018Ssam alarm(0); 17413018Ssam perror("tftp: sendto"); 17516384Ssam aborted = 1; 17616384Ssam goto done; 1777770Ssam } 17813018Ssam do { 17916384Ssam again: 18013018Ssam alarm(rexmtval); 18116384Ssam do { 18216384Ssam fromlen = sizeof (from); 18317269Ssam n = recvfrom(f, rbuf, sizeof (rbuf), 0, 18413018Ssam (caddr_t)&from, &fromlen); 18516384Ssam } while (n <= 0); 1867770Ssam alarm(0); 18713018Ssam if (n < 0) { 18813018Ssam perror("tftp: recvfrom"); 18916384Ssam aborted = 1; 19016384Ssam goto done; 19113018Ssam } 19216384Ssam if (to.sin_addr.s_addr != from.sin_addr.s_addr) { 19317269Ssam tpacket("discarded (wrong host)", 19417269Ssam &from, rtp, n); 19516384Ssam goto again; 19616384Ssam } 19716384Ssam if (to.sin_port = sin.sin_port) 19816384Ssam to.sin_port = from.sin_port; 19916384Ssam if (to.sin_port != from.sin_port) { 20017269Ssam tpacket("discarded (wrong port)", 20117269Ssam &from, rtp, n); 20216384Ssam goto again; 20316384Ssam } 20413018Ssam if (trace) 20517269Ssam tpacket("received", &from, rtp, n); 20617269Ssam rtp->th_opcode = ntohs(rtp->th_opcode); 20717269Ssam rtp->th_block = ntohs(rtp->th_block); 20817269Ssam if (rtp->th_opcode == ERROR) { 20917269Ssam printf("Error code %d: %s\n", rtp->th_code, 21017269Ssam rtp->th_msg); 21116384Ssam aborted = 1; 21216384Ssam goto done; 21313018Ssam } 21417269Ssam } while (rtp->th_opcode != DATA && rtp->th_block != block); 21517269Ssam size = write(fd, rtp->th_data, n - 4); 2167770Ssam if (size < 0) { 21716384Ssam perror("tftp: write"); 21816384Ssam nak(&to, errno + 100); 21916384Ssam aborted = 1; 22016384Ssam goto done; 2217770Ssam } 2227770Ssam amount += size; 2237770Ssam } while (size == SEGSIZE); 22416384Ssam done: 22517269Ssam stp->th_opcode = htons((u_short)ACK); 22617269Ssam stp->th_block = htons((u_short)block); 22717269Ssam (void) sendto(f, sbuf, 4, 0, &to, sizeof (to)); 2287770Ssam (void) close(fd); 22916384Ssam if (!aborted && amount > 0) { 2307770Ssam delta = time(0) - start; 2317770Ssam printf("Received %d bytes in %d seconds.\n", amount, delta); 2327770Ssam } 23316384Ssam return (aborted); 2347770Ssam } 2357770Ssam 2367770Ssam makerequest(request, name) 2377770Ssam int request; 2387770Ssam char *name; 2397770Ssam { 24017269Ssam register struct tftphdr *stp; 2417770Ssam int size; 2427770Ssam register char *cp; 2437770Ssam 24417269Ssam stp = (struct tftphdr *)sbuf; 24517269Ssam stp->th_opcode = htons((u_short)request); 24617269Ssam strcpy(stp->th_stuff, name); 2477770Ssam size = strlen(name); 24817269Ssam cp = stp->th_stuff + strlen(name); 2497770Ssam *cp++ = '\0'; 2507770Ssam strcpy(cp, mode); 2517770Ssam cp += sizeof ("netascii") - 1; 2527770Ssam *cp++ = '\0'; 25317269Ssam return (cp - sbuf); 2547770Ssam } 2557770Ssam 2567770Ssam struct errmsg { 2577770Ssam int e_code; 2587770Ssam char *e_msg; 2597770Ssam } errmsgs[] = { 2607770Ssam { EUNDEF, "Undefined error code" }, 2617770Ssam { ENOTFOUND, "File not found" }, 2627770Ssam { EACCESS, "Access violation" }, 2637770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2647770Ssam { EBADOP, "Illegal TFTP operation" }, 2657770Ssam { EBADID, "Unknown transfer ID" }, 2667770Ssam { EEXISTS, "File already exists" }, 2677770Ssam { ENOUSER, "No such user" }, 2687770Ssam { -1, 0 } 2697770Ssam }; 2707770Ssam 2717770Ssam /* 2727770Ssam * Send a nak packet (error message). 2737770Ssam * Error code passed in is one of the 2747770Ssam * standard TFTP codes, or a UNIX errno 2757770Ssam * offset by 100. 2767770Ssam */ 27716384Ssam nak(to, error) 27816384Ssam struct sockaddr_in *to; 2797770Ssam int error; 2807770Ssam { 28117269Ssam register struct tftphdr *stp; 2827770Ssam int length; 2837770Ssam register struct errmsg *pe; 2847770Ssam extern char *sys_errlist[]; 2857770Ssam 28617269Ssam stp = (struct tftphdr *)sbuf; 28717269Ssam stp->th_opcode = htons((u_short)ERROR); 28817269Ssam stp->th_code = htons((u_short)error); 2897770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 2907770Ssam if (pe->e_code == error) 2917770Ssam break; 2927770Ssam if (pe->e_code < 0) 2937770Ssam pe->e_msg = sys_errlist[error - 100]; 29417269Ssam strcpy(stp->th_msg, pe->e_msg); 2957770Ssam length = strlen(pe->e_msg) + 4; 2967770Ssam if (trace) 29717269Ssam tpacket("sent", to, stp, length); 29817269Ssam if (sendto(f, sbuf, length, 0, to, sizeof (*to)) != length) 29916384Ssam perror("tftp: nak"); 3007770Ssam } 3017770Ssam 30216384Ssam tpacket(s, sin, tp, n) 30316384Ssam struct sockaddr_in *sin; 3047770Ssam struct tftphdr *tp; 3057770Ssam int n; 3067770Ssam { 3077770Ssam static char *opcodes[] = 3087770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3097770Ssam register char *cp, *file; 3107770Ssam u_short op = ntohs(tp->th_opcode); 3117770Ssam char *index(); 3127770Ssam 31316384Ssam printf("%s ", s); 31416384Ssam if (sin) { 31516384Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 31616384Ssam sizeof (sin->sin_addr), AF_INET); 31716384Ssam 31816384Ssam printf("%s.%d ", 31916384Ssam hp == 0 ? inet_ntoa(sin->sin_addr) : hp->h_name, 32016384Ssam ntohs(sin->sin_port)); 32116384Ssam } 3227770Ssam if (op < RRQ || op > ERROR) 32316384Ssam printf("opcode=%x ", op); 3247770Ssam else 32516384Ssam printf("%s ", opcodes[op]); 3267770Ssam switch (op) { 3277770Ssam 3287770Ssam case RRQ: 3297770Ssam case WRQ: 3307770Ssam n -= 2; 3317770Ssam file = cp = tp->th_stuff; 3327770Ssam cp = index(cp, '\0'); 3337770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3347770Ssam break; 3357770Ssam 3367770Ssam case DATA: 3377770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3387770Ssam break; 3397770Ssam 3407770Ssam case ACK: 3417770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3427770Ssam break; 3437770Ssam 3447770Ssam case ERROR: 3457770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3467770Ssam break; 34717268Ssam 34817268Ssam default: 34917268Ssam putchar('\n'); 35017268Ssam break; 3517770Ssam } 3527770Ssam } 353