summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorMarcus Glocker <mglocker@cvs.openbsd.org>2006-07-24 17:29:59 +0000
committerMarcus Glocker <mglocker@cvs.openbsd.org>2006-07-24 17:29:59 +0000
commit57eeb2116f17a1e68f718e49ef96d999736bb220 (patch)
treeded4a76c8f6aec6872f952cd7db786be7ca5c4c2 /usr.bin
parent2edac0a82ef114521ab1e02618e37999b5c8657d (diff)
Adding TFTP Option Extension to the tftp client according to RFC 2347.
Implemented options are: - TFTP Blocksize Option, RFC 2348 - TFTP Timeout Interval and Transfer Size Options, RFC 2349 We have now on the tftp client side the same options supported as in our tftp server. ok claudio@
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/tftp/main.c83
-rw-r--r--usr.bin/tftp/tftp.128
-rw-r--r--usr.bin/tftp/tftp.c194
3 files changed, 269 insertions, 36 deletions
diff --git a/usr.bin/tftp/main.c b/usr.bin/tftp/main.c
index 4249eb2cc53..ce9d6ecb793 100644
--- a/usr.bin/tftp/main.c
+++ b/usr.bin/tftp/main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: main.c,v 1.25 2006/07/12 16:58:51 mglocker Exp $ */
+/* $OpenBSD: main.c,v 1.26 2006/07/24 17:29:58 mglocker Exp $ */
/* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */
/*
@@ -41,7 +41,7 @@ static const char copyright[] =
static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
#endif
static const char rcsid[] =
- "$OpenBSD: main.c,v 1.25 2006/07/12 16:58:51 mglocker Exp $";
+ "$OpenBSD: main.c,v 1.26 2006/07/24 17:29:58 mglocker Exp $";
#endif /* not lint */
/*
@@ -56,6 +56,7 @@ static const char rcsid[] =
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <arpa/tftp.h>
#include <ctype.h>
#include <err.h>
@@ -87,6 +88,9 @@ void setrexmt(int, char **);
void settimeout(int, char **);
void settrace(int, char **);
void setverbose(int, char **);
+void settsize(int, char **);
+void settout(int, char **);
+void setblksize(int, char **);
void status(int, char **);
int readcmd(char *, int, FILE *);
static void getusage(char *);
@@ -115,6 +119,11 @@ int maxtimeout = 5 * TIMEOUT;
char hostname[MAXHOSTNAMELEN];
FILE *file = NULL;
volatile sig_atomic_t intrflag = 0;
+char *ackbuf;
+int has_options = 0;
+int opt_tsize = 0;
+int opt_tout = 0;
+int opt_blksize = 0;
char vhelp[] = "toggle verbose mode";
char thelp[] = "toggle packet tracing";
@@ -129,6 +138,9 @@ char xhelp[] = "set per-packet retransmission timeout";
char ihelp[] = "set total retransmission timeout";
char ashelp[] = "set mode to netascii";
char bnhelp[] = "set mode to octet";
+char oshelp[] = "toggle tsize option";
+char othelp[] = "toggle timeout option";
+char obhelp[] = "set alternative blksize option";
struct cmd {
char *name;
@@ -149,6 +161,9 @@ struct cmd cmdtab[] = {
{ "ascii", ashelp, setascii },
{ "rexmt", xhelp, setrexmt },
{ "timeout", ihelp, settimeout },
+ { "tsize", oshelp, settsize },
+ { "tout", othelp, settout },
+ { "blksize", obhelp, setblksize },
{ "help", hhelp, help },
{ "?", hhelp, help },
{ NULL, NULL, NULL }
@@ -194,6 +209,10 @@ main(int argc, char *argv[])
/* catch SIGINT */
signal(SIGINT, intr);
+ /* allocate memory for packets */
+ if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL)
+ err(1, "malloc");
+
/* command prompt */
command();
@@ -484,7 +503,8 @@ getusage(char *s)
void
setrexmt(int argc, char *argv[])
{
- int t;
+ int t;
+ const char *errstr;
if (argc < 2) {
strlcpy(line, "Rexmt-timeout ", sizeof(line));
@@ -499,9 +519,9 @@ setrexmt(int argc, char *argv[])
printf("usage: %s value\n", argv[0]);
return;
}
- t = atoi(argv[1]);
- if (t < 0)
- printf("%s: bad value\n", argv[1]);
+ t = strtonum(argv[1], 1, 255, &errstr);
+ if (errstr)
+ printf("%s: value is %s\n", argv[1], errstr);
else
rexmtval = t;
}
@@ -709,6 +729,57 @@ setverbose(int argc, char *argv[])
printf("Verbose mode %s.\n", verbose ? "on" : "off");
}
+void
+settsize(int argc, char *argv[])
+{
+ opt_tsize = !opt_tsize;
+ printf("Tsize option %s.\n", opt_tsize ? "on" : "off");
+ if (opt_tsize)
+ has_options++;
+ else
+ has_options--;
+}
+
+void
+settout(int argc, char *argv[])
+{
+ opt_tout = !opt_tout;
+ printf("Timeout option %s.\n", opt_tout ? "on" : "off");
+ if (opt_tout)
+ has_options++;
+ else
+ has_options--;
+}
+
+void
+setblksize(int argc, char *argv[])
+{
+ int t;
+ const char *errstr;
+
+ if (argc < 2) {
+ strlcpy(line, "Blocksize ", sizeof(line));
+ printf("(value) ");
+ readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
+ if (makeargv())
+ return;
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ printf("usage: %s value\n", argv[0]);
+ return;
+ }
+ t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
+ if (errstr)
+ printf("%s: value is %s\n", argv[1], errstr);
+ else {
+ if (opt_blksize == 0)
+ has_options++;
+ opt_blksize = t;
+ }
+}
+
int
readcmd(char *input, int len, FILE *stream)
{
diff --git a/usr.bin/tftp/tftp.1 b/usr.bin/tftp/tftp.1
index 74e280351a5..ffe204432de 100644
--- a/usr.bin/tftp/tftp.1
+++ b/usr.bin/tftp/tftp.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tftp.1,v 1.11 2006/01/02 16:35:17 jmc Exp $
+.\" $OpenBSD: tftp.1,v 1.12 2006/07/24 17:29:58 mglocker Exp $
.\" $NetBSD: tftp.1,v 1.5 1995/08/18 14:45:44 pk Exp $
.\"
.\" Copyright (c) 1990, 1993, 1994
@@ -175,7 +175,8 @@ Exit
An end-of-file also exits.
.Pp
.It Ic rexmt Ar retransmission-timeout
-Set the per-packet retransmission timeout, in seconds.
+Set the per-packet retransmission timeout, in seconds. The default value
+is 5 seconds. Valid values are 1 second - 255 seconds.
.Pp
.It Ic status
Show current status.
@@ -188,6 +189,21 @@ Toggle packet tracing.
.Pp
.It Ic verbose
Toggle verbose mode.
+.Pp
+.It Ic tsize
+Toggle tsize option. The tsize option delivers the total size of the to be
+transfered file. With this value given, the client or server can decide
+if they are able to accept the file, or not.
+.Pp
+.It Ic tout
+Toggle tout option. The tout option devlivers the retransmission-timeout,
+which is set by
+.Ic rexmt ,
+to the server, so the server uses the same retransmission-timeout as the client.
+.Pp
+.It Ic blksize Ar block-size
+Set the block size in bytes for one packet. The default value is 512 bytes.
+Valid values are 8 bytes - 65464 bytes.
.El
.Sh SEE ALSO
.Xr ftp 1 ,
@@ -206,11 +222,3 @@ protocol, the remote site will probably have some
sort of file access restrictions in place.
The exact methods are specific to each site and therefore
difficult to document here.
-.Pp
-This implementation of
-.Nm
-does not support blocksize negotiation
-.Pq RFC 1783 ,
-so files larger than 33488896 octets
-.Pq 65535 blocks
-cannot be transferred.
diff --git a/usr.bin/tftp/tftp.c b/usr.bin/tftp/tftp.c
index 38b575708bc..9c817990407 100644
--- a/usr.bin/tftp/tftp.c
+++ b/usr.bin/tftp/tftp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tftp.c,v 1.18 2006/07/20 09:42:44 mglocker Exp $ */
+/* $OpenBSD: tftp.c,v 1.19 2006/07/24 17:29:58 mglocker Exp $ */
/* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */
/*
@@ -35,7 +35,7 @@
static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93";
#endif
static const char rcsid[] =
- "$OpenBSD: tftp.c,v 1.18 2006/07/20 09:42:44 mglocker Exp $";
+ "$OpenBSD: tftp.c,v 1.19 2006/07/24 17:29:58 mglocker Exp $";
#endif /* not lint */
/*
@@ -47,6 +47,7 @@ static const char rcsid[] =
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
+#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/tftp.h>
@@ -57,14 +58,13 @@ static const char rcsid[] =
#include <signal.h>
#include <stdio.h>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
#include "tftpsubs.h"
-#define PKTSIZE SEGSIZE + 4
-
static int makerequest(int, const char *, struct tftphdr *, const char *);
static void nak(int);
static void tpacket(const char *, struct tftphdr *, int);
@@ -72,6 +72,8 @@ static void startclock(void);
static void stopclock(void);
static void printstats(const char *, unsigned long);
static void printtimeout(void);
+static void oack(struct tftphdr *, int, int);
+static int oack_set(const char *, const char *);
extern struct sockaddr_in peeraddr; /* filled in by main */
extern int f; /* the opened socket */
@@ -81,10 +83,16 @@ extern int rexmtval;
extern int maxtimeout;
extern FILE *file;
extern volatile sig_atomic_t intrflag;
+extern char *ackbuf;
+extern int has_options;
+extern int opt_tsize;
+extern int opt_tout;
+extern int opt_blksize;
-char ackbuf[PKTSIZE];
struct timeval tstart;
struct timeval tstop;
+unsigned int segment_size = SEGSIZE;
+unsigned int packet_size = SEGSIZE + 4;
struct errmsg {
int e_code;
@@ -98,9 +106,25 @@ struct errmsg {
{ EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" },
{ ENOUSER, "No such user" },
+ { EOPTNEG, "Option negotiation failed" },
{ -1, NULL }
};
+struct options {
+ const char *o_type;
+} options[] = {
+ { "tsize" },
+ { "timeout" },
+ { "blksize" },
+ { NULL }
+};
+
+enum opt_enum {
+ OPT_TSIZE = 0,
+ OPT_TIMEOUT,
+ OPT_BLKSIZE
+};
+
/*
* Send the requested file.
*/
@@ -127,7 +151,7 @@ sendfile(int fd, char *name, char *mode)
if (!block)
size = makerequest(WRQ, name, dp, mode) - 4;
else {
- size = readit(file, &dp, convert, SEGSIZE);
+ size = readit(file, &dp, convert, segment_size);
if (size < 0) {
nak(errno + 100);
break;
@@ -152,7 +176,8 @@ sendfile(int fd, char *name, char *mode)
warn("sendto");
goto abort;
}
- read_ahead(file, convert, SEGSIZE);
+ if (block > 0)
+ read_ahead(file, convert, segment_size);
}
error = 0;
@@ -171,7 +196,7 @@ sendfile(int fd, char *name, char *mode)
goto abort;
}
fromlen = sizeof(from);
- n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
+ n = recvfrom(f, ackbuf, packet_size, 0,
(struct sockaddr *)&from, &fromlen);
if (n == 0) {
warn("recvfrom");
@@ -187,7 +212,14 @@ sendfile(int fd, char *name, char *mode)
peeraddr.sin_port = from.sin_port; /* added */
if (trace)
tpacket("received", ap, n);
+
ap->th_opcode = ntohs(ap->th_opcode);
+
+ if (ap->th_opcode == OACK) {
+ oack(ap, n, 0);
+ break;
+ }
+
ap->th_block = ntohs(ap->th_block);
if (ap->th_opcode == ERROR) {
@@ -212,7 +244,7 @@ sendfile(int fd, char *name, char *mode)
if (block > 0)
amount += size;
block++;
- } while ((size == SEGSIZE || block == 1) && !intrflag);
+ } while ((size == segment_size || block == 1) && !intrflag);
abort:
fclose(file);
@@ -248,6 +280,7 @@ recvfile(int fd, char *name, char *mode)
amount = 0;
firsttrip = 1;
+options:
do {
/* create new ACK packet */
if (firsttrip) {
@@ -295,7 +328,7 @@ recvfile(int fd, char *name, char *mode)
goto abort;
}
fromlen = sizeof(from);
- n = recvfrom(f, dp, PKTSIZE, 0,
+ n = recvfrom(f, dp, packet_size, 0,
(struct sockaddr *)&from, &fromlen);
if (n == 0) {
warn("recvfrom");
@@ -311,7 +344,15 @@ recvfile(int fd, char *name, char *mode)
peeraddr.sin_port = from.sin_port; /* added */
if (trace)
tpacket("received", dp, n);
+
dp->th_opcode = ntohs(dp->th_opcode);
+
+ if (dp->th_opcode == OACK) {
+ oack(dp, n, 0);
+ block = 0;
+ goto options;
+ }
+
dp->th_block = ntohs(dp->th_block);
if (dp->th_opcode == ERROR) {
@@ -340,7 +381,7 @@ recvfile(int fd, char *name, char *mode)
break;
}
amount += size;
- } while (size == SEGSIZE && !intrflag);
+ } while (size == segment_size && !intrflag);
abort:
/* ok to ack, since user has seen err msg */
@@ -363,17 +404,34 @@ static int
makerequest(int request, const char *name, struct tftphdr *tp,
const char *mode)
{
- char *cp;
- int len, pktlen;
+ char *cp;
+ int len, pktlen;
+ off_t fsize = 0;
+ struct stat st;
tp->th_opcode = htons((u_short)request);
cp = tp->th_stuff;
- pktlen = PKTSIZE - offsetof(struct tftphdr, th_stuff);
+ pktlen = packet_size - offsetof(struct tftphdr, th_stuff);
len = strlen(name) + 1;
strlcpy(cp, name, pktlen);
strlcpy(cp + len, mode, pktlen - len);
len += strlen(mode) + 1;
+ if (opt_tsize) {
+ if (request == WRQ) {
+ stat(name, &st);
+ fsize = st.st_size;
+ }
+ len += snprintf(cp + len, pktlen - len, "%s%c%lld%c",
+ options[OPT_TSIZE].o_type, 0, fsize, 0);
+ }
+ if (opt_tout)
+ len += snprintf(cp + len, pktlen - len, "%s%c%d%c",
+ options[OPT_TIMEOUT].o_type, 0, rexmtval, 0);
+ if (opt_blksize)
+ len += snprintf(cp + len, pktlen - len, "%s%c%d%c",
+ options[OPT_BLKSIZE].o_type, 0, opt_blksize, 0);
+
return (cp + len - (char *)tp);
}
@@ -400,9 +458,9 @@ nak(int error)
pe->e_msg = strerror(error - 100);
tp->th_code = EUNDEF;
}
- length = strlcpy(tp->th_msg, pe->e_msg, sizeof(ackbuf)) + 5;
- if (length > sizeof(ackbuf))
- length = sizeof(ackbuf);
+ length = strlcpy(tp->th_msg, pe->e_msg, packet_size) + 5;
+ if (length > packet_size)
+ length = packet_size;
if (trace)
tpacket("sent", tp, length);
if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
@@ -415,11 +473,11 @@ tpacket(const char *s, struct tftphdr *tp, int n)
{
char *cp, *file;
static char *opcodes[] =
- { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
+ { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
u_short op = ntohs(tp->th_opcode);
- if (op < RRQ || op > ERROR)
+ if (op < RRQ || op > OACK)
printf("%s opcode=%x ", s, op);
else
printf("%s %s ", s, opcodes[op]);
@@ -430,7 +488,10 @@ tpacket(const char *s, struct tftphdr *tp, int n)
n -= 2;
file = cp = tp->th_stuff;
cp = strchr(cp, '\0');
- printf("<file=%s, mode=%s>\n", file, cp + 1);
+ printf("<file=%s, mode=%s", file, cp + 1);
+ if (has_options)
+ oack(tp, n, 1);
+ printf(">\n");
break;
case DATA:
printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
@@ -441,6 +502,11 @@ tpacket(const char *s, struct tftphdr *tp, int n)
case ERROR:
printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
break;
+ case OACK:
+ printf("<");
+ oack(tp, n, 1);
+ printf(">\n");
+ break;
}
}
@@ -476,3 +542,91 @@ printtimeout(void)
{
printf("Transfer timed out.\n");
}
+
+static void
+oack(struct tftphdr *tp, int size, int trace)
+{
+ int i, len, off;
+ char *opt, *val;
+
+ u_short op = ntohs(tp->th_opcode);
+
+ opt = tp->th_u.tu_stuff;
+ val = tp->th_u.tu_stuff;
+
+ if (op == RRQ || op == WRQ) {
+ len = strlen(opt) + 1;
+ opt = strchr(opt, '\0');
+ opt++;
+ len += strlen(opt) + 1;
+ opt = strchr(opt, '\0');
+ opt++;
+ val = opt;
+ off = len;
+ if (trace)
+ printf(", ");
+ } else
+ off = 2;
+
+ for (i = off, len = 0; i < size - 1; i++) {
+ if (*val != '\0') {
+ val++;
+ continue;
+ }
+ /* got option and value */
+ val++;
+ if (trace)
+ printf("%s=%s", opt, val);
+ else
+ if (oack_set(opt, val) == -1)
+ break;
+ len = strlen(val) + 1;
+ val += len;
+ opt = val;
+ i += len;
+ if (trace && i < size - 1)
+ printf(", ");
+ }
+}
+
+int
+oack_set(const char *option, const char *value)
+{
+ int i, n;
+ const char *errstr;
+
+ for (i = 0; options[i].o_type != NULL; i++) {
+ if (!strcasecmp(options[i].o_type, option)) {
+ if (i == OPT_TSIZE) {
+ /* XXX verify OACK response */
+ }
+ if (i == OPT_TIMEOUT) {
+ /* verify OACK response */
+ n = strtonum(value, 1, 255, &errstr);
+ if (errstr || rexmtval != n ||
+ opt_tout == 0) {
+ nak(EOPTNEG);
+ intrflag = 1;
+ return (-1);
+ }
+ /* OK */
+ }
+ if (i == OPT_BLKSIZE) {
+ /* verify OACK response */
+ n = strtonum(value, SEGSIZE_MIN, SEGSIZE_MAX,
+ &errstr);
+ if (errstr || opt_blksize != n ||
+ opt_blksize == 0) {
+ nak(EOPTNEG);
+ intrflag = 1;
+ return (-1);
+ }
+ /* OK, set option */
+ segment_size = n;
+ packet_size = segment_size + 4;
+ }
+ }
+ }
+
+ return (1);
+}