Add support for IPv6 in the server and client.

Add support for IPv6 in the server and client.
You can force the use of IPv4 or IPv6 only with new
-4 and -6 commandline options, if IPv6 support was compiled in.

Signed-off-by: Karsten Keil <kkeil@suse.de>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
Karsten Keil 2008-07-23 18:32:21 +02:00 committed by H. Peter Anvin
parent 7fe0fb941c
commit 28f22b6591
9 changed files with 574 additions and 185 deletions

View file

@ -40,7 +40,6 @@
*/
#include <sys/file.h>
#include <ctype.h>
#include <netdb.h>
#ifdef WITH_READLINE
#include <readline/readline.h>
#ifdef HAVE_READLINE_HISTORY_H
@ -72,8 +71,16 @@ static const struct modes modes[] = {
#define MODE_NETASCII (&modes[0])
#define MODE_DEFAULT MODE_NETASCII
struct sockaddr_in peeraddr;
int f;
#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;
@ -184,14 +191,18 @@ const char *program;
static inline 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[])
{
struct sockaddr_in s_in;
union sock_addr sa;
int arg;
static int pargc, peerargc;
static int iscmd = 0;
@ -210,6 +221,14 @@ int main(int argc, char *argv[])
if (argv[arg][0] == '-') {
for (optx = &argv[arg][1]; *optx; optx++) {
switch (*optx) {
#ifdef HAVE_IPV6
case '4':
ai_fam = AF_INET;
break;
case '6':
ai_fam = AF_INET6;
break;
#endif
case 'v':
verbose = 1;
break;
@ -268,6 +287,8 @@ int main(int argc, char *argv[])
}
}
ai_fam_sock = ai_fam;
pargv = argv + arg;
pargc = argc - arg;
@ -283,18 +304,7 @@ int main(int argc, char *argv[])
sp->s_port = htons(IPPORT_TFTP);
sp->s_proto = (char *)"udp";
}
port = sp->s_port; /* Default port */
f = socket(AF_INET, SOCK_DGRAM, 0);
if (f < 0) {
perror("tftp: socket");
exit(EX_OSERR);
}
bzero((char *)&s_in, sizeof(s_in));
s_in.sin_family = AF_INET;
if (pick_port_bind(f, &s_in, portrange_from, portrange_to)) {
perror("tftp: bind");
exit(EX_OSERR);
}
bsd_signal(SIGINT, intr);
if (peerargc) {
@ -304,6 +314,21 @@ int main(int argc, char *argv[])
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;
@ -375,7 +400,7 @@ static void getmoreargs(const char *partial, const char *mprompt)
void setpeer(int argc, char *argv[])
{
struct hostent *host;
int err;
if (argc < 2) {
getmoreargs("connect ", "(to) ");
@ -388,16 +413,34 @@ void setpeer(int argc, char *argv[])
return;
}
host = gethostbyname(argv[1]);
if (host == 0) {
peeraddr.sa.sa_family = ai_fam;
err = set_sock_addr(argv[1], &peeraddr, &hostname);
if (err) {
connected = 0;
printf("%s: unknown host\n", argv[1]);
return;
}
peeraddr.sin_family = host->h_addrtype;
bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
hostname = xstrdup(host->h_name);
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;
@ -418,9 +461,13 @@ void setpeer(int argc, char *argv[])
}
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, inet_ntoa(peeraddr.sin_addr),
(unsigned int)ntohs(port));
hostname, tp, (unsigned int)ntohs(port));
}
connected = 1;
}
@ -484,7 +531,7 @@ static void settftpmode(const struct modes *newmode)
void put(int argc, char *argv[])
{
int fd;
int n;
int n, err;
char *cp, *targ;
if (argc < 2) {
@ -499,8 +546,6 @@ void put(int argc, char *argv[])
}
targ = argv[argc - 1];
if (!literal && strchr(argv[argc - 1], ':')) {
struct hostent *hp;
for (n = 1; n < argc - 1; n++)
if (strchr(argv[n], ':')) {
putusage(argv[0]);
@ -509,16 +554,14 @@ void put(int argc, char *argv[])
cp = argv[argc - 1];
targ = strchr(cp, ':');
*targ++ = 0;
hp = gethostbyname(cp);
if (hp == NULL) {
fprintf(stderr, "tftp: %s: ", cp);
herror((char *)NULL);
peeraddr.sa.sa_family = ai_fam;
err = set_sock_addr(cp, &peeraddr,&hostname);
if (err) {
connected = 0;
return;
}
bcopy(hp->h_addr, &peeraddr.sin_addr, hp->h_length);
peeraddr.sin_family = hp->h_addrtype;
ai_fam = peeraddr.sa.sa_family;
connected = 1;
hostname = xstrdup(hp->h_name);
}
if (!connected) {
printf("No target machine specified.\n");
@ -535,7 +578,7 @@ void put(int argc, char *argv[])
if (verbose)
printf("putting %s to %s:%s [%s]\n",
cp, hostname, targ, mode->m_mode);
peeraddr.sin_port = port;
sa_set_port(&peeraddr, port);
tftp_sendfile(fd, targ, mode->m_mode);
return;
}
@ -554,7 +597,7 @@ void put(int argc, char *argv[])
if (verbose)
printf("putting %s to %s:%s [%s]\n",
argv[n], hostname, targ, mode->m_mode);
peeraddr.sin_port = port;
sa_set_port(&peeraddr, port);
tftp_sendfile(fd, targ, mode->m_mode);
}
}
@ -597,19 +640,15 @@ void get(int argc, char *argv[])
if (literal || src == NULL)
src = argv[n];
else {
struct hostent *hp;
int err;
*src++ = 0;
hp = gethostbyname(argv[n]);
if (hp == NULL) {
fprintf(stderr, "tftp: %s: ", argv[n]);
herror((char *)NULL);
peeraddr.sa.sa_family = ai_fam;
err = set_sock_addr(argv[n], &peeraddr, &hostname);
if (err)
continue;
}
bcopy(hp->h_addr, (caddr_t) & peeraddr.sin_addr, hp->h_length);
peeraddr.sin_family = hp->h_addrtype;
ai_fam = peeraddr.sa.sa_family;
connected = 1;
hostname = xstrdup(hp->h_name);
}
if (argc < 4) {
cp = argc == 3 ? argv[2] : tail(src);
@ -623,7 +662,7 @@ void get(int argc, char *argv[])
if (verbose)
printf("getting from %s:%s to %s [%s]\n",
hostname, src, cp, mode->m_mode);
peeraddr.sin_port = port;
sa_set_port(&peeraddr, port);
tftp_recvfile(fd, src, mode->m_mode);
break;
}
@ -638,7 +677,7 @@ void get(int argc, char *argv[])
if (verbose)
printf("getting from %s:%s to %s [%s]\n",
hostname, src, cp, mode->m_mode);
peeraddr.sin_port = port;
sa_set_port(&peeraddr, port);
tftp_recvfile(fd, src, mode->m_mode);
}
}

