11590Srgrimes/* 21590Srgrimes * Copyright (c) 1983, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#ifndef lint 3128202Scharnierstatic const char copyright[] = 321590Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 331590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3487708Smarkm#endif 351590Srgrimes 3691387Sdwmalone#if 0 371590Srgrimes#ifndef lint 3891387Sdwmalonestatic char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 3928202Scharnier#endif 4091387Sdwmalone#endif 411590Srgrimes 4291387Sdwmalone#include <sys/cdefs.h> 4391387Sdwmalone__FBSDID("$FreeBSD: stable/10/usr.bin/tftp/main.c 339061 2018-10-01 16:10:27Z asomers $"); 4491387Sdwmalone 451590Srgrimes/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 461590Srgrimes 471590Srgrimes/* 481590Srgrimes * TFTP User Program -- Command Interface. 491590Srgrimes */ 5028202Scharnier#include <sys/param.h> 511590Srgrimes#include <sys/types.h> 521590Srgrimes#include <sys/socket.h> 53207607Simp#include <sys/sysctl.h> 541590Srgrimes#include <sys/file.h> 55207607Simp#include <sys/stat.h> 561590Srgrimes 571590Srgrimes#include <netinet/in.h> 581590Srgrimes#include <arpa/inet.h> 59207607Simp#include <arpa/tftp.h> 601590Srgrimes 611590Srgrimes#include <ctype.h> 6228202Scharnier#include <err.h> 6385155Sbde#include <histedit.h> 641590Srgrimes#include <netdb.h> 651590Srgrimes#include <setjmp.h> 661590Srgrimes#include <signal.h> 671590Srgrimes#include <stdio.h> 681590Srgrimes#include <stdlib.h> 691590Srgrimes#include <string.h> 701590Srgrimes#include <unistd.h> 711590Srgrimes 72207607Simp#include "tftp-utils.h" 73207607Simp#include "tftp-io.h" 74207607Simp#include "tftp-options.h" 75207607Simp#include "tftp.h" 761590Srgrimes 7785155Sbde#define MAXLINE 200 781590Srgrimes#define TIMEOUT 5 /* secs between rexmt's */ 791590Srgrimes 80223926Sdelphijtypedef struct sockaddr_storage peeraddr; 81207607Simpstatic int connected; 82207607Simpstatic char mode[32]; 83241737Sedstatic jmp_buf toplevel; 84207607Simpvolatile int txrx_error; 85207607Simpstatic int peer; 86207607Simp 87121015Stjr#define MAX_MARGV 20 88207607Simpstatic int margc; 89207607Simpstatic char *margv[MAX_MARGV]; 901590Srgrimes 91207607Simpint verbose; 92241737Sedstatic char *port = NULL; 931590Srgrimes 94207607Simpstatic void get(int, char **); 95207607Simpstatic void help(int, char **); 96207607Simpstatic void intr(int); 97207607Simpstatic void modecmd(int, char **); 98207607Simpstatic void put(int, char **); 99207607Simpstatic void quit(int, char **); 100207607Simpstatic void setascii(int, char **); 101207607Simpstatic void setbinary(int, char **); 102207607Simpstatic void setpeer0(char *, const char *); 103207607Simpstatic void setpeer(int, char **); 104207607Simpstatic void settimeoutpacket(int, char **); 105207607Simpstatic void settimeoutnetwork(int, char **); 106207607Simpstatic void setdebug(int, char **); 107207607Simpstatic void setverbose(int, char **); 108207607Simpstatic void showstatus(int, char **); 109207607Simpstatic void setblocksize(int, char **); 110207607Simpstatic void setblocksize2(int, char **); 111207607Simpstatic void setoptions(int, char **); 112207607Simpstatic void setrollover(int, char **); 113207607Simpstatic void setpacketdrop(int, char **); 114207607Simp 11592922Simpstatic void command(void) __dead2; 11692922Simpstatic const char *command_prompt(void); 1171590Srgrimes 118207607Simpstatic void urihandling(char *URI); 119207607Simpstatic void getusage(char *); 120207607Simpstatic void makeargv(char *line); 121207607Simpstatic void putusage(char *); 12292922Simpstatic void settftpmode(const char *); 1231590Srgrimes 124207607Simpstatic char *tail(char *); 125207607Simpstatic struct cmd *getcmd(char *); 12687708Smarkm 1271590Srgrimes#define HELPINDENT (sizeof("connect")) 1281590Srgrimes 1291590Srgrimesstruct cmd { 13087708Smarkm const char *name; 13192922Simp void (*handler)(int, char **); 132207607Simp const char *help; 1331590Srgrimes}; 1341590Srgrimes 135207607Simpstatic struct cmd cmdtab[] = { 136207607Simp { "connect", setpeer, "connect to remote tftp" }, 137207607Simp { "mode", modecmd, "set file transfer mode" }, 138207607Simp { "put", put, "send file" }, 139207607Simp { "get", get, "receive file" }, 140207607Simp { "quit", quit, "exit tftp" }, 141207607Simp { "verbose", setverbose, "toggle verbose mode" }, 142207607Simp { "status", showstatus, "show current status" }, 143207607Simp { "binary", setbinary, "set mode to octet" }, 144207607Simp { "ascii", setascii, "set mode to netascii" }, 145207607Simp { "rexmt", settimeoutpacket, 146207607Simp "set per-packet retransmission timeout[-]" }, 147207607Simp { "timeout", settimeoutnetwork, 148207607Simp "set total retransmission timeout" }, 149207607Simp { "trace", setdebug, "enable 'debug packet'[-]" }, 150207607Simp { "debug", setdebug, "enable verbose output" }, 151207607Simp { "blocksize", setblocksize, "set blocksize[*]" }, 152207607Simp { "blocksize2", setblocksize2, "set blocksize as a power of 2[**]" }, 153207607Simp { "rollover", setrollover, "rollover after 64K packets[**]" }, 154207607Simp { "options", setoptions, 155207607Simp "enable or disable RFC2347 style options" }, 156207607Simp { "help", help, "print help information" }, 157222534Simp { "packetdrop", setpacketdrop, "artificial packetloss feature" }, 158207607Simp { "?", help, "print help information" }, 159207607Simp { NULL, NULL, NULL } 160207607Simp}; 1611590Srgrimes 162207607Simpstatic struct modes { 163207607Simp const char *m_name; 164207607Simp const char *m_mode; 165207607Simp} modes[] = { 166207607Simp { "ascii", "netascii" }, 167207607Simp { "netascii", "netascii" }, 168207607Simp { "binary", "octet" }, 169207607Simp { "image", "octet" }, 170207607Simp { "octet", "octet" }, 171207607Simp { NULL, NULL } 1721590Srgrimes}; 1731590Srgrimes 1741590Srgrimesint 175183858Sdelphijmain(int argc, char *argv[]) 1761590Srgrimes{ 177207607Simp 178207607Simp acting_as_client = 1; 179207607Simp peer = -1; 1801590Srgrimes strcpy(mode, "netascii"); 1811590Srgrimes signal(SIGINT, intr); 1821590Srgrimes if (argc > 1) { 1831590Srgrimes if (setjmp(toplevel) != 0) 18496433Sbsd exit(txrx_error); 185207607Simp 186207607Simp if (strncmp(argv[1], "tftp://", 7) == 0) { 187207607Simp urihandling(argv[1]); 188207607Simp exit(txrx_error); 189207607Simp } 190207607Simp 1911590Srgrimes setpeer(argc, argv); 1921590Srgrimes } 1931590Srgrimes if (setjmp(toplevel) != 0) 1941590Srgrimes (void)putchar('\n'); 195207607Simp 196207607Simp init_options(); 1971590Srgrimes command(); 1981590Srgrimes} 1991590Srgrimes 200207607Simp/* 201207607Simp * RFC3617 handling of TFTP URIs: 202207607Simp * 203207607Simp * tftpURI = "tftp://" host "/" file [ mode ] 204207607Simp * mode = ";" "mode=" ( "netascii" / "octet" ) 205207607Simp * file = *( unreserved / escaped ) 206207607Simp * host = <as specified by RFC 2732> 207207607Simp * unreserved = <as specified in RFC 2396> 208207607Simp * escaped = <as specified in RFC 2396> 209207607Simp * 210207607Simp * We are cheating a little bit by allowing any mode as specified in the 211207607Simp * modes table defined earlier on in this file and mapping it on the real 212207607Simp * mode. 213207607Simp */ 214207607Simpstatic void 215207607Simpurihandling(char *URI) 216207607Simp{ 217207607Simp char uri[ARG_MAX]; 218207607Simp char *host = NULL; 219207607Simp char *path = NULL; 220213099Smarius char *opts = NULL; 221213099Smarius const char *tmode = "octet"; 222207607Simp char *s; 223207607Simp char line[MAXLINE]; 224207607Simp int i; 2251590Srgrimes 226287795Sdelphij strlcpy(uri, URI, ARG_MAX); 227207607Simp host = uri + 7; 228207607Simp 229207607Simp if ((s = strchr(host, '/')) == NULL) { 230207607Simp fprintf(stderr, 231207607Simp "Invalid URI: Couldn't find / after hostname\n"); 232207607Simp exit(1); 233207607Simp } 234207607Simp *s = '\0'; 235207607Simp path = s + 1; 236207607Simp 237207607Simp if ((s = strchr(path, ';')) != NULL) { 238207607Simp *s = '\0'; 239213099Smarius opts = s + 1; 240207607Simp 241213099Smarius if (strncmp(opts, "mode=", 5) == 0) { 242213099Smarius tmode = opts; 243213099Smarius tmode += 5; 244207607Simp 245207607Simp for (i = 0; modes[i].m_name != NULL; i++) { 246213099Smarius if (strcmp(modes[i].m_name, tmode) == 0) 247207607Simp break; 248207607Simp } 249207607Simp if (modes[i].m_name == NULL) { 250207607Simp fprintf(stderr, "Invalid mode: '%s'\n", mode); 251207607Simp exit(1); 252207607Simp } 253207607Simp settftpmode(modes[i].m_mode); 254207607Simp } 255207607Simp } else { 256207607Simp settftpmode("octet"); 257207607Simp } 258207607Simp 259207607Simp setpeer0(host, NULL); 260207607Simp 261207607Simp sprintf(line, "get %s", path); 262207607Simp makeargv(line); 263207607Simp get(margc, margv); 264207607Simp} 265207607Simp 266207607Simpstatic char hostname[MAXHOSTNAMELEN]; 267207607Simp 268207607Simpstatic void 269207607Simpsetpeer0(char *host, const char *lport) 27094443Sume{ 27194443Sume struct addrinfo hints, *res0, *res; 27294443Sume int error; 27394605Sdwmalone const char *cause = "unknown"; 27494443Sume 27594443Sume if (connected) { 276207607Simp close(peer); 277207607Simp peer = -1; 27894443Sume } 27994443Sume connected = 0; 28094443Sume 28194443Sume memset(&hints, 0, sizeof(hints)); 28294443Sume hints.ai_family = PF_UNSPEC; 28394443Sume hints.ai_socktype = SOCK_DGRAM; 28494443Sume hints.ai_protocol = IPPROTO_UDP; 28594443Sume hints.ai_flags = AI_CANONNAME; 286207607Simp if (!lport) 287207607Simp lport = "tftp"; 288207607Simp error = getaddrinfo(host, lport, &hints, &res0); 28994443Sume if (error) { 29094443Sume warnx("%s", gai_strerror(error)); 29194443Sume return; 29294443Sume } 29394443Sume 29494443Sume for (res = res0; res; res = res->ai_next) { 29594443Sume if (res->ai_addrlen > sizeof(peeraddr)) 29694443Sume continue; 297207607Simp peer = socket(res->ai_family, res->ai_socktype, 298207607Simp res->ai_protocol); 299207607Simp if (peer < 0) { 30094443Sume cause = "socket"; 30194443Sume continue; 30294443Sume } 30394443Sume 304207607Simp memset(&peer_sock, 0, sizeof(peer_sock)); 305207607Simp peer_sock.ss_family = res->ai_family; 306207607Simp peer_sock.ss_len = res->ai_addrlen; 307207607Simp if (bind(peer, (struct sockaddr *)&peer_sock, peer_sock.ss_len) < 0) { 30894443Sume cause = "bind"; 309207607Simp close(peer); 310207607Simp peer = -1; 31194443Sume continue; 31294443Sume } 31394443Sume 31494443Sume break; 31594443Sume } 31694443Sume 317207607Simp if (peer < 0) 31894443Sume warn("%s", cause); 31994443Sume else { 32094443Sume /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 321207607Simp memcpy(&peer_sock, res->ai_addr, res->ai_addrlen); 32294443Sume if (res->ai_canonname) { 323287795Sdelphij (void) strlcpy(hostname, res->ai_canonname, 32494443Sume sizeof(hostname)); 32594443Sume } else 326287795Sdelphij (void) strlcpy(hostname, host, sizeof(hostname)); 32794443Sume connected = 1; 32894443Sume } 32994443Sume 33094443Sume freeaddrinfo(res0); 33194443Sume} 33294443Sume 333207607Simpstatic void 334183858Sdelphijsetpeer(int argc, char *argv[]) 3351590Srgrimes{ 336207607Simp char line[MAXLINE]; 3371590Srgrimes 3381590Srgrimes if (argc < 2) { 3391590Srgrimes strcpy(line, "Connect "); 3401590Srgrimes printf("(to) "); 34113068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 342207607Simp makeargv(line); 3431590Srgrimes argc = margc; 3441590Srgrimes argv = margv; 3451590Srgrimes } 34688883Sguido if ((argc < 2) || (argc > 3)) { 347120647Ssimon printf("usage: %s [host [port]]\n", argv[0]); 3481590Srgrimes return; 3491590Srgrimes } 350207607Simp if (argc == 3) { 351207607Simp port = argv[2]; 352112582Sjon setpeer0(argv[1], argv[2]); 353207607Simp } else 35494443Sume setpeer0(argv[1], NULL); 3551590Srgrimes} 3561590Srgrimes 357207607Simpstatic void 358183858Sdelphijmodecmd(int argc, char *argv[]) 3591590Srgrimes{ 36087708Smarkm struct modes *p; 36187708Smarkm const char *sep; 3621590Srgrimes 3631590Srgrimes if (argc < 2) { 3641590Srgrimes printf("Using %s mode to transfer files.\n", mode); 3651590Srgrimes return; 3661590Srgrimes } 3671590Srgrimes if (argc == 2) { 3681590Srgrimes for (p = modes; p->m_name; p++) 3691590Srgrimes if (strcmp(argv[1], p->m_name) == 0) 3701590Srgrimes break; 3711590Srgrimes if (p->m_name) { 3721590Srgrimes settftpmode(p->m_mode); 3731590Srgrimes return; 3741590Srgrimes } 3751590Srgrimes printf("%s: unknown mode\n", argv[1]); 3761590Srgrimes /* drop through and print usage message */ 3771590Srgrimes } 3781590Srgrimes 3791590Srgrimes printf("usage: %s [", argv[0]); 3801590Srgrimes sep = " "; 381207607Simp for (p = modes; p->m_name != NULL; p++) { 3821590Srgrimes printf("%s%s", sep, p->m_name); 3831590Srgrimes if (*sep == ' ') 3841590Srgrimes sep = " | "; 3851590Srgrimes } 3861590Srgrimes printf(" ]\n"); 3871590Srgrimes return; 3881590Srgrimes} 3891590Srgrimes 390207607Simpstatic void 391183858Sdelphijsetbinary(int argc __unused, char *argv[] __unused) 3928874Srgrimes{ 3931590Srgrimes 3941590Srgrimes settftpmode("octet"); 3951590Srgrimes} 3961590Srgrimes 397207607Simpstatic void 398183858Sdelphijsetascii(int argc __unused, char *argv[] __unused) 3991590Srgrimes{ 4001590Srgrimes 4011590Srgrimes settftpmode("netascii"); 4021590Srgrimes} 4031590Srgrimes 4041590Srgrimesstatic void 405183858Sdelphijsettftpmode(const char *newmode) 4061590Srgrimes{ 407207607Simp 408339060Sasomers strlcpy(mode, newmode, sizeof(mode)); 4091590Srgrimes if (verbose) 4101590Srgrimes printf("mode set to %s\n", mode); 4111590Srgrimes} 4121590Srgrimes 4131590Srgrimes 4141590Srgrimes/* 4151590Srgrimes * Send file(s). 4161590Srgrimes */ 417207607Simpstatic void 418183858Sdelphijput(int argc, char *argv[]) 4191590Srgrimes{ 420207607Simp int fd; 421207607Simp int n; 422207607Simp char *cp, *targ; 423207607Simp char line[MAXLINE]; 424207607Simp struct stat sb; 4251590Srgrimes 4261590Srgrimes if (argc < 2) { 4271590Srgrimes strcpy(line, "send "); 4281590Srgrimes printf("(file) "); 42913068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 430207607Simp makeargv(line); 4311590Srgrimes argc = margc; 4321590Srgrimes argv = margv; 4331590Srgrimes } 4341590Srgrimes if (argc < 2) { 4351590Srgrimes putusage(argv[0]); 4361590Srgrimes return; 4371590Srgrimes } 4381590Srgrimes targ = argv[argc - 1]; 439229403Sed if (strrchr(argv[argc - 1], ':')) { 44087708Smarkm char *lcp; 4411590Srgrimes 4421590Srgrimes for (n = 1; n < argc - 1; n++) 443229403Sed if (strchr(argv[n], ':')) { 4441590Srgrimes putusage(argv[0]); 4451590Srgrimes return; 4461590Srgrimes } 44787708Smarkm lcp = argv[argc - 1]; 448229403Sed targ = strrchr(lcp, ':'); 4491590Srgrimes *targ++ = 0; 45094443Sume if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { 45194443Sume lcp[strlen(lcp) - 1] = '\0'; 45294443Sume lcp++; 4531590Srgrimes } 45494443Sume setpeer0(lcp, NULL); 4551590Srgrimes } 4561590Srgrimes if (!connected) { 4571590Srgrimes printf("No target machine specified.\n"); 4581590Srgrimes return; 4591590Srgrimes } 4601590Srgrimes if (argc < 4) { 4611590Srgrimes cp = argc == 2 ? tail(targ) : argv[1]; 4621590Srgrimes fd = open(cp, O_RDONLY); 4631590Srgrimes if (fd < 0) { 46428202Scharnier warn("%s", cp); 4651590Srgrimes return; 4661590Srgrimes } 467207607Simp 468339060Sasomers if (fstat(fd, &sb) < 0) { 469339060Sasomers warn("%s", cp); 470339060Sasomers return; 471339060Sasomers } 472207607Simp asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size); 473207607Simp 4741590Srgrimes if (verbose) 4751590Srgrimes printf("putting %s to %s:%s [%s]\n", 476207607Simp cp, hostname, targ, mode); 477207607Simp xmitfile(peer, port, fd, targ, mode); 478339061Sasomers close(fd); 4791590Srgrimes return; 4801590Srgrimes } 4811590Srgrimes /* this assumes the target is a directory */ 4821590Srgrimes /* on a remote unix system. hmmmm. */ 483229403Sed cp = strchr(targ, '\0'); 4841590Srgrimes *cp++ = '/'; 4851590Srgrimes for (n = 1; n < argc - 1; n++) { 4861590Srgrimes strcpy(cp, tail(argv[n])); 4871590Srgrimes fd = open(argv[n], O_RDONLY); 4881590Srgrimes if (fd < 0) { 48928202Scharnier warn("%s", argv[n]); 4901590Srgrimes continue; 4911590Srgrimes } 492207607Simp 493339060Sasomers if (fstat(fd, &sb) < 0) { 494339060Sasomers warn("%s", argv[n]); 495339060Sasomers continue; 496339060Sasomers } 497207607Simp asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size); 498207607Simp 4991590Srgrimes if (verbose) 5001590Srgrimes printf("putting %s to %s:%s [%s]\n", 501207607Simp argv[n], hostname, targ, mode); 502207607Simp xmitfile(peer, port, fd, targ, mode); 5031590Srgrimes } 5041590Srgrimes} 5051590Srgrimes 5061590Srgrimesstatic void 507207607Simpputusage(char *s) 5081590Srgrimes{ 509207607Simp 510207607Simp printf("usage: %s file [remotename]\n", s); 511207607Simp printf(" %s file host:remotename\n", s); 512120647Ssimon printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", s); 5131590Srgrimes} 5141590Srgrimes 5151590Srgrimes/* 5161590Srgrimes * Receive file(s). 5171590Srgrimes */ 518207607Simpstatic void 519183858Sdelphijget(int argc, char *argv[]) 5201590Srgrimes{ 5211590Srgrimes int fd; 52287708Smarkm int n; 52387708Smarkm char *cp; 5241590Srgrimes char *src; 525207607Simp char line[MAXLINE]; 5261590Srgrimes 5271590Srgrimes if (argc < 2) { 5281590Srgrimes strcpy(line, "get "); 5291590Srgrimes printf("(files) "); 53013068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 531207607Simp makeargv(line); 5321590Srgrimes argc = margc; 5331590Srgrimes argv = margv; 5341590Srgrimes } 5351590Srgrimes if (argc < 2) { 5361590Srgrimes getusage(argv[0]); 5371590Srgrimes return; 5381590Srgrimes } 5391590Srgrimes if (!connected) { 5401590Srgrimes for (n = 1; n < argc ; n++) 541229403Sed if (strrchr(argv[n], ':') == 0) { 542207607Simp printf("No remote host specified and " 543207607Simp "no host given for file '%s'\n", argv[n]); 5441590Srgrimes getusage(argv[0]); 5451590Srgrimes return; 5461590Srgrimes } 5471590Srgrimes } 5481590Srgrimes for (n = 1; n < argc ; n++) { 549229403Sed src = strrchr(argv[n], ':'); 5501590Srgrimes if (src == NULL) 5511590Srgrimes src = argv[n]; 5521590Srgrimes else { 55394443Sume char *lcp; 5541590Srgrimes 5551590Srgrimes *src++ = 0; 55694443Sume lcp = argv[n]; 55794443Sume if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { 55894443Sume lcp[strlen(lcp) - 1] = '\0'; 55994443Sume lcp++; 56094443Sume } 56194443Sume setpeer0(lcp, NULL); 56294443Sume if (!connected) 5631590Srgrimes continue; 5641590Srgrimes } 5651590Srgrimes if (argc < 4) { 5661590Srgrimes cp = argc == 3 ? argv[2] : tail(src); 5671590Srgrimes fd = creat(cp, 0644); 5681590Srgrimes if (fd < 0) { 56928202Scharnier warn("%s", cp); 5701590Srgrimes return; 5711590Srgrimes } 5721590Srgrimes if (verbose) 5731590Srgrimes printf("getting from %s:%s to %s [%s]\n", 574207607Simp hostname, src, cp, mode); 575207607Simp recvfile(peer, port, fd, src, mode); 5761590Srgrimes break; 5771590Srgrimes } 5781590Srgrimes cp = tail(src); /* new .. jdg */ 5791590Srgrimes fd = creat(cp, 0644); 5801590Srgrimes if (fd < 0) { 58128202Scharnier warn("%s", cp); 5821590Srgrimes continue; 5831590Srgrimes } 5841590Srgrimes if (verbose) 5851590Srgrimes printf("getting from %s:%s to %s [%s]\n", 586207607Simp hostname, src, cp, mode); 587207607Simp recvfile(peer, port, fd, src, mode); 5881590Srgrimes } 5891590Srgrimes} 5901590Srgrimes 5911590Srgrimesstatic void 592207607Simpgetusage(char *s) 5931590Srgrimes{ 594207607Simp 595207607Simp printf("usage: %s file [localname]\n", s); 596207607Simp printf(" %s [host:]file [localname]\n", s); 597120647Ssimon printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); 5981590Srgrimes} 5991590Srgrimes 600207607Simpstatic void 601207607Simpsettimeoutpacket(int argc, char *argv[]) 6021590Srgrimes{ 6031590Srgrimes int t; 604207607Simp char line[MAXLINE]; 6051590Srgrimes 6061590Srgrimes if (argc < 2) { 607207607Simp strcpy(line, "Packet timeout "); 6081590Srgrimes printf("(value) "); 60913068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 610207607Simp makeargv(line); 6111590Srgrimes argc = margc; 6121590Srgrimes argv = margv; 6131590Srgrimes } 6141590Srgrimes if (argc != 2) { 6151590Srgrimes printf("usage: %s value\n", argv[0]); 6161590Srgrimes return; 6171590Srgrimes } 6181590Srgrimes t = atoi(argv[1]); 619207607Simp if (t < 0) { 6201590Srgrimes printf("%s: bad value\n", argv[1]); 621207607Simp return; 622207607Simp } 623207607Simp 624207607Simp settimeouts(t, timeoutnetwork, maxtimeouts); 6251590Srgrimes} 6261590Srgrimes 627207607Simpstatic void 628207607Simpsettimeoutnetwork(int argc, char *argv[]) 6291590Srgrimes{ 6301590Srgrimes int t; 631207607Simp char line[MAXLINE]; 6321590Srgrimes 6331590Srgrimes if (argc < 2) { 634207607Simp strcpy(line, "Network timeout "); 6351590Srgrimes printf("(value) "); 63613068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 637207607Simp makeargv(line); 6381590Srgrimes argc = margc; 6391590Srgrimes argv = margv; 6401590Srgrimes } 6411590Srgrimes if (argc != 2) { 6421590Srgrimes printf("usage: %s value\n", argv[0]); 6431590Srgrimes return; 6441590Srgrimes } 6451590Srgrimes t = atoi(argv[1]); 646207607Simp if (t < 0) { 6471590Srgrimes printf("%s: bad value\n", argv[1]); 648207607Simp return; 649207607Simp } 650207607Simp 651207607Simp settimeouts(timeoutpacket, t, maxtimeouts); 6521590Srgrimes} 6531590Srgrimes 654207607Simpstatic void 655207607Simpshowstatus(int argc __unused, char *argv[] __unused) 6561590Srgrimes{ 657207607Simp 658207607Simp printf("Remote host: %s\n", 659207607Simp connected ? hostname : "none specified yet"); 660207607Simp printf("RFC2347 Options support: %s\n", 661207607Simp options_rfc_enabled ? "enabled" : "disabled"); 662207607Simp printf("Non-RFC defined options support: %s\n", 663207607Simp options_extra_enabled ? "enabled" : "disabled"); 664207607Simp printf("Mode: %s\n", mode); 665207607Simp printf("Verbose: %s\n", verbose ? "on" : "off"); 666207607Simp printf("Debug: %s\n", debug_show(debug)); 667207607Simp printf("Artificial packetloss: %d in 100 packets\n", 668207607Simp packetdroppercentage); 669207607Simp printf("Segment size: %d bytes\n", segsize); 670207607Simp printf("Network timeout: %d seconds\n", timeoutpacket); 671207607Simp printf("Maximum network timeout: %d seconds\n", timeoutnetwork); 672207607Simp printf("Maximum timeouts: %d \n", maxtimeouts); 6731590Srgrimes} 6741590Srgrimes 675207607Simpstatic void 676183858Sdelphijintr(int dummy __unused) 6771590Srgrimes{ 6781590Srgrimes 6791590Srgrimes signal(SIGALRM, SIG_IGN); 6801590Srgrimes alarm(0); 6811590Srgrimes longjmp(toplevel, -1); 6821590Srgrimes} 6831590Srgrimes 684207607Simpstatic char * 685183858Sdelphijtail(char *filename) 6861590Srgrimes{ 68787708Smarkm char *s; 6888874Srgrimes 6891590Srgrimes while (*filename) { 690229403Sed s = strrchr(filename, '/'); 6911590Srgrimes if (s == NULL) 6921590Srgrimes break; 6931590Srgrimes if (s[1]) 6941590Srgrimes return (s + 1); 6951590Srgrimes *s = '\0'; 6961590Srgrimes } 6971590Srgrimes return (filename); 6981590Srgrimes} 6991590Srgrimes 70085120Smdoddstatic const char * 701213099Smariuscommand_prompt(void) 70285155Sbde{ 70385155Sbde 70485120Smdodd return ("tftp> "); 70585120Smdodd} 70685120Smdodd 7071590Srgrimes/* 7081590Srgrimes * Command parser. 7091590Srgrimes */ 71018286Sbdestatic void 711183858Sdelphijcommand(void) 7121590Srgrimes{ 71385155Sbde HistEvent he; 71487708Smarkm struct cmd *c; 71585155Sbde static EditLine *el; 71685155Sbde static History *hist; 71785155Sbde const char *bp; 71813068Sjoerg char *cp; 71987708Smarkm int len, num, vrbose; 720207607Simp char line[MAXLINE]; 7211590Srgrimes 72287708Smarkm vrbose = isatty(0); 72387708Smarkm if (vrbose) { 72485120Smdodd el = el_init("tftp", stdin, stdout, stderr); 72585120Smdodd hist = history_init(); 726151471Sstefanf history(hist, &he, H_SETSIZE, 100); 72785120Smdodd el_set(el, EL_HIST, history, hist); 72885120Smdodd el_set(el, EL_EDITOR, "emacs"); 72985120Smdodd el_set(el, EL_PROMPT, command_prompt); 73085120Smdodd el_set(el, EL_SIGNAL, 1); 73185120Smdodd el_source(el, NULL); 73285120Smdodd } 7331590Srgrimes for (;;) { 73487708Smarkm if (vrbose) { 73585120Smdodd if ((bp = el_gets(el, &num)) == NULL || num == 0) 73685120Smdodd exit(0); 73785120Smdodd len = (num > MAXLINE) ? MAXLINE : num; 73885120Smdodd memcpy(line, bp, len); 73985120Smdodd line[len] = '\0'; 74085120Smdodd history(hist, &he, H_ENTER, bp); 74185120Smdodd } else { 742207607Simp line[0] = 0; 743230044Skevlo if (fgets(line, sizeof line , stdin) == NULL) { 74485120Smdodd if (feof(stdin)) { 74596433Sbsd exit(txrx_error); 74685120Smdodd } else { 74785120Smdodd continue; 74885120Smdodd } 7491590Srgrimes } 7501590Srgrimes } 75113068Sjoerg if ((cp = strchr(line, '\n'))) 75213068Sjoerg *cp = '\0'; 7531590Srgrimes if (line[0] == 0) 7541590Srgrimes continue; 755207607Simp makeargv(line); 7561590Srgrimes if (margc == 0) 7571590Srgrimes continue; 7581590Srgrimes c = getcmd(margv[0]); 7591590Srgrimes if (c == (struct cmd *)-1) { 7601590Srgrimes printf("?Ambiguous command\n"); 7611590Srgrimes continue; 7621590Srgrimes } 7631590Srgrimes if (c == 0) { 7641590Srgrimes printf("?Invalid command\n"); 7651590Srgrimes continue; 7661590Srgrimes } 7671590Srgrimes (*c->handler)(margc, margv); 7681590Srgrimes } 7691590Srgrimes} 7701590Srgrimes 771207607Simpstatic struct cmd * 772183858Sdelphijgetcmd(char *name) 7731590Srgrimes{ 77487708Smarkm const char *p, *q; 77587708Smarkm struct cmd *c, *found; 77687708Smarkm int nmatches, longest; 7771590Srgrimes 7781590Srgrimes longest = 0; 7791590Srgrimes nmatches = 0; 7801590Srgrimes found = 0; 7811590Srgrimes for (c = cmdtab; (p = c->name) != NULL; c++) { 7821590Srgrimes for (q = name; *q == *p++; q++) 7831590Srgrimes if (*q == 0) /* exact match? */ 7841590Srgrimes return (c); 7851590Srgrimes if (!*q) { /* the name was a prefix */ 7861590Srgrimes if (q - name > longest) { 7871590Srgrimes longest = q - name; 7881590Srgrimes nmatches = 1; 7891590Srgrimes found = c; 7901590Srgrimes } else if (q - name == longest) 7911590Srgrimes nmatches++; 7921590Srgrimes } 7931590Srgrimes } 7941590Srgrimes if (nmatches > 1) 7951590Srgrimes return ((struct cmd *)-1); 7961590Srgrimes return (found); 7971590Srgrimes} 7981590Srgrimes 7991590Srgrimes/* 8001590Srgrimes * Slice a string up into argc/argv. 8011590Srgrimes */ 8021590Srgrimesstatic void 803207607Simpmakeargv(char *line) 8041590Srgrimes{ 80587708Smarkm char *cp; 80687708Smarkm char **argp = margv; 8071590Srgrimes 8081590Srgrimes margc = 0; 809207607Simp if ((cp = strchr(line, '\n')) != NULL) 81013068Sjoerg *cp = '\0'; 811207607Simp for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) { 8121590Srgrimes while (isspace(*cp)) 8131590Srgrimes cp++; 8141590Srgrimes if (*cp == '\0') 8151590Srgrimes break; 8161590Srgrimes *argp++ = cp; 8171590Srgrimes margc += 1; 8181590Srgrimes while (*cp != '\0' && !isspace(*cp)) 8191590Srgrimes cp++; 8201590Srgrimes if (*cp == '\0') 8211590Srgrimes break; 8221590Srgrimes *cp++ = '\0'; 8231590Srgrimes } 8241590Srgrimes *argp++ = 0; 8251590Srgrimes} 8261590Srgrimes 827207607Simpstatic void 828183858Sdelphijquit(int argc __unused, char *argv[] __unused) 8291590Srgrimes{ 830207607Simp 83196433Sbsd exit(txrx_error); 8321590Srgrimes} 8331590Srgrimes 8341590Srgrimes/* 8351590Srgrimes * Help command. 8361590Srgrimes */ 837207607Simpstatic void 838183858Sdelphijhelp(int argc, char *argv[]) 8391590Srgrimes{ 84087708Smarkm struct cmd *c; 8411590Srgrimes 8421590Srgrimes if (argc == 1) { 8431590Srgrimes printf("Commands may be abbreviated. Commands are:\n\n"); 8441590Srgrimes for (c = cmdtab; c->name; c++) 8451590Srgrimes printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 846207607Simp 847207607Simp printf("\n[-] : You shouldn't use these ones anymore.\n"); 848223135Srodrigc printf("[*] : RFC2347 options support required.\n"); 849223135Srodrigc printf("[**] : Non-standard RFC2347 option.\n"); 8501590Srgrimes return; 8511590Srgrimes } 8521590Srgrimes while (--argc > 0) { 85387708Smarkm char *arg; 8541590Srgrimes arg = *++argv; 8551590Srgrimes c = getcmd(arg); 8561590Srgrimes if (c == (struct cmd *)-1) 857207607Simp printf("?Ambiguous help command: %s\n", arg); 8581590Srgrimes else if (c == (struct cmd *)0) 859207607Simp printf("?Invalid help command: %s\n", arg); 8601590Srgrimes else 8611590Srgrimes printf("%s\n", c->help); 8621590Srgrimes } 8631590Srgrimes} 8641590Srgrimes 865207607Simpstatic void 866207607Simpsetverbose(int argc __unused, char *argv[] __unused) 8671590Srgrimes{ 8681590Srgrimes 8691590Srgrimes verbose = !verbose; 8701590Srgrimes printf("Verbose mode %s.\n", verbose ? "on" : "off"); 8711590Srgrimes} 872207607Simp 873207607Simpstatic void 874207607Simpsetoptions(int argc, char *argv[]) 875207607Simp{ 876207607Simp 877207607Simp if (argc == 2) { 878207607Simp if (strcasecmp(argv[1], "enable") == 0 || 879207607Simp strcasecmp(argv[1], "on") == 0) { 880207607Simp options_extra_enabled = 1; 881207607Simp options_rfc_enabled = 1; 882207607Simp } 883207607Simp if (strcasecmp(argv[1], "disable") == 0 || 884207607Simp strcasecmp(argv[1], "off") == 0) { 885207607Simp options_extra_enabled = 0; 886207607Simp options_rfc_enabled = 0; 887207607Simp } 888207607Simp if (strcasecmp(argv[1], "extra") == 0) 889207607Simp options_extra_enabled = !options_extra_enabled; 890207607Simp } 891207607Simp printf("Support for RFC2347 style options are now %s.\n", 892207607Simp options_rfc_enabled ? "enabled" : "disabled"); 893207607Simp printf("Support for non-RFC defined options are now %s.\n", 894207607Simp options_extra_enabled ? "enabled" : "disabled"); 895207607Simp 896207607Simp printf("\nThe following options are available:\n" 897207607Simp "\toptions on : enable support for RFC2347 style options\n" 898207607Simp "\toptions off : disable support for RFC2347 style options\n" 899207607Simp "\toptions extra : toggle support for non-RFC defined options\n" 900207607Simp ); 901207607Simp} 902207607Simp 903207607Simpstatic void 904207607Simpsetrollover(int argc, char *argv[]) 905207607Simp{ 906207607Simp 907207607Simp if (argc == 2) { 908207607Simp if (strcasecmp(argv[1], "never") == 0 || 909207607Simp strcasecmp(argv[1], "none") == 0) { 910207607Simp free(options[OPT_ROLLOVER].o_request); 911207607Simp options[OPT_ROLLOVER].o_request = NULL; 912207607Simp } 913207607Simp if (strcasecmp(argv[1], "1") == 0) { 914207607Simp free(options[OPT_ROLLOVER].o_request); 915207607Simp options[OPT_ROLLOVER].o_request = strdup("1"); 916207607Simp } 917207607Simp if (strcasecmp(argv[1], "0") == 0) { 918207607Simp free(options[OPT_ROLLOVER].o_request); 919207607Simp options[OPT_ROLLOVER].o_request = strdup("0"); 920207607Simp } 921207607Simp } 922207607Simp printf("Support for the rollover options is %s.\n", 923207607Simp options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled"); 924207607Simp if (options[OPT_ROLLOVER].o_request != NULL) 925207607Simp printf("Block rollover will be to block %s.\n", 926207607Simp options[OPT_ROLLOVER].o_request); 927207607Simp 928207607Simp 929207607Simp printf("\nThe following rollover options are available:\n" 930207607Simp "\trollover 0 : rollover to block zero (default)\n" 931207607Simp "\trollover 1 : rollover to block one\n" 932207607Simp "\trollover never : do not support the rollover option\n" 933207607Simp "\trollover none : do not support the rollover option\n" 934207607Simp ); 935207607Simp} 936207607Simp 937207607Simpstatic void 938207607Simpsetdebug(int argc, char *argv[]) 939207607Simp{ 940207607Simp int i; 941207607Simp 942207607Simp if (argc != 1) { 943207607Simp i = 1; 944207607Simp while (i < argc) 945207607Simp debug ^= debug_find(argv[i++]); 946207607Simp } 947207607Simp printf("The following debugging is enabled: %s\n", debug_show(debug)); 948207607Simp 949207607Simp printf("\nThe following debugs are available:\n"); 950207607Simp i = 0; 951207607Simp while (debugs[i].name != NULL) { 952207607Simp printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc); 953207607Simp i++; 954207607Simp } 955207607Simp} 956207607Simp 957207607Simpstatic void 958207607Simpsetblocksize(int argc, char *argv[]) 959207607Simp{ 960207607Simp 961207607Simp if (!options_rfc_enabled) 962207607Simp printf("RFC2347 style options are not enabled " 963222534Simp "(but proceeding anyway)\n"); 964207607Simp 965207607Simp if (argc != 1) { 966207607Simp int size = atoi(argv[1]); 967207607Simp size_t max; 968213099Smarius u_long maxdgram; 969207607Simp 970213099Smarius max = sizeof(maxdgram); 971207607Simp if (sysctlbyname("net.inet.udp.maxdgram", 972213099Smarius &maxdgram, &max, NULL, 0) < 0) { 973207607Simp perror("sysctl: net.inet.udp.maxdgram"); 974207607Simp return; 975207607Simp } 976207607Simp 977207607Simp if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { 978207607Simp printf("Blocksize should be between %d and %d bytes.\n", 979207607Simp BLKSIZE_MIN, BLKSIZE_MAX); 980207607Simp return; 981213099Smarius } else if (size > (int)maxdgram - 4) { 982213099Smarius printf("Blocksize can't be bigger than %ld bytes due " 983207607Simp "to the net.inet.udp.maxdgram sysctl limitation.\n", 984213099Smarius maxdgram - 4); 985207607Simp asprintf(&options[OPT_BLKSIZE].o_request, 986213099Smarius "%ld", maxdgram - 4); 987207607Simp } else { 988207607Simp asprintf(&options[OPT_BLKSIZE].o_request, "%d", size); 989207607Simp } 990207607Simp } 991207607Simp printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request); 992207607Simp} 993207607Simp 994207607Simpstatic void 995207607Simpsetblocksize2(int argc, char *argv[]) 996207607Simp{ 997207607Simp 998207607Simp if (!options_rfc_enabled || !options_extra_enabled) 999207607Simp printf( 1000207607Simp "RFC2347 style or non-RFC defined options are not enabled " 1001222534Simp "(but proceeding anyway)\n"); 1002207607Simp 1003207607Simp if (argc != 1) { 1004207607Simp int size = atoi(argv[1]); 1005207607Simp int i; 1006207607Simp size_t max; 1007213099Smarius u_long maxdgram; 1008207607Simp 1009207607Simp int sizes[] = { 1010207607Simp 8, 16, 32, 64, 128, 256, 512, 1024, 1011207607Simp 2048, 4096, 8192, 16384, 32768, 0 1012207607Simp }; 1013207607Simp 1014213099Smarius max = sizeof(maxdgram); 1015207607Simp if (sysctlbyname("net.inet.udp.maxdgram", 1016213099Smarius &maxdgram, &max, NULL, 0) < 0) { 1017207607Simp perror("sysctl: net.inet.udp.maxdgram"); 1018207607Simp return; 1019207607Simp } 1020207607Simp 1021207607Simp for (i = 0; sizes[i] != 0; i++) { 1022207607Simp if (sizes[i] == size) break; 1023207607Simp } 1024207607Simp if (sizes[i] == 0) { 1025207607Simp printf("Blocksize2 should be a power of two between " 1026207607Simp "8 and 32768.\n"); 1027207607Simp return; 1028207607Simp } 1029207607Simp 1030207607Simp if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { 1031207607Simp printf("Blocksize2 should be between " 1032207607Simp "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX); 1033207607Simp return; 1034213099Smarius } else if (size > (int)maxdgram - 4) { 1035213099Smarius printf("Blocksize2 can't be bigger than %ld bytes due " 1036207607Simp "to the net.inet.udp.maxdgram sysctl limitation.\n", 1037213099Smarius maxdgram - 4); 1038207607Simp for (i = 0; sizes[i+1] != 0; i++) { 1039213099Smarius if ((int)maxdgram < sizes[i+1]) break; 1040207607Simp } 1041207607Simp asprintf(&options[OPT_BLKSIZE2].o_request, 1042207607Simp "%d", sizes[i]); 1043207607Simp } else { 1044207607Simp asprintf(&options[OPT_BLKSIZE2].o_request, "%d", size); 1045207607Simp } 1046207607Simp } 1047207607Simp printf("Blocksize2 is now %s bytes.\n", 1048207607Simp options[OPT_BLKSIZE2].o_request); 1049207607Simp} 1050207607Simp 1051207607Simpstatic void 1052207607Simpsetpacketdrop(int argc, char *argv[]) 1053207607Simp{ 1054207607Simp 1055207607Simp if (argc != 1) 1056207607Simp packetdroppercentage = atoi(argv[1]); 1057207607Simp 1058207607Simp printf("Randomly %d in 100 packets will be dropped\n", 1059207607Simp packetdroppercentage); 1060207607Simp} 1061