1158115Sume/*- 2158115Sume * SPDX-License-Identifier: BSD-3-Clause 3158115Sume * 4158115Sume * Copyright (c) 1983, 1993 5158115Sume * The Regents of the University of California. All rights reserved. 6158115Sume * 7158115Sume * Redistribution and use in source and binary forms, with or without 8158115Sume * modification, are permitted provided that the following conditions 9158115Sume * are met: 10158115Sume * 1. Redistributions of source code must retain the above copyright 11158115Sume * notice, this list of conditions and the following disclaimer. 12158115Sume * 2. Redistributions in binary form must reproduce the above copyright 13158115Sume * notice, this list of conditions and the following disclaimer in the 14158115Sume * documentation and/or other materials provided with the distribution. 15158115Sume * 3. Neither the name of the University nor the names of its contributors 16158115Sume * may be used to endorse or promote products derived from this software 17158115Sume * without specific prior written permission. 18158115Sume * 19158115Sume * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20158115Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21158115Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22158115Sume * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23158115Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24158115Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25158115Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26158115Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27158115Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28158115Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29158115Sume * SUCH DAMAGE. 30158115Sume */ 31194089Sdes 32158115Sume/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 33158115Sume 34194089Sdes/* 35158115Sume * TFTP User Program -- Command Interface. 36158115Sume */ 37194089Sdes#include <sys/param.h> 38158115Sume#include <sys/file.h> 39158115Sume#include <sys/socket.h> 40158115Sume#include <sys/stat.h> 41158115Sume#include <sys/sysctl.h> 42158115Sume 43158115Sume#include <netinet/in.h> 44158115Sume#include <arpa/inet.h> 45158115Sume#include <arpa/tftp.h> 46158115Sume 47158115Sume#include <ctype.h> 48158115Sume#include <err.h> 49158115Sume#include <histedit.h> 50158115Sume#include <netdb.h> 51158115Sume#include <setjmp.h> 52158115Sume#include <signal.h> 53158115Sume#include <stdbool.h> 54158115Sume#include <stddef.h> 55158115Sume#include <stdio.h> 56158115Sume#include <stdlib.h> 57171795Sbushman#include <string.h> 58158115Sume#include <unistd.h> 59158115Sume 60158115Sume#include "tftp-utils.h" 61158115Sume#include "tftp-io.h" 62158115Sume#include "tftp-options.h" 63171795Sbushman#include "tftp.h" 64158115Sume 65171795Sbushman#define MAXLINE (2 * MAXPATHLEN) 66158115Sume#define TIMEOUT 5 /* secs between rexmt's */ 67158115Sume 68158115Sumetypedef struct sockaddr_storage peeraddr; 69158115Sumestatic int connected; 70158115Sumestatic char mode[32]; 71158115Sumestatic jmp_buf toplevel; 72158115Sumestatic int txrx_error; 73158115Sumestatic int peer; 74158115Sume 75158115Sume#define MAX_MARGV 20 76158115Sumestatic int margc; 77158115Sumestatic char *margv[MAX_MARGV]; 78158115Sume 79158115Sumeint verbose; 80158115Sumestatic char *port = NULL; 81158115Sume 82158115Sumestatic void get(int, char **); 83158115Sumestatic void help(int, char **); 84158115Sumestatic void intr(int); 85158115Sumestatic void modecmd(int, char **); 86158115Sumestatic void put(int, char **); 87158115Sumestatic void quit(int, char **); 88158115Sumestatic void setascii(int, char **); 89158115Sumestatic void setbinary(int, char **); 90158115Sumestatic void setpeer0(char *, const char *); 91158115Sumestatic void setpeer(int, char **); 92158115Sumestatic void settimeoutpacket(int, char **); 93158115Sumestatic void settimeoutnetwork(int, char **); 94158115Sumestatic void setdebug(int, char **); 95162891Srustatic void setverbose(int, char **); 96171795Sbushmanstatic void showstatus(int, char **); 97158115Sumestatic void setblocksize(int, char **); 98158115Sumestatic void setblocksize2(int, char **); 99158115Sumestatic void setoptions(int, char **); 100158115Sumestatic void setrollover(int, char **); 101158115Sumestatic void setpacketdrop(int, char **); 102158115Sumestatic void setwindowsize(int, char **); 103158115Sume 104158115Sumestatic void command(bool, EditLine *, History *, HistEvent *) __dead2; 105158115Sumestatic const char *command_prompt(void); 106158115Sume 107158115Sumestatic void urihandling(char *URI); 108158115Sumestatic void getusage(char *); 109158115Sumestatic void makeargv(char *line); 110158115Sumestatic void putusage(char *); 111158115Sumestatic void settftpmode(const char *); 112158115Sume 113158115Sumestatic char *tail(char *); 114158115Sumestatic const struct cmd *getcmd(const char *); 115158115Sume 116158115Sume#define HELPINDENT (sizeof("connect")) 117158115Sume 118158115Sumestruct cmd { 119158115Sume const char *name; 120158115Sume void (*handler)(int, char **); 121158115Sume const char *help; 122158115Sume}; 123158115Sume 124158115Sumestatic struct cmd cmdtab[] = { 125158115Sume { "connect", setpeer, "connect to remote tftp" }, 126194097Sdes { "mode", modecmd, "set file transfer mode" }, 127158115Sume { "put", put, "send file" }, 128158115Sume { "get", get, "receive file" }, 129158115Sume { "quit", quit, "exit tftp" }, 130158115Sume { "verbose", setverbose, "toggle verbose mode" }, 131158115Sume { "status", showstatus, "show current status" }, 132158115Sume { "binary", setbinary, "set mode to octet" }, 133194097Sdes { "ascii", setascii, "set mode to netascii" }, 134158115Sume { "rexmt", settimeoutpacket, 135158115Sume "set per-packet retransmission timeout[-]" }, 136158115Sume { "timeout", settimeoutnetwork, 137158115Sume "set total retransmission timeout" }, 138158115Sume { "trace", setdebug, "enable 'debug packet'[-]" }, 139158115Sume { "debug", setdebug, "enable verbose output" }, 140158115Sume { "blocksize", setblocksize, "set blocksize[*]" }, 141158115Sume { "blocksize2", setblocksize2, "set blocksize as a power of 2[**]" }, 142158115Sume { "rollover", setrollover, "rollover after 64K packets[**]" }, 143158115Sume { "options", setoptions, 144158115Sume "enable or disable RFC2347 style options" }, 145158115Sume { "help", help, "print help information" }, 146158115Sume { "packetdrop", setpacketdrop, "artificial packetloss feature" }, 147158115Sume { "windowsize", setwindowsize, "set windowsize[*]" }, 148158115Sume { "?", help, "print help information" }, 149158115Sume { NULL, NULL, NULL } 150158115Sume}; 151158115Sume 152158115Sumestatic struct modes { 153158115Sume const char *m_name; 154158115Sume const char *m_mode; 155158115Sume} modes[] = { 156158115Sume { "ascii", "netascii" }, 157158115Sume { "netascii", "netascii" }, 158158115Sume { "binary", "octet" }, 159158115Sume { "image", "octet" }, 160158115Sume { "octet", "octet" }, 161158115Sume { NULL, NULL } 162158115Sume}; 163158115Sume 164158115Sumeint 165158115Sumemain(int argc, char *argv[]) 166158115Sume{ 167194104Sdes HistEvent he; 168158115Sume static EditLine *el; 169158115Sume static History *hist; 170158115Sume bool interactive; 171158115Sume 172158115Sume acting_as_client = 1; 173158115Sume peer = -1; 174158115Sume strcpy(mode, "octet"); 175158115Sume signal(SIGINT, intr); 176158115Sume 177184187Sdelphij interactive = isatty(STDIN_FILENO); 178158115Sume if (interactive) { 179158115Sume el = el_init("tftp", stdin, stdout, stderr); 180158115Sume hist = history_init(); 181158115Sume history(hist, &he, H_SETSIZE, 100); 182158115Sume el_set(el, EL_HIST, history, hist); 183158115Sume el_set(el, EL_EDITOR, "emacs"); 184158115Sume el_set(el, EL_PROMPT, command_prompt); 185158115Sume el_set(el, EL_SIGNAL, 1); 186158115Sume el_source(el, NULL); 187158115Sume } 188158115Sume 189158115Sume if (argc > 1) { 190158115Sume if (setjmp(toplevel) != 0) 191158115Sume exit(txrx_error); 192158115Sume 193158115Sume if (strncmp(argv[1], "tftp://", 7) == 0) { 194158115Sume urihandling(argv[1]); 195158115Sume exit(txrx_error); 196158115Sume } 197158115Sume 198158115Sume setpeer(argc, argv); 199158115Sume } 200158115Sume 201158115Sume if (setjmp(toplevel) != 0) { 202158115Sume if (interactive) 203158115Sume el_reset(el); 204158115Sume (void)putchar('\n'); 205158115Sume } 206158115Sume 207158115Sume init_options(); 208158115Sume command(interactive, el, hist, &he); 209158115Sume} 210158115Sume 211158115Sume/* 212158115Sume * RFC3617 handling of TFTP URIs: 213158115Sume * 214158115Sume * tftpURI = "tftp://" host "/" file [ mode ] 215158115Sume * mode = ";" "mode=" ( "netascii" / "octet" ) 216158115Sume * file = *( unreserved / escaped ) 217158115Sume * host = <as specified by RFC 2732> 218158115Sume * unreserved = <as specified in RFC 2396> 219158115Sume * escaped = <as specified in RFC 2396> 220158115Sume * 221158115Sume * We are cheating a little bit by allowing any mode as specified in the 222158115Sume * modes table defined earlier on in this file and mapping it on the real 223158115Sume * mode. 224158115Sume */ 225158115Sumestatic void 226158115Sumeurihandling(char *URI) 227158115Sume{ 228158115Sume char uri[ARG_MAX]; 229158115Sume char *host = NULL; 230158115Sume char *path = NULL; 231158115Sume char *opts = NULL; 232158115Sume const char *tmode = "octet"; 233158115Sume char *s; 234158115Sume char line[MAXLINE]; 235158115Sume int i; 236158115Sume 237158115Sume strlcpy(uri, URI, ARG_MAX); 238158115Sume host = uri + 7; 239158115Sume 240158115Sume if ((s = strchr(host, '/')) == NULL) { 241158115Sume fprintf(stderr, 242158115Sume "Invalid URI: Couldn't find / after hostname\n"); 243158115Sume exit(1); 244158115Sume } 245158115Sume *s = '\0'; 246158115Sume path = s + 1; 247158115Sume 248158115Sume if ((s = strchr(path, ';')) != NULL) { 249158115Sume *s = '\0'; 250158115Sume opts = s + 1; 251158115Sume 252158115Sume if (strncmp(opts, "mode=", 5) == 0) { 253158115Sume tmode = opts; 254158115Sume tmode += 5; 255158115Sume 256158115Sume for (i = 0; modes[i].m_name != NULL; i++) { 257158115Sume if (strcmp(modes[i].m_name, tmode) == 0) 258158115Sume break; 259158115Sume } 260158115Sume if (modes[i].m_name == NULL) { 261158115Sume fprintf(stderr, "Invalid mode: '%s'\n", mode); 262158115Sume exit(1); 263158115Sume } 264158115Sume settftpmode(modes[i].m_mode); 265158115Sume } 266158115Sume } else { 267158115Sume settftpmode("octet"); 268158115Sume } 269158115Sume 270158115Sume setpeer0(host, NULL); 271158115Sume 272158115Sume sprintf(line, "get %s", path); 273158115Sume makeargv(line); 274158115Sume get(margc, margv); 275158115Sume} 276158115Sume 277158115Sumestatic char hostname[MAXHOSTNAMELEN]; 278158115Sume 279158115Sumestatic void 280158115Sumesetpeer0(char *host, const char *lport) 281158115Sume{ 282158115Sume struct addrinfo hints, *res0, *res; 283158115Sume int error; 284158115Sume const char *cause = "unknown"; 285158115Sume 286158115Sume if (connected) { 287158115Sume close(peer); 288158115Sume peer = -1; 289158115Sume } 290158115Sume connected = 0; 291158115Sume 292158115Sume memset(&hints, 0, sizeof(hints)); 293158115Sume hints.ai_family = PF_UNSPEC; 294158115Sume hints.ai_socktype = SOCK_DGRAM; 295158115Sume hints.ai_protocol = IPPROTO_UDP; 296158115Sume hints.ai_flags = AI_CANONNAME; 297158115Sume if (!lport) 298158115Sume lport = "tftp"; 299158115Sume error = getaddrinfo(host, lport, &hints, &res0); 300158115Sume if (error) { 301158115Sume warnx("%s", gai_strerror(error)); 302158115Sume return; 303158115Sume } 304158115Sume 305158115Sume for (res = res0; res; res = res->ai_next) { 306158115Sume if (res->ai_addrlen > sizeof(peeraddr)) 307158115Sume continue; 308158115Sume peer = socket(res->ai_family, res->ai_socktype, 309158115Sume res->ai_protocol); 310158115Sume if (peer < 0) { 311158115Sume cause = "socket"; 312158115Sume continue; 313158115Sume } 314158115Sume 315158115Sume memset(&peer_sock, 0, sizeof(peer_sock)); 316158115Sume peer_sock.ss_family = res->ai_family; 317158115Sume peer_sock.ss_len = res->ai_addrlen; 318158115Sume if (bind(peer, (struct sockaddr *)&peer_sock, peer_sock.ss_len) < 0) { 319194096Sdes cause = "bind"; 320158115Sume close(peer); 321194096Sdes peer = -1; 322158115Sume continue; 323158115Sume } 324158115Sume 325158115Sume break; 326158115Sume } 327158115Sume 328158115Sume if (peer < 0) 329158115Sume warn("%s", cause); 330158115Sume else { 331158115Sume /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 332158115Sume memcpy(&peer_sock, res->ai_addr, res->ai_addrlen); 333158115Sume if (res->ai_canonname) { 334158115Sume (void) strlcpy(hostname, res->ai_canonname, 335158115Sume sizeof(hostname)); 336158115Sume } else 337158115Sume (void) strlcpy(hostname, host, sizeof(hostname)); 338158115Sume connected = 1; 339158115Sume } 340158115Sume 341158115Sume freeaddrinfo(res0); 342158115Sume} 343158115Sume 344158115Sumestatic void 345158115Sumesetpeer(int argc, char *argv[]) 346158115Sume{ 347158115Sume char line[MAXLINE]; 348158115Sume 349158115Sume if (argc < 2) { 350158115Sume strcpy(line, "Connect "); 351158115Sume printf("(to) "); 352158115Sume fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 353158115Sume makeargv(line); 354158115Sume argc = margc; 355158115Sume argv = margv; 356158115Sume } 357158115Sume if ((argc < 2) || (argc > 3)) { 358158115Sume printf("usage: %s [host [port]]\n", argv[0]); 359158115Sume return; 360158115Sume } 361158115Sume if (argc == 3) { 362158115Sume port = argv[2]; 363158115Sume setpeer0(argv[1], argv[2]); 364158115Sume } else 365158115Sume setpeer0(argv[1], NULL); 366158115Sume} 367158115Sume 368158115Sumestatic void 369158115Sumemodecmd(int argc, char *argv[]) 370158115Sume{ 371158115Sume struct modes *p; 372158115Sume const char *sep; 373158115Sume 374158115Sume if (argc < 2) { 375158115Sume printf("Using %s mode to transfer files.\n", mode); 376158115Sume return; 377158115Sume } 378158115Sume if (argc == 2) { 379158115Sume for (p = modes; p->m_name; p++) 380158115Sume if (strcmp(argv[1], p->m_name) == 0) 381158115Sume break; 382158115Sume if (p->m_name) { 383158115Sume settftpmode(p->m_mode); 384158115Sume return; 385158115Sume } 386158115Sume printf("%s: unknown mode\n", argv[1]); 387158115Sume /* drop through and print usage message */ 388158115Sume } 389158115Sume 390158115Sume printf("usage: %s [", argv[0]); 391158115Sume sep = " "; 392158115Sume for (p = modes; p->m_name != NULL; p++) { 393158115Sume printf("%s%s", sep, p->m_name); 394158115Sume if (*sep == ' ') 395158115Sume sep = " | "; 396158115Sume } 397158115Sume printf(" ]\n"); 398158115Sume return; 399158115Sume} 400158115Sume 401158115Sumestatic void 402158115Sumesetbinary(int argc __unused, char *argv[] __unused) 403158115Sume{ 404158115Sume 405158115Sume settftpmode("octet"); 406158115Sume} 407158115Sume 408158115Sumestatic void 409158115Sumesetascii(int argc __unused, char *argv[] __unused) 410158115Sume{ 411194104Sdes 412158115Sume settftpmode("netascii"); 413158115Sume} 414158115Sume 415158115Sumestatic void 416158115Sumesettftpmode(const char *newmode) 417158115Sume{ 418158115Sume 419158115Sume strlcpy(mode, newmode, sizeof(mode)); 420158115Sume if (verbose) 421158115Sume printf("mode set to %s\n", mode); 422158115Sume} 423158115Sume 424158115Sume 425158115Sume/* 426158115Sume * Send file(s). 427158115Sume */ 428158115Sumestatic void 429158115Sumeput(int argc, char *argv[]) 430158115Sume{ 431158115Sume int fd; 432158115Sume int n; 433158115Sume char *cp, *targ, *path; 434158115Sume char line[MAXLINE]; 435158115Sume struct stat sb; 436158115Sume 437158115Sume if (argc < 2) { 438158115Sume strcpy(line, "send "); 439158115Sume printf("(file) "); 440158115Sume fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 441158115Sume makeargv(line); 442158115Sume argc = margc; 443158115Sume argv = margv; 444158115Sume } 445158115Sume if (argc < 2) { 446158115Sume putusage(argv[0]); 447158115Sume return; 448158115Sume } 449158115Sume targ = argv[argc - 1]; 450158115Sume if (strrchr(argv[argc - 1], ':')) { 451158115Sume char *lcp; 452158115Sume 453158115Sume for (n = 1; n < argc - 1; n++) 454158115Sume if (strchr(argv[n], ':')) { 455158115Sume putusage(argv[0]); 456158115Sume return; 457158115Sume } 458158115Sume lcp = argv[argc - 1]; 459158115Sume targ = strrchr(lcp, ':'); 460158115Sume *targ++ = 0; 461158115Sume if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { 462158115Sume lcp[strlen(lcp) - 1] = '\0'; 463158115Sume lcp++; 464158115Sume } 465158115Sume setpeer0(lcp, NULL); 466158115Sume } 467158115Sume if (!connected) { 468158115Sume printf("No target machine specified.\n"); 469158115Sume return; 470158115Sume } 471158115Sume if (argc < 4) { 472158115Sume cp = argc == 2 ? tail(targ) : argv[1]; 473158115Sume fd = open(cp, O_RDONLY); 474158115Sume if (fd < 0) { 475158115Sume warn("%s", cp); 476158115Sume return; 477158115Sume } 478158115Sume 479158115Sume if (fstat(fd, &sb) < 0) { 480158115Sume warn("%s", cp); 481158115Sume close(fd); 482158115Sume return; 483158115Sume } 484158115Sume options_set_request(OPT_TSIZE, "%ju", (uintmax_t)sb.st_size); 485158115Sume 486158115Sume if (verbose) 487158115Sume printf("putting %s to %s:%s [%s]\n", 488158115Sume cp, hostname, targ, mode); 489158115Sume if (xmitfile(peer, port, fd, targ, mode)) 490158115Sume txrx_error = 1; 491158115Sume close(fd); 492158115Sume return; 493158115Sume } 494158115Sume /* this assumes the target is a directory */ 495158115Sume /* on a remote unix system. hmmmm. */ 496158115Sume for (n = 1; n < argc - 1; n++) { 497158115Sume if (asprintf(&path, "%s/%s", targ, tail(argv[n])) < 0) 498158115Sume err(1, "malloc"); 499158115Sume 500158115Sume fd = open(argv[n], O_RDONLY); 501158115Sume if (fd < 0) { 502158115Sume warn("%s", argv[n]); 503194096Sdes free(path); 504158115Sume continue; 505158115Sume } 506158115Sume 507158115Sume if (fstat(fd, &sb) < 0) { 508158115Sume warn("%s", argv[n]); 509158115Sume close(fd); 510158115Sume free(path); 511158115Sume continue; 512158115Sume } 513158115Sume options_set_request(OPT_TSIZE, "%ju", (uintmax_t)sb.st_size); 514158115Sume 515158115Sume if (verbose) 516158115Sume printf("putting %s to %s:%s [%s]\n", 517158115Sume argv[n], hostname, path, mode); 518158115Sume if (xmitfile(peer, port, fd, path, mode) != 0) 519158115Sume txrx_error = 1; 520158115Sume close(fd); 521158115Sume 522158115Sume free(path); 523158115Sume } 524158115Sume} 525158115Sume 526158115Sumestatic void 527158115Sumeputusage(char *s) 528158115Sume{ 529158115Sume 530158115Sume printf("usage: %s file [remotename]\n", s); 531158115Sume printf(" %s file host:remotename\n", s); 532158115Sume printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", s); 533158115Sume} 534158115Sume 535158115Sume/* 536158115Sume * Receive file(s). 537158115Sume */ 538158115Sumestatic void 539158115Sumeget(int argc, char *argv[]) 540158115Sume{ 541158115Sume int fd; 542158115Sume int n; 543158115Sume char *cp; 544158115Sume char *src; 545158115Sume char line[MAXLINE]; 546158115Sume 547158115Sume if (argc < 2) { 548158115Sume strcpy(line, "get "); 549158115Sume printf("(files) "); 550158115Sume fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 551158115Sume makeargv(line); 552158115Sume argc = margc; 553158115Sume argv = margv; 554158115Sume } 555158115Sume if (argc < 2) { 556158115Sume getusage(argv[0]); 557158115Sume return; 558158115Sume } 559158115Sume if (!connected) { 560158115Sume for (n = 1; n < argc ; n++) 561158115Sume if (strrchr(argv[n], ':') == 0) { 562158115Sume printf("No remote host specified and " 563158115Sume "no host given for file '%s'\n", argv[n]); 564158115Sume getusage(argv[0]); 565158115Sume return; 566158115Sume } 567158115Sume } 568158115Sume for (n = 1; n < argc ; n++) { 569158115Sume src = strrchr(argv[n], ':'); 570158115Sume if (src == NULL) 571158115Sume src = argv[n]; 572158115Sume else { 573158115Sume char *lcp; 574158115Sume 575158115Sume *src++ = 0; 576158115Sume lcp = argv[n]; 577194086Sdes if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { 578194086Sdes lcp[strlen(lcp) - 1] = '\0'; 579194086Sdes lcp++; 580194086Sdes } 581194086Sdes setpeer0(lcp, NULL); 582194086Sdes if (!connected) 583194086Sdes continue; 584194086Sdes } 585194086Sdes if (argc < 4) { 586158115Sume cp = argc == 3 ? argv[2] : tail(src); 587194086Sdes fd = creat(cp, 0644); 588158115Sume if (fd < 0) { 589158115Sume warn("%s", cp); 590158115Sume return; 591158115Sume } 592158115Sume if (verbose) 593158115Sume printf("getting from %s:%s to %s [%s]\n", 594158115Sume hostname, src, cp, mode); 595158115Sume if (recvfile(peer, port, fd, src, mode) != 0) 596158115Sume txrx_error = 1; 597158115Sume break; 598158115Sume } 599158115Sume cp = tail(src); /* new .. jdg */ 600158115Sume fd = creat(cp, 0644); 601158115Sume if (fd < 0) { 602158115Sume warn("%s", cp); 603158115Sume continue; 604158115Sume } 605158115Sume if (verbose) 606158115Sume printf("getting from %s:%s to %s [%s]\n", 607158115Sume hostname, src, cp, mode); 608158115Sume if (recvfile(peer, port, fd, src, mode) != 0) 609158115Sume txrx_error = 1; 610158115Sume } 611158115Sume} 612158115Sume 613158115Sumestatic void 614158115Sumegetusage(char *s) 615158115Sume{ 616158115Sume 617158115Sume printf("usage: %s file [localname]\n", s); 618158115Sume printf(" %s [host:]file [localname]\n", s); 619158115Sume printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); 620158115Sume} 621158115Sume 622158115Sumestatic void 623158115Sumesettimeoutpacket(int argc, char *argv[]) 624158115Sume{ 625158115Sume int t; 626158115Sume char line[MAXLINE]; 627158115Sume 628158115Sume if (argc < 2) { 629158115Sume strcpy(line, "Packet timeout "); 630158115Sume printf("(value) "); 631158115Sume fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 632158115Sume makeargv(line); 633158115Sume argc = margc; 634158115Sume argv = margv; 635158115Sume } 636158115Sume if (argc != 2) { 637158115Sume printf("usage: %s value\n", argv[0]); 638158115Sume return; 639158115Sume } 640158115Sume t = atoi(argv[1]); 641158115Sume if (t < 0) { 642158115Sume printf("%s: bad value\n", argv[1]); 643158115Sume return; 644158115Sume } 645158115Sume 646158115Sume settimeouts(t, timeoutnetwork, maxtimeouts); 647158115Sume} 648158115Sume 649158115Sumestatic void 650158115Sumesettimeoutnetwork(int argc, char *argv[]) 651158115Sume{ 652158115Sume int t; 653158115Sume char line[MAXLINE]; 654158115Sume 655158115Sume if (argc < 2) { 656158115Sume strcpy(line, "Network timeout "); 657158115Sume printf("(value) "); 658158115Sume fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 659158115Sume makeargv(line); 660158115Sume argc = margc; 661158115Sume argv = margv; 662158115Sume } 663158115Sume if (argc != 2) { 664158115Sume printf("usage: %s value\n", argv[0]); 665158115Sume return; 666158115Sume } 667158115Sume t = atoi(argv[1]); 668158115Sume if (t < 0) { 669158115Sume printf("%s: bad value\n", argv[1]); 670158115Sume return; 671158115Sume } 672158115Sume 673171795Sbushman settimeouts(timeoutpacket, t, maxtimeouts); 674171795Sbushman} 675158115Sume 676158115Sumestatic void 677158115Sumeshowstatus(int argc __unused, char *argv[] __unused) 678158115Sume{ 679158115Sume 680158115Sume printf("Remote host: %s\n", 681158115Sume connected ? hostname : "none specified yet"); 682158115Sume printf("RFC2347 Options support: %s\n", 683158115Sume options_rfc_enabled ? "enabled" : "disabled"); 684158115Sume printf("Non-RFC defined options support: %s\n", 685158115Sume options_extra_enabled ? "enabled" : "disabled"); 686158115Sume printf("Mode: %s\n", mode); 687158115Sume printf("Verbose: %s\n", verbose ? "on" : "off"); 688158115Sume printf("Debug: %s\n", debug_show(debug)); 689158115Sume printf("Artificial packetloss: %d in 100 packets\n", 690158115Sume packetdroppercentage); 691158115Sume printf("Segment size: %d bytes\n", segsize); 692158115Sume printf("Network timeout: %d seconds\n", timeoutpacket); 693158115Sume printf("Maximum network timeout: %d seconds\n", timeoutnetwork); 694171795Sbushman printf("Maximum timeouts: %d \n", maxtimeouts); 695158115Sume} 696171795Sbushman 697171795Sbushmanstatic void 698158115Sumeintr(int dummy __unused) 699158115Sume{ 700158115Sume 701171795Sbushman signal(SIGALRM, SIG_IGN); 702158115Sume alarm(0); 703158115Sume longjmp(toplevel, -1); 704158115Sume} 705158115Sume 706158115Sumestatic char * 707158115Sumetail(char *filename) 708158115Sume{ 709158115Sume char *s; 710158115Sume 711158115Sume while (*filename) { 712158115Sume s = strrchr(filename, '/'); 713158115Sume if (s == NULL) 714158115Sume break; 715158115Sume if (s[1]) 716158115Sume return (s + 1); 717171795Sbushman *s = '\0'; 718158115Sume } 719158115Sume return (filename); 720158115Sume} 721158115Sume 722158115Sumestatic const char * 723158115Sumecommand_prompt(void) 724158115Sume{ 725158115Sume 726158115Sume return ("tftp> "); 727158115Sume} 728158115Sume 729171795Sbushman/* 730158115Sume * Command parser. 731158115Sume */ 732158115Sumestatic void 733158115Sumecommand(bool interactive, EditLine *el, History *hist, HistEvent *hep) 734158115Sume{ 735158115Sume const struct cmd *c; 736158115Sume const char *bp; 737158115Sume char *cp; 738158115Sume int len, num; 739158115Sume char line[MAXLINE]; 740158115Sume 741158115Sume for (;;) { 742158115Sume if (interactive) { 743158115Sume if ((bp = el_gets(el, &num)) == NULL || num == 0) 744158115Sume exit(0); 745158115Sume len = MIN(MAXLINE, num); 746158115Sume memcpy(line, bp, len); 747158115Sume line[len - 1] = '\0'; 748158115Sume history(hist, hep, H_ENTER, bp); 749158115Sume } else { 750158115Sume line[0] = 0; 751158115Sume if (fgets(line, sizeof line , stdin) == NULL) { 752158115Sume if (feof(stdin)) { 753158115Sume exit(txrx_error); 754158115Sume } else { 755158115Sume continue; 756158115Sume } 757158115Sume } 758158115Sume } 759158115Sume if ((cp = strchr(line, '\n'))) 760158115Sume *cp = '\0'; 761158115Sume if (line[0] == 0) 762158115Sume continue; 763158115Sume makeargv(line); 764158115Sume if (margc == 0) 765158115Sume continue; 766158115Sume c = getcmd(margv[0]); 767158115Sume if (c == (struct cmd *)-1) { 768158115Sume printf("?Ambiguous command\n"); 769158115Sume continue; 770158115Sume } 771158115Sume if (c == NULL) { 772158115Sume printf("?Invalid command\n"); 773158115Sume continue; 774158115Sume } 775158115Sume (*c->handler)(margc, margv); 776158115Sume } 777158115Sume} 778158115Sume 779158115Sumestatic const struct cmd * 780158115Sumegetcmd(const char *name) 781158115Sume{ 782158115Sume const char *p, *q; 783158115Sume const struct cmd *c, *found; 784158115Sume ptrdiff_t longest; 785158115Sume int nmatches; 786158115Sume 787158115Sume longest = 0; 788158115Sume nmatches = 0; 789158115Sume found = 0; 790158115Sume for (c = cmdtab; (p = c->name) != NULL; c++) { 791158115Sume for (q = name; *q == *p++; q++) 792158115Sume if (*q == '\0') /* exact match? */ 793158115Sume return (c); 794158115Sume if (*q == '\0') { /* the name was a prefix */ 795158115Sume if (q - name > longest) { 796158115Sume longest = q - name; 797158115Sume nmatches = 1; 798158115Sume found = c; 799158115Sume } else if (q - name == longest) 800158115Sume nmatches++; 801158115Sume } 802158115Sume } 803158115Sume if (nmatches > 1) 804158115Sume return ((struct cmd *)-1); 805158115Sume return (found); 806158115Sume} 807158115Sume 808158115Sume/* 809158115Sume * Slice a string up into argc/argv. 810158115Sume */ 811158115Sumestatic void 812158115Sumemakeargv(char *line) 813158115Sume{ 814158115Sume char *cp; 815158115Sume char **argp = margv; 816158115Sume 817158115Sume margc = 0; 818158115Sume if ((cp = strchr(line, '\n')) != NULL) 819158115Sume *cp = '\0'; 820158115Sume for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) { 821158115Sume while (isspace(*cp)) 822158115Sume cp++; 823158115Sume if (*cp == '\0') 824158115Sume break; 825158115Sume *argp++ = cp; 826158115Sume margc += 1; 827158115Sume while (*cp != '\0' && !isspace(*cp)) 828158115Sume cp++; 829158115Sume if (*cp == '\0') 830158115Sume break; 831194104Sdes *cp++ = '\0'; 832158115Sume } 833158115Sume *argp++ = 0; 834194104Sdes} 835194104Sdes 836158115Sumestatic void 837158115Sumequit(int argc __unused, char *argv[] __unused) 838158115Sume{ 839158115Sume 840158115Sume exit(txrx_error); 841158115Sume} 842158115Sume 843158115Sume/* 844158115Sume * Help command. 845158115Sume */ 846158115Sumestatic void 847158115Sumehelp(int argc, char *argv[]) 848158115Sume{ 849158115Sume const struct cmd *c; 850158115Sume 851158115Sume if (argc == 1) { 852158115Sume printf("Commands may be abbreviated. Commands are:\n\n"); 853158115Sume for (c = cmdtab; c->name; c++) 854158115Sume printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 855158115Sume 856158115Sume printf("\n[-] : You shouldn't use these ones anymore.\n"); 857158115Sume printf("[*] : RFC2347 options support required.\n"); 858158115Sume printf("[**] : Non-standard RFC2347 option.\n"); 859158115Sume return; 860158115Sume } 861158115Sume while (--argc > 0) { 862158115Sume char *arg; 863158115Sume arg = *++argv; 864158115Sume c = getcmd(arg); 865158115Sume if (c == (struct cmd *)-1) 866158115Sume printf("?Ambiguous help command: %s\n", arg); 867158115Sume else if (c == (struct cmd *)0) 868158115Sume printf("?Invalid help command: %s\n", arg); 869158115Sume else 870158115Sume printf("%s\n", c->help); 871 } 872} 873 874static void 875setverbose(int argc __unused, char *argv[] __unused) 876{ 877 878 verbose = !verbose; 879 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 880} 881 882static void 883setoptions(int argc, char *argv[]) 884{ 885 886 if (argc == 2) { 887 if (strcasecmp(argv[1], "enable") == 0 || 888 strcasecmp(argv[1], "on") == 0) { 889 options_extra_enabled = 1; 890 options_rfc_enabled = 1; 891 } 892 if (strcasecmp(argv[1], "disable") == 0 || 893 strcasecmp(argv[1], "off") == 0) { 894 options_extra_enabled = 0; 895 options_rfc_enabled = 0; 896 } 897 if (strcasecmp(argv[1], "extra") == 0) 898 options_extra_enabled = !options_extra_enabled; 899 } 900 printf("Support for RFC2347 style options are now %s.\n", 901 options_rfc_enabled ? "enabled" : "disabled"); 902 printf("Support for non-RFC defined options are now %s.\n", 903 options_extra_enabled ? "enabled" : "disabled"); 904 905 printf("\nThe following options are available:\n" 906 "\toptions on : enable support for RFC2347 style options\n" 907 "\toptions off : disable support for RFC2347 style options\n" 908 "\toptions extra : toggle support for non-RFC defined options\n" 909 ); 910} 911 912static void 913setrollover(int argc, char *argv[]) 914{ 915 916 if (argc == 2) { 917 if (strcasecmp(argv[1], "never") == 0 || 918 strcasecmp(argv[1], "none") == 0) { 919 options_set_request(OPT_ROLLOVER, NULL); 920 } 921 if (strcasecmp(argv[1], "1") == 0) { 922 options_set_request(OPT_ROLLOVER, "1"); 923 } 924 if (strcasecmp(argv[1], "0") == 0) { 925 options_set_request(OPT_ROLLOVER, "0"); 926 } 927 } 928 printf("Support for the rollover options is %s.\n", 929 options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled"); 930 if (options[OPT_ROLLOVER].o_request != NULL) 931 printf("Block rollover will be to block %s.\n", 932 options[OPT_ROLLOVER].o_request); 933 934 935 printf("\nThe following rollover options are available:\n" 936 "\trollover 0 : rollover to block zero (default)\n" 937 "\trollover 1 : rollover to block one\n" 938 "\trollover never : do not support the rollover option\n" 939 "\trollover none : do not support the rollover option\n" 940 ); 941} 942 943static void 944setdebug(int argc, char *argv[]) 945{ 946 int i; 947 948 if (argc != 1) { 949 i = 1; 950 while (i < argc) 951 debug ^= debug_find(argv[i++]); 952 } 953 printf("The following debugging is enabled: %s\n", debug_show(debug)); 954 955 printf("\nThe following debugs are available:\n"); 956 i = 0; 957 while (debugs[i].name != NULL) { 958 printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc); 959 i++; 960 } 961} 962 963static void 964setblocksize(int argc, char *argv[]) 965{ 966 967 if (!options_rfc_enabled) 968 printf("RFC2347 style options are not enabled " 969 "(but proceeding anyway)\n"); 970 971 if (argc != 1) { 972 int size = atoi(argv[1]); 973 size_t max; 974 u_long maxdgram; 975 976 max = sizeof(maxdgram); 977 if (sysctlbyname("net.inet.udp.maxdgram", 978 &maxdgram, &max, NULL, 0) < 0) { 979 perror("sysctl: net.inet.udp.maxdgram"); 980 return; 981 } 982 983 if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { 984 printf("Blocksize should be between %d and %d bytes.\n", 985 BLKSIZE_MIN, BLKSIZE_MAX); 986 return; 987 } else if (size > (int)maxdgram - 4) { 988 printf("Blocksize can't be bigger than %ld bytes due " 989 "to the net.inet.udp.maxdgram sysctl limitation.\n", 990 maxdgram - 4); 991 options_set_request(OPT_BLKSIZE, "%ld", maxdgram - 4); 992 } else { 993 options_set_request(OPT_BLKSIZE, "%d", size); 994 } 995 } 996 printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request); 997} 998 999static void 1000setblocksize2(int argc, char *argv[]) 1001{ 1002 1003 if (!options_rfc_enabled || !options_extra_enabled) 1004 printf( 1005 "RFC2347 style or non-RFC defined options are not enabled " 1006 "(but proceeding anyway)\n"); 1007 1008 if (argc != 1) { 1009 int size = atoi(argv[1]); 1010 int i; 1011 size_t max; 1012 u_long maxdgram; 1013 1014 int sizes[] = { 1015 8, 16, 32, 64, 128, 256, 512, 1024, 1016 2048, 4096, 8192, 16384, 32768, 0 1017 }; 1018 1019 max = sizeof(maxdgram); 1020 if (sysctlbyname("net.inet.udp.maxdgram", 1021 &maxdgram, &max, NULL, 0) < 0) { 1022 perror("sysctl: net.inet.udp.maxdgram"); 1023 return; 1024 } 1025 1026 for (i = 0; sizes[i] != 0; i++) { 1027 if (sizes[i] == size) break; 1028 } 1029 if (sizes[i] == 0) { 1030 printf("Blocksize2 should be a power of two between " 1031 "8 and 32768.\n"); 1032 return; 1033 } 1034 1035 if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { 1036 printf("Blocksize2 should be between " 1037 "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX); 1038 return; 1039 } else if (size > (int)maxdgram - 4) { 1040 printf("Blocksize2 can't be bigger than %ld bytes due " 1041 "to the net.inet.udp.maxdgram sysctl limitation.\n", 1042 maxdgram - 4); 1043 for (i = 0; sizes[i+1] != 0; i++) { 1044 if ((int)maxdgram < sizes[i+1]) break; 1045 } 1046 options_set_request(OPT_BLKSIZE2, "%d", sizes[i]); 1047 } else { 1048 options_set_request(OPT_BLKSIZE2, "%d", size); 1049 } 1050 } 1051 printf("Blocksize2 is now %s bytes.\n", 1052 options[OPT_BLKSIZE2].o_request); 1053} 1054 1055static void 1056setpacketdrop(int argc, char *argv[]) 1057{ 1058 1059 if (argc != 1) 1060 packetdroppercentage = atoi(argv[1]); 1061 1062 printf("Randomly %d in 100 packets will be dropped\n", 1063 packetdroppercentage); 1064} 1065 1066static void 1067setwindowsize(int argc, char *argv[]) 1068{ 1069 1070 if (!options_rfc_enabled) 1071 printf("RFC2347 style options are not enabled " 1072 "(but proceeding anyway)\n"); 1073 1074 if (argc != 1) { 1075 int size = atoi(argv[1]); 1076 1077 if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) { 1078 printf("Windowsize should be between %d and %d " 1079 "blocks.\n", WINDOWSIZE_MIN, WINDOWSIZE_MAX); 1080 return; 1081 } else { 1082 options_set_request(OPT_WINDOWSIZE, "%d", size); 1083 } 1084 } 1085 printf("Windowsize is now %s blocks.\n", 1086 options[OPT_WINDOWSIZE].o_request); 1087} 1088