Support negotiation and use of TFTP Option Extension (rfc 2347) for the

'blksize' option (rfc 2348) and the 'timeout' and 'tsize' options (rfc 2349).

Contributed by Wasabi Systems, Inc.
This commit is contained in:
briggs 2003-06-11 01:43:52 +00:00
parent 39d51ab631
commit 4441128638
7 changed files with 708 additions and 87 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: tftpd.8,v 1.17 2003/05/14 12:15:17 wiz Exp $ .\" $NetBSD: tftpd.8,v 1.18 2003/06/11 01:43:52 briggs Exp $
.\" .\"
.\" Copyright (c) 1983, 1991, 1993 .\" Copyright (c) 1983, 1991, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -155,6 +155,24 @@ as well.
.%D July 1992 .%D July 1992
.%T "The TFTP Protocol (Revision 2)" .%T "The TFTP Protocol (Revision 2)"
.Re .Re
.Rs
.%R RFC
.%N 2347
.%D May 1998
.%T "TFTP Option Extension"
.Re
.Rs
.%R RFC
.%N 2348
.%D May 1998
.%T "TFTP Blocksize Option"
.Re
.Rs
.%R RFC
.%N 2349
.%D May 1998
.%T "TFTP Timeout Interval and Transfer Size Options"
.Re
.Sh HISTORY .Sh HISTORY
The The
.Nm .Nm
@ -174,9 +192,13 @@ flags appeared in
.Nx 1.4 . .Nx 1.4 .
.Pp .Pp
IPv6 support was implemented by WIDE/KAME project in 1999. IPv6 support was implemented by WIDE/KAME project in 1999.
.Pp
TFTP options were implemented by Wasabi Systems, Inc., in 2003,
and first appeared in
.Nx 2.0 .
.Sh BUGS .Sh BUGS
Files larger than 33488896 octets (65535 blocks) cannot be transferred Files larger than 33488896 octets (65535 blocks) cannot be transferred
without client and server supporting blocksize negotiation (RFC1783). without client and server supporting blocksize negotiation (RFCs 2347 & 2348).
.Pp .Pp
Many tftp clients will not transfer files over 16744448 octets (32767 blocks). Many tftp clients will not transfer files over 16744448 octets (32767 blocks).
.Sh SECURITY CONSIDERATIONS .Sh SECURITY CONSIDERATIONS

View File

