Index: tftp/main.c
===================================================================
--- tftp/main.c (nonexistent)
+++ tftp/main.c (revision 5)
@@ -0,0 +1,945 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "common/tftpsubs.h"
+
+/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
+
+/*
+ * TFTP User Program -- Command Interface.
+ */
+#include <sys/file.h>
+#include <ctype.h>
+#ifdef WITH_READLINE
+#include <readline/readline.h>
+#ifdef HAVE_READLINE_HISTORY_H
+#include <readline/history.h>
+#endif
+#endif
+
+#include "extern.h"
+
+#define TIMEOUT 5 /* secs between rexmt's */
+#define LBUFLEN 200 /* size of input buffer */
+
+struct modes {
+ const char *m_name;
+ const char *m_mode;
+ int m_openflags;
+};
+
+static const struct modes modes[] = {
+ {"netascii", "netascii", O_TEXT},
+ {"ascii", "netascii", O_TEXT},
+ {"octet", "octet", O_BINARY},
+ {"binary", "octet", O_BINARY},
+ {"image", "octet", O_BINARY},
+ {0, 0, 0}
+};
+
+#define MODE_OCTET (&modes[2])
+#define MODE_NETASCII (&modes[0])
+#define MODE_DEFAULT MODE_NETASCII
+
+#ifdef HAVE_IPV6
+int ai_fam = AF_UNSPEC;
+int ai_fam_sock = AF_UNSPEC;
+#else
+int ai_fam = AF_INET;
+int ai_fam_sock = AF_INET;
+#endif
+
+union sock_addr peeraddr;
+int f = -1;
+u_short port;
+int trace;
+int verbose;
+int literal;
+int connected;
+const struct modes *mode;
+#ifdef WITH_READLINE
+char *line = NULL;
+#else
+char line[LBUFLEN];
+#endif
+int margc;
+char *margv[20];
+const char *prompt = "tftp> ";
+sigjmp_buf toplevel;
+void intr(int);
+struct servent *sp;
+int portrange = 0;
+unsigned int portrange_from = 0;
+unsigned int portrange_to = 0;
+
+void get(int, char **);
+void help(int, char **);
+void modecmd(int, char **);
+void put(int, char **);
+void quit(int, char **);
+void setascii(int, char **);
+void setbinary(int, char **);
+void setpeer(int, char **);
+void setrexmt(int, char **);
+void settimeout(int, char **);
+void settrace(int, char **);
+void setverbose(int, char **);
+void status(int, char **);
+void setliteral(int, char **);
+
+static void command(void);
+
+static void getusage(char *);
+static void makeargv(void);
+static void putusage(char *);
+static void settftpmode(const struct modes *);
+
+#define HELPINDENT (sizeof("connect"))
+
+struct cmd {
+ const char *name;
+ const char *help;
+ void (*handler) (int, char **);
+};
+
+struct cmd cmdtab[] = {
+ {"connect",
+ "connect to remote tftp",
+ setpeer},
+ {"mode",
+ "set file transfer mode",
+ modecmd},
+ {"put",
+ "send file",
+ put},
+ {"get",
+ "receive file",
+ get},
+ {"quit",
+ "exit tftp",
+ quit},
+ {"verbose",
+ "toggle verbose mode",
+ setverbose},
+ {"trace",
+ "toggle packet tracing",
+ settrace},
+ {"literal",
+ "toggle literal mode, ignore ':' in file name",
+ setliteral},
+ {"status",
+ "show current status",
+ status},
+ {"binary",
+ "set mode to octet",
+ setbinary},
+ {"ascii",
+ "set mode to netascii",
+ setascii},
+ {"rexmt",
+ "set per-packet transmission timeout",
+ setrexmt},
+ {"timeout",
+ "set total retransmission timeout",
+ settimeout},
+ {"?",
+ "print help information",
+ help},
+ {"help",
+ "print help information",
+ help},
+ {0, 0, 0}
+};
+
+struct cmd *getcmd(char *);
+char *tail(char *);
+
+char *xstrdup(const char *);
+
+const char *program;
+
+static void usage(int errcode)
+{
+ fprintf(stderr,
+#ifdef HAVE_IPV6
+ "Usage: %s [-4][-6][-v][-l][-m mode] [host [port]] [-c command]\n",
+#else
+ "Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n",
+#endif
+ program);
+ exit(errcode);
+}
+
+int main(int argc, char *argv[])
+{
+ union sock_addr sa;
+ int arg;
+ static int pargc, peerargc;
+ static int iscmd = 0;
+ char **pargv;
+ const char *optx;
+ char *peerargv[3];
+
+ program = argv[0];
+
+ mode = MODE_DEFAULT;
+
+ peerargv[0] = argv[0];
+ peerargc = 1;
+
+ for (arg = 1; !iscmd && arg < argc; arg++) {
+ if (argv[arg][0] == '-') {
+ for (optx = &argv[arg][1]; *optx; optx++) {
+ switch (*optx) {
+ case '4':
+ ai_fam = AF_INET;
+ break;
+#ifdef HAVE_IPV6
+ case '6':
+ ai_fam = AF_INET6;
+ break;
+#endif
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ /* Print version and configuration to stdout and exit */
+ printf("%s\n", TFTP_CONFIG_STR);
+ exit(0);
+ case 'l':
+ literal = 1;
+ break;
+ case 'm':
+ if (++arg >= argc)
+ usage(EX_USAGE);
+ {
+ const struct modes *p;
+
+ for (p = modes; p->m_name; p++) {
+ if (!strcmp(argv[arg], p->m_name))
+ break;
+ }
+ if (p->m_name) {
+ settftpmode(p);
+ } else {
+ fprintf(stderr, "%s: invalid mode: %s\n",
+ argv[0], argv[arg]);
+ exit(EX_USAGE);
+ }
+ }
+ break;
+ case 'c':
+ iscmd = 1;
+ break;
+ case 'R':
+ if (++arg >= argc)
+ usage(EX_USAGE);
+ if (sscanf
+ (argv[arg], "%u:%u", &portrange_from,
+ &portrange_to) != 2
+ || portrange_from > portrange_to
+ || portrange_to > 65535) {
+ fprintf(stderr, "Bad port range: %s\n", argv[arg]);
+ exit(EX_USAGE);
+ }
+ portrange = 1;
+ break;
+ case 'h':
+ default:
+ usage(*optx == 'h' ? 0 : EX_USAGE);
+ }
+ }
+ } else {
+ if (peerargc >= 3)
+ usage(EX_USAGE);
+
+ peerargv[peerargc++] = argv[arg];
+ }
+ }
+
+ ai_fam_sock = ai_fam;
+
+ pargv = argv + arg;
+ pargc = argc - arg;
+
+ sp = getservbyname("tftp", "udp");
+ if (sp == 0) {
+ /* Use canned values */
+ if (verbose)
+ fprintf(stderr,
+ "tftp: tftp/udp: unknown service, faking it...\n");
+ sp = xmalloc(sizeof(struct servent));
+ sp->s_name = (char *)"tftp";
+ sp->s_aliases = NULL;
+ sp->s_port = htons(IPPORT_TFTP);
+ sp->s_proto = (char *)"udp";
+ }
+
+ bsd_signal(SIGINT, intr);
+
+ if (peerargc) {
+ /* Set peer */
+ if (sigsetjmp(toplevel, 1) != 0)
+ exit(EX_NOHOST);
+ setpeer(peerargc, peerargv);
+ }
+
+ if (ai_fam_sock == AF_UNSPEC)
+ ai_fam_sock = AF_INET;
+
+ f = socket(ai_fam_sock, SOCK_DGRAM, 0);
+ if (f < 0) {
+ perror("tftp: socket");
+ exit(EX_OSERR);
+ }
+ bzero(&sa, sizeof(sa));
+ sa.sa.sa_family = ai_fam_sock;
+ if (pick_port_bind(f, &sa, portrange_from, portrange_to)) {
+ perror("tftp: bind");
+ exit(EX_OSERR);
+ }
+
+ if (iscmd && pargc) {
+ /* -c specified; execute command and exit */
+ struct cmd *c;
+
+ if (sigsetjmp(toplevel, 1) != 0)
+ exit(EX_UNAVAILABLE);
+
+ c = getcmd(pargv[0]);
+ if (c == (struct cmd *)-1 || c == (struct cmd *)0) {
+ fprintf(stderr, "%s: invalid command: %s\n", argv[0],
+ pargv[1]);
+ exit(EX_USAGE);
+ }
+ (*c->handler) (pargc, pargv);
+ exit(0);
+ }
+#ifdef WITH_READLINE
+#ifdef HAVE_READLINE_HISTORY_H
+ using_history();
+#endif
+#endif
+
+ if (sigsetjmp(toplevel, 1) != 0)
+ (void)putchar('\n');
+ command();
+
+ return 0; /* Never reached */
+}
+
+char *hostname;
+
+/* Called when a command is incomplete; modifies
+ the global variable "line" */
+static void getmoreargs(const char *partial, const char *mprompt)
+{
+#ifdef WITH_READLINE
+ char *eline;
+ int len, elen;
+
+ len = strlen(partial);
+ eline = readline(mprompt);
+ if (!eline)
+ exit(0); /* EOF */
+
+ elen = strlen(eline);
+
+ if (line) {
+ free(line);
+ line = NULL;
+ }
+ line = xmalloc(len + elen + 1);
+ strcpy(line, partial);
+ strcpy(line + len, eline);
+ free(eline);
+
+#ifdef HAVE_READLINE_HISTORY_H
+ add_history(line);
+#endif
+#else
+ int len = strlen(partial);
+
+ strcpy(line, partial);
+ fputs(mprompt, stdout);
+ if (fgets(line + len, LBUFLEN - len, stdin) == 0)
+ if (feof(stdin))
+ exit(0); /* EOF */
+#endif
+}
+
+void setpeer(int argc, char *argv[])
+{
+ int err;
+
+ if (argc < 2) {
+ getmoreargs("connect ", "(to) ");
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if ((argc < 2) || (argc > 3)) {
+ printf("usage: %s host-name [port]\n", argv[0]);
+ return;
+ }
+
+ peeraddr.sa.sa_family = ai_fam;
+ err = set_sock_addr(argv[1], &peeraddr, &hostname);
+ if (err) {
+ printf("Error: %s\n", gai_strerror(err));
+ printf("%s: unknown host\n", argv[1]);
+ connected = 0;
+ return;
+ }
+ ai_fam = peeraddr.sa.sa_family;
+ if (f == -1) { /* socket not open */
+ ai_fam_sock = ai_fam;
+ } else { /* socket was already open */
+ if (ai_fam_sock != ai_fam) { /* need reopen socken for new family */
+ union sock_addr sa;
+
+ close(f);
+ ai_fam_sock = ai_fam;
+ f = socket(ai_fam_sock, SOCK_DGRAM, 0);
+ if (f < 0) {
+ perror("tftp: socket");
+ exit(EX_OSERR);
+ }
+ bzero((char *)&sa, sizeof (sa));
+ sa.sa.sa_family = ai_fam_sock;
+ if (pick_port_bind(f, &sa, portrange_from, portrange_to)) {
+ perror("tftp: bind");
+ exit(EX_OSERR);
+ }
+ }
+ }
+ port = sp->s_port;
+ if (argc == 3) {
+ struct servent *usp;
+ usp = getservbyname(argv[2], "udp");
+ if (usp) {
+ port = usp->s_port;
+ } else {
+ unsigned long myport;
+ char *ep;
+ myport = strtoul(argv[2], &ep, 10);
+ if (*ep || myport > 65535UL) {
+ printf("%s: bad port number\n", argv[2]);
+ connected = 0;
+ return;
+ }
+ port = htons((u_short) myport);
+ }
+ }
+
+ if (verbose) {
+ char tmp[INET6_ADDRSTRLEN], *tp;
+ tp = (char *)inet_ntop(peeraddr.sa.sa_family, SOCKADDR_P(&peeraddr),
+ tmp, INET6_ADDRSTRLEN);
+ if (!tp)
+ tp = (char *)"???";
+ printf("Connected to %s (%s), port %u\n",
+ hostname, tp, (unsigned int)ntohs(port));
+ }
+ connected = 1;
+}
+
+void modecmd(int argc, char *argv[])
+{
+ const struct modes *p;
+ const char *sep;
+
+ if (argc < 2) {
+ printf("Using %s mode to transfer files.\n", mode->m_mode);
+ return;
+ }
+ if (argc == 2) {
+ for (p = modes; p->m_name; p++)
+ if (strcmp(argv[1], p->m_name) == 0)
+ break;
+ if (p->m_name) {
+ settftpmode(p);
+ return;
+ }
+ printf("%s: unknown mode\n", argv[1]);
+ /* drop through and print usage message */
+ }
+
+ printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = modes; p->m_name; p++) {
+ printf("%s%s", sep, p->m_name);
+ if (*sep == ' ')
+ sep = " | ";
+ }
+ printf(" ]\n");
+ return;
+}
+
+void setbinary(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+ settftpmode(MODE_OCTET);
+}
+
+void setascii(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+ settftpmode(MODE_NETASCII);
+}
+
+static void settftpmode(const struct modes *newmode)
+{
+ mode = newmode;
+ if (verbose)
+ printf("mode set to %s\n", mode->m_mode);
+}
+
+/*
+ * Send file(s).
+ */
+void put(int argc, char *argv[])
+{
+ int fd;
+ int n, err;
+ char *cp, *targ;
+
+ if (argc < 2) {
+ getmoreargs("send ", "(file) ");
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ putusage(argv[0]);
+ return;
+ }
+ targ = argv[argc - 1];
+ if (!literal && strchr(argv[argc - 1], ':')) {
+ for (n = 1; n < argc - 1; n++)
+ if (strchr(argv[n], ':')) {
+ putusage(argv[0]);
+ return;
+ }
+ cp = argv[argc - 1];
+ targ = strchr(cp, ':');
+ *targ++ = 0;
+ peeraddr.sa.sa_family = ai_fam;
+ err = set_sock_addr(cp, &peeraddr,&hostname);
+ if (err) {
+ printf("Error: %s\n", gai_strerror(err));
+ printf("%s: unknown host\n", argv[1]);
+ connected = 0;
+ return;
+ }
+ ai_fam = peeraddr.sa.sa_family;
+ connected = 1;
+ }
+ if (!connected) {
+ printf("No target machine specified.\n");
+ return;
+ }
+ if (argc < 4) {
+ cp = argc == 2 ? tail(targ) : argv[1];
+ fd = open(cp, O_RDONLY | mode->m_openflags);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: ");
+ perror(cp);
+ return;
+ }
+ if (verbose)
+ printf("putting %s to %s:%s [%s]\n",
+ cp, hostname, targ, mode->m_mode);
+ sa_set_port(&peeraddr, port);
+ tftp_sendfile(fd, targ, mode->m_mode);
+ return;
+ }
+ /* this assumes the target is a directory */
+ /* on a remote unix system. hmmmm. */
+ cp = strchr(targ, '\0');
+ *cp++ = '/';
+ for (n = 1; n < argc - 1; n++) {
+ strcpy(cp, tail(argv[n]));
+ fd = open(argv[n], O_RDONLY | mode->m_openflags);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: ");
+ perror(argv[n]);
+ continue;
+ }
+ if (verbose)
+ printf("putting %s to %s:%s [%s]\n",
+ argv[n], hostname, targ, mode->m_mode);
+ sa_set_port(&peeraddr, port);
+ tftp_sendfile(fd, targ, mode->m_mode);
+ }
+}
+
+static void putusage(char *s)
+{
+ printf("usage: %s file ... host:target, or\n", s);
+ printf(" %s file ... target (when already connected)\n", s);
+}
+
+/*
+ * Receive file(s).
+ */
+void get(int argc, char *argv[])
+{
+ int fd;
+ int n;
+ char *cp;
+ char *src;
+
+ if (argc < 2) {
+ getmoreargs("get ", "(files) ");
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ getusage(argv[0]);
+ return;
+ }
+ if (!connected) {
+ for (n = 1; n < argc; n++)
+ if (literal || strchr(argv[n], ':') == 0) {
+ getusage(argv[0]);
+ return;
+ }
+ }
+ for (n = 1; n < argc; n++) {
+ src = strchr(argv[n], ':');
+ if (literal || src == NULL)
+ src = argv[n];
+ else {
+ int err;
+
+ *src++ = 0;
+ peeraddr.sa.sa_family = ai_fam;
+ err = set_sock_addr(argv[n], &peeraddr, &hostname);
+ if (err) {
+ printf("Warning: %s\n", gai_strerror(err));
+ printf("%s: unknown host\n", argv[1]);
+ continue;
+ }
+ ai_fam = peeraddr.sa.sa_family;
+ connected = 1;
+ }
+ if (argc < 4) {
+ cp = argc == 3 ? argv[2] : tail(src);
+ fd = open(cp, O_WRONLY | O_CREAT | O_TRUNC | mode->m_openflags,
+ 0666);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: ");
+ perror(cp);
+ return;
+ }
+ if (verbose)
+ printf("getting from %s:%s to %s [%s]\n",
+ hostname, src, cp, mode->m_mode);
+ sa_set_port(&peeraddr, port);
+ tftp_recvfile(fd, src, mode->m_mode);
+ break;
+ }
+ cp = tail(src); /* new .. jdg */
+ fd = open(cp, O_WRONLY | O_CREAT | O_TRUNC | mode->m_openflags,
+ 0666);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: ");
+ perror(cp);
+ continue;
+ }
+ if (verbose)
+ printf("getting from %s:%s to %s [%s]\n",
+ hostname, src, cp, mode->m_mode);
+ sa_set_port(&peeraddr, port);
+ tftp_recvfile(fd, src, mode->m_mode);
+ }
+}
+
+static void getusage(char *s)
+{
+ printf("usage: %s host:file host:file ... file, or\n", s);
+ printf(" %s file file ... file if connected\n", s);
+}
+
+int rexmtval = TIMEOUT;
+
+void setrexmt(int argc, char *argv[])
+{
+ int t;
+
+ if (argc < 2) {
+ getmoreargs("rexmt-timeout ", "(value) ");
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ printf("usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 0)
+ printf("%s: bad value\n", argv[1]);
+ else
+ rexmtval = t;
+}
+
+int maxtimeout = 5 * TIMEOUT;
+
+void settimeout(int argc, char *argv[])
+{
+ int t;
+
+ if (argc < 2) {
+ getmoreargs("maximum-timeout ", "(value) ");
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ printf("usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 0)
+ printf("%s: bad value\n", argv[1]);
+ else
+ maxtimeout = t;
+}
+
+void setliteral(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+ literal = !literal;
+ printf("Literal mode %s.\n", literal ? "on" : "off");
+}
+
+void status(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+ if (connected)
+ printf("Connected to %s.\n", hostname);
+ else
+ printf("Not connected.\n");
+ printf("Mode: %s Verbose: %s Tracing: %s Literal: %s\n", mode->m_mode,
+ verbose ? "on" : "off", trace ? "on" : "off",
+ literal ? "on" : "off");
+ printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
+ rexmtval, maxtimeout);
+}
+
+void intr(int sig)
+{
+ (void)sig; /* Quiet unused warning */
+
+ bsd_signal(SIGALRM, SIG_IGN);
+ alarm(0);
+ siglongjmp(toplevel, -1);
+}
+
+char *tail(char *filename)
+{
+ char *s;
+
+ while (*filename) {
+ s = strrchr(filename, '/');
+ if (s == NULL)
+ break;
+ if (s[1])
+ return (s + 1);
+ *s = '\0';
+ }
+ return (filename);
+}
+
+/*
+ * Command parser.
+ */
+static void command(void)
+{
+ struct cmd *c;
+
+ for (;;) {
+#ifdef WITH_READLINE
+ if (line) {
+ free(line);
+ line = NULL;
+ }
+ line = readline(prompt);
+ if (!line)
+ exit(0); /* EOF */
+#else
+ fputs(prompt, stdout);
+ if (fgets(line, LBUFLEN, stdin) == 0) {
+ if (feof(stdin)) {
+ exit(0);
+ } else {
+ continue;
+ }
+ }
+#endif
+ if ((line[0] == 0) || (line[0] == '\n'))
+ continue;
+#ifdef WITH_READLINE
+#ifdef HAVE_READLINE_HISTORY_H
+ add_history(line);
+#endif
+#endif
+ makeargv();
+ if (margc == 0)
+ continue;
+
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ (*c->handler) (margc, margv);
+ }
+}
+
+struct cmd *getcmd(char *name)
+{
+ const char *p;
+ char *q;
+ struct cmd *c, *found;
+ int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; (p = c->name) != NULL; c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return (c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return ((struct cmd *)-1);
+ return (found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+static void makeargv(void)
+{
+ char *cp;
+ char **argp = margv;
+
+ margc = 0;
+ for (cp = line; *cp;) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp++ = 0;
+}
+
+void quit(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+ exit(0);
+}
+
+/*
+ * Help command.
+ */
+void help(int argc, char *argv[])
+{
+ struct cmd *c;
+
+ printf("%s\n", VERSION);
+
+ if (argc == 1) {
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c->name; c++)
+ printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
+ return;
+ }
+ while (--argc > 0) {
+ char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%s\n", c->help);
+ }
+}
+
+void settrace(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+
+ trace = !trace;
+ printf("Packet tracing %s.\n", trace ? "on" : "off");
+}
+
+void setverbose(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv; /* Quiet unused warning */
+
+ verbose = !verbose;
+ printf("Verbose mode %s.\n", verbose ? "on" : "off");
+}
Index: tftp/tftp.c
===================================================================
--- tftp/tftp.c (nonexistent)
+++ tftp/tftp.c (revision 5)
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "common/tftpsubs.h"
+
+/*
+ * TFTP User Program -- Protocol Machines
+ */
+#include "extern.h"
+
+extern union sock_addr peeraddr; /* filled in by main */
+extern int f; /* the opened socket */
+extern int trace;
+extern int verbose;
+extern int rexmtval;
+extern int maxtimeout;
+
+#define PKTSIZE SEGSIZE+4
+char ackbuf[PKTSIZE];
+int timeout;
+extern sigjmp_buf toplevel;
+sigjmp_buf timeoutbuf;
+
+static void nak(int, const char *);
+static int makerequest(int, const char *, struct tftphdr *, const char *);
+static void printstats(const char *, unsigned long);
+static void startclock(void);
+static void stopclock(void);
+static void timer(int);
+static void tpacket(const char *, struct tftphdr *, int);
+
+/*
+ * Send the requested file.
+ */
+void tftp_sendfile(int fd, const char *name, const char *mode)
+{
+ struct tftphdr *ap; /* data and ack packets */
+ struct tftphdr *dp;
+ int n;
+ volatile int is_request;
+ volatile u_short block;
+ volatile int size, convert;
+ volatile off_t amount;
+ union sock_addr from;
+ socklen_t fromlen;
+ FILE *file;
+ u_short ap_opcode, ap_block;
+
+ startclock(); /* start stat's clock */
+ dp = r_init(); /* reset fillbuf/read-ahead code */
+ ap = (struct tftphdr *)ackbuf;
+ convert = !strcmp(mode, "netascii");
+ file = fdopen(fd, convert ? "rt" : "rb");
+ block = 0;
+ is_request = 1; /* First packet is the actual WRQ */
+ amount = 0;
+
+ bsd_signal(SIGALRM, timer);
+ do {
+ if (is_request) {
+ size = makerequest(WRQ, name, dp, mode) - 4;
+ } else {
+ /* size = read(fd, dp->th_data, SEGSIZE); */
+ size = readit(file, &dp, convert);
+ if (size < 0) {
+ nak(errno + 100, NULL);
+ break;
+ }
+ dp->th_opcode = htons((u_short) DATA);
+ dp->th_block = htons((u_short) block);
+ }
+ timeout = 0;
+ (void)sigsetjmp(timeoutbuf, 1);
+
+ if (trace)
+ tpacket("sent", dp, size + 4);
+ n = sendto(f, dp, size + 4, 0,
+ &peeraddr.sa, SOCKLEN(&peeraddr));
+ if (n != size + 4) {
+ perror("tftp: sendto");
+ goto abort;
+ }
+ read_ahead(file, convert);
+ for (;;) {
+ alarm(rexmtval);
+ do {
+ fromlen = sizeof(from);
+ n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
+ &from.sa, &fromlen);
+ } while (n <= 0);
+ alarm(0);
+ if (n < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */
+ if (trace)
+ tpacket("received", ap, n);
+ /* should verify packet came from server */
+ ap_opcode = ntohs((u_short) ap->th_opcode);
+ ap_block = ntohs((u_short) ap->th_block);
+ if (ap_opcode == ERROR) {
+ printf("Error code %d: %s\n", ap_block, ap->th_msg);
+ goto abort;
+ }
+ if (ap_opcode == ACK) {
+ int j;
+
+ if (ap_block == block) {
+ break;
+ }
+ /* On an error, try to synchronize
+ * both sides.
+ */
+ j = synchnet(f);
+ if (j && trace) {
+ printf("discarded %d packets\n", j);
+ }
+ /*
+ * RFC1129/RFC1350: We MUST NOT re-send the DATA
+ * packet in response to an invalid ACK. Doing so
+ * would cause the Sorcerer's Apprentice bug.
+ */
+ }
+ }
+ if (!is_request)
+ amount += size;
+ is_request = 0;
+ block++;
+ } while (size == SEGSIZE || block == 1);
+ abort:
+ fclose(file);
+ stopclock();
+ if (amount > 0)
+ printstats("Sent", amount);
+}
+
+/*
+ * Receive a file.
+ */
+void tftp_recvfile(int fd, const char *name, const char *mode)
+{
+ struct tftphdr *ap;
+ struct tftphdr *dp;
+ int n;
+ volatile u_short block;
+ volatile int size, firsttrip;
+ volatile unsigned long amount;
+ union sock_addr from;
+ socklen_t fromlen;
+ FILE *file;
+ volatile int convert; /* true if converting crlf -> lf */
+ u_short dp_opcode, dp_block;
+
+ startclock();
+ dp = w_init();
+ ap = (struct tftphdr *)ackbuf;
+ convert = !strcmp(mode, "netascii");
+ file = fdopen(fd, convert ? "wt" : "wb");
+ block = 1;
+ firsttrip = 1;
+ amount = 0;
+
+ bsd_signal(SIGALRM, timer);
+ do {
+ if (firsttrip) {
+ size = makerequest(RRQ, name, ap, mode);
+ firsttrip = 0;
+ } else {
+ ap->th_opcode = htons((u_short) ACK);
+ ap->th_block = htons((u_short) block);
+ size = 4;
+ block++;
+ }
+ timeout = 0;
+ (void)sigsetjmp(timeoutbuf, 1);
+ send_ack:
+ if (trace)
+ tpacket("sent", ap, size);
+ if (sendto(f, ackbuf, size, 0, &peeraddr.sa,
+ SOCKLEN(&peeraddr)) != size) {
+ alarm(0);
+ perror("tftp: sendto");
+ goto abort;
+ }
+ write_behind(file, convert);
+ for (;;) {
+ alarm(rexmtval);
+ do {
+ fromlen = sizeof(from);
+ n = recvfrom(f, dp, PKTSIZE, 0,
+ &from.sa, &fromlen);
+ } while (n <= 0);
+ alarm(0);
+ if (n < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */
+ if (trace)
+ tpacket("received", dp, n);
+ /* should verify client address */
+ dp_opcode = ntohs((u_short) dp->th_opcode);
+ dp_block = ntohs((u_short) dp->th_block);
+ if (dp_opcode == ERROR) {
+ printf("Error code %d: %s\n", dp_block, dp->th_msg);
+ goto abort;
+ }
+ if (dp_opcode == DATA) {
+ int j;
+
+ if (dp_block == block) {
+ break; /* have next packet */
+ }
+ /* On an error, try to synchronize
+ * both sides.
+ */
+ j = synchnet(f);
+ if (j && trace) {
+ printf("discarded %d packets\n", j);
+ }
+ if (dp_block == (block - 1)) {
+ goto send_ack; /* resend ack */
+ }
+ }
+ }
+ /* size = write(fd, dp->th_data, n - 4); */
+ size = writeit(file, &dp, n - 4, convert);
+ if (size < 0) {
+ nak(errno + 100, NULL);
+ break;
+ }
+ amount += size;
+ } while (size == SEGSIZE);
+ abort: /* ok to ack, since user */
+ ap->th_opcode = htons((u_short) ACK); /* has seen err msg */
+ ap->th_block = htons((u_short) block);
+ (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
+ SOCKLEN(&peeraddr));
+ write_behind(file, convert); /* flush last buffer */
+ fclose(file);
+ stopclock();
+ if (amount > 0)
+ printstats("Received", amount);
+}
+
+static int
+makerequest(int request, const char *name,
+ struct tftphdr *tp, const char *mode)
+{
+ char *cp;
+ size_t len = 0;
+
+ tp->th_opcode = htons((u_short) request);
+ cp = (char *)&(tp->th_stuff);
+ len = strlen(name) + 1;
+ memcpy(cp, name, len);
+ cp += len;
+ len = strlen(mode) + 1;
+ memcpy(cp, mode, len);
+ cp += len;
+ return (cp - (char *)tp);
+}
+
+static const char *const errmsgs[] = {
+ "Undefined error code", /* 0 - EUNDEF */
+ "File not found", /* 1 - ENOTFOUND */
+ "Access denied", /* 2 - EACCESS */
+ "Disk full or allocation exceeded", /* 3 - ENOSPACE */
+ "Illegal TFTP operation", /* 4 - EBADOP */
+ "Unknown transfer ID", /* 5 - EBADID */
+ "File already exists", /* 6 - EEXISTS */
+ "No such user", /* 7 - ENOUSER */
+ "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */
+};
+
+#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *))
+
+/*
+ * Send a nak packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+static void nak(int error, const char *msg)
+{
+ struct tftphdr *tp;
+ int length;
+
+ tp = (struct tftphdr *)ackbuf;
+ tp->th_opcode = htons((u_short) ERROR);
+ tp->th_code = htons((u_short) error);
+
+ if (error >= 100) {
+ /* This is a Unix errno+100 */
+ if (!msg)
+ msg = strerror(error - 100);
+ error = EUNDEF;
+ } else {
+ if ((unsigned)error >= ERR_CNT)
+ error = EUNDEF;
+
+ if (!msg)
+ msg = errmsgs[error];
+ }
+
+ tp->th_code = htons((u_short) error);
+
+ length = strlen(msg) + 1;
+ memcpy(tp->th_msg, msg, length);
+ length += 4; /* Add space for header */
+
+ if (trace)
+ tpacket("sent", tp, length);
+ if (sendto(f, ackbuf, length, 0, &peeraddr.sa,
+ SOCKLEN(&peeraddr)) != length)
+ perror("nak");
+}
+
+static void tpacket(const char *s, struct tftphdr *tp, int n)
+{
+ static const char *opcodes[] =
+ { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
+ char *cp, *file;
+ u_short op = ntohs((u_short) tp->th_opcode);
+
+ if (op < RRQ || op > ERROR)
+ printf("%s opcode=%x ", s, op);
+ else
+ printf("%s %s ", s, opcodes[op]);
+ switch (op) {
+
+ case RRQ:
+ case WRQ:
+ n -= 2;
+ file = cp = (char *)&(tp->th_stuff);
+ cp = strchr(cp, '\0');
+ printf("<file=%s, mode=%s>\n", file, cp + 1);
+ break;
+
+ case DATA:
+ printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
+ break;
+
+ case ACK:
+ printf("<block=%d>\n", ntohs(tp->th_block));
+ break;
+
+ case ERROR:
+ printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
+ break;
+ }
+}
+
+struct timeval tstart;
+struct timeval tstop;
+
+static void startclock(void)
+{
+ (void)gettimeofday(&tstart, NULL);
+}
+
+static void stopclock(void)
+{
+
+ (void)gettimeofday(&tstop, NULL);
+}
+
+static void printstats(const char *direction, unsigned long amount)
+{
+ double delta;
+
+ delta = (tstop.tv_sec + (tstop.tv_usec / 100000.0)) -
+ (tstart.tv_sec + (tstart.tv_usec / 100000.0));
+ if (verbose) {
+ printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
+ printf(" [%.0f bit/s]", (amount * 8.) / delta);
+ putchar('\n');
+ }
+}
+
+static void timer(int sig)
+{
+ int save_errno = errno;
+
+ (void)sig; /* Shut up unused warning */
+
+ timeout += rexmtval;
+ if (timeout >= maxtimeout) {
+ printf("Transfer timed out.\n");
+ errno = save_errno;
+ siglongjmp(toplevel, -1);
+ }
+ errno = save_errno;
+ siglongjmp(timeoutbuf, 1);
+}
Index: tftp
===================================================================
--- tftp (nonexistent)
+++ tftp (revision 5)
Property changes on: tftp
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: .
===================================================================
--- . (nonexistent)
+++ . (revision 5)
Property changes on: .
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~