Spell IP_RECVDSTADDR correctly
[tftp-hpa.git] / tftp / main.c
blob961c0c461b543ea95ee99b1da00b6c696370fd80
1 /* $OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $ */
2 /* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */
4 /*
5 * Copyright (c) 1983, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 #include "tftpsubs.h"
39 #ifndef lint
40 static const char *copyright UNUSED =
41 "@(#) Copyright (c) 1983, 1993\n\
42 The Regents of the University of California. All rights reserved.\n";
43 /* static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; */
44 /* static char rcsid[] = "$OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $"; */
45 static const char *rcsid UNUSED =
46 "tftp-hpa $Id$";
47 #endif /* not lint */
49 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
52 * TFTP User Program -- Command Interface.
54 #include <sys/file.h>
55 #include <ctype.h>
56 #include <netdb.h>
57 #ifdef WITH_READLINE
58 #include <readline/readline.h>
59 #ifdef HAVE_READLINE_HISTORY_H
60 #include <readline/history.h>
61 #endif
62 #endif
64 #include "extern.h"
66 #define TIMEOUT 5 /* secs between rexmt's */
67 #define LBUFLEN 200 /* size of input buffer */
69 struct modes {
70 const char *m_name;
71 const char *m_mode;
72 int m_openflags;
75 static const struct modes modes[] = {
76 { "netascii", "netascii", O_TEXT },
77 { "ascii", "netascii", O_TEXT },
78 { "octet", "octet", O_BINARY },
79 { "binary", "octet", O_BINARY },
80 { "image", "octet", O_BINARY },
81 { 0, 0, 0 }
83 #define MODE_OCTET (&modes[2])
84 #define MODE_NETASCII (&modes[0])
85 #define MODE_DEFAULT MODE_NETASCII
87 struct sockaddr_in peeraddr;
88 int f;
89 u_short port;
90 int trace;
91 int verbose;
92 int connected;
93 const struct modes *mode;
94 #ifdef WITH_READLINE
95 char *line = NULL;
96 #else
97 char line[LBUFLEN];
98 #endif
99 int margc;
100 char *margv[20];
101 const char *prompt = "tftp> ";
102 sigjmp_buf toplevel;
103 void intr(int);
104 struct servent *sp;
106 void get (int, char **);
107 void help (int, char **);
108 void modecmd (int, char **);
109 void put (int, char **);
110 void quit (int, char **);
111 void setascii (int, char **);
112 void setbinary (int, char **);
113 void setpeer (int, char **);
114 void setrexmt (int, char **);
115 void settimeout (int, char **);
116 void settrace (int, char **);
117 void setverbose (int, char **);
118 void status (int, char **);
120 static void command (void);
122 static void getusage (char *);
123 static void makeargv (void);
124 static void putusage (char *);
125 static void settftpmode (const struct modes *);
127 #define HELPINDENT (sizeof("connect"))
129 struct cmd {
130 const char *name;
131 const char *help;
132 void (*handler) (int, char **);
135 struct cmd cmdtab[] = {
136 { "connect",
137 "connect to remote tftp",
138 setpeer },
139 { "mode",
140 "set file transfer mode",
141 modecmd },
142 { "put",
143 "send file",
144 put },
145 { "get",
146 "receive file",
147 get },
148 { "quit",
149 "exit tftp",
150 quit },
151 { "verbose",
152 "toggle verbose mode",
153 setverbose },
154 { "trace",
155 "toggle packet tracing",
156 settrace },
157 { "status",
158 "show current status",
159 status },
160 { "binary",
161 "set mode to octet",
162 setbinary },
163 { "ascii",
164 "set mode to netascii",
165 setascii },
166 { "rexmt",
167 "set per-packet transmission timeout",
168 setrexmt },
169 { "timeout",
170 "set total retransmission timeout",
171 settimeout },
172 { "?",
173 "print help information",
174 help },
175 { "help",
176 "print help information",
177 help },
178 { 0, 0, 0 }
181 struct cmd *getcmd(char *);
182 char *tail(char *);
184 char *xstrdup(const char *);
186 const char *program;
188 static inline void usage(int errcode)
190 fprintf(stderr, "Usage: %s [-v][-m mode] [host [port]] [-c command]\n", program);
191 exit(errcode);
195 main(int argc, char *argv[])
197 struct sockaddr_in s_in;
198 int arg;
199 static int pargc, peerargc;
200 static int iscmd = 0;
201 char **pargv;
202 const char *optx;
203 char *peerargv[3];
205 program = argv[0];
207 mode = MODE_DEFAULT;
209 peerargv[0] = argv[0];
210 peerargc = 1;
212 for ( arg = 1 ; !iscmd && arg < argc ; arg++ ) {
213 if ( argv[arg][0] == '-' ) {
214 for ( optx = &argv[arg][1] ; *optx ; optx++ ) {
215 switch ( *optx ) {
216 case 'v':
217 verbose = 1;
218 break;
219 case 'V':
220 /* Print version and configuration to stdout and exit */
221 printf("%s\n", TFTP_CONFIG_STR);
222 exit(0);
223 case 'm':
224 if ( ++arg >= argc )
225 usage(EX_USAGE);
227 const struct modes *p;
229 for ( p = modes ; p->m_name ; p++ ) {
230 if (!strcmp(argv[arg], p->m_name))
231 break;
233 if (p->m_name) {
234 settftpmode(p);
235 } else {
236 fprintf(stderr, "%s: invalid mode: %s\n", argv[0], argv[arg]);
237 exit(EX_USAGE);
240 break;
241 case 'c':
242 iscmd = 1;
243 break;
244 case 'h':
245 default:
246 usage(*optx == 'h' ? 0 : EX_USAGE);
249 } else {
250 if ( peerargc >= 3 )
251 usage(EX_USAGE);
253 peerargv[peerargc++] = argv[arg];
257 pargv = argv + arg;
258 pargc = argc - arg;
260 sp = getservbyname("tftp", "udp");
261 if (sp == 0) {
262 /* Use canned values */
263 if (verbose)
264 fprintf(stderr, "tftp: tftp/udp: unknown service, faking it...\n");
265 sp = xmalloc(sizeof(struct servent));
266 sp->s_name = (char *)"tftp";
267 sp->s_aliases = NULL;
268 sp->s_port = htons(IPPORT_TFTP);
269 sp->s_proto = (char *)"udp";
271 port = sp->s_port; /* Default port */
272 f = socket(AF_INET, SOCK_DGRAM, 0);
273 if (f < 0) {
274 perror("tftp: socket");
275 exit(EX_OSERR);
277 bzero((char *)&s_in, sizeof (s_in));
278 s_in.sin_family = AF_INET;
279 if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) {
280 perror("tftp: bind");
281 exit(EX_OSERR);
283 bsd_signal(SIGINT, intr);
285 if ( peerargc ) {
286 /* Set peer */
287 if (sigsetjmp(toplevel,1) != 0)
288 exit(EX_NOHOST);
289 setpeer(peerargc, peerargv);
292 if ( iscmd && pargc ) {
293 /* -c specified; execute command and exit */
294 struct cmd *c;
296 if (sigsetjmp(toplevel,1) != 0)
297 exit(EX_UNAVAILABLE);
299 c = getcmd(pargv[0]);
300 if ( c == (struct cmd *)-1 || c == (struct cmd *)0 ) {
301 fprintf(stderr, "%s: invalid command: %s\n", argv[0], pargv[1]);
302 exit(EX_USAGE);
304 (*c->handler)(pargc, pargv);
305 exit(0);
307 if (sigsetjmp(toplevel,1) != 0)
308 (void)putchar('\n');
310 #ifdef WITH_READLINE
311 #ifdef HAVE_READLINE_HISTORY_H
312 using_history();
313 #endif
314 #endif
316 command();
318 return 0; /* Never reached */
321 char *hostname;
323 /* Called when a command is incomplete; modifies
324 the global variable "line" */
325 static void
326 getmoreargs(const char *partial, const char *mprompt)
328 #ifdef WITH_READLINE
329 char *eline;
330 int len, elen;
332 len = strlen(partial);
333 eline = readline(mprompt);
334 if (!eline)
335 exit(0); /* EOF */
337 elen = strlen(eline);
339 if (line)
340 free(line);
341 line = xmalloc(len+elen+1);
342 strcpy(line, partial);
343 strcpy(line+len, eline);
344 free(eline);
346 #ifdef HAVE_READLINE_HISTORY_H
347 add_history(line);
348 #endif
349 #else
350 int len = strlen(partial);
352 strcpy(line, partial);
353 fputs(mprompt, stdout);
354 if ( fgets(line+len, LBUFLEN-len, stdin) == 0 )
355 if ( feof(stdin) )
356 exit(0); /* EOF */
357 #endif
360 void
361 setpeer(int argc, char *argv[])
363 struct hostent *host;
365 if (argc < 2) {
366 getmoreargs("connect ", "(to) ");
367 makeargv();
368 argc = margc;
369 argv = margv;
371 if ((argc < 2) || (argc > 3)) {
372 printf("usage: %s host-name [port]\n", argv[0]);
373 return;
376 host = gethostbyname(argv[1]);
377 if (host == 0) {
378 connected = 0;
379 printf("%s: unknown host\n", argv[1]);
380 return;
382 peeraddr.sin_family = host->h_addrtype;
383 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
384 hostname = xstrdup(host->h_name);
386 port = sp->s_port;
387 if (argc == 3) {
388 struct servent *usp;
389 usp = getservbyname(argv[2], "udp");
390 if ( usp ) {
391 port = usp->s_port;
392 } else {
393 unsigned long myport;
394 char *ep;
395 myport = strtoul(argv[2], &ep, 10);
396 if (*ep || myport > 65535UL) {
397 printf("%s: bad port number\n", argv[2]);
398 connected = 0;
399 return;
401 port = htons((u_short)myport);
405 if (verbose) {
406 printf("Connected to %s (%s), port %u\n",
407 hostname, inet_ntoa(peeraddr.sin_addr),
408 (unsigned int)ntohs(port));
410 connected = 1;
413 void
414 modecmd(int argc, char *argv[])
416 const struct modes *p;
417 const char *sep;
419 if (argc < 2) {
420 printf("Using %s mode to transfer files.\n", mode->m_mode);
421 return;
423 if (argc == 2) {
424 for (p = modes; p->m_name; p++)
425 if (strcmp(argv[1], p->m_name) == 0)
426 break;
427 if (p->m_name) {
428 settftpmode(p);
429 return;
431 printf("%s: unknown mode\n", argv[1]);
432 /* drop through and print usage message */
435 printf("usage: %s [", argv[0]);
436 sep = " ";
437 for (p = modes; p->m_name; p++) {
438 printf("%s%s", sep, p->m_name);
439 if (*sep == ' ')
440 sep = " | ";
442 printf(" ]\n");
443 return;
446 void
447 setbinary(int argc, char *argv[])
449 (void)argc; (void)argv; /* Quiet unused warning */
450 settftpmode(MODE_OCTET);
453 void
454 setascii(int argc, char *argv[])
456 (void)argc; (void)argv; /* Quiet unused warning */
457 settftpmode(MODE_NETASCII);
460 static void
461 settftpmode(const struct modes *newmode)
463 mode = newmode;
464 if (verbose)
465 printf("mode set to %s\n", mode->m_mode);
470 * Send file(s).
472 void
473 put(int argc, char *argv[])
475 int fd;
476 int n;
477 char *cp, *targ;
479 if (argc < 2) {
480 getmoreargs("send ", "(file) ");
481 makeargv();
482 argc = margc;
483 argv = margv;
485 if (argc < 2) {
486 putusage(argv[0]);
487 return;
489 targ = argv[argc - 1];
490 if (strchr(argv[argc - 1], ':')) {
491 struct hostent *hp;
493 for (n = 1; n < argc - 1; n++)
494 if (strchr(argv[n], ':')) {
495 putusage(argv[0]);
496 return;
498 cp = argv[argc - 1];
499 targ = strchr(cp, ':');
500 *targ++ = 0;
501 hp = gethostbyname(cp);
502 if (hp == NULL) {
503 fprintf(stderr, "tftp: %s: ", cp);
504 herror((char *)NULL);
505 return;
507 bcopy(hp->h_addr, &peeraddr.sin_addr, hp->h_length);
508 peeraddr.sin_family = hp->h_addrtype;
509 connected = 1;
510 hostname = xstrdup(hp->h_name);
512 if (!connected) {
513 printf("No target machine specified.\n");
514 return;
516 if (argc < 4) {
517 cp = argc == 2 ? tail(targ) : argv[1];
518 fd = open(cp, O_RDONLY|mode->m_openflags);
519 if (fd < 0) {
520 fprintf(stderr, "tftp: "); perror(cp);
521 return;
523 if (verbose)
524 printf("putting %s to %s:%s [%s]\n",
525 cp, hostname, targ, mode->m_mode);
526 peeraddr.sin_port = port;
527 tftp_sendfile(fd, targ, mode->m_mode);
528 return;
530 /* this assumes the target is a directory */
531 /* on a remote unix system. hmmmm. */
532 cp = strchr(targ, '\0');
533 *cp++ = '/';
534 for (n = 1; n < argc - 1; n++) {
535 strcpy(cp, tail(argv[n]));
536 fd = open(argv[n], O_RDONLY|mode->m_openflags);
537 if (fd < 0) {
538 fprintf(stderr, "tftp: "); perror(argv[n]);
539 continue;
541 if (verbose)
542 printf("putting %s to %s:%s [%s]\n",
543 argv[n], hostname, targ, mode->m_mode);
544 peeraddr.sin_port = port;
545 tftp_sendfile(fd, targ, mode->m_mode);
549 static void
550 putusage(char *s)
552 printf("usage: %s file ... host:target, or\n", s);
553 printf(" %s file ... target (when already connected)\n", s);
557 * Receive file(s).
559 void
560 get(int argc, char *argv[])
562 int fd;
563 int n;
564 char *cp;
565 char *src;
567 if (argc < 2) {
568 getmoreargs("get ", "(files) ");
569 makeargv();
570 argc = margc;
571 argv = margv;
573 if (argc < 2) {
574 getusage(argv[0]);
575 return;
577 if (!connected) {
578 for (n = 1; n < argc ; n++)
579 if (strchr(argv[n], ':') == 0) {
580 getusage(argv[0]);
581 return;
584 for (n = 1; n < argc ; n++) {
585 src = strchr(argv[n], ':');
586 if (src == NULL)
587 src = argv[n];
588 else {
589 struct hostent *hp;
591 *src++ = 0;
592 hp = gethostbyname(argv[n]);
593 if (hp == NULL) {
594 fprintf(stderr, "tftp: %s: ", argv[n]);
595 herror((char *)NULL);
596 continue;
598 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
599 hp->h_length);
600 peeraddr.sin_family = hp->h_addrtype;
601 connected = 1;
602 hostname = xstrdup(hp->h_name);
604 if (argc < 4) {
605 cp = argc == 3 ? argv[2] : tail(src);
606 fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC|mode->m_openflags, 0666);
607 if (fd < 0) {
608 fprintf(stderr, "tftp: "); perror(cp);
609 return;
611 if (verbose)
612 printf("getting from %s:%s to %s [%s]\n",
613 hostname, src, cp, mode->m_mode);
614 peeraddr.sin_port = port;
615 tftp_recvfile(fd, src, mode->m_mode);
616 break;
618 cp = tail(src); /* new .. jdg */
619 fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC|mode->m_openflags, 0666);
620 if (fd < 0) {
621 fprintf(stderr, "tftp: "); perror(cp);
622 continue;
624 if (verbose)
625 printf("getting from %s:%s to %s [%s]\n",
626 hostname, src, cp, mode->m_mode);
627 peeraddr.sin_port = port;
628 tftp_recvfile(fd, src, mode->m_mode);
632 static void
633 getusage(char *s)
635 printf("usage: %s host:file host:file ... file, or\n", s);
636 printf(" %s file file ... file if connected\n", s);
639 int rexmtval = TIMEOUT;
641 void
642 setrexmt(int argc, char *argv[])
644 int t;
646 if (argc < 2) {
647 getmoreargs("rexmt-timeout ", "(value) ");
648 makeargv();
649 argc = margc;
650 argv = margv;
652 if (argc != 2) {
653 printf("usage: %s value\n", argv[0]);
654 return;
656 t = atoi(argv[1]);
657 if (t < 0)
658 printf("%s: bad value\n", argv[1]);
659 else
660 rexmtval = t;
663 int maxtimeout = 5 * TIMEOUT;
665 void
666 settimeout(int argc, char *argv[])
668 int t;
670 if (argc < 2) {
671 getmoreargs("maximum-timeout ", "(value) ");
672 makeargv();
673 argc = margc;
674 argv = margv;
676 if (argc != 2) {
677 printf("usage: %s value\n", argv[0]);
678 return;
680 t = atoi(argv[1]);
681 if (t < 0)
682 printf("%s: bad value\n", argv[1]);
683 else
684 maxtimeout = t;
687 void
688 status(int argc, char *argv[])
690 (void)argc; (void)argv; /* Quiet unused warning */
691 if (connected)
692 printf("Connected to %s.\n", hostname);
693 else
694 printf("Not connected.\n");
695 printf("Mode: %s Verbose: %s Tracing: %s\n", mode->m_mode,
696 verbose ? "on" : "off", trace ? "on" : "off");
697 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
698 rexmtval, maxtimeout);
701 void
702 intr(int sig)
704 (void)sig; /* Quiet unused warning */
706 bsd_signal(SIGALRM, SIG_IGN);
707 alarm(0);
708 siglongjmp(toplevel, -1);
711 char *
712 tail(char *filename)
714 char *s;
716 while (*filename) {
717 s = strrchr(filename, '/');
718 if (s == NULL)
719 break;
720 if (s[1])
721 return (s + 1);
722 *s = '\0';
724 return (filename);
728 * Command parser.
730 static void
731 command(void)
733 struct cmd *c;
735 for (;;) {
736 #ifdef WITH_READLINE
737 if ( line )
738 free(line);
739 line = readline(prompt);
740 if ( !line )
741 exit(0); /* EOF */
742 #else
743 fputs(prompt, stdout);
744 if (fgets(line, LBUFLEN, stdin) == 0) {
745 if (feof(stdin)) {
746 exit(0);
747 } else {
748 continue;
751 #endif
752 if ((line[0] == 0) || (line[0] == '\n'))
753 continue;
754 #ifdef WITH_READLINE
755 #ifdef HAVE_READLINE_HISTORY_H
756 add_history(line);
757 #endif
758 #endif
759 makeargv();
760 if (margc == 0)
761 continue;
763 c = getcmd(margv[0]);
764 if (c == (struct cmd *)-1) {
765 printf("?Ambiguous command\n");
766 continue;
768 if (c == 0) {
769 printf("?Invalid command\n");
770 continue;
772 (*c->handler)(margc, margv);
776 struct cmd *
777 getcmd(char *name)
779 const char *p;
780 char *q;
781 struct cmd *c, *found;
782 int nmatches, longest;
784 longest = 0;
785 nmatches = 0;
786 found = 0;
787 for (c = cmdtab; (p = c->name) != NULL; c++) {
788 for (q = name; *q == *p++; q++)
789 if (*q == 0) /* exact match? */
790 return (c);
791 if (!*q) { /* the name was a prefix */
792 if (q - name > longest) {
793 longest = q - name;
794 nmatches = 1;
795 found = c;
796 } else if (q - name == longest)
797 nmatches++;
800 if (nmatches > 1)
801 return ((struct cmd *)-1);
802 return (found);
806 * Slice a string up into argc/argv.
808 static void
809 makeargv(void)
811 char *cp;
812 char **argp = margv;
814 margc = 0;
815 for (cp = line; *cp;) {
816 while (isspace(*cp))
817 cp++;
818 if (*cp == '\0')
819 break;
820 *argp++ = cp;
821 margc += 1;
822 while (*cp != '\0' && !isspace(*cp))
823 cp++;
824 if (*cp == '\0')
825 break;
826 *cp++ = '\0';
828 *argp++ = 0;
831 void
832 quit(int argc, char *argv[])
834 (void)argc; (void)argv; /* Quiet unused warning */
835 exit(0);
839 * Help command.
841 void
842 help(int argc, char *argv[])
844 struct cmd *c;
846 printf("%s\n", VERSION);
848 if (argc == 1) {
849 printf("Commands may be abbreviated. Commands are:\n\n");
850 for (c = cmdtab; c->name; c++)
851 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
852 return;
854 while (--argc > 0) {
855 char *arg;
856 arg = *++argv;
857 c = getcmd(arg);
858 if (c == (struct cmd *)-1)
859 printf("?Ambiguous help command %s\n", arg);
860 else if (c == (struct cmd *)0)
861 printf("?Invalid help command %s\n", arg);
862 else
863 printf("%s\n", c->help);
867 void
868 settrace(int argc, char *argv[])
870 (void)argc; (void)argv; /* Quiet unused warning */
872 trace = !trace;
873 printf("Packet tracing %s.\n", trace ? "on" : "off");
876 void
877 setverbose(int argc, char *argv[])
879 (void)argc; (void)argv; /* Quiet unused warning */
881 verbose = !verbose;
882 printf("Verbose mode %s.\n", verbose ? "on" : "off");