PR: bin/118874 Index: main.c =================================================================== --- main.c (revision 181160) +++ main.c (working copy) @@ -60,6 +60,7 @@ #include #include +#include #include #include @@ -81,6 +82,10 @@ int f; int trace; int verbose; +int tsize; +int tout; +int def_blksize = SEGSIZE; +int blksize = SEGSIZE; int connected; char mode[32]; char line[MAXLINE]; @@ -98,11 +103,14 @@ void quit(int, char **); void setascii(int, char **); void setbinary(int, char **); +void setblksize(int, char **); void setpeer0(char *, const char *); void setpeer(int, char **); void setrexmt(int, char **); void settimeout(int, char **); +void settimeoutopt(int, char **); void settrace(int, char **); +void settsize(int, char **); void setverbose(int, char **); void status(int, char **); @@ -127,6 +135,9 @@ char vhelp[] = "toggle verbose mode"; char thelp[] = "toggle packet tracing"; +char tshelp[] = "toggle extended tsize option"; +char tohelp[] = "toggle extended timeout option"; +char blhelp[] = "set an alternative blocksize (def. 512)"; char chelp[] = "connect to remote tftp"; char qhelp[] = "exit tftp"; char hhelp[] = "print help information"; @@ -146,12 +157,15 @@ { "get", rhelp, get }, { "quit", qhelp, quit }, { "verbose", vhelp, setverbose }, + { "blksize", blhelp, setblksize }, + { "tsize", tshelp, settsize }, { "trace", thelp, settrace }, { "status", sthelp, status }, { "binary", bnhelp, setbinary }, { "ascii", ashelp, setascii }, { "rexmt", xhelp, setrexmt }, { "timeout", ihelp, settimeout }, + { "tout", tohelp, settimeoutopt }, { "?", hhelp, help }, { NULL, NULL, NULL } }; @@ -161,10 +175,29 @@ int argc; char *argv[]; { + int c; + f = -1; strcpy(mode, "netascii"); signal(SIGINT, intr); - if (argc > 1) { + while ((c = getopt(argc, argv, "e")) != -1) { + switch (c) { + case 'e': + blksize = MAXSEGSIZE; + strcpy(mode, "octet"); + tsize = 1; + tout = 1; + break; + default: + printf("usage: %s [-e] host-name [port]\n", + getprogname()); + exit(1); + } + } + argc -= optind; + argv += optind; + + if (argc >= 1) { if (setjmp(toplevel) != 0) exit(txrx_error); setpeer(argc, argv); @@ -182,7 +215,7 @@ const char *port; { struct addrinfo hints, *res0, *res; - int error; + int error, soopt; struct sockaddr_storage ss; const char *cause = "unknown"; @@ -227,6 +260,22 @@ break; } + if (f >= 0) { + soopt = 65536; + if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt)) + < 0) { + close(f); + f = -1; + cause = "setsockopt SNDBUF"; + } + if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, sizeof(soopt)) + < 0) { + close(f); + f = -1; + cause = "setsockopt RCVBUF"; + } + } + if (f < 0) warn("%s", cause); else { @@ -250,7 +299,7 @@ char *argv[]; { - if (argc < 2) { + if (argc < 1) { strcpy(line, "Connect "); printf("(to) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); @@ -512,6 +561,33 @@ printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); } +void +setblksize(argc, argv) + int argc; + char *argv[]; +{ + int t; + + if (argc < 2) { + strcpy(line, "blksize "); + printf("(blksize) "); + fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 8 || t > 65464) + printf("%s: bad value\n", argv[1]); + else + blksize = t; +} + +int def_rexmtval = TIMEOUT; int rexmtval = TIMEOUT; void @@ -790,3 +866,21 @@ verbose = !verbose; printf("Verbose mode %s.\n", verbose ? "on" : "off"); } + +void +settsize(argc, argv) + int argc; + char **argv; +{ + tsize = !tsize; + printf("Tsize mode %s.\n", tsize ? "on" : "off"); +} + +void +settimeoutopt(argc, argv) + int argc; + char **argv; +{ + tout = !tout; + printf("Timeout option %s.\n", tout ? "on" : "off"); +} Index: tftp.1 =================================================================== --- tftp.1 (revision 181160) +++ tftp.1 (working copy) @@ -1,4 +1,4 @@ -.\" Copyright (c) 1990, 1993, 1994 +.\" $NetBSD: tftp.1,v 1.16 2003/06/11 01:44:32 briggs Exp $ .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -40,6 +40,7 @@ .Nd "trivial file transfer program" .Sh SYNOPSIS .Nm +.Op Fl e .Op Ar host Op Ar port .Sh DESCRIPTION The @@ -57,6 +58,16 @@ as the default host for future transfers (see the .Ic connect command below). +.Pp +The optional +.Fl e +argument sets a binary transfer mode as well as setting the extended options +as if +.Cm tout , +.Cm tsize , +and +.Cm blksize 65464 , +had been given. .Sh COMMANDS Once .Nm @@ -76,6 +87,21 @@ Shorthand for .Ic mode Cm binary . .Pp +.It Cm blksize Ar blk-size +Set the tftp blksize option to +.Ar blk-size +octets (8-bit bytes). +Since the number of blocks in a tftp +.Cm get +or +.Cm put +is 65535, the default block size of 512 bytes only allows a maximum of +just under 32 megabytes to be transferred. +The value given for +.Ar blk-size +must be between 8 and 65464, inclusive. +Note that many servers will not respect this option. +.Pp .It Ic connect Ar host Op Ar port Set the .Ar host @@ -169,9 +195,26 @@ .It Ic timeout Ar total-transmission-timeout Set the total transmission timeout, in seconds. .Pp +.It Cm tout +Toggle the tftp +.Dq timeout +option. +If enabled, the client will pass its +.Ar retransmission-timeout +to the server. +Note that many servers will not respect this option. +.Pp .It Ic trace Toggle packet tracing. .Pp +.It Cm tsize +Toggle the tftp +.Dq tsize +option. +If enabled, the client will pass and request the filesize of a file at the +beginning of a file transfer. +Note that many servers will not respect this option. +.Pp .It Ic verbose Toggle verbose mode. .El Index: tftp.c =================================================================== --- tftp.c (revision 181160) +++ tftp.c (working copy) @@ -40,13 +40,15 @@ #include __FBSDID("$FreeBSD$"); -/* Many bug fixes are from Jim Guyton */ +/* $NetBSD: tftp.c,v 1.17 2003/06/11 01:44:32 briggs Exp $ */ /* * TFTP User Program -- Protocol Machines */ #include +#include #include +#include #include #include @@ -59,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -70,18 +73,22 @@ 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 volatile int txrx_error; -#define PKTSIZE SEGSIZE+4 char ackbuf[PKTSIZE]; int timeout; jmp_buf toplevel; jmp_buf timeoutbuf; static void nak(int, struct sockaddr *); -static int makerequest(int, const char *, struct tftphdr *, const char *); +static int makerequest(int, const char *, struct tftphdr *, const char *, off_t); static void printstats(const char *, unsigned long); static void startclock(void); static void stopclock(void); @@ -89,6 +96,57 @@ static void tpacket(const char *, struct tftphdr *, int); static int cmpport(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. */ @@ -100,11 +158,13 @@ { struct tftphdr *ap; /* data and ack packets */ struct tftphdr *dp; - int n; + int j, n; volatile unsigned short block; volatile int size, convert; volatile unsigned long amount; struct sockaddr_storage from; + struct stat sbuf; + off_t filesize = 0; socklen_t fromlen; FILE *file; struct sockaddr_storage peer; @@ -113,6 +173,13 @@ 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; @@ -123,10 +190,10 @@ 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, (struct sockaddr *)&peer); break; @@ -145,7 +212,8 @@ warn("sendto"); goto abort; } - read_ahead(file, convert); + if (block) + read_ahead(file, blksize, convert); for ( ; ; ) { alarm(rexmtval); do { @@ -170,7 +238,6 @@ tpacket("received", ap, n); /* should verify packet came from server */ ap->th_opcode = ntohs(ap->th_opcode); - ap->th_block = ntohs(ap->th_block); if (ap->th_opcode == ERROR) { printf("Error code %d: %s\n", ap->th_code, ap->th_msg); @@ -178,15 +245,25 @@ goto abort; } if (ap->th_opcode == ACK) { - int j; - + ap->th_block = ntohs(ap->th_block); + 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); @@ -195,11 +272,19 @@ 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(); @@ -219,7 +304,7 @@ { struct tftphdr *ap; struct tftphdr *dp; - int n; + int j, n, oack = 0, readlen; volatile unsigned short block; volatile int size, firsttrip; volatile unsigned long amount; @@ -244,11 +329,13 @@ 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++; } @@ -268,7 +355,7 @@ 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); @@ -288,7 +375,6 @@ tpacket("received", dp, n); /* should verify client address */ dp->th_opcode = ntohs(dp->th_opcode); - dp->th_block = ntohs(dp->th_block); if (dp->th_opcode == ERROR) { printf("Error code %d: %s\n", dp->th_code, dp->th_msg); @@ -296,15 +382,19 @@ goto abort; } if (dp->th_opcode == DATA) { - int j; - + dp->th_block = ntohs(dp->th_block); + 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); } @@ -312,6 +402,19 @@ 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); @@ -320,7 +423,7 @@ break; } amount += size; - } while (size == SEGSIZE); + } while (size == blksize); abort: /* ok to ack, since user */ ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ ap->th_block = htons((u_short)block); @@ -335,11 +438,12 @@ } 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; { char *cp; @@ -351,6 +455,30 @@ 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); } @@ -366,6 +494,7 @@ { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, + { EOPTNEG, "Option negotiation failed" }, { -1, 0 } }; @@ -409,11 +538,13 @@ int n; { static const char *opcodes[] = - { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; - char *cp, *file; + { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; + char *cp, *file, *endp, *opt = NULL; + const char *spc; u_short op = ntohs(tp->th_opcode); + 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]); @@ -423,8 +554,25 @@ case WRQ: n -= 2; file = cp = tp->th_stuff; - cp = index(cp, '\0'); - printf("\n", file, cp + 1); + endp = cp + n - 1; + if (*endp != '\0') { /* Shouldn't happen, but... */ + *endp = '\0'; + } + cp = strchr(cp, '\0') + 1; + printf("\n", file, cp); + cp = strchr(cp, '\0') + 1; + o = 0; + while (cp < endp) { + i = strlen(cp) + 1; + if (o) { + printf(", %s=%s", opt, cp); + } else { + opt = cp; + } + o = (o + 1) % 2; + cp += i; + } + printf(">\n"); break; case DATA: @@ -438,6 +586,30 @@ 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; } }