View file

@ -42,7 +42,7 @@
.br
.SH DESCRIPTION
.B tftp
is a client for the IPv4 Trivial file Transfer Protocol, which can be
is a client for the Trivial file Transfer Protocol, which can be
used to transfer files to and from remote machines, including some
very minimalistic, usually embedded, systems. The remote
.I host
@ -55,6 +55,11 @@ as the default host for future transfers (see the
command below.)
.SH OPTIONS
.TP
.B \-4
Connect with IPv4 only, if IPv6 support was compiled in.
.TP
.B \-6
.TP
\fB\-c\fP \fIcommand\fP
Execute \fIcommand\fP as if it had been entered on the tftp prompt.
Must be specified last on the command line.

View file

@ -38,8 +38,8 @@
*/
#include "extern.h"
extern struct sockaddr_in peeraddr; /* filled in by main */
extern int f; /* the opened socket */
extern union sock_addr peeraddr; /* filled in by main */
extern int f; /* the opened socket */
extern int trace;
extern int verbose;
extern int rexmtval;
@ -71,7 +71,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode)
volatile u_short block;
volatile int size, convert;
volatile off_t amount;
struct sockaddr_in from;
union sock_addr from;
socklen_t fromlen;
FILE *file;
u_short ap_opcode, ap_block;
@ -105,7 +105,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode)
if (trace)
tpacket("sent", dp, size + 4);
n = sendto(f, dp, size + 4, 0,
(struct sockaddr *)&peeraddr, sizeof(peeraddr));
&peeraddr.sa, SOCKLEN(&peeraddr));
if (n != size + 4) {
perror("tftp: sendto");
goto abort;
@ -116,14 +116,14 @@ void tftp_sendfile(int fd, const char *name, const char *mode)
do {
fromlen = sizeof(from);
n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
(struct sockaddr *)&from, &fromlen);
&from.sa, &fromlen);
} while (n <= 0);
alarm(0);
if (n < 0) {
perror("tftp: recvfrom");
goto abort;
}
peeraddr.sin_port = from.sin_port; /* added */
sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */
if (trace)
tpacket("received", ap, n);
/* should verify packet came from server */
@ -176,7 +176,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode)
volatile u_short block;
volatile int size, firsttrip;
volatile unsigned long amount;
struct sockaddr_in from;
union sock_addr from;
socklen_t fromlen;
FILE *file;
volatile int convert; /* true if converting crlf -> lf */
@ -207,8 +207,8 @@ void tftp_recvfile(int fd, const char *name, const char *mode)
send_ack:
if (trace)
tpacket("sent", ap, size);
if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
sizeof(peeraddr)) != size) {
if (sendto(f, ackbuf, size, 0, &peeraddr.sa,
SOCKLEN(&peeraddr)) != size) {
alarm(0);
perror("tftp: sendto");
goto abort;
@ -219,14 +219,14 @@ void tftp_recvfile(int fd, const char *name, const char *mode)
do {
fromlen = sizeof(from);
n = recvfrom(f, dp, PKTSIZE, 0,
(struct sockaddr *)&from, &fromlen);
&from.sa, &fromlen);
} while (n <= 0);
alarm(0);
if (n < 0) {
perror("tftp: recvfrom");
goto abort;
}
peeraddr.sin_port = from.sin_port; /* added */
sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */
if (trace)
tpacket("received", dp, n);
/* should verify client address */
@ -266,7 +266,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode)
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,
sizeof(peeraddr));
SOCKLEN(&peeraddr));
write_behind(file, convert); /* flush last buffer */
fclose(file);
stopclock();
@ -341,8 +341,8 @@ static void nak(int error, const char *msg)
if (trace)
tpacket("sent", tp, length);
if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
sizeof(peeraddr)) != length)
if (sendto(f, ackbuf, length, 0, &peeraddr.sa,
SOCKLEN(&peeraddr)) != length)
perror("nak");
}