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$"); 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 226207607Simp strncpy(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) { 323207607Simp (void) strncpy(hostname, res->ai_canonname, 32494443Sume sizeof(hostname)); 32594443Sume } else 326207607Simp (void) strncpy(hostname, host, sizeof(hostname)); 327207607Simp hostname[sizeof(hostname)-1] = 0; 32894443Sume connected = 1; 32994443Sume } 33094443Sume 33194443Sume freeaddrinfo(res0); 33294443Sume} 33394443Sume 334207607Simpstatic void 335183858Sdelphijsetpeer(int argc, char *argv[]) 3361590Srgrimes{ 337207607Simp char line[MAXLINE]; 3381590Srgrimes 3391590Srgrimes if (argc < 2) { 3401590Srgrimes strcpy(line, "Connect "); 3411590Srgrimes printf("(to) "); 34213068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 343207607Simp makeargv(line); 3441590Srgrimes argc = margc; 3451590Srgrimes argv = margv; 3461590Srgrimes } 34788883Sguido if ((argc < 2) || (argc > 3)) { 348120647Ssimon printf("usage: %s [host [port]]\n", argv[0]); 3491590Srgrimes return; 3501590Srgrimes } 351207607Simp if (argc == 3) { 352207607Simp port = argv[2]; 353112582Sjon setpeer0(argv[1], argv[2]); 354207607Simp } else 35594443Sume setpeer0(argv[1], NULL); 3561590Srgrimes} 3571590Srgrimes 358207607Simpstatic void 359183858Sdelphijmodecmd(int argc, char *argv[]) 3601590Srgrimes{ 36187708Smarkm struct modes *p; 36287708Smarkm const char *sep; 3631590Srgrimes 3641590Srgrimes if (argc < 2) { 3651590Srgrimes printf("Using %s mode to transfer files.\n", mode); 3661590Srgrimes return; 3671590Srgrimes } 3681590Srgrimes if (argc == 2) { 3691590Srgrimes for (p = modes; p->m_name; p++) 3701590Srgrimes if (strcmp(argv[1], p->m_name) == 0) 3711590Srgrimes break; 3721590Srgrimes if (p->m_name) { 3731590Srgrimes settftpmode(p->m_mode); 3741590Srgrimes return; 3751590Srgrimes } 3761590Srgrimes printf("%s: unknown mode\n", argv[1]); 3771590Srgrimes /* drop through and print usage message */ 3781590Srgrimes } 3791590Srgrimes 3801590Srgrimes printf("usage: %s [", argv[0]); 3811590Srgrimes sep = " "; 382207607Simp for (p = modes; p->m_name != NULL; p++) { 3831590Srgrimes printf("%s%s", sep, p->m_name); 3841590Srgrimes if (*sep == ' ') 3851590Srgrimes sep = " | "; 3861590Srgrimes } 3871590Srgrimes printf(" ]\n"); 3881590Srgrimes return; 3891590Srgrimes} 3901590Srgrimes 391207607Simpstatic void 392183858Sdelphijsetbinary(int argc __unused, char *argv[] __unused) 3938874Srgrimes{ 3941590Srgrimes 3951590Srgrimes settftpmode("octet"); 3961590Srgrimes} 3971590Srgrimes 398207607Simpstatic void 399183858Sdelphijsetascii(int argc __unused, char *argv[] __unused) 4001590Srgrimes{ 4011590Srgrimes 4021590Srgrimes settftpmode("netascii"); 4031590Srgrimes} 4041590Srgrimes 4051590Srgrimesstatic void 406183858Sdelphijsettftpmode(const char *newmode) 4071590Srgrimes{ 408207607Simp 4091590Srgrimes strcpy(mode, newmode); 4101590Srgrimes if (verbose) 4111590Srgrimes printf("mode set to %s\n", mode); 4121590Srgrimes} 4131590Srgrimes 4141590Srgrimes 4151590Srgrimes/* 4161590Srgrimes * Send file(s). 4171590Srgrimes */ 418207607Simpstatic void 419183858Sdelphijput(int argc, char *argv[]) 4201590Srgrimes{ 421207607Simp int fd; 422207607Simp int n; 423207607Simp char *cp, *targ; 424207607Simp char line[MAXLINE]; 425207607Simp struct stat sb; 4261590Srgrimes 4271590Srgrimes if (argc < 2) { 4281590Srgrimes strcpy(line, "send "); 4291590Srgrimes printf("(file) "); 43013068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 431207607Simp makeargv(line); 4321590Srgrimes argc = margc; 4331590Srgrimes argv = margv; 4341590Srgrimes } 4351590Srgrimes if (argc < 2) { 4361590Srgrimes putusage(argv[0]); 4371590Srgrimes return; 4381590Srgrimes } 4391590Srgrimes targ = argv[argc - 1]; 440229403Sed if (strrchr(argv[argc - 1], ':')) { 44187708Smarkm char *lcp; 4421590Srgrimes 4431590Srgrimes for (n = 1; n < argc - 1; n++) 444229403Sed if (strchr(argv[n], ':')) { 4451590Srgrimes putusage(argv[0]); 4461590Srgrimes return; 4471590Srgrimes } 44887708Smarkm lcp = argv[argc - 1]; 449229403Sed targ = strrchr(lcp, ':'); 4501590Srgrimes *targ++ = 0; 45194443Sume if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { 45294443Sume lcp[strlen(lcp) - 1] = '\0'; 45394443Sume lcp++; 4541590Srgrimes } 45594443Sume setpeer0(lcp, NULL); 4561590Srgrimes } 4571590Srgrimes if (!connected) { 4581590Srgrimes printf("No target machine specified.\n"); 4591590Srgrimes return; 4601590Srgrimes } 4611590Srgrimes if (argc < 4) { 4621590Srgrimes cp = argc == 2 ? tail(targ) : argv[1]; 4631590Srgrimes fd = open(cp, O_RDONLY); 4641590Srgrimes if (fd < 0) { 46528202Scharnier warn("%s", cp); 4661590Srgrimes return; 4671590Srgrimes } 468207607Simp 469207607Simp stat(cp, &sb); 470207607Simp asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size); 471207607Simp 4721590Srgrimes if (verbose) 4731590Srgrimes printf("putting %s to %s:%s [%s]\n", 474207607Simp cp, hostname, targ, mode); 475207607Simp xmitfile(peer, port, fd, targ, mode); 4761590Srgrimes return; 4771590Srgrimes } 4781590Srgrimes /* this assumes the target is a directory */ 4791590Srgrimes /* on a remote unix system. hmmmm. */ 480229403Sed cp = strchr(targ, '\0'); 4811590Srgrimes *cp++ = '/'; 4821590Srgrimes for (n = 1; n < argc - 1; n++) { 4831590Srgrimes strcpy(cp, tail(argv[n])); 4841590Srgrimes fd = open(argv[n], O_RDONLY); 4851590Srgrimes if (fd < 0) { 48628202Scharnier warn("%s", argv[n]); 4871590Srgrimes continue; 4881590Srgrimes } 489207607Simp 490207607Simp stat(cp, &sb); 491207607Simp asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size); 492207607Simp 4931590Srgrimes if (verbose) 4941590Srgrimes printf("putting %s to %s:%s [%s]\n", 495207607Simp argv[n], hostname, targ, mode); 496207607Simp xmitfile(peer, port, fd, targ, mode); 4971590Srgrimes } 4981590Srgrimes} 4991590Srgrimes 5001590Srgrimesstatic void 501207607Simpputusage(char *s) 5021590Srgrimes{ 503207607Simp 504207607Simp printf("usage: %s file [remotename]\n", s); 505207607Simp printf(" %s file host:remotename\n", s); 506120647Ssimon printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", s); 5071590Srgrimes} 5081590Srgrimes 5091590Srgrimes/* 5101590Srgrimes * Receive file(s). 5111590Srgrimes */ 512207607Simpstatic void 513183858Sdelphijget(int argc, char *argv[]) 5141590Srgrimes{ 5151590Srgrimes int fd; 51687708Smarkm int n; 51787708Smarkm char *cp; 5181590Srgrimes char *src; 519207607Simp char line[MAXLINE]; 5201590Srgrimes 5211590Srgrimes if (argc < 2) { 5221590Srgrimes strcpy(line, "get "); 5231590Srgrimes printf("(files) "); 52413068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 525207607Simp makeargv(line); 5261590Srgrimes argc = margc; 5271590Srgrimes argv = margv; 5281590Srgrimes } 5291590Srgrimes if (argc < 2) { 5301590Srgrimes getusage(argv[0]); 5311590Srgrimes return; 5321590Srgrimes } 5331590Srgrimes if (!connected) { 5341590Srgrimes for (n = 1; n < argc ; n++) 535229403Sed if (strrchr(argv[n], ':') == 0) { 536207607Simp printf("No remote host specified and " 537207607Simp "no host given for file '%s'\n", argv[n]); 5381590Srgrimes getusage(argv[0]); 5391590Srgrimes return; 5401590Srgrimes } 5411590Srgrimes } 5421590Srgrimes for (n = 1; n < argc ; n++) { 543229403Sed src = strrchr(argv[n], ':'); 5441590Srgrimes if (src == NULL) 5451590Srgrimes src = argv[n]; 5461590Srgrimes else { 54794443Sume char *lcp; 5481590Srgrimes 5491590Srgrimes *src++ = 0; 55094443Sume lcp = argv[n]; 55194443Sume if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { 55294443Sume lcp[strlen(lcp) - 1] = '\0'; 55394443Sume lcp++; 55494443Sume } 55594443Sume setpeer0(lcp, NULL); 55694443Sume if (!connected) 5571590Srgrimes continue; 5581590Srgrimes } 5591590Srgrimes if (argc < 4) { 5601590Srgrimes cp = argc == 3 ? argv[2] : tail(src); 5611590Srgrimes fd = creat(cp, 0644); 5621590Srgrimes if (fd < 0) { 56328202Scharnier warn("%s", cp); 5641590Srgrimes return; 5651590Srgrimes } 5661590Srgrimes if (verbose) 5671590Srgrimes printf("getting from %s:%s to %s [%s]\n", 568207607Simp hostname, src, cp, mode); 569207607Simp recvfile(peer, port, fd, src, mode); 5701590Srgrimes break; 5711590Srgrimes } 5721590Srgrimes cp = tail(src); /* new .. jdg */ 5731590Srgrimes fd = creat(cp, 0644); 5741590Srgrimes if (fd < 0) { 57528202Scharnier warn("%s", cp); 5761590Srgrimes continue; 5771590Srgrimes } 5781590Srgrimes if (verbose) 5791590Srgrimes printf("getting from %s:%s to %s [%s]\n", 580207607Simp hostname, src, cp, mode); 581207607Simp recvfile(peer, port, fd, src, mode); 5821590Srgrimes } 5831590Srgrimes} 5841590Srgrimes 5851590Srgrimesstatic void 586207607Simpgetusage(char *s) 5871590Srgrimes{ 588207607Simp 589207607Simp printf("usage: %s file [localname]\n", s); 590207607Simp printf(" %s [host:]file [localname]\n", s); 591120647Ssimon printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); 5921590Srgrimes} 5931590Srgrimes 594207607Simpstatic void 595207607Simpsettimeoutpacket(int argc, char *argv[]) 5961590Srgrimes{ 5971590Srgrimes int t; 598207607Simp char line[MAXLINE]; 5991590Srgrimes 6001590Srgrimes if (argc < 2) { 601207607Simp strcpy(line, "Packet timeout "); 6021590Srgrimes printf("(value) "); 60313068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 604207607Simp makeargv(line); 6051590Srgrimes argc = margc; 6061590Srgrimes argv = margv; 6071590Srgrimes } 6081590Srgrimes if (argc != 2) { 6091590Srgrimes printf("usage: %s value\n", argv[0]); 6101590Srgrimes return; 6111590Srgrimes } 6121590Srgrimes t = atoi(argv[1]); 613207607Simp if (t < 0) { 6141590Srgrimes printf("%s: bad value\n", argv[1]); 615207607Simp return; 616207607Simp } 617207607Simp 618207607Simp settimeouts(t, timeoutnetwork, maxtimeouts); 6191590Srgrimes} 6201590Srgrimes 621207607Simpstatic void 622207607Simpsettimeoutnetwork(int argc, char *argv[]) 6231590Srgrimes{ 6241590Srgrimes int t; 625207607Simp char line[MAXLINE]; 6261590Srgrimes 6271590Srgrimes if (argc < 2) { 628207607Simp strcpy(line, "Network timeout "); 6291590Srgrimes printf("(value) "); 63013068Sjoerg fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 631207607Simp makeargv(line); 6321590Srgrimes argc = margc; 6331590Srgrimes argv = margv; 6341590Srgrimes } 6351590Srgrimes if (argc != 2) { 6361590Srgrimes printf("usage: %s value\n", argv[0]); 6371590Srgrimes return; 6381590Srgrimes } 6391590Srgrimes t = atoi(argv[1]); 640207607Simp if (t < 0) { 6411590Srgrimes printf("%s: bad value\n", argv[1]); 642207607Simp return; 643207607Simp } 644207607Simp 645207607Simp settimeouts(timeoutpacket, t, maxtimeouts); 6461590Srgrimes} 6471590Srgrimes 648207607Simpstatic void 649207607Simpshowstatus(int argc __unused, char *argv[] __unused) 6501590Srgrimes{ 651207607Simp 652207607Simp printf("Remote host: %s\n", 653207607Simp connected ? hostname : "none specified yet"); 654207607Simp printf("RFC2347 Options support: %s\n", 655207607Simp options_rfc_enabled ? "enabled" : "disabled"); 656207607Simp printf("Non-RFC defined options support: %s\n", 657207607Simp options_extra_enabled ? "enabled" : "disabled"); 658207607Simp printf("Mode: %s\n", mode); 659207607Simp printf("Verbose: %s\n", verbose ? "on" : "off"); 660207607Simp printf("Debug: %s\n", debug_show(debug)); 661207607Simp printf("Artificial packetloss: %d in 100 packets\n", 662207607Simp packetdroppercentage); 663207607Simp printf("Segment size: %d bytes\n", segsize); 664207607Simp printf("Network timeout: %d seconds\n", timeoutpacket); 665207607Simp printf("Maximum network timeout: %d seconds\n", timeoutnetwork); 666207607Simp printf("Maximum timeouts: %d \n", maxtimeouts); 6671590Srgrimes} 6681590Srgrimes 669207607Simpstatic void 670183858Sdelphijintr(int dummy __unused) 6711590Srgrimes{ 6721590Srgrimes 6731590Srgrimes signal(SIGALRM, SIG_IGN); 6741590Srgrimes alarm(0); 6751590Srgrimes longjmp(toplevel, -1); 6761590Srgrimes} 6771590Srgrimes 678207607Simpstatic char * 679183858Sdelphijtail(char *filename) 6801590Srgrimes{ 68187708Smarkm char *s; 6828874Srgrimes 6831590Srgrimes while (*filename) { 684229403Sed s = strrchr(filename, '/'); 6851590Srgrimes if (s == NULL) 6861590Srgrimes break; 6871590Srgrimes if (s[1]) 6881590Srgrimes return (s + 1); 6891590Srgrimes *s = '\0'; 6901590Srgrimes } 6911590Srgrimes return (filename); 6921590Srgrimes} 6931590Srgrimes 69485120Smdoddstatic const char * 695213099Smariuscommand_prompt(void) 69685155Sbde{ 69785155Sbde 69885120Smdodd return ("tftp> "); 69985120Smdodd} 70085120Smdodd 7011590Srgrimes/* 7021590Srgrimes * Command parser. 7031590Srgrimes */ 70418286Sbdestatic void 705183858Sdelphijcommand(void) 7061590Srgrimes{ 70785155Sbde HistEvent he; 70887708Smarkm struct cmd *c; 70985155Sbde static EditLine *el; 71085155Sbde static History *hist; 71185155Sbde const char *bp; 71213068Sjoerg char *cp; 71387708Smarkm int len, num, vrbose; 714207607Simp char line[MAXLINE]; 7151590Srgrimes 71687708Smarkm vrbose = isatty(0); 71787708Smarkm if (vrbose) { 71885120Smdodd el = el_init("tftp", stdin, stdout, stderr); 71985120Smdodd hist = history_init(); 720151471Sstefanf history(hist, &he, H_SETSIZE, 100); 72185120Smdodd el_set(el, EL_HIST, history, hist); 72285120Smdodd el_set(el, EL_EDITOR, "emacs"); 72385120Smdodd el_set(el, EL_PROMPT, command_prompt); 72485120Smdodd el_set(el, EL_SIGNAL, 1); 72585120Smdodd el_source(el, NULL); 72685120Smdodd } 7271590Srgrimes for (;;) { 72887708Smarkm if (vrbose) { 72985120Smdodd if ((bp = el_gets(el, &num)) == NULL || num == 0) 73085120Smdodd exit(0); 73185120Smdodd len = (num > MAXLINE) ? MAXLINE : num; 73285120Smdodd memcpy(line, bp, len); 73385120Smdodd line[len] = '\0'; 73485120Smdodd history(hist, &he, H_ENTER, bp); 73585120Smdodd } else { 736207607Simp line[0] = 0; 737230044Skevlo if (fgets(line, sizeof line , stdin) == NULL) { 73885120Smdodd if (feof(stdin)) { 73996433Sbsd exit(txrx_error); 74085120Smdodd } else { 74185120Smdodd continue; 74285120Smdodd } 7431590Srgrimes } 7441590Srgrimes } 74513068Sjoerg if ((cp = strchr(line, '\n'))) 74613068Sjoerg *cp = '\0'; 7471590Srgrimes if (line[0] == 0) 7481590Srgrimes continue; 749207607Simp makeargv(line); 7501590Srgrimes if (margc == 0) 7511590Srgrimes continue; 7521590Srgrimes c = getcmd(margv[0]); 7531590Srgrimes if (c == (struct cmd *)-1) { 7541590Srgrimes printf("?Ambiguous command\n"); 7551590Srgrimes continue; 7561590Srgrimes } 7571590Srgrimes if (c == 0) { 7581590Srgrimes printf("?Invalid command\n"); 7591590Srgrimes continue; 7601590Srgrimes } 7611590Srgrimes (*c->handler)(margc, margv); 7621590Srgrimes } 7631590Srgrimes} 7641590Srgrimes 765207607Simpstatic struct cmd * 766183858Sdelphijgetcmd(char *name) 7671590Srgrimes{ 76887708Smarkm const char *p, *q; 76987708Smarkm struct cmd *c, *found; 77087708Smarkm int nmatches, longest; 7711590Srgrimes 7721590Srgrimes longest = 0; 7731590Srgrimes nmatches = 0; 7741590Srgrimes found = 0; 7751590Srgrimes for (c = cmdtab; (p = c->name) != NULL; c++) { 7761590Srgrimes for (q = name; *q == *p++; q++) 7771590Srgrimes if (*q == 0) /* exact match? */ 7781590Srgrimes return (c); 7791590Srgrimes if (!*q) { /* the name was a prefix */ 7801590Srgrimes if (q - name > longest) { 7811590Srgrimes longest = q - name; 7821590Srgrimes nmatches = 1; 7831590Srgrimes found = c; 7841590Srgrimes } else if (q - name == longest) 7851590Srgrimes nmatches++; 7861590Srgrimes } 7871590Srgrimes } 7881590Srgrimes if (nmatches > 1) 7891590Srgrimes return ((struct cmd *)-1); 7901590Srgrimes return (found); 7911590Srgrimes} 7921590Srgrimes 7931590Srgrimes/* 7941590Srgrimes * Slice a string up into argc/argv. 7951590Srgrimes */ 7961590Srgrimesstatic void 797207607Simpmakeargv(char *line) 7981590Srgrimes{ 79987708Smarkm char *cp; 80087708Smarkm char **argp = margv; 8011590Srgrimes 8021590Srgrimes margc = 0; 803207607Simp if ((cp = strchr(line, '\n')) != NULL) 80413068Sjoerg *cp = '\0'; 805207607Simp for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) { 8061590Srgrimes while (isspace(*cp)) 8071590Srgrimes cp++; 8081590Srgrimes if (*cp == '\0') 8091590Srgrimes break; 8101590Srgrimes *argp++ = cp; 8111590Srgrimes margc += 1; 8121590Srgrimes while (*cp != '\0' && !isspace(*cp)) 8131590Srgrimes cp++; 8141590Srgrimes if (*cp == '\0') 8151590Srgrimes break; 8161590Srgrimes *cp++ = '\0'; 8171590Srgrimes } 8181590Srgrimes *argp++ = 0; 8191590Srgrimes} 8201590Srgrimes 821207607Simpstatic void 822183858Sdelphijquit(int argc __unused, char *argv[] __unused) 8231590Srgrimes{ 824207607Simp 82596433Sbsd exit(txrx_error); 8261590Srgrimes} 8271590Srgrimes 8281590Srgrimes/* 8291590Srgrimes * Help command. 8301590Srgrimes */ 831207607Simpstatic void 832183858Sdelphijhelp(int argc, char *argv[]) 8331590Srgrimes{ 83487708Smarkm struct cmd *c; 8351590Srgrimes 8361590Srgrimes if (argc == 1) { 8371590Srgrimes printf("Commands may be abbreviated. Commands are:\n\n"); 8381590Srgrimes for (c = cmdtab; c->name; c++) 8391590Srgrimes printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 840207607Simp 841207607Simp printf("\n[-] : You shouldn't use these ones anymore.\n"); 842223135Srodrigc printf("[*] : RFC2347 options support required.\n"); 843223135Srodrigc printf("[**] : Non-standard RFC2347 option.\n"); 8441590Srgrimes return; 8451590Srgrimes } 8461590Srgrimes while (--argc > 0) { 84787708Smarkm char *arg; 8481590Srgrimes arg = *++argv; 8491590Srgrimes c = getcmd(arg); 8501590Srgrimes if (c == (struct cmd *)-1) 851207607Simp printf("?Ambiguous help command: %s\n", arg); 8521590Srgrimes else if (c == (struct cmd *)0) 853207607Simp printf("?Invalid help command: %s\n", arg); 8541590Srgrimes else 8551590Srgrimes printf("%s\n", c->help); 8561590Srgrimes } 8571590Srgrimes} 8581590Srgrimes 859207607Simpstatic void 860207607Simpsetverbose(int argc __unused, char *argv[] __unused) 8611590Srgrimes{ 8621590Srgrimes 8631590Srgrimes verbose = !verbose; 8641590Srgrimes printf("Verbose mode %s.\n", verbose ? "on" : "off"); 8651590Srgrimes} 866207607Simp 867207607Simpstatic void 868207607Simpsetoptions(int argc, char *argv[]) 869207607Simp{ 870207607Simp 871207607Simp if (argc == 2) { 872207607Simp if (strcasecmp(argv[1], "enable") == 0 || 873207607Simp strcasecmp(argv[1], "on") == 0) { 874207607Simp options_extra_enabled = 1; 875207607Simp options_rfc_enabled = 1; 876207607Simp } 877207607Simp if (strcasecmp(argv[1], "disable") == 0 || 878207607Simp strcasecmp(argv[1], "off") == 0) { 879207607Simp options_extra_enabled = 0; 880207607Simp options_rfc_enabled = 0; 881207607Simp } 882207607Simp if (strcasecmp(argv[1], "extra") == 0) 883207607Simp options_extra_enabled = !options_extra_enabled; 884207607Simp } 885207607Simp printf("Support for RFC2347 style options are now %s.\n", 886207607Simp options_rfc_enabled ? "enabled" : "disabled"); 887207607Simp printf("Support for non-RFC defined options are now %s.\n", 888207607Simp options_extra_enabled ? "enabled" : "disabled"); 889207607Simp 890207607Simp printf("\nThe following options are available:\n" 891207607Simp "\toptions on : enable support for RFC2347 style options\n" 892207607Simp "\toptions off : disable support for RFC2347 style options\n" 893207607Simp "\toptions extra : toggle support for non-RFC defined options\n" 894207607Simp ); 895207607Simp} 896207607Simp 897207607Simpstatic void 898207607Simpsetrollover(int argc, char *argv[]) 899207607Simp{ 900207607Simp 901207607Simp if (argc == 2) { 902207607Simp if (strcasecmp(argv[1], "never") == 0 || 903207607Simp strcasecmp(argv[1], "none") == 0) { 904207607Simp free(options[OPT_ROLLOVER].o_request); 905207607Simp options[OPT_ROLLOVER].o_request = NULL; 906207607Simp } 907207607Simp if (strcasecmp(argv[1], "1") == 0) { 908207607Simp free(options[OPT_ROLLOVER].o_request); 909207607Simp options[OPT_ROLLOVER].o_request = strdup("1"); 910207607Simp } 911207607Simp if (strcasecmp(argv[1], "0") == 0) { 912207607Simp free(options[OPT_ROLLOVER].o_request); 913207607Simp options[OPT_ROLLOVER].o_request = strdup("0"); 914207607Simp } 915207607Simp } 916207607Simp printf("Support for the rollover options is %s.\n", 917207607Simp options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled"); 918207607Simp if (options[OPT_ROLLOVER].o_request != NULL) 919207607Simp printf("Block rollover will be to block %s.\n", 920207607Simp options[OPT_ROLLOVER].o_request); 921207607Simp 922207607Simp 923207607Simp printf("\nThe following rollover options are available:\n" 924207607Simp "\trollover 0 : rollover to block zero (default)\n" 925207607Simp "\trollover 1 : rollover to block one\n" 926207607Simp "\trollover never : do not support the rollover option\n" 927207607Simp "\trollover none : do not support the rollover option\n" 928207607Simp ); 929207607Simp} 930207607Simp 931207607Simpstatic void 932207607Simpsetdebug(int argc, char *argv[]) 933207607Simp{ 934207607Simp int i; 935207607Simp 936207607Simp if (argc != 1) { 937207607Simp i = 1; 938207607Simp while (i < argc) 939207607Simp debug ^= debug_find(argv[i++]); 940207607Simp } 941207607Simp printf("The following debugging is enabled: %s\n", debug_show(debug)); 942207607Simp 943207607Simp printf("\nThe following debugs are available:\n"); 944207607Simp i = 0; 945207607Simp while (debugs[i].name != NULL) { 946207607Simp printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc); 947207607Simp i++; 948207607Simp } 949207607Simp} 950207607Simp 951207607Simpstatic void 952207607Simpsetblocksize(int argc, char *argv[]) 953207607Simp{ 954207607Simp 955207607Simp if (!options_rfc_enabled) 956207607Simp printf("RFC2347 style options are not enabled " 957222534Simp "(but proceeding anyway)\n"); 958207607Simp 959207607Simp if (argc != 1) { 960207607Simp int size = atoi(argv[1]); 961207607Simp size_t max; 962213099Smarius u_long maxdgram; 963207607Simp 964213099Smarius max = sizeof(maxdgram); 965207607Simp if (sysctlbyname("net.inet.udp.maxdgram", 966213099Smarius &maxdgram, &max, NULL, 0) < 0) { 967207607Simp perror("sysctl: net.inet.udp.maxdgram"); 968207607Simp return; 969207607Simp } 970207607Simp 971207607Simp if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { 972207607Simp printf("Blocksize should be between %d and %d bytes.\n", 973207607Simp BLKSIZE_MIN, BLKSIZE_MAX); 974207607Simp return; 975213099Smarius } else if (size > (int)maxdgram - 4) { 976213099Smarius printf("Blocksize can't be bigger than %ld bytes due " 977207607Simp "to the net.inet.udp.maxdgram sysctl limitation.\n", 978213099Smarius maxdgram - 4); 979207607Simp asprintf(&options[OPT_BLKSIZE].o_request, 980213099Smarius "%ld", maxdgram - 4); 981207607Simp } else { 982207607Simp asprintf(&options[OPT_BLKSIZE].o_request, "%d", size); 983207607Simp } 984207607Simp } 985207607Simp printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request); 986207607Simp} 987207607Simp 988207607Simpstatic void 989207607Simpsetblocksize2(int argc, char *argv[]) 990207607Simp{ 991207607Simp 992207607Simp if (!options_rfc_enabled || !options_extra_enabled) 993207607Simp printf( 994207607Simp "RFC2347 style or non-RFC defined options are not enabled " 995222534Simp "(but proceeding anyway)\n"); 996207607Simp 997207607Simp if (argc != 1) { 998207607Simp int size = atoi(argv[1]); 999207607Simp int i; 1000207607Simp size_t max; 1001213099Smarius u_long maxdgram; 1002207607Simp 1003207607Simp int sizes[] = { 1004207607Simp 8, 16, 32, 64, 128, 256, 512, 1024, 1005207607Simp 2048, 4096, 8192, 16384, 32768, 0 1006207607Simp }; 1007207607Simp 1008213099Smarius max = sizeof(maxdgram); 1009207607Simp if (sysctlbyname("net.inet.udp.maxdgram", 1010213099Smarius &maxdgram, &max, NULL, 0) < 0) { 1011207607Simp perror("sysctl: net.inet.udp.maxdgram"); 1012207607Simp return; 1013207607Simp } 1014207607Simp 1015207607Simp for (i = 0; sizes[i] != 0; i++) { 1016207607Simp if (sizes[i] == size) break; 1017207607Simp } 1018207607Simp if (sizes[i] == 0) { 1019207607Simp printf("Blocksize2 should be a power of two between " 1020207607Simp "8 and 32768.\n"); 1021207607Simp return; 1022207607Simp } 1023207607Simp 1024207607Simp if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { 1025207607Simp printf("Blocksize2 should be between " 1026207607Simp "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX); 1027207607Simp return; 1028213099Smarius } else if (size > (int)maxdgram - 4) { 1029213099Smarius printf("Blocksize2 can't be bigger than %ld bytes due " 1030207607Simp "to the net.inet.udp.maxdgram sysctl limitation.\n", 1031213099Smarius maxdgram - 4); 1032207607Simp for (i = 0; sizes[i+1] != 0; i++) { 1033213099Smarius if ((int)maxdgram < sizes[i+1]) break; 1034207607Simp } 1035207607Simp asprintf(&options[OPT_BLKSIZE2].o_request, 1036207607Simp "%d", sizes[i]); 1037207607Simp } else { 1038207607Simp asprintf(&options[OPT_BLKSIZE2].o_request, "%d", size); 1039207607Simp } 1040207607Simp } 1041207607Simp printf("Blocksize2 is now %s bytes.\n", 1042207607Simp options[OPT_BLKSIZE2].o_request); 1043207607Simp} 1044207607Simp 1045207607Simpstatic void 1046207607Simpsetpacketdrop(int argc, char *argv[]) 1047207607Simp{ 1048207607Simp 1049207607Simp if (argc != 1) 1050207607Simp packetdroppercentage = atoi(argv[1]); 1051207607Simp 1052207607Simp printf("Randomly %d in 100 packets will be dropped\n", 1053207607Simp packetdroppercentage); 1054207607Simp} 1055