4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
40 #pragma ident "%Z%%M% %I% %E% SMI"
43 * TFTP User Program -- Command Interface.
45 #include <sys/types.h>
46 #include <sys/socket.h>
48 #include <arpa/inet.h>
60 #include "tftpcommon.h"
61 #include "tftpprivate.h"
63 #define NELEM(a) (sizeof (a) / sizeof ((a)[0]))
65 #define TIMEOUT 5 /* secs between rexmt's */
67 struct sockaddr_in6 sin6
;
69 int maxtimeout
= 5 * TIMEOUT
;
74 int rexmtval
= TIMEOUT
;
78 static int default_port
, port
;
81 static char line
[200];
82 static char *prompt
= "tftp";
83 static char hostname
[MAXHOSTNAMELEN
];
85 static void intr(int);
86 static void quit(int, char **);
87 static void help(int, char **);
88 static void setverbose(int, char **);
89 static void settrace(int, char **);
90 static void status(int, char **);
91 static void get(int, char **);
92 static void put(int, char **);
93 static void setpeer(int, char **);
94 static void modecmd(int, char **);
95 static void setrexmt(int, char **);
96 static void settimeout(int, char **);
97 static void setbinary(int, char **);
98 static void setascii(int, char **);
99 static void setblksize(int, char **);
100 static void setsrexmt(int, char **);
101 static void settsize(int, char **);
102 static void setmode(char *);
103 static void putusage(char *);
104 static void getusage(char *);
105 static char *finddelimiter(char *);
106 static char *removebrackets(char *);
107 static int prompt_for_arg(char *, int, char *);
108 static struct cmd
*getcmd(char *);
109 static char *tail(char *);
110 static void command(int);
111 static void makeargv(int *, char ***);
113 #define HELPINDENT (sizeof ("connect"))
118 void (*handler
)(int, char **);
121 static char vhelp
[] = "toggle verbose mode";
122 static char thelp
[] = "toggle packet tracing";
123 static char chelp
[] = "connect to remote tftp";
124 static char qhelp
[] = "exit tftp";
125 static char hhelp
[] = "print help information";
126 static char shelp
[] = "send file";
127 static char rhelp
[] = "receive file";
128 static char mhelp
[] = "set file transfer mode";
129 static char sthelp
[] = "show current status";
130 static char xhelp
[] = "set per-packet retransmission timeout";
131 static char ihelp
[] = "set total retransmission timeout";
132 static char ashelp
[] = "set mode to netascii";
133 static char bnhelp
[] = "set mode to octet";
134 static char bshelp
[] = "set transfer blocksize to negotiate with the "
136 static char srhelp
[] = "set preferred per-packet retransmission "
137 "timeout for server";
138 static char tshelp
[] = "toggle sending the transfer size option to "
141 static struct cmd cmdtab
[] = {
142 { "connect", chelp
, setpeer
},
143 { "mode", mhelp
, modecmd
},
144 { "put", shelp
, put
},
145 { "get", rhelp
, get
},
146 { "quit", qhelp
, quit
},
147 { "verbose", vhelp
, setverbose
},
148 { "trace", thelp
, settrace
},
149 { "status", sthelp
, status
},
150 { "binary", bnhelp
, setbinary
},
151 { "ascii", ashelp
, setascii
},
152 { "rexmt", xhelp
, setrexmt
},
153 { "timeout", ihelp
, settimeout
},
154 { "blksize", bshelp
, setblksize
},
155 { "srexmt", srhelp
, setsrexmt
},
156 { "tsize", tshelp
, settsize
},
157 { "?", hhelp
, help
},
161 #define AMBIGCMD (&cmdtab[NELEM(cmdtab)])
164 main(int argc
, char **argv
)
167 struct sockaddr_in6 sin6
;
170 sp
= getservbyname("tftp", "udp");
171 default_port
= (sp
!= NULL
) ? sp
->s_port
: htons(IPPORT_TFTP
);
174 f
= socket(AF_INET6
, SOCK_DGRAM
, 0);
176 perror("tftp: socket");
180 (void) memset(&sin6
, 0, sizeof (sin6
));
181 sin6
.sin6_family
= AF_INET6
;
182 if (bind(f
, (struct sockaddr
*)&sin6
, sizeof (sin6
)) < 0) {
183 perror("tftp: bind");
187 (void) strlcpy(mode
, "netascii", sizeof (mode
));
188 (void) signal(SIGINT
, intr
);
190 if (setjmp(toplevel
) != 0)
195 top
= (setjmp(toplevel
) == 0);
203 /* Prompt for command argument, add to buffer with space separator */
205 prompt_for_arg(char *buffer
, int buffer_size
, char *prompt
)
209 if (strlcat(buffer
, " ", buffer_size
) >= buffer_size
) {
210 (void) fputs("?Line too long\n", stderr
);
213 (void) printf("(%s) ", prompt
);
214 if (fgets(buffer
+ strlen(buffer
), buffer_size
- strlen(buffer
),
218 /* Flush what didn't fit in the buffer */
219 if (buffer
[strlen(buffer
)-1] != '\n') {
220 while (((ch
= getchar()) != EOF
) && (ch
!= '\n'))
222 (void) fputs("?Line too long\n", stderr
);
225 buffer
[strlen(buffer
)-1] = '\0';
231 unknown_host(int error
, char *hostname
)
233 if (error
== TRY_AGAIN
)
234 (void) fprintf(stderr
, "%s: Unknown host (try again later).\n",
237 (void) fprintf(stderr
, "%s: Unknown host.\n", hostname
);
241 setpeer(int argc
, char **argv
)
243 struct hostent
*host
;
245 struct in6_addr ipv6addr
;
246 struct in_addr ipv4addr
;
250 if (prompt_for_arg(line
, sizeof (line
), "to") == -1)
252 makeargv(&argc
, &argv
);
254 if (argc
> 3 || argc
< 2) {
255 (void) fprintf(stderr
, "usage: %s host-name [port]\n",
259 hostnameinput
= removebrackets(argv
[1]);
261 (void) memset(&sin6
, 0, sizeof (sin6
));
262 sin6
.sin6_family
= AF_INET6
;
263 if (host
= getipnodebyname(hostnameinput
, AF_INET6
,
264 AI_ALL
| AI_ADDRCONFIG
| AI_V4MAPPED
, &error_num
)) {
265 (void) memcpy(&sin6
.sin6_addr
, host
->h_addr_list
[0],
268 * If host->h_name is a IPv4-mapped IPv6 literal, we'll convert
269 * it to IPv4 literal address.
271 if ((inet_pton(AF_INET6
, host
->h_name
, &ipv6addr
) > 0) &&
272 IN6_IS_ADDR_V4MAPPED(&ipv6addr
)) {
273 IN6_V4MAPPED_TO_INADDR(&ipv6addr
, &ipv4addr
);
274 (void) inet_ntop(AF_INET
, &ipv4addr
, hostname
,
277 (void) strlcpy(hostname
, host
->h_name
,
282 /* Keeping with previous semantics */
284 unknown_host(error_num
, hostnameinput
);
290 port
= atoi(argv
[2]);
291 if ((port
< 1) || (port
> 65535)) {
292 (void) fprintf(stderr
, "%s: bad port number\n",
302 static struct modes
{
306 { "ascii", "netascii" },
307 { "netascii", "netascii" },
308 { "binary", "octet" },
309 { "image", "octet" },
310 { "octet", "octet" },
311 /* { "mail", "mail" }, */
316 modecmd(int argc
, char **argv
)
321 (void) fprintf(stderr
, "Using %s mode to transfer files.\n",
326 for (p
= modes
; p
->m_name
!= NULL
; p
++)
327 if (strcmp(argv
[1], p
->m_name
) == 0) {
331 (void) fprintf(stderr
, "%s: unknown mode\n", argv
[1]);
332 /* drop through and print usage message */
336 (void) fprintf(stderr
, "usage: %s [ %s", argv
[0], p
->m_name
);
337 for (p
++; p
->m_name
!= NULL
; p
++)
338 (void) fprintf(stderr
, " | %s", p
->m_name
);
344 setbinary(int argc
, char **argv
)
351 setascii(int argc
, char **argv
)
357 setmode(char *newmode
)
359 (void) strlcpy(mode
, newmode
, sizeof (mode
));
361 (void) printf("mode set to %s\n", mode
);
368 put(int argc
, char **argv
)
373 struct in6_addr ipv6addr
;
374 struct in_addr ipv4addr
;
375 char buf
[PATH_MAX
+ 1], *argtail
;
378 if (prompt_for_arg(line
, sizeof (line
), "file") == -1)
380 makeargv(&argc
, &argv
);
386 targ
= argv
[argc
- 1];
387 if (finddelimiter(argv
[argc
- 1])) {
392 for (n
= 1; n
< argc
- 1; n
++)
393 if (finddelimiter(argv
[n
])) {
398 targ
= finddelimiter(cp
);
400 cp
= removebrackets(cp
);
402 if ((hp
= getipnodebyname(cp
,
403 AF_INET6
, AI_ALL
| AI_ADDRCONFIG
| AI_V4MAPPED
,
404 &error_num
)) == NULL
) {
405 unknown_host(error_num
, cp
);
408 (void) memcpy(&sin6
.sin6_addr
, hp
->h_addr_list
[0],
411 sin6
.sin6_family
= AF_INET6
;
414 * If hp->h_name is a IPv4-mapped IPv6 literal, we'll convert
415 * it to IPv4 literal address.
417 if ((inet_pton(AF_INET6
, hp
->h_name
, &ipv6addr
) > 0) &&
418 IN6_IS_ADDR_V4MAPPED(&ipv6addr
)) {
419 IN6_V4MAPPED_TO_INADDR(&ipv6addr
, &ipv4addr
);
420 (void) inet_ntop(AF_INET
, &ipv4addr
, hostname
,
423 (void) strlcpy(hostname
, hp
->h_name
,
428 (void) fputs("No target machine specified.\n", stderr
);
432 cp
= argc
== 2 ? tail(targ
) : argv
[1];
433 fd
= open(cp
, O_RDONLY
);
435 (void) fprintf(stderr
, "tftp: %s: %s\n", cp
,
440 (void) printf("putting %s to %s:%s [%s]\n",
441 cp
, hostname
, targ
, mode
);
442 sin6
.sin6_port
= port
;
443 tftp_sendfile(fd
, targ
, mode
);
446 /* this assumes the target is a directory */
447 /* on a remote unix system. hmmmm. */
448 if (strlen(targ
) + 1 >= sizeof (buf
)) {
449 (void) fprintf(stderr
, "tftp: filename too long: %s\n", targ
);
452 for (n
= 1; n
< argc
- 1; n
++) {
453 argtail
= tail(argv
[n
]);
454 if (snprintf(buf
, sizeof (buf
), "%s/%s", targ
, argtail
) >=
456 (void) fprintf(stderr
,
457 "tftp: filename too long: %s/%s\n", targ
, argtail
);
460 fd
= open(argv
[n
], O_RDONLY
);
462 (void) fprintf(stderr
, "tftp: %s: %s\n", argv
[n
],
467 (void) printf("putting %s to %s:%s [%s]\n",
468 argv
[n
], hostname
, buf
, mode
);
469 sin6
.sin6_port
= port
;
470 tftp_sendfile(fd
, buf
, mode
);
477 (void) fprintf(stderr
, "usage: %s file ... host:target, or\n"
478 " %s file ... target (when already connected)\n", s
, s
);
485 get(int argc
, char **argv
)
491 struct in6_addr ipv6addr
;
492 struct in_addr ipv4addr
;
496 if (prompt_for_arg(line
, sizeof (line
), "files") == -1)
498 makeargv(&argc
, &argv
);
505 for (n
= 1; n
< argc
; n
++)
506 if (finddelimiter(argv
[n
]) == 0) {
511 for (n
= 1; n
< argc
; n
++) {
512 src
= finddelimiter(argv
[n
]);
520 hostnameinput
= removebrackets(argv
[n
]);
522 if ((hp
= getipnodebyname(hostnameinput
, AF_INET6
,
523 AI_ALL
| AI_ADDRCONFIG
| AI_V4MAPPED
,
524 &error_num
)) == NULL
) {
525 unknown_host(error_num
, hostnameinput
);
528 (void) memcpy((caddr_t
)&sin6
.sin6_addr
,
529 hp
->h_addr_list
[0], hp
->h_length
);
531 sin6
.sin6_family
= AF_INET6
;
534 * If hp->h_name is a IPv4-mapped IPv6 literal, we'll
535 * convert it to IPv4 literal address.
537 if ((inet_pton(AF_INET6
, hp
->h_name
, &ipv6addr
) > 0) &&
538 IN6_IS_ADDR_V4MAPPED(&ipv6addr
)) {
539 IN6_V4MAPPED_TO_INADDR(&ipv6addr
, &ipv4addr
);
540 (void) inet_ntop(AF_INET
, &ipv4addr
, hostname
,
543 (void) strlcpy(hostname
, hp
->h_name
,
548 cp
= argc
== 3 ? argv
[2] : tail(src
);
549 fd
= creat(cp
, 0644);
551 (void) fprintf(stderr
, "tftp: %s: %s\n", cp
,
556 (void) printf("getting from %s:%s to %s [%s]\n",
557 hostname
, src
, cp
, mode
);
558 sin6
.sin6_port
= port
;
559 tftp_recvfile(fd
, src
, mode
);
562 cp
= tail(src
); /* new .. jdg */
563 fd
= creat(cp
, 0644);
565 (void) fprintf(stderr
, "tftp: %s: %s\n", cp
,
570 (void) printf("getting from %s:%s to %s [%s]\n",
571 hostname
, src
, cp
, mode
);
572 sin6
.sin6_port
= port
;
573 tftp_recvfile(fd
, src
, mode
);
580 (void) fprintf(stderr
, "usage: %s host:file host:file ... file, or\n"
581 " %s file file ... file if connected\n", s
, s
);
585 setrexmt(int argc
, char **argv
)
590 if (prompt_for_arg(line
, sizeof (line
), "value") == -1)
592 makeargv(&argc
, &argv
);
595 (void) fprintf(stderr
, "usage: %s value\n", argv
[0]);
600 (void) fprintf(stderr
, "%s: bad value\n", argv
[1]);
606 settimeout(int argc
, char **argv
)
611 if (prompt_for_arg(line
, sizeof (line
), "value") == -1)
613 makeargv(&argc
, &argv
);
616 (void) fprintf(stderr
, "usage: %s value\n", argv
[0]);
621 (void) fprintf(stderr
, "%s: bad value\n", argv
[1]);
628 status(int argc
, char **argv
)
631 (void) printf("Connected to %s.\n", hostname
);
633 (void) puts("Not connected.");
634 (void) printf("Mode: %s Verbose: %s Tracing: %s\n", mode
,
635 verbose
? "on" : "off", trace
? "on" : "off");
636 (void) printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
637 rexmtval
, maxtimeout
);
638 (void) printf("Transfer blocksize option: ");
642 (void) printf("%d bytes\n", blksize
);
643 (void) printf("Server rexmt-interval option: ");
647 (void) printf("%d seconds\n", srexmtval
);
648 (void) printf("Transfer size option: %s\n", tsize_opt
? "on" : "off");
655 (void) cancel_alarm();
656 longjmp(toplevel
, -1);
664 while (*filename
!= '\0') {
665 s
= strrchr(filename
, '/');
685 (void) putchar('\n');
687 (void) printf("%s> ", prompt
);
688 if (fgets(line
, sizeof (line
), stdin
) == NULL
) {
695 /* Flush what didn't fit in the buffer */
696 if (line
[strlen(line
)-1] != '\n') {
697 while (((ch
= getchar()) != EOF
) && (ch
!= '\n'))
699 (void) fputs("?Line too long\n", stderr
);
701 line
[strlen(line
)-1] = '\0';
702 if (line
[0] != '\0') {
706 makeargv(&argc
, &argv
);
709 (void) fputs("?Ambiguous command\n",
712 (void) fputs("?Invalid command\n",
715 (*c
->handler
)(argc
, argv
);
725 struct cmd
*c
, *found
;
731 for (c
= cmdtab
; (p
= c
->name
) != NULL
; c
++) {
732 for (q
= name
; *q
== *p
++; q
++)
733 if (*q
== '\0') /* exact match? */
735 if (*q
== '\0') /* the name was a prefix */
736 found
= (found
== NULL
) ? c
: AMBIGCMD
;
742 * Given a string, this function returns the pointer to the delimiting ':'.
743 * The string can contain an IPv6 literal address, which should be inside a
744 * pair of brackets, e.g. [1::2]. Any colons inside a pair of brackets are not
745 * accepted as delimiters. Returns NULL if delimiting ':' is not found.
748 finddelimiter(char *str
)
750 boolean_t is_bracket_open
= B_FALSE
;
753 for (cp
= str
; *cp
!= '\0'; cp
++) {
755 is_bracket_open
= B_TRUE
;
757 is_bracket_open
= B_FALSE
;
758 else if (*cp
== ':' && !is_bracket_open
)
765 * Given a string which is possibly surrounded by brackets, e.g. [1::2], this
766 * function returns a string after removing those brackets. If the brackets
767 * don't match, it does nothing.
770 removebrackets(char *str
)
774 if ((str
[0] == '[') && (str
[strlen(str
) - 1] == ']')) {
776 str
[strlen(str
) - 1] = '\0';
784 * Slice a string up into argc/argv.
787 makeargv(int *argcp
, char ***argvp
)
793 static int argv_size
;
796 argv_size
= MARGV_INC
;
797 if ((argv
= malloc(argv_size
* sizeof (char *))) == NULL
) {
798 perror("tftp: malloc");
804 for (cp
= line
; *cp
!= '\0'; ) {
811 if (argc
== argv_size
) {
812 argv_size
+= MARGV_INC
;
813 if ((argv
= reallocarray(argv
, argv_size
,
814 sizeof (char *))) == NULL
) {
815 perror("tftp: realloc");
820 while (*cp
!= '\0' && !isspace(*cp
))
834 quit(int argc
, char **argv
)
843 help(int argc
, char **argv
)
848 (void) puts("Commands may be abbreviated. Commands are:\n");
849 for (c
= cmdtab
; c
->name
!= NULL
; c
++)
850 (void) printf("%-*s\t%s\n", HELPINDENT
, c
->name
,
859 (void) fprintf(stderr
, "?Ambiguous help command %s\n",
862 (void) fprintf(stderr
, "?Invalid help command %s\n",
865 (void) fprintf(stderr
, "%s\n", c
->help
);
871 settrace(int argc
, char **argv
)
874 (void) printf("Packet tracing %s.\n", trace
? "on" : "off");
879 setverbose(int argc
, char **argv
)
882 (void) printf("Verbose mode %s.\n", verbose
? "on" : "off");
886 setblksize(int argc
, char **argv
)
891 if (prompt_for_arg(line
, sizeof (line
), "value") == -1)
893 makeargv(&argc
, &argv
);
896 (void) fprintf(stderr
, "usage: %s value\n", argv
[0]);
901 /* RFC 2348 specifies valid blksize range, allow 0 to turn option off */
902 if ((b
< MIN_BLKSIZE
|| b
> MAX_BLKSIZE
) && b
!= 0)
903 (void) fprintf(stderr
, "%s: bad value\n", argv
[1]);
909 setsrexmt(int argc
, char **argv
)
914 if (prompt_for_arg(line
, sizeof (line
), "value") == -1)
916 makeargv(&argc
, &argv
);
919 (void) fprintf(stderr
, "usage: %s value\n", argv
[0]);
924 /* RFC 2349 specifies valid timeout range, allow 0 to turn option off */
925 if ((t
< MIN_TIMEOUT
|| t
> MAX_TIMEOUT
) && t
!= 0)
926 (void) fprintf(stderr
, "%s: bad value\n", argv
[1]);
932 settsize(int argc
, char **argv
)
935 (void) fprintf(stderr
, "usage: %s\n", argv
[0]);
938 tsize_opt
= !tsize_opt
;
939 (void) printf("Transfer size option %s.\n", tsize_opt
? "on" : "off");