Index: head/usr.bin/tftp/main.c =================================================================== --- head/usr.bin/tftp/main.c (revision 338257) +++ head/usr.bin/tftp/main.c (revision 338258) @@ -1,1070 +1,1070 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif #if 0 #ifndef lint static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; #endif #endif #include __FBSDID("$FreeBSD$"); /* Many bug fixes are from Jim Guyton */ /* * TFTP User Program -- Command Interface. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tftp-utils.h" #include "tftp-io.h" #include "tftp-options.h" #include "tftp.h" #define MAXLINE (2 * MAXPATHLEN) #define TIMEOUT 5 /* secs between rexmt's */ typedef struct sockaddr_storage peeraddr; static int connected; static char mode[32]; static jmp_buf toplevel; volatile int txrx_error; static int peer; #define MAX_MARGV 20 static int margc; static char *margv[MAX_MARGV]; int verbose; static char *port = NULL; static void get(int, char **); static void help(int, char **); static void intr(int); static void modecmd(int, char **); static void put(int, char **); static void quit(int, char **); static void setascii(int, char **); static void setbinary(int, char **); static void setpeer0(char *, const char *); static void setpeer(int, char **); static void settimeoutpacket(int, char **); static void settimeoutnetwork(int, char **); static void setdebug(int, char **); static void setverbose(int, char **); static void showstatus(int, char **); static void setblocksize(int, char **); static void setblocksize2(int, char **); static void setoptions(int, char **); static void setrollover(int, char **); static void setpacketdrop(int, char **); static void command(bool, EditLine *, History *, HistEvent *) __dead2; static const char *command_prompt(void); static void urihandling(char *URI); static void getusage(char *); static void makeargv(char *line); static void putusage(char *); static void settftpmode(const char *); static char *tail(char *); static struct cmd *getcmd(char *); #define HELPINDENT (sizeof("connect")) struct cmd { const char *name; void (*handler)(int, char **); const char *help; }; static struct cmd cmdtab[] = { { "connect", setpeer, "connect to remote tftp" }, { "mode", modecmd, "set file transfer mode" }, { "put", put, "send file" }, { "get", get, "receive file" }, { "quit", quit, "exit tftp" }, { "verbose", setverbose, "toggle verbose mode" }, { "status", showstatus, "show current status" }, { "binary", setbinary, "set mode to octet" }, { "ascii", setascii, "set mode to netascii" }, { "rexmt", settimeoutpacket, "set per-packet retransmission timeout[-]" }, { "timeout", settimeoutnetwork, "set total retransmission timeout" }, { "trace", setdebug, "enable 'debug packet'[-]" }, { "debug", setdebug, "enable verbose output" }, { "blocksize", setblocksize, "set blocksize[*]" }, { "blocksize2", setblocksize2, "set blocksize as a power of 2[**]" }, { "rollover", setrollover, "rollover after 64K packets[**]" }, { "options", setoptions, "enable or disable RFC2347 style options" }, { "help", help, "print help information" }, { "packetdrop", setpacketdrop, "artificial packetloss feature" }, { "?", help, "print help information" }, { NULL, NULL, NULL } }; static struct modes { const char *m_name; const char *m_mode; } modes[] = { { "ascii", "netascii" }, { "netascii", "netascii" }, { "binary", "octet" }, { "image", "octet" }, { "octet", "octet" }, { NULL, NULL } }; int main(int argc, char *argv[]) { HistEvent he; static EditLine *el; static History *hist; bool interactive; acting_as_client = 1; peer = -1; - strcpy(mode, "netascii"); + strcpy(mode, "octet"); signal(SIGINT, intr); interactive = isatty(STDIN_FILENO); if (interactive) { el = el_init("tftp", stdin, stdout, stderr); hist = history_init(); history(hist, &he, H_SETSIZE, 100); el_set(el, EL_HIST, history, hist); el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_PROMPT, command_prompt); el_set(el, EL_SIGNAL, 1); el_source(el, NULL); } if (argc > 1) { if (setjmp(toplevel) != 0) exit(txrx_error); if (strncmp(argv[1], "tftp://", 7) == 0) { urihandling(argv[1]); exit(txrx_error); } setpeer(argc, argv); } if (setjmp(toplevel) != 0) { if (interactive) el_reset(el); (void)putchar('\n'); } init_options(); command(interactive, el, hist, &he); } /* * RFC3617 handling of TFTP URIs: * * tftpURI = "tftp://" host "/" file [ mode ] * mode = ";" "mode=" ( "netascii" / "octet" ) * file = *( unreserved / escaped ) * host = * unreserved = * escaped = * * We are cheating a little bit by allowing any mode as specified in the * modes table defined earlier on in this file and mapping it on the real * mode. */ static void urihandling(char *URI) { char uri[ARG_MAX]; char *host = NULL; char *path = NULL; char *opts = NULL; const char *tmode = "octet"; char *s; char line[MAXLINE]; int i; strlcpy(uri, URI, ARG_MAX); host = uri + 7; if ((s = strchr(host, '/')) == NULL) { fprintf(stderr, "Invalid URI: Couldn't find / after hostname\n"); exit(1); } *s = '\0'; path = s + 1; if ((s = strchr(path, ';')) != NULL) { *s = '\0'; opts = s + 1; if (strncmp(opts, "mode=", 5) == 0) { tmode = opts; tmode += 5; for (i = 0; modes[i].m_name != NULL; i++) { if (strcmp(modes[i].m_name, tmode) == 0) break; } if (modes[i].m_name == NULL) { fprintf(stderr, "Invalid mode: '%s'\n", mode); exit(1); } settftpmode(modes[i].m_mode); } } else { settftpmode("octet"); } setpeer0(host, NULL); sprintf(line, "get %s", path); makeargv(line); get(margc, margv); } static char hostname[MAXHOSTNAMELEN]; static void setpeer0(char *host, const char *lport) { struct addrinfo hints, *res0, *res; int error; const char *cause = "unknown"; if (connected) { close(peer); peer = -1; } connected = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_CANONNAME; if (!lport) lport = "tftp"; error = getaddrinfo(host, lport, &hints, &res0); if (error) { warnx("%s", gai_strerror(error)); return; } for (res = res0; res; res = res->ai_next) { if (res->ai_addrlen > sizeof(peeraddr)) continue; peer = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (peer < 0) { cause = "socket"; continue; } memset(&peer_sock, 0, sizeof(peer_sock)); peer_sock.ss_family = res->ai_family; peer_sock.ss_len = res->ai_addrlen; if (bind(peer, (struct sockaddr *)&peer_sock, peer_sock.ss_len) < 0) { cause = "bind"; close(peer); peer = -1; continue; } break; } if (peer < 0) warn("%s", cause); else { /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ memcpy(&peer_sock, res->ai_addr, res->ai_addrlen); if (res->ai_canonname) { (void) strlcpy(hostname, res->ai_canonname, sizeof(hostname)); } else (void) strlcpy(hostname, host, sizeof(hostname)); connected = 1; } freeaddrinfo(res0); } static void setpeer(int argc, char *argv[]) { char line[MAXLINE]; if (argc < 2) { strcpy(line, "Connect "); printf("(to) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if ((argc < 2) || (argc > 3)) { printf("usage: %s [host [port]]\n", argv[0]); return; } if (argc == 3) { port = argv[2]; setpeer0(argv[1], argv[2]); } else setpeer0(argv[1], NULL); } static void modecmd(int argc, char *argv[]) { struct modes *p; const char *sep; if (argc < 2) { printf("Using %s mode to transfer files.\n", mode); return; } if (argc == 2) { for (p = modes; p->m_name; p++) if (strcmp(argv[1], p->m_name) == 0) break; if (p->m_name) { settftpmode(p->m_mode); return; } printf("%s: unknown mode\n", argv[1]); /* drop through and print usage message */ } printf("usage: %s [", argv[0]); sep = " "; for (p = modes; p->m_name != NULL; p++) { printf("%s%s", sep, p->m_name); if (*sep == ' ') sep = " | "; } printf(" ]\n"); return; } static void setbinary(int argc __unused, char *argv[] __unused) { settftpmode("octet"); } static void setascii(int argc __unused, char *argv[] __unused) { settftpmode("netascii"); } static void settftpmode(const char *newmode) { strlcpy(mode, newmode, sizeof(mode)); if (verbose) printf("mode set to %s\n", mode); } /* * Send file(s). */ static void put(int argc, char *argv[]) { int fd; int n; char *cp, *targ; char line[MAXLINE]; struct stat sb; if (argc < 2) { strcpy(line, "send "); printf("(file) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if (argc < 2) { putusage(argv[0]); return; } targ = argv[argc - 1]; if (strrchr(argv[argc - 1], ':')) { char *lcp; for (n = 1; n < argc - 1; n++) if (strchr(argv[n], ':')) { putusage(argv[0]); return; } lcp = argv[argc - 1]; targ = strrchr(lcp, ':'); *targ++ = 0; if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { lcp[strlen(lcp) - 1] = '\0'; lcp++; } setpeer0(lcp, NULL); } if (!connected) { printf("No target machine specified.\n"); return; } if (argc < 4) { cp = argc == 2 ? tail(targ) : argv[1]; fd = open(cp, O_RDONLY); if (fd < 0) { warn("%s", cp); return; } if (fstat(fd, &sb) < 0) { warn("%s", cp); return; } asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size); if (verbose) printf("putting %s to %s:%s [%s]\n", cp, hostname, targ, mode); xmitfile(peer, port, fd, targ, mode); close(fd); return; } /* this assumes the target is a directory */ /* on a remote unix system. hmmmm. */ cp = strchr(targ, '\0'); *cp++ = '/'; for (n = 1; n < argc - 1; n++) { strcpy(cp, tail(argv[n])); fd = open(argv[n], O_RDONLY); if (fd < 0) { warn("%s", argv[n]); continue; } if (fstat(fd, &sb) < 0) { warn("%s", argv[n]); continue; } asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size); if (verbose) printf("putting %s to %s:%s [%s]\n", argv[n], hostname, targ, mode); xmitfile(peer, port, fd, targ, mode); } } static void putusage(char *s) { printf("usage: %s file [remotename]\n", s); printf(" %s file host:remotename\n", s); printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", s); } /* * Receive file(s). */ static void get(int argc, char *argv[]) { int fd; int n; char *cp; char *src; char line[MAXLINE]; if (argc < 2) { strcpy(line, "get "); printf("(files) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if (argc < 2) { getusage(argv[0]); return; } if (!connected) { for (n = 1; n < argc ; n++) if (strrchr(argv[n], ':') == 0) { printf("No remote host specified and " "no host given for file '%s'\n", argv[n]); getusage(argv[0]); return; } } for (n = 1; n < argc ; n++) { src = strrchr(argv[n], ':'); if (src == NULL) src = argv[n]; else { char *lcp; *src++ = 0; lcp = argv[n]; if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { lcp[strlen(lcp) - 1] = '\0'; lcp++; } setpeer0(lcp, NULL); if (!connected) continue; } if (argc < 4) { cp = argc == 3 ? argv[2] : tail(src); fd = creat(cp, 0644); if (fd < 0) { warn("%s", cp); return; } if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); recvfile(peer, port, fd, src, mode); break; } cp = tail(src); /* new .. jdg */ fd = creat(cp, 0644); if (fd < 0) { warn("%s", cp); continue; } if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); recvfile(peer, port, fd, src, mode); } } static void getusage(char *s) { printf("usage: %s file [localname]\n", s); printf(" %s [host:]file [localname]\n", s); printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); } static void settimeoutpacket(int argc, char *argv[]) { int t; char line[MAXLINE]; if (argc < 2) { strcpy(line, "Packet timeout "); printf("(value) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if (argc != 2) { printf("usage: %s value\n", argv[0]); return; } t = atoi(argv[1]); if (t < 0) { printf("%s: bad value\n", argv[1]); return; } settimeouts(t, timeoutnetwork, maxtimeouts); } static void settimeoutnetwork(int argc, char *argv[]) { int t; char line[MAXLINE]; if (argc < 2) { strcpy(line, "Network timeout "); printf("(value) "); fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); makeargv(line); argc = margc; argv = margv; } if (argc != 2) { printf("usage: %s value\n", argv[0]); return; } t = atoi(argv[1]); if (t < 0) { printf("%s: bad value\n", argv[1]); return; } settimeouts(timeoutpacket, t, maxtimeouts); } static void showstatus(int argc __unused, char *argv[] __unused) { printf("Remote host: %s\n", connected ? hostname : "none specified yet"); printf("RFC2347 Options support: %s\n", options_rfc_enabled ? "enabled" : "disabled"); printf("Non-RFC defined options support: %s\n", options_extra_enabled ? "enabled" : "disabled"); printf("Mode: %s\n", mode); printf("Verbose: %s\n", verbose ? "on" : "off"); printf("Debug: %s\n", debug_show(debug)); printf("Artificial packetloss: %d in 100 packets\n", packetdroppercentage); printf("Segment size: %d bytes\n", segsize); printf("Network timeout: %d seconds\n", timeoutpacket); printf("Maximum network timeout: %d seconds\n", timeoutnetwork); printf("Maximum timeouts: %d \n", maxtimeouts); } static void intr(int dummy __unused) { signal(SIGALRM, SIG_IGN); alarm(0); longjmp(toplevel, -1); } static char * tail(char *filename) { char *s; while (*filename) { s = strrchr(filename, '/'); if (s == NULL) break; if (s[1]) return (s + 1); *s = '\0'; } return (filename); } static const char * command_prompt(void) { return ("tftp> "); } /* * Command parser. */ static void command(bool interactive, EditLine *el, History *hist, HistEvent *hep) { struct cmd *c; const char *bp; char *cp; int len, num; char line[MAXLINE]; for (;;) { if (interactive) { if ((bp = el_gets(el, &num)) == NULL || num == 0) exit(0); len = MIN(MAXLINE, num); memcpy(line, bp, len); line[len] = '\0'; history(hist, hep, H_ENTER, bp); } else { line[0] = 0; if (fgets(line, sizeof line , stdin) == NULL) { if (feof(stdin)) { exit(txrx_error); } else { continue; } } } if ((cp = strchr(line, '\n'))) *cp = '\0'; if (line[0] == 0) continue; makeargv(line); if (margc == 0) continue; c = getcmd(margv[0]); if (c == (struct cmd *)-1) { printf("?Ambiguous command\n"); continue; } if (c == NULL) { printf("?Invalid command\n"); continue; } (*c->handler)(margc, margv); } } static struct cmd * getcmd(char *name) { const char *p, *q; struct cmd *c, *found; int nmatches, longest; longest = 0; nmatches = 0; found = 0; for (c = cmdtab; (p = c->name) != NULL; c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return (c); if (!*q) { /* the name was a prefix */ if (q - name > longest) { longest = q - name; nmatches = 1; found = c; } else if (q - name == longest) nmatches++; } } if (nmatches > 1) return ((struct cmd *)-1); return (found); } /* * Slice a string up into argc/argv. */ static void makeargv(char *line) { char *cp; char **argp = margv; margc = 0; if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) { while (isspace(*cp)) cp++; if (*cp == '\0') break; *argp++ = cp; margc += 1; while (*cp != '\0' && !isspace(*cp)) cp++; if (*cp == '\0') break; *cp++ = '\0'; } *argp++ = 0; } static void quit(int argc __unused, char *argv[] __unused) { exit(txrx_error); } /* * Help command. */ static void help(int argc, char *argv[]) { struct cmd *c; if (argc == 1) { printf("Commands may be abbreviated. Commands are:\n\n"); for (c = cmdtab; c->name; c++) printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); printf("\n[-] : You shouldn't use these ones anymore.\n"); printf("[*] : RFC2347 options support required.\n"); printf("[**] : Non-standard RFC2347 option.\n"); return; } while (--argc > 0) { char *arg; arg = *++argv; c = getcmd(arg); if (c == (struct cmd *)-1) printf("?Ambiguous help command: %s\n", arg); else if (c == (struct cmd *)0) printf("?Invalid help command: %s\n", arg); else printf("%s\n", c->help); } } static void setverbose(int argc __unused, char *argv[] __unused) { verbose = !verbose; printf("Verbose mode %s.\n", verbose ? "on" : "off"); } static void setoptions(int argc, char *argv[]) { if (argc == 2) { if (strcasecmp(argv[1], "enable") == 0 || strcasecmp(argv[1], "on") == 0) { options_extra_enabled = 1; options_rfc_enabled = 1; } if (strcasecmp(argv[1], "disable") == 0 || strcasecmp(argv[1], "off") == 0) { options_extra_enabled = 0; options_rfc_enabled = 0; } if (strcasecmp(argv[1], "extra") == 0) options_extra_enabled = !options_extra_enabled; } printf("Support for RFC2347 style options are now %s.\n", options_rfc_enabled ? "enabled" : "disabled"); printf("Support for non-RFC defined options are now %s.\n", options_extra_enabled ? "enabled" : "disabled"); printf("\nThe following options are available:\n" "\toptions on : enable support for RFC2347 style options\n" "\toptions off : disable support for RFC2347 style options\n" "\toptions extra : toggle support for non-RFC defined options\n" ); } static void setrollover(int argc, char *argv[]) { if (argc == 2) { if (strcasecmp(argv[1], "never") == 0 || strcasecmp(argv[1], "none") == 0) { free(options[OPT_ROLLOVER].o_request); options[OPT_ROLLOVER].o_request = NULL; } if (strcasecmp(argv[1], "1") == 0) { free(options[OPT_ROLLOVER].o_request); options[OPT_ROLLOVER].o_request = strdup("1"); } if (strcasecmp(argv[1], "0") == 0) { free(options[OPT_ROLLOVER].o_request); options[OPT_ROLLOVER].o_request = strdup("0"); } } printf("Support for the rollover options is %s.\n", options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled"); if (options[OPT_ROLLOVER].o_request != NULL) printf("Block rollover will be to block %s.\n", options[OPT_ROLLOVER].o_request); printf("\nThe following rollover options are available:\n" "\trollover 0 : rollover to block zero (default)\n" "\trollover 1 : rollover to block one\n" "\trollover never : do not support the rollover option\n" "\trollover none : do not support the rollover option\n" ); } static void setdebug(int argc, char *argv[]) { int i; if (argc != 1) { i = 1; while (i < argc) debug ^= debug_find(argv[i++]); } printf("The following debugging is enabled: %s\n", debug_show(debug)); printf("\nThe following debugs are available:\n"); i = 0; while (debugs[i].name != NULL) { printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc); i++; } } static void setblocksize(int argc, char *argv[]) { if (!options_rfc_enabled) printf("RFC2347 style options are not enabled " "(but proceeding anyway)\n"); if (argc != 1) { int size = atoi(argv[1]); size_t max; u_long maxdgram; max = sizeof(maxdgram); if (sysctlbyname("net.inet.udp.maxdgram", &maxdgram, &max, NULL, 0) < 0) { perror("sysctl: net.inet.udp.maxdgram"); return; } if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { printf("Blocksize should be between %d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX); return; } else if (size > (int)maxdgram - 4) { printf("Blocksize can't be bigger than %ld bytes due " "to the net.inet.udp.maxdgram sysctl limitation.\n", maxdgram - 4); asprintf(&options[OPT_BLKSIZE].o_request, "%ld", maxdgram - 4); } else { asprintf(&options[OPT_BLKSIZE].o_request, "%d", size); } } printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request); } static void setblocksize2(int argc, char *argv[]) { if (!options_rfc_enabled || !options_extra_enabled) printf( "RFC2347 style or non-RFC defined options are not enabled " "(but proceeding anyway)\n"); if (argc != 1) { int size = atoi(argv[1]); int i; size_t max; u_long maxdgram; int sizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 0 }; max = sizeof(maxdgram); if (sysctlbyname("net.inet.udp.maxdgram", &maxdgram, &max, NULL, 0) < 0) { perror("sysctl: net.inet.udp.maxdgram"); return; } for (i = 0; sizes[i] != 0; i++) { if (sizes[i] == size) break; } if (sizes[i] == 0) { printf("Blocksize2 should be a power of two between " "8 and 32768.\n"); return; } if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { printf("Blocksize2 should be between " "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX); return; } else if (size > (int)maxdgram - 4) { printf("Blocksize2 can't be bigger than %ld bytes due " "to the net.inet.udp.maxdgram sysctl limitation.\n", maxdgram - 4); for (i = 0; sizes[i+1] != 0; i++) { if ((int)maxdgram < sizes[i+1]) break; } asprintf(&options[OPT_BLKSIZE2].o_request, "%d", sizes[i]); } else { asprintf(&options[OPT_BLKSIZE2].o_request, "%d", size); } } printf("Blocksize2 is now %s bytes.\n", options[OPT_BLKSIZE2].o_request); } static void setpacketdrop(int argc, char *argv[]) { if (argc != 1) packetdroppercentage = atoi(argv[1]); printf("Randomly %d in 100 packets will be dropped\n", packetdroppercentage); } Index: head/usr.bin/tftp/tftp.1 =================================================================== --- head/usr.bin/tftp/tftp.1 (revision 338257) +++ head/usr.bin/tftp/tftp.1 (revision 338258) @@ -1,272 +1,272 @@ .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)tftp.1 8.2 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" -.Dd June 22, 2011 +.Dd Aug 22, 2018 .Dt TFTP 1 .Os .Sh NAME .Nm tftp .Nd trivial file transfer program .Sh SYNOPSIS .Nm .Op Ar host Op Ar port .Sh DESCRIPTION The .Nm utility is the user interface to the Internet .Tn TFTP (Trivial File Transfer Protocol), which allows users to transfer files to and from a remote machine. The remote .Ar host may be specified on the command line, in which case .Nm uses .Ar host as the default host for future transfers (see the .Cm connect command below). .Sh COMMANDS Once .Nm is running, it issues the prompt .Dq Li tftp> and recognizes the following commands: .Pp .Bl -tag -width verbose -compact .It Cm \&? Ar command-name ... Print help information. .Pp .It Cm ascii Shorthand for "mode ascii" .Pp .It Cm binary Shorthand for "mode binary" .Pp .It Cm blocksize Ar [size] Sets the TFTP blksize option in TFTP Read Request or Write Request packets to .Ar [size] as specified in RFC 2348. Valid values are between 8 and 65464. If no blocksize is specified, then by default a blocksize of 512 bytes will be used. .Pp .It Cm blocksize2 Ar [size] Sets the TFTP blksize2 option in TFTP Read Request or Write Request packets to .Ar [size] . Values are restricted to powers of 2 between 8 and 32768. This is a non-standard TFTP option. .Pp .It Cm connect Ar host Op Ar port Set the .Ar host (and optionally .Ar port ) for transfers. Note that the .Tn TFTP protocol, unlike the .Tn FTP protocol, does not maintain connections between transfers; thus, the .Cm connect command does not actually create a connection, but merely remembers what host is to be used for transfers. You do not have to use the .Cm connect command; the remote host can be specified as part of the .Cm get or .Cm put commands. .Pp .It Cm debug Ar level Enable or disable debugging levels during verbose output. The value of .Ar level can be one of .Cm packet , simple , options , or .Cm access . .Pp .It Cm get Oo Ar host : Oc Ns Ar file Op Ar localname .It Cm get Xo .Oo Ar host1 : Oc Ns Ar file1 .Oo Ar host2 : Oc Ns Ar file2 ... .Oo Ar hostN : Oc Ns Ar fileN .Xc Get one or more files from the remote host. When using the .Ar host argument, the .Ar host will be used as default host for future transfers. If .Ar localname is specified, the file is stored locally as .Ar localname , otherwise the original filename is used. Note that it is not possible to download two files at a time, only one, three, or more than three files, at a time. .Pp To specify an IPv6 numeric address for a host, wrap it using square brackets like .Dq Li [3ffe:2900:e00c:ffee::1234] : Ns Ar file to disambiguate the colons used in the IPv6 address from the colon separating the host and the filename. .Pp .It Cm mode Ar transfer-mode Set the mode for transfers; .Ar transfer-mode may be one of .Em ascii or .Em binary . The default is -.Em ascii . +.Em binary . .Pp .It Cm packetdrop [arg] Randomly drop .Ar arg out of 100 packets during a transfer. This is a debugging feature. .Pp .It Cm put Ar file Op Oo Ar host : Oc Ns Ar remotename .It Cm put Ar file1 file2 ... fileN Op Oo Ar host : Oc Ns Ar remote-directory Put a file or set of files to the remote host. When .Ar remotename is specified, the file is stored remotely as .Ar remotename , otherwise the original filename is used. If the .Ar remote-directory argument is used, the remote host is assumed to be a .Ux machine. To specify an IPv6 numeric address for a .Ar host , see the example under the .Cm get command. .Pp .It Cm options Ar [arg] Enable or disable support for TFTP options. The valid values of .Ar arg are .Cm on (enable RFC 2347 options), .Cm off (disable RFC 2347 options), and .Cm extra (toggle support for non-RFC defined options). .Pp .It Cm quit Exit .Nm . An end of file also exits. .Pp .It Cm rexmt Ar retransmission-timeout Set the per-packet retransmission timeout, in seconds. .Pp .It Cm rollover [arg] Specify the rollover option in TFTP Read Request or Write Request packets. After 65535 packets have been transmitted, set the block counter to .Ar arg . Valid values of .Ar arg are 0 and 1. This is a non-standard TFTP option. .Pp .It Cm status Show current status. .Pp .It Cm timeout Ar total-transmission-timeout Set the total transmission timeout, in seconds. .Pp .It Cm trace Toggle packet tracing. .Pp .It Cm verbose Toggle verbose mode. .El .Sh SEE ALSO .Xr tftpd 8 .Pp The following RFC's are supported: .Rs .%T RFC 1350: The TFTP Protocol (Revision 2) .Re .Rs .%T RFC 2347: TFTP Option Extension .Re .Rs .%T RFC 2348: TFTP Blocksize Option .Re .Rs .%T RFC 2349: TFTP Timeout Interval and Transfer Size Options .Re .Rs .%T RFC 3617: Uniform Resource Identifier (URI) Scheme and Applicability Statement for the Trivial File Transfer Protocol (TFTP) .Re .Pp The non-standard .Cm rollover and .Cm blksize2 TFTP options are mentioned here: .Rs .%T Extending TFTP .%U http://www.compuphase.com/tftp.htm .Re .Sh HISTORY The .Nm command appeared in .Bx 4.3 . .Pp Edwin Groothuis performed a major rewrite of the .Xr tftpd 8 and .Nm code to support RFC2348. .Sh NOTES Because there is no user-login or validation within the .Tn TFTP 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 Files larger than 33488896 octets (65535 blocks) cannot be transferred without client and server supporting the TFTP blocksize option (RFC2348), or the non-standard TFTP rollover option.