X-Git-Url: https://git.saurik.com/apple/network_cmds.git/blobdiff_plain/921c0aec360b8ccf45c99d2bc3e61da02f13aff5..f47db663cb3ae4d2fc391bb3acf9d0c2b38a41b7:/tftp.tproj/tftp.c diff --git a/tftp.tproj/tftp.c b/tftp.tproj/tftp.c index bec2978..048c970 100644 --- a/tftp.tproj/tftp.c +++ b/tftp.tproj/tftp.c @@ -1,26 +1,5 @@ -/* - * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (the 'License'). You may not use this file - * except in compliance with the License. Please obtain a copy of the - * License at http://www.apple.com/publicsource and read it before using - * this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License." - * - * @APPLE_LICENSE_HEADER_END@ - */ +/* $NetBSD: tftp.c,v 1.18 2003/08/07 11:16:14 agc Exp $ */ + /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. @@ -33,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -54,6 +29,14 @@ * SUCH DAMAGE. */ +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: tftp.c,v 1.18 2003/08/07 11:16:14 agc Exp $"); +#endif +#endif /* not lint */ /* Many bug fixes are from Jim Guyton */ @@ -61,81 +44,154 @@ * TFTP User Program -- Protocol Machines */ #include +#include #include +#include #include #include -#include +#include "tftp.h" +#include #include #include #include #include +#include +#include #include +#include #include "extern.h" #include "tftpsubs.h" -extern int errno; - -extern struct sockaddr_in peeraddr; /* filled in by main */ +extern struct sockaddr_storage peeraddr; /* filled in by main */ extern int f; /* the opened socket */ extern int trace; extern int verbose; +extern int def_rexmtval; extern int rexmtval; extern int maxtimeout; +extern int tsize; +extern int tout; +extern int def_blksize; +extern int blksize; -extern jmp_buf toplevel; /* filled in by main */ - -#define PKTSIZE SEGSIZE+4 char ackbuf[PKTSIZE]; int timeout; +extern jmp_buf toplevel; jmp_buf timeoutbuf; -static void nak __P((int)); -static int makerequest __P((int, const char *, struct tftphdr *, const char *)); +static void nak __P((int, struct sockaddr *)); +static int makerequest __P((int, const char *, struct tftphdr *, const char *, off_t)); static void printstats __P((const char *, unsigned long)); static void startclock __P((void)); static void stopclock __P((void)); static void timer __P((int)); static void tpacket __P((const char *, struct tftphdr *, int)); +static int cmpport __P((struct sockaddr *, struct sockaddr *)); + +static void get_options(struct tftphdr *, int); + +static void +get_options(struct tftphdr *ap, int size) +{ + unsigned long val; + char *opt, *endp, *nextopt, *valp; + int l; + + size -= 2; /* skip over opcode */ + opt = ap->th_stuff; + endp = opt + size - 1; + *endp = '\0'; + + while (opt < endp) { + l = strlen(opt) + 1; + valp = opt + l; + if (valp < endp) { + val = strtoul(valp, NULL, 10); + l = strlen(valp) + 1; + nextopt = valp + l; + if (val == ULONG_MAX && errno == ERANGE) { + /* Report illegal value */ + opt = nextopt; + continue; + } + } else { + /* Badly formed OACK */ + break; + } + if (strcmp(opt, "tsize") == 0) { + /* cool, but we'll ignore it */ + } else if (strcmp(opt, "timeout") == 0) { + if (val >= 1 && val <= 255) { + rexmtval = val; + } else { + /* Report error? */ + } + } else if (strcmp(opt, "blksize") == 0) { + if (val >= 8 && val <= MAXSEGSIZE) { + blksize = val; + } else { + /* Report error? */ + } + } else { + /* unknown option */ + } + opt = nextopt; + } +} /* * Send the requested file. */ void -tftp_sendfile(fd, name, mode) +sendfile(fd, name, mode) int fd; char *name; char *mode; { - register struct tftphdr *ap; /* data and ack packets */ - struct tftphdr *r_init(), *dp; - register int n; - volatile int block, size, convert; + struct tftphdr *ap; /* data and ack packets */ + struct tftphdr *dp; + int n; + volatile unsigned int block; + volatile int size, convert; volatile unsigned long amount; - struct sockaddr_in from; + struct sockaddr_storage from; + struct stat sbuf; + off_t filesize=0; int fromlen; FILE *file; + struct sockaddr_storage peer; + struct sockaddr_storage serv; /* valid server port number */ startclock(); /* start stat's clock */ dp = r_init(); /* reset fillbuf/read-ahead code */ ap = (struct tftphdr *)ackbuf; + if (tsize) { + if (fstat(fd, &sbuf) == 0) { + filesize = sbuf.st_size; + } else { + filesize = -1ULL; + } + } file = fdopen(fd, "r"); convert = !strcmp(mode, "netascii"); block = 0; amount = 0; + memcpy(&peer, &peeraddr, peeraddr.ss_len); + memset(&serv, 0, sizeof(serv)); signal(SIGALRM, timer); do { if (block == 0) - size = makerequest(WRQ, name, dp, mode) - 4; + size = makerequest(WRQ, name, dp, mode, filesize) - 4; else { /* size = read(fd, dp->th_data, SEGSIZE); */ - size = readit(file, &dp, convert); + size = readit(file, &dp, blksize, convert); if (size < 0) { - nak(errno + 100); + nak(errno + 100, (struct sockaddr *)&peer); break; } dp->th_opcode = htons((u_short)DATA); @@ -147,12 +203,13 @@ send_data: if (trace) tpacket("sent", dp, size + 4); n = sendto(f, dp, size + 4, 0, - (struct sockaddr *)&peeraddr, sizeof(peeraddr)); + (struct sockaddr *)&peer, peer.ss_len); if (n != size + 4) { - perror("tftp: sendto"); + warn("sendto"); goto abort; } - read_ahead(file, convert); + if (block) + read_ahead(file, blksize, convert); for ( ; ; ) { alarm(rexmtval); do { @@ -162,10 +219,17 @@ send_data: } while (n <= 0); alarm(0); if (n < 0) { - perror("tftp: recvfrom"); + warn("recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + if (!serv.ss_family) + serv = from; + else if (!cmpport((struct sockaddr *)&serv, + (struct sockaddr *)&from)) { + warn("server port mismatch"); + goto abort; + } + peer = from; if (trace) tpacket("received", ap, n); /* should verify packet came from server */ @@ -179,13 +243,24 @@ send_data: if (ap->th_opcode == ACK) { int j; + if (ap->th_block == 0) { + /* + * If the extended options are enabled, + * the server just refused 'em all. + * The only one that _really_ + * matters is blksize, but we'll + * clear timeout, too. + */ + blksize = def_blksize; + rexmtval = def_rexmtval; + } if (ap->th_block == block) { break; } /* On an error, try to synchronize * both sides. */ - j = synchnet(f); + j = synchnet(f, blksize+4); if (j && trace) { printf("discarded %d packets\n", j); @@ -194,11 +269,19 @@ send_data: goto send_data; } } + if (ap->th_opcode == OACK) { + if (block == 0) { + blksize = def_blksize; + rexmtval = def_rexmtval; + get_options(ap, n); + break; + } + } } if (block > 0) amount += size; block++; - } while (size == SEGSIZE || block == 1); + } while (size == blksize || block == 1); abort: fclose(file); stopclock(); @@ -215,15 +298,18 @@ recvfile(fd, name, mode) char *name; char *mode; { - register struct tftphdr *ap; - struct tftphdr *dp, *w_init(); - register int n; - volatile int block, size, firsttrip; + struct tftphdr *ap; + struct tftphdr *dp; + int n, oack=0; + volatile unsigned int block; + volatile int size, firsttrip; volatile unsigned long amount; - struct sockaddr_in from; - int fromlen; + struct sockaddr_storage from; + int fromlen, readlen; FILE *file; volatile int convert; /* true if converting crlf -> lf */ + struct sockaddr_storage peer; + struct sockaddr_storage serv; /* valid server port number */ startclock(); dp = w_init(); @@ -233,15 +319,19 @@ recvfile(fd, name, mode) block = 1; firsttrip = 1; amount = 0; + memcpy(&peer, &peeraddr, peeraddr.ss_len); + memset(&serv, 0, sizeof(serv)); signal(SIGALRM, timer); do { if (firsttrip) { - size = makerequest(RRQ, name, ap, mode); + size = makerequest(RRQ, name, ap, mode, 0); + readlen = PKTSIZE; firsttrip = 0; } else { ap->th_opcode = htons((u_short)ACK); ap->th_block = htons((u_short)(block)); + readlen = blksize+4; size = 4; block++; } @@ -250,10 +340,10 @@ recvfile(fd, name, mode) send_ack: if (trace) tpacket("sent", ap, size); - if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)) != size) { + if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer, + peer.ss_len) != size) { alarm(0); - perror("tftp: sendto"); + warn("sendto"); goto abort; } write_behind(file, convert); @@ -261,15 +351,22 @@ send_ack: alarm(rexmtval); do { fromlen = sizeof(from); - n = recvfrom(f, dp, PKTSIZE, 0, + n = recvfrom(f, dp, readlen, 0, (struct sockaddr *)&from, &fromlen); } while (n <= 0); alarm(0); if (n < 0) { - perror("tftp: recvfrom"); + warn("recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + if (!serv.ss_family) + serv = from; + else if (!cmpport((struct sockaddr *)&serv, + (struct sockaddr *)&from)) { + warn("server port mismatch"); + goto abort; + } + peer = from; if (trace) tpacket("received", dp, n); /* should verify client address */ @@ -283,13 +380,18 @@ send_ack: if (dp->th_opcode == DATA) { int j; + if (dp->th_block == 1 && !oack) { + /* no OACK, revert to defaults */ + blksize = def_blksize; + rexmtval = def_rexmtval; + } if (dp->th_block == block) { break; /* have next packet */ } /* On an error, try to synchronize * both sides. */ - j = synchnet(f); + j = synchnet(f, blksize); if (j && trace) { printf("discarded %d packets\n", j); } @@ -297,20 +399,33 @@ send_ack: goto send_ack; /* resend ack */ } } + if (dp->th_opcode == OACK) { + if (block == 1) { + oack = 1; + blksize = def_blksize; + rexmtval = def_rexmtval; + get_options(dp, n); + ap->th_opcode = htons(ACK); + ap->th_block = 0; + readlen = blksize+4; + size = 4; + goto send_ack; + } + } } /* size = write(fd, dp->th_data, n - 4); */ size = writeit(file, &dp, n - 4, convert); if (size < 0) { - nak(errno + 100); + nak(errno + 100, (struct sockaddr *)&peer); break; } amount += size; - } while (size == SEGSIZE); + } while (size == blksize || block == 1); abort: /* ok to ack, since user */ ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ ap->th_block = htons((u_short)block); - (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)); + (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer, + peer.ss_len); write_behind(file, convert); /* flush last buffer */ fclose(file); stopclock(); @@ -319,28 +434,57 @@ abort: /* ok to ack, since user */ } static int -makerequest(request, name, tp, mode) +makerequest(request, name, tp, mode, filesize) int request; const char *name; struct tftphdr *tp; const char *mode; + off_t filesize; { - register char *cp; + char *cp; tp->th_opcode = htons((u_short)request); +#ifndef __SVR4 cp = tp->th_stuff; +#else + cp = (void *)&tp->th_stuff; +#endif strcpy(cp, name); cp += strlen(name); *cp++ = '\0'; strcpy(cp, mode); cp += strlen(mode); *cp++ = '\0'; + if (tsize) { + strcpy(cp, "tsize"); + cp += strlen(cp); + *cp++ = '\0'; + sprintf(cp, "%lu", (unsigned long) filesize); + cp += strlen(cp); + *cp++ = '\0'; + } + if (tout) { + strcpy(cp, "timeout"); + cp += strlen(cp); + *cp++ = '\0'; + sprintf(cp, "%d", rexmtval); + cp += strlen(cp); + *cp++ = '\0'; + } + if (blksize != SEGSIZE) { + strcpy(cp, "blksize"); + cp += strlen(cp); + *cp++ = '\0'; + sprintf(cp, "%d", blksize); + cp += strlen(cp); + *cp++ = '\0'; + } return (cp - (char *)tp); } -struct errmsg { +const struct errmsg { int e_code; - char *e_msg; + const char *e_msg; } errmsgs[] = { { EUNDEF, "Undefined error code" }, { ENOTFOUND, "File not found" }, @@ -350,6 +494,7 @@ struct errmsg { { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, + { EOPTNEG, "Option negotiation failed" }, { -1, 0 } }; @@ -360,31 +505,34 @@ struct errmsg { * offset by 100. */ static void -nak(error) +nak(error, peer) int error; + struct sockaddr *peer; { - register struct errmsg *pe; - register struct tftphdr *tp; + const struct errmsg *pe; + struct tftphdr *tp; int length; - char *strerror(); + size_t msglen; tp = (struct tftphdr *)ackbuf; tp->th_opcode = htons((u_short)ERROR); - tp->th_code = htons((u_short)error); + msglen = sizeof(ackbuf) - (&tp->th_msg[0] - ackbuf); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) break; if (pe->e_code < 0) { - pe->e_msg = strerror(error - 100); tp->th_code = EUNDEF; + strlcpy(tp->th_msg, strerror(error - 100), msglen); + } else { + tp->th_code = htons((u_short)error); + strlcpy(tp->th_msg, pe->e_msg, msglen); } - strcpy(tp->th_msg, pe->e_msg); - length = strlen(pe->e_msg) + 4; + length = strlen(tp->th_msg); + msglen = &tp->th_msg[length + 1] - ackbuf; if (trace) - tpacket("sent", tp, length); - if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)) != length) - perror("nak"); + tpacket("sent", tp, (int)msglen); + if (sendto(f, ackbuf, msglen, 0, peer, peer->sa_len) != msglen) + warn("nak"); } static void @@ -394,12 +542,12 @@ tpacket(s, tp, n) int n; { static char *opcodes[] = - { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; - register char *cp, *file; + { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; + char *cp, *file, *endp, *opt, *spc; u_short op = ntohs(tp->th_opcode); - char *index(); + int i, o; - if (op < RRQ || op > ERROR) + if (op < RRQ || op > OACK) printf("%s opcode=%x ", s, op); else printf("%s %s ", s, opcodes[op]); @@ -408,9 +556,31 @@ tpacket(s, tp, n) case RRQ: case WRQ: n -= 2; - file = cp = tp->th_stuff; - cp = index(cp, '\0'); - printf("\n", file, cp + 1); +#ifndef __SVR4 + cp = tp->th_stuff; +#else + cp = (void *) &tp->th_stuff; +#endif + endp = cp + n - 1; + if (*endp != '\0') { /* Shouldn't happen, but... */ + *endp = '\0'; + } + file = cp; + cp = strchr(cp, '\0') + 1; + printf("\n"); break; case DATA: @@ -424,6 +594,30 @@ tpacket(s, tp, n) case ERROR: printf("\n", ntohs(tp->th_code), tp->th_msg); break; + + case OACK: + o = 0; + n -= 2; + cp = tp->th_stuff; + endp = cp + n - 1; + if (*endp != '\0') { /* Shouldn't happen, but... */ + *endp = '\0'; + } + printf("<"); + spc = ""; + while (cp < endp) { + i = strlen(cp) + 1; + if (o) { + printf("%s%s=%s", spc, opt, cp); + spc = ", "; + } else { + opt = cp; + } + o = (o+1) % 2; + cp += i; + } + printf(">\n"); + break; } } @@ -450,11 +644,12 @@ printstats(direction, amount) unsigned long amount; { double delta; - /* compute delta in 1/10's second units */ + + /* compute delta in 1/10's second units */ delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); delta = delta/10.; /* back to seconds */ - printf("%s %d bytes in %.1f seconds", direction, amount, delta); + printf("%s %ld bytes in %.1f seconds", direction, amount, delta); if (verbose) printf(" [%.0f bits/sec]", (amount*8.)/delta); putchar('\n'); @@ -472,3 +667,20 @@ timer(sig) } longjmp(timeoutbuf, 1); } + +static int +cmpport(sa, sb) + struct sockaddr *sa; + struct sockaddr *sb; +{ + char a[NI_MAXSERV], b[NI_MAXSERV]; + + if (getnameinfo(sa, sa->sa_len, NULL, 0, a, sizeof(a), NI_NUMERICSERV)) + return 0; + if (getnameinfo(sb, sb->sa_len, NULL, 0, b, sizeof(b), NI_NUMERICSERV)) + return 0; + if (strcmp(a, b) != 0) + return 0; + + return 1; +}