@ -1,4 +1,4 @@
/* $NetBSD: tftpd.c,v 1.24 2001/10/09 18:46:18 christos Exp $ */ /* $NetBSD: tftpd.c,v 1.25 2003/06/11 01:43:52 briggs Exp $ */
/* /*
* Copyright (c) 1983, 1993 * Copyright (c) 1983, 1993
@ -40,7 +40,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
#if 0 #if 0
static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93";
#else #else
__RCSID("$NetBSD: tftpd.c,v 1.24 2001/10/09 18:46:18 christos Exp $"); __RCSID("$NetBSD: tftpd.c,v 1.25 2003/06/11 01:43:52 briggs Exp $");
#endif #endif
#endif /* not lint */ #endif /* not lint */
@ -86,13 +86,17 @@ int peer;
int rexmtval = TIMEOUT; int rexmtval = TIMEOUT;
int maxtimeout = 5*TIMEOUT; int maxtimeout = 5*TIMEOUT;
#define PKTSIZE SEGSIZE+4 char buf[MAXPKTSIZE];
char buf[PKTSIZE];
char ackbuf[PKTSIZE]; char ackbuf[PKTSIZE];
char oackbuf[PKTSIZE];
struct sockaddr_storage from; struct sockaddr_storage from;
int fromlen; int fromlen;
int debug; int debug;
int tftp_opt_tsize = 0;
int tftp_blksize = SEGSIZE;
int tftp_tsize = 0;
/* /*
* Null-terminated directory prefix list for absolute pathname requests and * Null-terminated directory prefix list for absolute pathname requests and
* search list for relative pathname requests. * search list for relative pathname requests.
@ -119,8 +123,8 @@ static void usage(void);
static char *verifyhost(struct sockaddr *); static char *verifyhost(struct sockaddr *);
void justquit(int); void justquit(int);
int main(int, char **); int main(int, char **);
void recvfile(struct formats *); void recvfile(struct formats *, int, int);
void sendfile(struct formats *); void sendfile(struct formats *, int, int);
void timer(int); void timer(int);
static const char *opcode(int); static const char *opcode(int);
int validate_access(char **, int); int validate_access(char **, int);
@ -128,8 +132,8 @@ int validate_access(char **, int);
struct formats { struct formats {
const char *f_mode; const char *f_mode;
int (*f_validate)(char **, int); int (*f_validate)(char **, int);
void (*f_send)(struct formats *); void (*f_send)(struct formats *, int, int);
void (*f_recv)(struct formats *); void (*f_recv)(struct formats *, int, int);
int f_convert; int f_convert;
} formats[] = { } formats[] = {
{ "netascii", validate_access, sendfile, recvfile, 1 }, { "netascii", validate_access, sendfile, recvfile, 1 },
@ -156,7 +160,7 @@ main(int argc, char *argv[])
struct tftphdr *tp; struct tftphdr *tp;
char *tgtuser, *tgtgroup, *ep; char *tgtuser, *tgtgroup, *ep;
int n, ch, on, fd; int n, ch, on, fd;
int len; int len, soopt;
uid_t curuid, tgtuid; uid_t curuid, tgtuid;
gid_t curgid, tgtgid; gid_t curgid, tgtgid;
long nid; long nid;
@ -387,6 +391,16 @@ main(int argc, char *argv[])
syslog(LOG_ERR, "connect: %m"); syslog(LOG_ERR, "connect: %m");
exit(1); exit(1);
} }
soopt = 65536; /* larger than we'll ever need */
if (setsockopt(peer, SOL_SOCKET, SO_SNDBUF, (void *) &soopt, sizeof(soopt)) < 0) {
syslog(LOG_ERR, "set SNDBUF: %m");
exit(1);
}
if (setsockopt(peer, SOL_SOCKET, SO_RCVBUF, (void *) &soopt, sizeof(soopt)) < 0) {
syslog(LOG_ERR, "set RCVBUF: %m");
exit(1);
}
tp = (struct tftphdr *)buf; tp = (struct tftphdr *)buf;
tp->th_opcode = ntohs(tp->th_opcode); tp->th_opcode = ntohs(tp->th_opcode);
if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
@ -394,6 +408,200 @@ main(int argc, char *argv[])
exit(1); exit(1);
} }
static int
blk_handler(struct tftphdr *tp, char *opt, char *val, char *ack,
int *ackl, int *ec)
{
unsigned long bsize;
char *endp;
int l;
/*
* On these failures, we could just ignore the blocksize option.
* Perhaps that should be a command-line option.
*/
errno = 0;
bsize = strtoul(val, &endp, 10);
if ((bsize == ULONG_MAX && errno == ERANGE) || *endp) {
syslog(LOG_NOTICE, "%s: %s request for %s: "
"illegal value %s for blksize option",
verifyhost((struct sockaddr *)&from),
tp->th_opcode == WRQ ? "write" : "read",
tp->th_stuff, val);
return 0;
}
if (bsize < 8 || bsize > 65464) {
syslog(LOG_NOTICE, "%s: %s request for %s: "
"out of range value %s for blksize option",
verifyhost((struct sockaddr *)&from),
tp->th_opcode == WRQ ? "write" : "read",
tp->th_stuff, val);
return 0;
}
tftp_blksize = bsize;
strcpy(ack + *ackl, "blksize");
*ackl += 8;
l = sprintf(ack + *ackl, "%lu", bsize);
*ackl += l + 1;
return 0;
}
static int
timeout_handler(struct tftphdr *tp, char *opt, char *val, char *ack,
int *ackl, int *ec)
{
unsigned long tout;
char *endp;
int l;
errno = 0;
tout = strtoul(val, &endp, 10);
if ((tout == ULONG_MAX && errno == ERANGE) || *endp) {
syslog(LOG_NOTICE, "%s: %s request for %s: "
"illegal value %s for timeout option",
verifyhost((struct sockaddr *)&from),
tp->th_opcode == WRQ ? "write" : "read",
tp->th_stuff, val);
return 0;
}
if (tout < 1 || tout > 255) {
syslog(LOG_NOTICE, "%s: %s request for %s: "
"out of range value %s for timeout option",
verifyhost((struct sockaddr *)&from),
tp->th_opcode == WRQ ? "write" : "read",
tp->th_stuff, val);
return 0;
}
rexmtval = tout;
strcpy(ack + *ackl, "timeout");
*ackl += 8;
l = sprintf(ack + *ackl, "%lu", tout);
*ackl += l + 1;
/*
* Arbitrarily pick a maximum timeout on a request to 3
* retransmissions if the interval timeout is more than
* one minute. Longest possible timeout is therefore
* 3 * 255 - 1, or 764 seconds.
*/
if (rexmtval > 60) {
maxtimeout = rexmtval * 3;
} else {
maxtimeout = rexmtval * 5;
}
return 0;
}
static int
tsize_handler(struct tftphdr *tp, char *opt, char *val, char *ack,
int *ackl, int *ec)
{
unsigned long fsize;
char *endp;
/*
* Maximum file even with extended tftp is 65535 blocks of
* length 65464, or 4290183240 octets (4784056 less than 2^32).
* unsigned long is at least 32 bits on all NetBSD archs.
*/
errno = 0;
fsize = strtoul(val, &endp, 10);
if ((fsize == ULONG_MAX && errno == ERANGE) || *endp) {
syslog(LOG_NOTICE, "%s: %s request for %s: "
"illegal value %s for tsize option",
verifyhost((struct sockaddr *)&from),
tp->th_opcode == WRQ ? "write" : "read",
tp->th_stuff, val);
return 0;
}
if (fsize > (unsigned long) 65535 * 65464) {
syslog(LOG_NOTICE, "%s: %s request for %s: "
"out of range value %s for tsize option",
verifyhost((struct sockaddr *)&from),
tp->th_opcode == WRQ ? "write" : "read",
tp->th_stuff, val);
return 0;
}
tftp_opt_tsize = 1;
tftp_tsize = fsize;
/*
* We will report this later -- either replying with the fsize (WRQ)
* or replying with the actual filesize (RRQ).
*/
return 0;
}
struct tftp_options {
char *o_name;
int (*o_handler)(struct tftphdr *, char *, char *, char *,
int *, int *);
} options[] = {
{ "blksize", blk_handler },
{ "timeout", timeout_handler },
{ "tsize", tsize_handler },
{ NULL, NULL }
};
/*
* Get options for an extended tftp session. Stuff the ones we
* recognize in oackbuf.
*/
static int
get_options(struct tftphdr *tp, char *cp, int size, char *ackb,
int *alen, int *err)
{
struct tftp_options *op;
char *option, *value, *endp;
int r, rv=0, ec=0;
endp = cp + size;
while (cp < endp) {
option = cp;
while (*cp && cp < endp) {
*cp = tolower(*cp);
cp++;
}
if (*cp) {
/* if we have garbage at the end, just ignore it */
break;
}
cp++; /* skip over NUL */
value = cp;
while (*cp && cp < endp) {
cp++;
}
if (*cp) {
/* if we have garbage at the end, just ignore it */
break;
}
cp++;
for (op = options; op->o_name; op++) {
if (strcmp(op->o_name, option) == 0)
break;
}
if (op->o_name) {
r = op->o_handler(tp, option, value, ackb, alen, &ec);
if (r < 0) {
rv = -1;
break;
}
rv++;
} /* else ignore unknown options */
}
if (rv < 0)
*err = ec;
return rv;
}
/* /*
* Handle initial connection protocol. * Handle initial connection protocol.
*/ */
@ -403,7 +611,7 @@ tftp(struct tftphdr *tp, int size)
struct formats *pf; struct formats *pf;
char *cp; char *cp;
char *filename, *mode; char *filename, *mode;
int first, ecode; int first, ecode, alen, etftp=0, r;
first = 1; first = 1;
mode = NULL; mode = NULL;
@ -434,6 +642,28 @@ again:
nak(EBADOP); nak(EBADOP);
exit(1); exit(1);
} }
/*
* cp currently points to the NUL byte following the mode.
*
* If we have some valid options, then let's assume that we're
* now dealing with an extended tftp session. Note that if we
* don't get any options, then we *must* assume that we do not
* have an extended tftp session. If we get options, we fill
* in the ack buf to acknowledge them. If we skip that, then
* the client *must* assume that we are not using an extended
* session.
*/
size -= (++cp - (char *) tp);
if (size > 0 && *cp) {
alen = 2; /* Skip over opcode */
r = get_options(tp, cp, size, oackbuf, &alen, &ecode);
if (r > 0) {
etftp = 1;
} else if (r < 0) {
nak(ecode);
exit(1);
}
}
ecode = (*pf->f_validate)(&filename, tp->th_opcode); ecode = (*pf->f_validate)(&filename, tp->th_opcode);
if (logging) { if (logging) {
syslog(LOG_INFO, "%s: %s request for %s: %s", syslog(LOG_INFO, "%s: %s request for %s: %s",
@ -451,10 +681,26 @@ again:
nak(ecode); nak(ecode);
exit(1); exit(1);
} }
if (etftp) {
struct tftphdr *oack_h;
if (tftp_opt_tsize) {
int l;
strcpy(oackbuf + alen, "tsize");
alen += 6;
l = sprintf(oackbuf + alen, "%u", tftp_tsize);
alen += l + 1;
}
oack_h = (struct tftphdr *) oackbuf;
oack_h->th_opcode = htons(OACK);
}
if (tp->th_opcode == WRQ) if (tp->th_opcode == WRQ)
(*pf->f_recv)(pf); (*pf->f_recv)(pf, etftp, alen);
else else
(*pf->f_send)(pf); (*pf->f_send)(pf, etftp, alen);
exit(0); exit(0);
} }
@ -562,6 +808,10 @@ validate_access(char **filep, int mode)
*filep = filename; *filep = filename;
} }
} }
if (tftp_opt_tsize && mode == RRQ)
tftp_tsize = (unsigned long) stbuf.st_size;
fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY | O_TRUNC); fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY | O_TRUNC);
if (fd < 0) if (fd < 0)
return (errno + 100); return (errno + 100);
@ -602,6 +852,8 @@ opcode(int code)
return "ACK"; return "ACK";
case ERROR: case ERROR:
return "ERROR"; return "ERROR";
case OACK:
return "OACK";
default: default:
(void)snprintf(buf, sizeof(buf), "*code %d*", code); (void)snprintf(buf, sizeof(buf), "*code %d*", code);
return buf; return buf;
@ -612,7 +864,7 @@ opcode(int code)
* Send the requested file. * Send the requested file.
*/ */
void void
sendfile(struct formats *pf) sendfile(struct formats *pf, int etftp, int acklength)
{ {
volatile unsigned int block; volatile unsigned int block;
struct tftphdr *dp; struct tftphdr *dp;
@ -620,31 +872,42 @@ sendfile(struct formats *pf)
int size, n; int size, n;
signal(SIGALRM, timer); signal(SIGALRM, timer);
dp = r_init();
ap = (struct tftphdr *)ackbuf; ap = (struct tftphdr *)ackbuf;
block = 1; if (etftp) {
dp = (struct tftphdr *)oackbuf;
size = acklength - 4;
block = 0;
} else {
dp = r_init();
size = 0;
block = 1;
}
do { do {
size = readit(file, &dp, pf->f_convert); if (block > 0) {
if (size < 0) { size = readit(file, &dp, tftp_blksize, pf->f_convert);
nak(errno + 100); if (size < 0) {
goto abort; nak(errno + 100);
goto abort;
}
dp->th_opcode = htons((u_short)DATA);
dp->th_block = htons((u_short)block);
} }
dp->th_opcode = htons((u_short)DATA);
dp->th_block = htons((u_short)block);
timeout = 0; timeout = 0;
(void)setjmp(timeoutbuf); (void)setjmp(timeoutbuf);
send_data: send_data:
if (debug) if (!etftp && debug)
syslog(LOG_DEBUG, "Send DATA %u", block); syslog(LOG_DEBUG, "Send DATA %u", block);
if (send(peer, dp, size + 4, 0) != size + 4) { if ((n = send(peer, dp, size + 4, 0)) != size + 4) {
syslog(LOG_ERR, "tftpd: write: %m"); syslog(LOG_ERR, "tftpd: write: %m");
goto abort; goto abort;
} }
read_ahead(file, pf->f_convert); if (block)
read_ahead(file, tftp_blksize, pf->f_convert);
for ( ; ; ) { for ( ; ; ) {
alarm(rexmtval); /* read the ack */ alarm(rexmtval); /* read the ack */
n = recv(peer, ackbuf, sizeof (ackbuf), 0); n = recv(peer, ackbuf, tftp_blksize, 0);
alarm(0); alarm(0);
if (n < 0) { if (n < 0) {
syslog(LOG_ERR, "tftpd: read: %m"); syslog(LOG_ERR, "tftpd: read: %m");
@ -657,13 +920,19 @@ send_data:
goto abort; goto abort;
case ACK: case ACK:
if (ap->th_block == 0) {
etftp = 0;
acklength = 0;
dp = r_init();
goto done;
}
if (ap->th_block == block) if (ap->th_block == block)
goto done; goto done;
if (debug) if (debug)
syslog(LOG_DEBUG, "Resync ACK %u != %u", syslog(LOG_DEBUG, "Resync ACK %u != %u",
(unsigned int)ap->th_block, block); (unsigned int)ap->th_block, block);
/* Re-synchronize with the other side */ /* Re-synchronize with the other side */
(void) synchnet(peer); (void) synchnet(peer, tftp_blksize);
if (ap->th_block == (block -1)) if (ap->th_block == (block -1))
goto send_data; goto send_data;
default: default:
@ -676,7 +945,7 @@ done:
if (debug) if (debug)
syslog(LOG_DEBUG, "Received ACK for block %u", block); syslog(LOG_DEBUG, "Received ACK for block %u", block);
block++; block++;
} while (size == SEGSIZE); } while (size == tftp_blksize || block == 1);
abort: abort:
(void) fclose(file); (void) fclose(file);
} }
@ -692,7 +961,7 @@ justquit(int dummy)
* Receive a file. * Receive a file.
*/ */
void void
recvfile(struct formats *pf) recvfile(struct formats *pf, int etftp, int acklength)
{ {
volatile unsigned int block; volatile unsigned int block;
struct tftphdr *dp; struct tftphdr *dp;
@ -701,30 +970,35 @@ recvfile(struct formats *pf)
signal(SIGALRM, timer); signal(SIGALRM, timer);
dp = w_init(); dp = w_init();
ap = (struct tftphdr *)ackbuf; ap = (struct tftphdr *)oackbuf;
block = 0; block = 0;
do { do {
timeout = 0; timeout = 0;
ap->th_opcode = htons((u_short)ACK); if (etftp == 0) {
ap->th_block = htons((u_short)block); ap = (struct tftphdr *)ackbuf;
ap->th_opcode = htons((u_short)ACK);
ap->th_block = htons((u_short)block);
acklength = 4;
}
if (debug) if (debug)
syslog(LOG_DEBUG, "Sending ACK for block %u\n", block); syslog(LOG_DEBUG, "Sending ACK for block %u\n", block);
block++; block++;
(void) setjmp(timeoutbuf); (void) setjmp(timeoutbuf);
send_ack: send_ack:
if (send(peer, ackbuf, 4, 0) != 4) { if (send(peer, ap, acklength, 0) != acklength) {
syslog(LOG_ERR, "tftpd: write: %m"); syslog(LOG_ERR, "tftpd: write: %m");
goto abort; goto abort;
} }
write_behind(file, pf->f_convert); write_behind(file, pf->f_convert);
for ( ; ; ) { for ( ; ; ) {
alarm(rexmtval); alarm(rexmtval);
n = recv(peer, dp, PKTSIZE, 0); n = recv(peer, dp, tftp_blksize + 4, 0);
alarm(0); alarm(0);
if (n < 0) { /* really? */ if (n < 0) { /* really? */
syslog(LOG_ERR, "tftpd: read: %m"); syslog(LOG_ERR, "tftpd: read: %m");
goto abort; goto abort;
} }
etftp = 0;
dp->th_opcode = ntohs((u_short)dp->th_opcode); dp->th_opcode = ntohs((u_short)dp->th_opcode);
dp->th_block = ntohs((u_short)dp->th_block); dp->th_block = ntohs((u_short)dp->th_block);
if (debug) if (debug)
@ -742,7 +1016,7 @@ send_ack:
syslog(LOG_DEBUG, "Resync %u != %u", syslog(LOG_DEBUG, "Resync %u != %u",
(unsigned int)dp->th_block, block); (unsigned int)dp->th_block, block);
/* Re-synchronize with the other side */ /* Re-synchronize with the other side */
(void) synchnet(peer); (void) synchnet(peer, tftp_blksize);
if (dp->th_block == (block-1)) if (dp->th_block == (block-1))
goto send_ack; /* rexmit */ goto send_ack; /* rexmit */
break; break;
@ -762,7 +1036,7 @@ done:
else nak(ENOSPACE); else nak(ENOSPACE);
goto abort; goto abort;
} }
} while (size == SEGSIZE); } while (size == tftp_blksize);
write_behind(file, pf->f_convert); write_behind(file, pf->f_convert);
(void) fclose(file); /* close data file */ (void) fclose(file); /* close data file */
@ -797,6 +1071,7 @@ const struct errmsg {
{ EBADID, "Unknown transfer ID" }, { EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" }, { EEXISTS, "File already exists" },
{ ENOUSER, "No such user" }, { ENOUSER, "No such user" },
{ EOPTNEG, "Option negotiation failed" },
{ -1, 0 } { -1, 0 }
}; };

View File

@ -1,4 +1,4 @@
/* $NetBSD: main.c,v 1.14 2000/12/30 18:00:18 itojun Exp $ */ /* $NetBSD: main.c,v 1.15 2003/06/11 01:44:32 briggs Exp $ */
/* /*
* Copyright (c) 1983, 1993 * Copyright (c) 1983, 1993
@ -40,7 +40,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
#if 0 #if 0
static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
#else #else
__RCSID("$NetBSD: main.c,v 1.14 2000/12/30 18:00:18 itojun Exp $"); __RCSID("$NetBSD: main.c,v 1.15 2003/06/11 01:44:32 briggs Exp $");
#endif #endif
#endif /* not lint */ #endif /* not lint */
@ -55,6 +55,7 @@ __RCSID("$NetBSD: main.c,v 1.14 2000/12/30 18:00:18 itojun Exp $");
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <arpa/tftp.h>
#include <ctype.h> #include <ctype.h>
#include <fcntl.h> #include <fcntl.h>
@ -77,6 +78,10 @@ struct sockaddr_storage peeraddr;
int f; int f;
int trace; int trace;
int verbose; int verbose;
int tsize=0;
int tout=0;
int def_blksize=SEGSIZE;
int blksize=SEGSIZE;
int connected; int connected;
char mode[32]; char mode[32];
char line[LBUFLEN]; char line[LBUFLEN];
@ -98,6 +103,9 @@ void setrexmt __P((int, char **));
void settimeout __P((int, char **)); void settimeout __P((int, char **));
void settrace __P((int, char **)); void settrace __P((int, char **));
void setverbose __P((int, char **)); void setverbose __P((int, char **));
void setblksize __P((int, char **));
void settsize __P((int, char **));
void settimeoutopt __P((int, char **));
void status __P((int, char **)); void status __P((int, char **));
char *tail __P((char *)); char *tail __P((char *));
int main __P((int, char *[])); int main __P((int, char *[]));
@ -121,6 +129,9 @@ struct cmd {
char vhelp[] = "toggle verbose mode"; char vhelp[] = "toggle verbose mode";
char thelp[] = "toggle packet tracing"; 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 chelp[] = "connect to remote tftp";
char qhelp[] = "exit tftp"; char qhelp[] = "exit tftp";
char hhelp[] = "print help information"; char hhelp[] = "print help information";
@ -140,12 +151,15 @@ struct cmd cmdtab[] = {
{ "get", rhelp, get }, { "get", rhelp, get },
{ "quit", qhelp, quit }, { "quit", qhelp, quit },
{ "verbose", vhelp, setverbose }, { "verbose", vhelp, setverbose },
{ "blksize", blhelp, setblksize },
{ "tsize", tshelp, settsize },
{ "trace", thelp, settrace }, { "trace", thelp, settrace },
{ "status", sthelp, status }, { "status", sthelp, status },
{ "binary", bnhelp, setbinary }, { "binary", bnhelp, setbinary },
{ "ascii", ashelp, setascii }, { "ascii", ashelp, setascii },
{ "rexmt", xhelp, setrexmt }, { "rexmt", xhelp, setrexmt },
{ "timeout", ihelp, settimeout }, { "timeout", ihelp, settimeout },
{ "tout", tohelp, settimeoutopt },
{ "?", hhelp, help }, { "?", hhelp, help },
{ 0 } { 0 }
}; };
@ -155,10 +169,31 @@ main(argc, argv)
int argc; int argc;
char *argv[]; char *argv[];
{ {
int c;
f = -1; f = -1;
strcpy(mode, "netascii"); strcpy(mode, "netascii");
signal(SIGINT, intr); signal(SIGINT, intr);
if (argc > 1) {
setprogname(argv[0]);
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) if (setjmp(toplevel) != 0)
exit(0); exit(0);
setpeer(argc, argv); setpeer(argc, argv);
@ -177,7 +212,7 @@ setpeer0(host, port)
char *port; char *port;
{ {
struct addrinfo hints, *res0, *res; struct addrinfo hints, *res0, *res;
int error; int error, soopt;
struct sockaddr_storage ss; struct sockaddr_storage ss;
char *cause = "unknown"; char *cause = "unknown";
@ -222,6 +257,22 @@ setpeer0(host, port)
break; 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) if (f < 0)
warn("%s", cause); warn("%s", cause);
else { else {
@ -245,7 +296,7 @@ setpeer(argc, argv)
char *argv[]; char *argv[];
{ {
if (argc < 2) { if (argc < 1) {
strcpy(line, "Connect "); strcpy(line, "Connect ");
printf("(to) "); printf("(to) ");
fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
@ -253,14 +304,14 @@ setpeer(argc, argv)
argc = margc; argc = margc;
argv = margv; argv = margv;
} }
if ((argc < 2) || (argc > 3)) { if ((argc < 1) || (argc > 2)) {
printf("usage: %s host-name [port]\n", argv[0]); printf("usage: %s [-e] host-name [port]\n", getprogname());
return; return;
} }
if (argc == 2) if (argc == 1)
setpeer0(argv[1], NULL); setpeer0(argv[0], NULL);
else else
setpeer0(argv[1], argv[2]); setpeer0(argv[0], argv[1]);
} }
struct modes { struct modes {
@ -506,6 +557,33 @@ getusage(s)
printf(" %s file file ... file if connected\n", s); printf(" %s file file ... file if connected\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; int rexmtval = TIMEOUT;
void void
@ -749,3 +827,21 @@ setverbose(argc, argv)
verbose = !verbose; verbose = !verbose;
printf("Verbose mode %s.\n", verbose ? "on" : "off"); 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");
}

View File

@ -1,4 +1,4 @@
.\" $NetBSD: tftp.1,v 1.15 2003/02/25 10:35:57 wiz Exp $ .\" $NetBSD: tftp.1,v 1.16 2003/06/11 01:44:32 briggs Exp $
.\" .\"
.\" Copyright (c) 1990, 1993, 1994 .\" Copyright (c) 1990, 1993, 1994
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -41,7 +41,9 @@
.Nd trivial file transfer program .Nd trivial file transfer program
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl e
.Op Ar host .Op Ar host
.Op Ar port
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
is the user interface to the Internet is the user interface to the Internet
@ -50,13 +52,27 @@ is the user interface to the Internet
which allows users to transfer files to and from a remote machine. which allows users to transfer files to and from a remote machine.
The remote The remote
.Ar host .Ar host
(and optional
.Ar port )
may be specified on the command line, in which case may be specified on the command line, in which case
.Nm .Nm
uses uses
.Ar host .Ar host
as the default host for future transfers (see the (and
.Ar port )
as the default for future transfers (see the
.Cm connect .Cm connect
command below). 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 .Sh COMMANDS
Once Once
.Nm .Nm
@ -74,6 +90,19 @@ Shorthand for "mode ascii"
.It Cm binary .It Cm binary
Shorthand for "mode binary" Shorthand for "mode binary"
.Pp .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 Cm connect Ar host-name Op Ar port .It Cm connect Ar host-name Op Ar port
Set the Set the
.Ar host .Ar host
@ -158,9 +187,20 @@ Show current status.
.It Cm timeout Ar total-transmission-timeout .It Cm timeout Ar total-transmission-timeout
Set the total transmission timeout, in seconds. Set the total transmission timeout, in seconds.
.Pp .Pp
.It Cm tout
Toggle the tftp "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 Cm trace .It Cm trace
Toggle packet tracing. Toggle packet tracing.
.Pp .Pp
.It Cm tsize
Toggle the tftp "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 Cm verbose .It Cm verbose
Toggle verbose mode. Toggle verbose mode.
.El .El
@ -170,6 +210,9 @@ The
command appeared in command appeared in
.Bx 4.3 . .Bx 4.3 .
IPv6 support was implemented by WIDE/KAME project in 1999. IPv6 support was implemented by WIDE/KAME project in 1999.
TFTP options were implemented by Wasabi Systems, Inc., in 2003,
and first appeared in
.Nx 2.0 .
.Sh SECURITY CONSIDERATIONS .Sh SECURITY CONSIDERATIONS
Because there is no user-login or validation within Because there is no user-login or validation within
the the

View File

@ -1,4 +1,4 @@
/* $NetBSD: tftp.c,v 1.16 2003/02/01 16:42:31 wiz Exp $ */ /* $NetBSD: tftp.c,v 1.17 2003/06/11 01:44:32 briggs Exp $ */
/* /*
* Copyright (c) 1983, 1993 * Copyright (c) 1983, 1993
@ -38,7 +38,7 @@
#if 0 #if 0
static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93";
#else #else
__RCSID("$NetBSD: tftp.c,v 1.16 2003/02/01 16:42:31 wiz Exp $"); __RCSID("$NetBSD: tftp.c,v 1.17 2003/06/11 01:44:32 briggs Exp $");
#endif #endif
#endif /* not lint */ #endif /* not lint */
@ -48,7 +48,9 @@ __RCSID("$NetBSD: tftp.c,v 1.16 2003/02/01 16:42:31 wiz Exp $");
* TFTP User Program -- Protocol Machines * TFTP User Program -- Protocol Machines
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -60,6 +62,7 @@ __RCSID("$NetBSD: tftp.c,v 1.16 2003/02/01 16:42:31 wiz Exp $");
#include <setjmp.h> #include <setjmp.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <netdb.h> #include <netdb.h>
@ -71,17 +74,21 @@ extern struct sockaddr_storage peeraddr; /* filled in by main */
extern int f; /* the opened socket */ extern int f; /* the opened socket */
extern int trace; extern int trace;
extern int verbose; extern int verbose;
extern int def_rexmtval;
extern int rexmtval; extern int rexmtval;
extern int maxtimeout; extern int maxtimeout;
extern int tsize;
extern int tout;
extern int def_blksize;
extern int blksize;
#define PKTSIZE SEGSIZE+4
char ackbuf[PKTSIZE]; char ackbuf[PKTSIZE];
int timeout; int timeout;
jmp_buf toplevel; jmp_buf toplevel;
jmp_buf timeoutbuf; jmp_buf timeoutbuf;
static void nak __P((int, struct sockaddr *)); static void nak __P((int, struct sockaddr *));
static int makerequest __P((int, const char *, struct tftphdr *, const char *)); static int makerequest __P((int, const char *, struct tftphdr *, const char *, off_t));
static void printstats __P((const char *, unsigned long)); static void printstats __P((const char *, unsigned long));
static void startclock __P((void)); static void startclock __P((void));
static void stopclock __P((void)); static void stopclock __P((void));
@ -89,6 +96,57 @@ static void timer __P((int));
static void tpacket __P((const char *, struct tftphdr *, int)); static void tpacket __P((const char *, struct tftphdr *, int));
static int cmpport __P((struct sockaddr *, struct sockaddr *)); 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. * Send the requested file.
*/ */
@ -105,6 +163,8 @@ sendfile(fd, name, mode)
volatile int size, convert; volatile int size, convert;
volatile unsigned long amount; volatile unsigned long amount;
struct sockaddr_storage from; struct sockaddr_storage from;
struct stat sbuf;
off_t filesize=0;
int fromlen; int fromlen;
FILE *file; FILE *file;
struct sockaddr_storage peer; struct sockaddr_storage peer;
@ -113,6 +173,13 @@ sendfile(fd, name, mode)
startclock(); /* start stat's clock */ startclock(); /* start stat's clock */
dp = r_init(); /* reset fillbuf/read-ahead code */ dp = r_init(); /* reset fillbuf/read-ahead code */
ap = (struct tftphdr *)ackbuf; ap = (struct tftphdr *)ackbuf;
if (tsize) {
if (fstat(fd, &sbuf) == 0) {
filesize = sbuf.st_size;
} else {
filesize = -1ULL;
}
}
file = fdopen(fd, "r"); file = fdopen(fd, "r");
convert = !strcmp(mode, "netascii"); convert = !strcmp(mode, "netascii");
block = 0; block = 0;
@ -123,10 +190,10 @@ sendfile(fd, name, mode)
signal(SIGALRM, timer); signal(SIGALRM, timer);
do { do {
if (block == 0) if (block == 0)
size = makerequest(WRQ, name, dp, mode) - 4; size = makerequest(WRQ, name, dp, mode, filesize) - 4;
else { else {
/* size = read(fd, dp->th_data, SEGSIZE); */ /* size = read(fd, dp->th_data, SEGSIZE); */
size = readit(file, &dp, convert); size = readit(file, &dp, blksize, convert);
if (size < 0) { if (size < 0) {
nak(errno + 100, (struct sockaddr *)&peer); nak(errno + 100, (struct sockaddr *)&peer);
break; break;
@ -145,7 +212,8 @@ send_data:
warn("sendto"); warn("sendto");
goto abort; goto abort;
} }
read_ahead(file, convert); if (block)
read_ahead(file, blksize, convert);
for ( ; ; ) { for ( ; ; ) {
alarm(rexmtval); alarm(rexmtval);
do { do {
@ -179,13 +247,24 @@ send_data:
if (ap->th_opcode == ACK) { if (ap->th_opcode == ACK) {
int j; 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) { if (ap->th_block == block) {
break; break;
} }
/* On an error, try to synchronize /* On an error, try to synchronize
* both sides. * both sides.
*/ */
j = synchnet(f); j = synchnet(f, blksize+4);
if (j && trace) { if (j && trace) {
printf("discarded %d packets\n", printf("discarded %d packets\n",
j); j);
@ -194,11 +273,19 @@ send_data:
goto 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) if (block > 0)
amount += size; amount += size;
block++; block++;
} while (size == SEGSIZE || block == 1); } while (size == blksize || block == 1);
abort: abort:
fclose(file); fclose(file);
stopclock(); stopclock();
@ -217,12 +304,12 @@ recvfile(fd, name, mode)
{ {
struct tftphdr *ap; struct tftphdr *ap;
struct tftphdr *dp; struct tftphdr *dp;
int n; int n, oack=0;
volatile unsigned int block; volatile unsigned int block;
volatile int size, firsttrip; volatile int size, firsttrip;
volatile unsigned long amount; volatile unsigned long amount;
struct sockaddr_storage from; struct sockaddr_storage from;
int fromlen; int fromlen, readlen;
FILE *file; FILE *file;
volatile int convert; /* true if converting crlf -> lf */ volatile int convert; /* true if converting crlf -> lf */
struct sockaddr_storage peer; struct sockaddr_storage peer;
@ -242,11 +329,13 @@ recvfile(fd, name, mode)
signal(SIGALRM, timer); signal(SIGALRM, timer);
do { do {
if (firsttrip) { if (firsttrip) {
size = makerequest(RRQ, name, ap, mode); size = makerequest(RRQ, name, ap, mode, 0);
readlen = PKTSIZE;
firsttrip = 0; firsttrip = 0;
} else { } else {
ap->th_opcode = htons((u_short)ACK); ap->th_opcode = htons((u_short)ACK);
ap->th_block = htons((u_short)(block)); ap->th_block = htons((u_short)(block));
readlen = blksize+4;
size = 4; size = 4;
block++; block++;
} }
@ -266,7 +355,7 @@ send_ack:
alarm(rexmtval); alarm(rexmtval);
do { do {
fromlen = sizeof(from); fromlen = sizeof(from);
n = recvfrom(f, dp, PKTSIZE, 0, n = recvfrom(f, dp, readlen, 0,
(struct sockaddr *)&from, &fromlen); (struct sockaddr *)&from, &fromlen);
} while (n <= 0); } while (n <= 0);
alarm(0); alarm(0);
@ -295,13 +384,18 @@ send_ack:
if (dp->th_opcode == DATA) { if (dp->th_opcode == DATA) {
int j; int j;
if (dp->th_block == 1 && !oack) {
/* no OACK, revert to defaults */
blksize = def_blksize;
rexmtval = def_rexmtval;
}
if (dp->th_block == block) { if (dp->th_block == block) {
break; /* have next packet */ break; /* have next packet */
} }
/* On an error, try to synchronize /* On an error, try to synchronize
* both sides. * both sides.
*/ */
j = synchnet(f); j = synchnet(f, blksize);
if (j && trace) { if (j && trace) {
printf("discarded %d packets\n", j); printf("discarded %d packets\n", j);
} }
@ -309,6 +403,19 @@ send_ack:
goto send_ack; /* resend 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 = write(fd, dp->th_data, n - 4); */
size = writeit(file, &dp, n - 4, convert); size = writeit(file, &dp, n - 4, convert);
@ -317,7 +424,7 @@ send_ack:
break; break;
} }
amount += size; amount += size;
} while (size == SEGSIZE); } while (size == blksize || block == 1);
abort: /* ok to ack, since user */ abort: /* ok to ack, since user */
ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
ap->th_block = htons((u_short)block); ap->th_block = htons((u_short)block);
@ -331,11 +438,12 @@ abort: /* ok to ack, since user */
} }
static int static int
makerequest(request, name, tp, mode) makerequest(request, name, tp, mode, filesize)
int request; int request;
const char *name; const char *name;
struct tftphdr *tp; struct tftphdr *tp;
const char *mode; const char *mode;
off_t filesize;
{ {
char *cp; char *cp;
@ -351,6 +459,30 @@ makerequest(request, name, tp, mode)
strcpy(cp, mode); strcpy(cp, mode);
cp += strlen(mode); cp += strlen(mode);
*cp++ = '\0'; *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); return (cp - (char *)tp);
} }
@ -366,6 +498,7 @@ const struct errmsg {
{ EBADID, "Unknown transfer ID" }, { EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" }, { EEXISTS, "File already exists" },
{ ENOUSER, "No such user" }, { ENOUSER, "No such user" },
{ EOPTNEG, "Option negotiation failed" },
{ -1, 0 } { -1, 0 }
}; };
@ -413,11 +546,12 @@ tpacket(s, tp, n)
int n; int n;
{ {
static char *opcodes[] = static char *opcodes[] =
{ "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
char *cp, *file; char *cp, *file, *endp, *opt, *spc;
u_short op = ntohs(tp->th_opcode); 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); printf("%s opcode=%x ", s, op);
else else
printf("%s %s ", s, opcodes[op]); printf("%s %s ", s, opcodes[op]);
@ -431,9 +565,26 @@ tpacket(s, tp, n)
#else #else
cp = (void *) &tp->th_stuff; cp = (void *) &tp->th_stuff;
#endif #endif
endp = cp + n - 1;
if (*endp != '\0') { /* Shouldn't happen, but... */
*endp = '\0';
}
file = cp; file = cp;
cp = strchr(cp, '\0'); cp = strchr(cp, '\0') + 1;
printf("<file=%s, mode=%s>\n", file, cp + 1); printf("<file=%s, mode=%s", 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; break;
case DATA: case DATA:
@ -447,6 +598,30 @@ tpacket(s, tp, n)
case ERROR: case ERROR:
printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
break; 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;
} }
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: tftpsubs.c,v 1.6 1999/07/12 20:19:21 itojun Exp $ */ /* $NetBSD: tftpsubs.c,v 1.7 2003/06/11 01:44:32 briggs Exp $ */
/* /*
* Copyright (c) 1983, 1993 * Copyright (c) 1983, 1993
@ -38,7 +38,7 @@
#if 0 #if 0
static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93";
#else #else
__RCSID("$NetBSD: tftpsubs.c,v 1.6 1999/07/12 20:19:21 itojun Exp $"); __RCSID("$NetBSD: tftpsubs.c,v 1.7 2003/06/11 01:44:32 briggs Exp $");
#endif #endif
#endif /* not lint */ #endif /* not lint */
@ -64,11 +64,9 @@ __RCSID("$NetBSD: tftpsubs.c,v 1.6 1999/07/12 20:19:21 itojun Exp $");
#include "tftpsubs.h" #include "tftpsubs.h"
#define PKTSIZE SEGSIZE+4 /* should be moved to tftp.h */
struct bf { struct bf {
int counter; /* size of data in buffer, or flag */ int counter; /* size of data in buffer, or flag */
char buf[PKTSIZE]; /* room for data packet */ char buf[MAXPKTSIZE]; /* room for data packet */
} bfs[2]; } bfs[2];
/* Values for bf.counter */ /* Values for bf.counter */
@ -85,8 +83,17 @@ int prevchar = -1; /* putbuf: previous char (cr check) */
static struct tftphdr *rw_init __P((int)); static struct tftphdr *rw_init __P((int));
struct tftphdr *w_init() { return rw_init(0); } /* write-behind */ struct tftphdr *
struct tftphdr *r_init() { return rw_init(1); } /* read-ahead */ w_init() /* write-behind */
{
return rw_init(0);
}
struct tftphdr *
r_init() /* read-ahead */
{
return rw_init(1);
}
static struct tftphdr * static struct tftphdr *
rw_init(x) /* init for either read-ahead or write-behind */ rw_init(x) /* init for either read-ahead or write-behind */
@ -105,9 +112,10 @@ rw_init(x) /* init for either read-ahead or write-behind */
Free it and return next buffer filled with data. Free it and return next buffer filled with data.
*/ */
int int
readit(file, dpp, convert) readit(file, dpp, amt, convert)
FILE *file; /* file opened for read */ FILE *file; /* file opened for read */
struct tftphdr **dpp; struct tftphdr **dpp;
int amt;
int convert; /* if true, convert to ascii */ int convert; /* if true, convert to ascii */
{ {
struct bf *b; struct bf *b;
@ -117,7 +125,7 @@ readit(file, dpp, convert)
b = &bfs[current]; /* look at new buffer */ b = &bfs[current]; /* look at new buffer */
if (b->counter == BF_FREE) /* if it's empty */ if (b->counter == BF_FREE) /* if it's empty */
read_ahead(file, convert); /* fill it */ read_ahead(file, amt, convert); /* fill it */
/* assert(b->counter != BF_FREE);*//* check */ /* assert(b->counter != BF_FREE);*//* check */
*dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */
return b->counter; return b->counter;
@ -128,8 +136,9 @@ readit(file, dpp, convert)
* conversions are lf -> cr,lf and cr -> cr, nul * conversions are lf -> cr,lf and cr -> cr, nul
*/ */
void void
read_ahead(file, convert) read_ahead(file, amt, convert)
FILE *file; /* file opened for read */ FILE *file; /* file opened for read */
int amt; /* number of bytes to read */
int convert; /* if true, convert to ascii */ int convert; /* if true, convert to ascii */
{ {
int i; int i;
@ -146,12 +155,12 @@ read_ahead(file, convert)
dp = (struct tftphdr *)b->buf; dp = (struct tftphdr *)b->buf;
if (convert == 0) { if (convert == 0) {
b->counter = read(fileno(file), dp->th_data, SEGSIZE); b->counter = read(fileno(file), dp->th_data, amt);
return; return;
} }
p = dp->th_data; p = dp->th_data;
for (i = 0 ; i < SEGSIZE; i++) { for (i = 0 ; i < amt; i++) {
if (newline) { if (newline) {
if (prevchar == '\n') if (prevchar == '\n')
c = '\n'; /* lf to cr,lf */ c = '\n'; /* lf to cr,lf */
@ -257,8 +266,9 @@ skipit:
*/ */
int int
synchnet(f) synchnet(f, bsize)
int f; /* socket to flush */ int f; /* socket to flush */
int bsize; /* size of buffer to sync */
{ {
int i, j = 0; int i, j = 0;
char rbuf[PKTSIZE]; char rbuf[PKTSIZE];

View File

@ -1,4 +1,4 @@
/* $NetBSD: tftpsubs.h,v 1.2 1994/12/08 09:51:32 jtc Exp $ */ /* $NetBSD: tftpsubs.h,v 1.3 2003/06/11 01:44:32 briggs Exp $ */
/* /*
* Copyright (c) 1993 * Copyright (c) 1993
@ -40,10 +40,10 @@
* server. * server.
*/ */
struct tftphdr *r_init __P((void)); struct tftphdr *r_init __P((void));
void read_ahead __P((FILE *, int)); void read_ahead __P((FILE *, int, int));
int readit __P((FILE *, struct tftphdr **, int)); int readit __P((FILE *, struct tftphdr **, int, int));
int synchnet __P((int)); int synchnet __P((int, int));
struct tftphdr *w_init __P((void)); struct tftphdr *w_init __P((void));
int write_behind __P((FILE *, int)); int write_behind __P((FILE *, int));