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:
parent
39d51ab631
commit
4441128638
@ -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
|
||||||
|
@ -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 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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];
|
||||||
|
@ -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));
|
||||||
|
Loading…
Reference in New Issue
Block a user