aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarsten Keil <kkeil@suse.de>2008-07-23 18:32:21 +0200
committerH. Peter Anvin <hpa@zytor.com>2008-07-23 14:29:41 -0400
commit28f22b6591c3bca4d6ead5f943107ddfe0d6705c (patch)
treed6736e135d3940407f5789906d41a7be38a1c03f
parent7fe0fb941cb8ee2562cae02028df4e80f61548f4 (diff)
downloadtftp-hpa-28f22b6591c3bca4d6ead5f943107ddfe0d6705c.tar.gz
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>
-rw-r--r--common/tftpsubs.c44
-rw-r--r--common/tftpsubs.h54
-rw-r--r--tftp/main.c137
-rw-r--r--tftp/tftp.1.in7
-rw-r--r--tftp/tftp.c28
-rw-r--r--tftpd/recvfrom.c140
-rw-r--r--tftpd/recvfrom.h4
-rw-r--r--tftpd/tftpd.8.in8
-rw-r--r--tftpd/tftpd.c339
9 files changed, 575 insertions, 186 deletions
diff --git a/common/tftpsubs.c b/common/tftpsubs.c
index ef05cc4..45f4907 100644
--- a/common/tftpsubs.c
+++ b/common/tftpsubs.c
@@ -237,7 +237,7 @@ int synchnet(int f)
{ /* socket to flush */
int pktcount = 0;
char rbuf[PKTSIZE];
- struct sockaddr_in from;
+ union sock_addr from;
socklen_t fromlen;
fd_set socketset;
struct timeval notime;
@@ -253,15 +253,15 @@ int synchnet(int f)
/* Otherwise drain the packet */
pktcount++;
- fromlen = sizeof from;
+ fromlen = sizeof(from);
(void)recvfrom(f, rbuf, sizeof(rbuf), 0,
- (struct sockaddr *)&from, &fromlen);
+ &from.sa, &fromlen);
}
return pktcount; /* Return packets drained */
}
-int pick_port_bind(int sockfd, struct sockaddr_in *myaddr,
+int pick_port_bind(int sockfd, union sock_addr *myaddr,
unsigned int port_range_from,
unsigned int port_range_to)
{
@@ -279,9 +279,8 @@ int pick_port_bind(int sockfd, struct sockaddr_in *myaddr,
port = firstport;
do {
- myaddr->sin_port = htons(port);
-
- if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) {
+ sa_set_port(myaddr, htons(port));
+ if (bind(sockfd, &myaddr->sa, SOCKLEN(myaddr)) < 0) {
/* Some versions of Linux return EINVAL instead of EADDRINUSE */
if (!(port_range && (errno == EINVAL || errno == EADDRINUSE)))
return -1;
@@ -299,3 +298,34 @@ int pick_port_bind(int sockfd, struct sockaddr_in *myaddr,
return -1;
}
+
+int
+set_sock_addr(char *host,union sock_addr *s, char **name)
+{
+ struct addrinfo *addrResult;
+ struct addrinfo hints;
+ int err;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = s->sa.sa_family;
+ hints.ai_flags = AI_CANONNAME;
+ err = getaddrinfo(host, NULL, &hints, &addrResult);
+ if (err) {
+ printf("Error : %s\n", gai_strerror(err));
+ printf("%s: unknown host\n", host);
+ return err;
+ }
+ if (addrResult == NULL) {
+ printf("%s: unknown host\n", host);
+ return EAI_NONAME;
+ }
+ memcpy(s, addrResult->ai_addr, addrResult->ai_addrlen);
+ if (name) {
+ if (addrResult->ai_canonname)
+ *name = xstrdup(addrResult->ai_canonname);
+ else
+ *name = xstrdup(host);
+ }
+ freeaddrinfo(addrResult);
+ return 0;
+}
diff --git a/common/tftpsubs.h b/common/tftpsubs.h
index a5c88e3..20cf47e 100644
--- a/common/tftpsubs.h
+++ b/common/tftpsubs.h
@@ -40,6 +40,58 @@
#include "config.h"
+union sock_addr {
+ struct sockaddr sa;
+ struct sockaddr_in si;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 s6;
+#endif
+};
+
+#define SOCKLEN(sock) \
+ (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \
+ (sizeof(struct sockaddr_in)) : \
+ (sizeof(union sock_addr)))
+
+#ifdef HAVE_IPV6
+#define SOCKPORT(sock) \
+ (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \
+ ((union sock_addr*)sock)->si.sin_port : \
+ ((union sock_addr*)sock)->s6.sin6_port)
+#else
+#define SOCKPORT(sock) \
+ (((union sock_addr*)sock)->si.sin_port)
+#endif
+
+#ifdef HAVE_IPV6
+#define SOCKADDR_P(sock) \
+ (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \
+ (void *)&((union sock_addr*)sock)->si.sin_addr : \
+ (void *)&((union sock_addr*)sock)->s6.sin6_addr)
+#else
+#define SOCKADDR_P(sock) \
+ (void *)&((union sock_addr*)sock)->si.sin_addr
+#endif
+
+static inline int sa_set_port(union sock_addr *s, u_short port)
+{
+ switch (s->sa.sa_family) {
+ case AF_INET:
+ s->si.sin_port = port;
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ s->s6.sin6_port = port;
+ break;
+#endif
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int set_sock_addr(char *, union sock_addr *, char **);
+
struct tftphdr;
struct tftphdr *r_init(void);
@@ -55,7 +107,7 @@ int writeit(FILE *, struct tftphdr **, int, int);
extern int segsize;
#define MAX_SEGSIZE 65464
-int pick_port_bind(int sockfd, struct sockaddr_in *myaddr,
+int pick_port_bind(int sockfd, union sock_addr *myaddr,
unsigned int from, unsigned int to);
#endif
diff --git a/tftp/main.c b/tftp/main.c
index ec57bc2..6176b37 100644
--- a/tftp/main.c
+++ b/tftp/main.c
@@ -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);
}
}
diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in
index 8ef1e86..8bbb796 100644
--- a/tftp/tftp.1.in
+++ b/tftp/tftp.1.in
@@ -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.
diff --git a/tftp/tftp.c b/tftp/tftp.c
index 2d26b4b..d15da22 100644
--- a/tftp/tftp.c
+++ b/tftp/tftp.c
@@ -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");
}
diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c
index fee6d03..389ba82 100644
--- a/tftpd/recvfrom.c
+++ b/tftpd/recvfrom.c
@@ -18,8 +18,8 @@
*/
#include "config.h" /* Must be included first! */
-#include "recvfrom.h"
#include "common/tftpsubs.h"
+#include "recvfrom.h"
#ifdef HAVE_MACHINE_PARAM_H
#include <machine/param.h> /* Needed on some versions of FreeBSD */
#endif
@@ -55,31 +55,47 @@ struct in_pktinfo {
* end up having the same local and remote address when trying to
* bind to it.
*/
-static int address_is_local(const struct sockaddr_in *addr)
+static int address_is_local(const union sock_addr *addr)
{
- struct sockaddr_in sin;
+ union sock_addr sa;
int sockfd = -1;
int e;
int rv = 0;
socklen_t addrlen;
/* Multicast or universal broadcast address? */
- if (ntohl(addr->sin_addr.s_addr) >= (224UL << 24))
+ if (addr->sa.sa_family == AF_INET) {
+ if (ntohl(addr->si.sin_addr.s_addr) >= (224UL << 24))
+ return 0;
+ }
+#ifdef HAVE_IPV6
+ else if (addr->sa.sa_family == AF_INET6) {
+ if (IN6_IS_ADDR_MULTICAST(&addr->s6.sin6_addr))
+ return 0;
+ }
+#endif
+ else
return 0;
- sockfd = socket(PF_INET, SOCK_DGRAM, 0);
+ sockfd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
if (sockfd < 0)
goto err;
- if (connect(sockfd, (const struct sockaddr *)addr, sizeof *addr))
+ if (connect(sockfd, &addr->sa, SOCKLEN(addr)))
goto err;
- addrlen = sizeof sin;
- if (getsockname(sockfd, (struct sockaddr *)&sin, &addrlen))
+ addrlen = SOCKLEN(addr);
+ if (getsockname(sockfd, (struct sockaddr *)&sa, &addrlen))
goto err;
- rv = sin.sin_addr.s_addr == addr->sin_addr.s_addr;
-
+ if (addr->sa.sa_family == AF_INET)
+ rv = sa.si.sin_addr.s_addr == addr->si.sin_addr.s_addr;
+#ifdef HAVE_IPV6
+ else if (addr->sa.sa_family == AF_INET6)
+ rv = IN6_ARE_ADDR_EQUAL(&sa.s6.sin6_addr, &addr->s6.sin6_addr);
+#endif
+ else
+ rv = 0;
err:
e = errno;
@@ -93,7 +109,7 @@ static int address_is_local(const struct sockaddr_in *addr)
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
struct sockaddr *from, socklen_t * fromlen,
- struct sockaddr_in *myaddr)
+ union sock_addr *myaddr)
{
struct msghdr msg;
struct iovec iov;
@@ -107,23 +123,41 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags,
#else
char control[CMSG_SPACE(sizeof(struct in_addr))];
#endif
+#ifdef HAVE_IPV6
+#ifdef HAVE_STRUCT_IN6_PKTINFO
+ char control6[CMSG_SPACE(sizeof(struct in6_addr)) +
+ CMSG_SPACE(sizeof(struct in6_pktinfo))];
+#else
+ char control6[CMSG_SPACE(sizeof(struct in6_addr))];
+#endif
+#endif
} control_un;
int on = 1;
#ifdef IP_PKTINFO
struct in_pktinfo pktinfo;
#endif
+#ifdef HAVE_STRUCT_IN6_PKTINFO
+ struct in6_pktinfo pktinfo6;
+#endif
/* Try to enable getting the return address */
#ifdef IP_RECVDSTADDR
- setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
+ if (from->sa_family == AF_INET)
+ setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
#endif
#ifdef IP_PKTINFO
- setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
+ if (from->sa_family == AF_INET)
+ setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
+#endif
+#ifdef HAVE_IPV6
+#ifdef IPV6_RECVPKTINFO
+ if (from->sa_family == AF_INET6)
+ setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+#endif
#endif
-
bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */
msg.msg_control = control_un.control;
- msg.msg_controllen = sizeof(control_un.control);
+ msg.msg_controllen = sizeof(control_un);
msg.msg_flags = 0;
msg.msg_name = from;
@@ -139,8 +173,8 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags,
*fromlen = msg.msg_namelen;
if (myaddr) {
- bzero(myaddr, sizeof(struct sockaddr_in));
- myaddr->sin_family = AF_INET;
+ bzero(myaddr, sizeof(*myaddr));
+ myaddr->sa.sa_family = from->sa_family;
if (msg.msg_controllen < sizeof(struct cmsghdr) ||
(msg.msg_flags & MSG_CTRUNC))
@@ -149,31 +183,61 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags,
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
cmptr = CMSG_NXTHDR(&msg, cmptr)) {
+ if (from->sa_family == AF_INET) {
+ myaddr->sa.sa_family = AF_INET;
#ifdef IP_RECVDSTADDR
- if (cmptr->cmsg_level == IPPROTO_IP &&
- cmptr->cmsg_type == IP_RECVDSTADDR) {
- memcpy(&myaddr->sin_addr, CMSG_DATA(cmptr),
- sizeof(struct in_addr));
- }
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_RECVDSTADDR) {
+ memcpy(&myaddr->si.sin_addr, CMSG_DATA(cmptr),
+ sizeof(struct in_addr));
+ }
#endif
#ifdef IP_PKTINFO
- if (cmptr->cmsg_level == IPPROTO_IP &&
- cmptr->cmsg_type == IP_PKTINFO) {
- memcpy(&pktinfo, CMSG_DATA(cmptr),
- sizeof(struct in_pktinfo));
- memcpy(&myaddr->sin_addr, &pktinfo.ipi_addr,
- sizeof(struct in_addr));
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_PKTINFO) {
+ memcpy(&pktinfo, CMSG_DATA(cmptr),
+ sizeof(struct in_pktinfo));
+ memcpy(&myaddr->si.sin_addr, &pktinfo.ipi_addr,
+ sizeof(struct in_addr));
+ }
+#endif
}
+#ifdef HAVE_IPV6
+ else if (from->sa_family == AF_INET6) {
+ myaddr->sa.sa_family = AF_INET6;
+#ifdef IP6_RECVDSTADDR
+ if (cmptr->cmsg_level == IPPROTO_IPV6 &&
+ cmptr->cmsg_type == IPV6_RECVDSTADDR )
+ memcpy(&myaddr->s6.sin6_addr, CMSG_DATA(cmptr),
+ sizeof(struct in6_addr));
#endif
+#ifdef HAVE_STRUCT_IN6_PKTINFO
+ if (cmptr->cmsg_level == IPPROTO_IPV6 &&
+ (cmptr->cmsg_type == IPV6_RECVPKTINFO ||
+ cmptr->cmsg_type == IPV6_PKTINFO)) {
+ memcpy(&pktinfo6, CMSG_DATA(cmptr),
+ sizeof(struct in6_pktinfo));
+ memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr,
+ sizeof(struct in6_addr));
+ }
+#endif
+ }
+#endif
+ }
+ /* If the address is not a valid local address,
+ * then bind to any address...
+ */
+ if (address_is_local(myaddr) != 1) {
+ if (myaddr->sa.sa_family == AF_INET)
+ ((struct sockaddr_in *)myaddr)->sin_addr.s_addr = INADDR_ANY;
+#ifdef HAVE_IPV6
+ else if (myaddr->sa.sa_family == AF_INET6)
+ memset(&myaddr->s6.sin6_addr, 0, sizeof(struct in6_addr));
+#endif
}
}
-
- /* If the address is not a valid local address, then bind to any address... */
- if (address_is_local(myaddr) != 1)
- myaddr->sin_addr.s_addr = INADDR_ANY;
-
return n;
}
@@ -181,15 +245,13 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags,
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
- struct sockaddr *from, int *fromlen, struct sockaddr_in *myaddr)
+ struct sockaddr *from, int *fromlen, union sock_addr *myaddr)
{
/* There is no way we can get the local address, fudge it */
- bzero(myaddr, sizeof(struct sockaddr_in));
- myaddr->sin_family = AF_INET;
-
- myaddr->sin_port = htons(IPPORT_TFTP);
- bzero(&myaddr->sin_addr, sizeof(myaddr->sin_addr));
+ bzero(myaddr, sizeof(*myaddr));
+ myaddr->sa.sa_family = from->sa_family;
+ sa_set_port(myaddr, htons(IPPORT_TFTP));
return recvfrom(s, buf, len, flags, from, fromlen);
}
diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h
index fda65c0..e3c4055 100644
--- a/tftpd/recvfrom.h
+++ b/tftpd/recvfrom.h
@@ -19,5 +19,5 @@
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
- struct sockaddr *from, socklen_t * fromlen,
- struct sockaddr_in *myaddr);
+ struct sockaddr *from, socklen_t *fromlen,
+ union sock_addr *myaddr);
diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in
index 49cf359..b4d1ac3 100644
--- a/tftpd/tftpd.8.in
+++ b/tftpd/tftpd.8.in
@@ -40,7 +40,7 @@
.I directory...
.SH DESCRIPTION
.B tftpd
-is a server for the IPv4 Trivial File Transfer Protocol. The TFTP
+is a server for the Trivial File Transfer Protocol. The TFTP
protocol is extensively used to support remote booting of diskless
devices. The server is normally started by
.BR inetd ,
@@ -48,6 +48,12 @@ but can also run standalone.
.PP
.SH OPTIONS
.TP
+.B \-4
+Connect with IPv4 only, if IPv6 support was compiled in.
+.TP
+.B \-6
+Connect with IPv6 only, if IPv6 support was compiled in.
+.TP
.B \-l
Run the server in standalone (listen) mode, rather than run from
.BR inetd .
diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c
index b5798a6..38f5e7b 100644
--- a/tftpd/tftpd.c
+++ b/tftpd/tftpd.c
@@ -42,7 +42,6 @@
#include <sys/ioctl.h>
#include <signal.h>
-#include <netdb.h>
#include <ctype.h>
#include <pwd.h>
#include <limits.h>
@@ -65,6 +64,12 @@ int allow_severity = -1; /* Don't log at all */
struct request_info wrap_request;
#endif
+#ifdef HAVE_IPV6
+int ai_fam = AF_UNSPEC;
+#else
+int ai_fam = AF_INET;
+#endif
+
#define TIMEOUT 1000000 /* Default timeout (us) */
#define TRIES 6 /* Number of attempts to send each packet */
#define TIMEOUT_LIMIT ((1 << TRIES)-1)
@@ -82,7 +87,9 @@ char buf[PKTSIZE];
char ackbuf[PKTSIZE];
unsigned int max_blksize = MAX_SEGSIZE;
-struct sockaddr_in from;
+char tmpbuf[INET6_ADDRSTRLEN], *tmp_p;
+
+union sock_addr from;
socklen_t fromlen;
off_t tsize;
int tsize_ok;
@@ -254,10 +261,16 @@ int main(int argc, char **argv)
struct tftphdr *tp;
struct passwd *pw;
struct options *opt;
- struct sockaddr_in myaddr;
- struct sockaddr_in bindaddr;
+ union sock_addr myaddr;
+ struct sockaddr_in bindaddr4;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 bindaddr6;
+#endif
int n;
- int fd = 0;
+ int fd = -1;
+ int fd4 = -1;
+ int fd6 = -1;
+ int fdmax = 0;
int standalone = 0; /* Standalone (listen) mode */
int nodaemon = 0; /* Do not detach process */
char *address = NULL; /* Address to listen to */
@@ -283,8 +296,16 @@ int main(int argc, char **argv)
srand(time(NULL) ^ getpid());
- while ((c = getopt(argc, argv, "cspvVlLa:B:u:U:r:t:T:R:m:")) != -1)
+ while ((c = getopt(argc, argv, "46cspvVlLa:B:u:U:r:t:T:R:m:")) != -1)
switch (c) {
+#ifdef HAVE_IPV6
+ case '4':
+ ai_fam = AF_INET;
+ break;
+ case '6':
+ ai_fam = AF_INET6;
+ break;
+#endif
case 'c':
cancreate = 1;
break;
@@ -417,12 +438,6 @@ int main(int argc, char **argv)
if (spec_umask || !unixperms)
umask(my_umask);
- /* Note: on Cygwin, select() on a nonblocking socket becomes
- a nonblocking select. */
-#ifndef __CYGWIN__
- set_socket_nonblock(fd, 1);
-#endif
-
#ifdef WITH_REGEX
if (rewrite_file)
rewrite_rules = read_remap_rules(rewrite_file);
@@ -430,16 +445,45 @@ int main(int argc, char **argv)
/* If we're running standalone, set up the input port */
if (standalone) {
- fd = socket(PF_INET, SOCK_DGRAM, 0);
-
- memset(&bindaddr, 0, sizeof bindaddr);
- bindaddr.sin_family = AF_INET;
- bindaddr.sin_addr.s_addr = INADDR_ANY;
- bindaddr.sin_port = htons(IPPORT_TFTP);
-
+#ifdef HAVE_IPV6
+ if (ai_fam != AF_INET6) {
+#endif
+ fd4 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd4 < 0) {
+ syslog(LOG_ERR, "cannot open IPv4 socket: %m");
+ exit(EX_OSERR);
+ }
+#ifndef __CYGWIN__
+ set_socket_nonblock(fd4, 1);
+#endif
+ memset(&bindaddr4, 0, sizeof bindaddr4);
+ bindaddr4.sin_family = AF_INET;
+ bindaddr4.sin_addr.s_addr = INADDR_ANY;
+ bindaddr4.sin_port = htons(IPPORT_TFTP);
+#ifdef HAVE_IPV6
+ }
+ if (ai_fam != AF_INET) {
+ fd6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd6 < 0) {
+ if (fd4 < 0) {
+ syslog(LOG_ERR, "cannot open IPv6 socket: %m");
+ exit(EX_OSERR);
+ } else {
+ syslog(LOG_ERR,
+ "cannot open IPv6 socket, disable IPv6: %m");
+ }
+ }
+#ifndef __CYGWIN__
+ set_socket_nonblock(fd6, 1);
+#endif
+ memset(&bindaddr6, 0, sizeof bindaddr6);
+ bindaddr6.sin6_family = AF_INET6;
+ bindaddr6.sin6_port = htons(IPPORT_TFTP);
+ }
+#endif
if (address) {
char *portptr, *eportptr;
- struct hostent *hostent;
+ int err;
struct servent *servent;
unsigned long port;
@@ -447,17 +491,40 @@ int main(int argc, char **argv)
portptr = strrchr(address, ':');
if (portptr)
*portptr++ = '\0';
-
+ else
+ portptr = (char *)"tftp";
if (*address) {
- hostent = gethostbyname(address);
- if (!hostent || hostent->h_addrtype != AF_INET) {
- syslog(LOG_ERR,
- "cannot resolve local bind address: %s",
- address);
- exit(EX_NOINPUT);
+ if (fd4 >= 0) {
+ bindaddr4.sin_family = AF_INET;
+ err = set_sock_addr(address,
+ (union sock_addr *)&bindaddr4, NULL);
+ if (err) {
+ syslog(LOG_ERR,
+ "cannot resolve local IPv4 bind address: %s",
+ address);
+ exit(EX_NOINPUT);
+ }
+ }
+#ifdef HAVE_IPV6
+ if (fd6 >= 0) {
+ err = set_sock_addr(address,
+ (union sock_addr *)&bindaddr6, NULL);
+ if (err) {
+ if (fd4 >= 0) {
+ syslog(LOG_ERR,
+ "cannot resolve local IPv6 bind address: %s"
+ "; using IPv4 only", address);
+ close(fd6);
+ fd6 = -1;
+ } else {
+ syslog(LOG_ERR,
+ "cannot resolve local IPv6 bind address: %s",
+ address);
+ exit(EX_NOINPUT);
+ }
+ }
}
- memcpy(&bindaddr.sin_addr, hostent->h_addr,
- hostent->h_length);
+#endif
} else {
/* Default to using INADDR_ANY */
}
@@ -465,10 +532,20 @@ int main(int argc, char **argv)
if (portptr && *portptr) {
servent = getservbyname(portptr, "udp");
if (servent) {
- bindaddr.sin_port = servent->s_port;
+ if (fd4 >= 0)
+ bindaddr4.sin_port = servent->s_port;
+#ifdef HAVE_IPV6
+ if (fd6 >= 0)
+ bindaddr6.sin6_port = servent->s_port;
+#endif
} else if ((port = strtoul(portptr, &eportptr, 0))
&& !*eportptr) {
- bindaddr.sin_port = htons(port);
+ if (fd4 >= 0)
+ bindaddr4.sin_port = htons(port);
+#ifdef HAVE_IPV6
+ if (fd6 >= 0)
+ bindaddr6.sin6_port = htons(port);
+#endif
} else if (!strcmp(portptr, "tftp")) {
/* It's TFTP, we're OK */
} else {
@@ -479,11 +556,37 @@ int main(int argc, char **argv)
}
}
- if (bind(fd, (struct sockaddr *)&bindaddr, sizeof bindaddr) < 0) {
- syslog(LOG_ERR, "cannot bind to local socket: %m");
- exit(EX_OSERR);
+ if (fd4 >= 0) {
+ if (bind(fd4, (struct sockaddr *)&bindaddr4,
+ sizeof(bindaddr4)) < 0) {
+ syslog(LOG_ERR, "cannot bind to local IPv4 socket: %m");
+ exit(EX_OSERR);
+ }
}
-
+#ifdef HAVE_IPV6
+ if (fd6 >= 0) {
+ int on = 1;
+#if defined(IPV6_V6ONLY)
+ if (setsockopt(fd6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on,
+ sizeof(on))) {
+ syslog(LOG_ERR, "cannot setsockopt IPV6_V6ONLY %m");
+ }
+#endif
+ if (bind(fd6, (struct sockaddr *)&bindaddr6,
+ sizeof(bindaddr6)) < 0) {
+ if (fd4 >= 0) {
+ syslog(LOG_ERR,
+ "cannot bind to local IPv6 socket,"
+ "IPv6 disabled: %m");
+ close(fd6);
+ fd6 = -1;
+ } else {
+ syslog(LOG_ERR, "cannot bind to local IPv6 socket: %m");
+ exit(EX_OSERR);
+ }
+ }
+ }
+#endif
/* Daemonize this process */
/* Note: when running in secure mode (-s), we must not chroot, since
we are already in the proper directory. */
@@ -491,10 +594,21 @@ int main(int argc, char **argv)
syslog(LOG_ERR, "cannot daemonize: %m");
exit(EX_OSERR);
}
+ if (fd6 > fd4)
+ fdmax = fd6;
+ else
+ fdmax = fd4;
} else {
/* 0 is our socket descriptor */
close(1);
close(2);
+ fd = 0;
+ fdmax = 0;
+ /* Note: on Cygwin, select() on a nonblocking socket becomes
+ a nonblocking select. */
+#ifndef __CYGWIN__
+ set_socket_nonblock(fd, 1);
+#endif
}
/* Disable path MTU discovery */
@@ -533,29 +647,61 @@ int main(int argc, char **argv)
}
FD_ZERO(&readset);
- FD_SET(fd, &readset);
+ if (standalone) {
+ if (fd4 >= 0) {
+ FD_SET(fd4, &readset);
+#ifdef __CYGWIN__
+ /* On Cygwin, select() on a nonblocking socket returns
+ immediately, with a rv of 0! */
+ set_socket_nonblock(fd4, 0);
+#endif
+ }
+ if (fd6 >= 0) {
+ FD_SET(fd6, &readset);
+#ifdef __CYGWIN__
+ /* On Cygwin, select() on a nonblocking socket returns
+ immediately, with a rv of 0! */
+ set_socket_nonblock(fd6, 0);
+#endif
+ }
+ } else { /* fd always 0 */
+ fd = 0;
+#ifdef __CYGWIN__
+ /* On Cygwin, select() on a nonblocking socket returns
+ immediately, with a rv of 0! */
+ set_socket_nonblock(fd, 0);
+#endif
+ FD_SET(fd, &readset);
+ }
tv_waittime.tv_sec = waittime;
tv_waittime.tv_usec = 0;
-#ifdef __CYGWIN__
- /* On Cygwin, select() on a nonblocking socket returns immediately,
- with a rv of 0! */
- set_socket_nonblock(fd, 0);
-#endif
/* Never time out if we're in standalone mode */
- rv = select(fd + 1, &readset, NULL, NULL,
+ rv = select(fdmax + 1, &readset, NULL, NULL,
standalone ? NULL : &tv_waittime);
if (rv == -1 && errno == EINTR)
continue; /* Signal caught, reloop */
+
if (rv == -1) {
syslog(LOG_ERR, "select loop: %m");
exit(EX_IOERR);
} else if (rv == 0) {
exit(0); /* Timeout, return to inetd */
}
+
+ if (standalone) {
+ if ((fd4 >= 0) && FD_ISSET(fd4, &readset))
+ fd = fd4;
+ else if ((fd6 >= 0) && FD_ISSET(fd6, &readset))
+ fd = fd6;
+ else /* not in set ??? */
+ continue;
+ }
#ifdef __CYGWIN__
- set_socket_nonblock(fd, 1);
+ /* On Cygwin, select() on a nonblocking socket returns
+ immediately, with a rv of 0! */
+ set_socket_nonblock(fd, 0);
#endif
fromlen = sizeof(from);
@@ -570,18 +716,32 @@ int main(int argc, char **argv)
exit(EX_IOERR);
}
}
-
- if (from.sin_family != AF_INET) {
- syslog(LOG_ERR,
- "received address was not AF_INET, please check your inetd config");
+#ifdef HAVE_IPV6
+ if ((from.sa.sa_family != AF_INET) && (from.sa.sa_family != AF_INET6)) {
+ syslog(LOG_ERR, "received address was not AF_INET/AF_INET6,"
+ " please check your inetd config");
+#else
+ if (from.sa.sa_family != AF_INET) {
+ syslog(LOG_ERR, "received address was not AF_INET,"
+ " please check your inetd config");
+#endif
exit(EX_PROTOCOL);
}
- if (standalone && myaddr.sin_addr.s_addr == INADDR_ANY) {
- /* myrecvfrom() didn't capture the source address; but we might
- have bound to a specific address, if so we should use it */
- memcpy(&myaddr.sin_addr, &bindaddr.sin_addr,
- sizeof bindaddr.sin_addr);
+ if (standalone) {
+ if ((from.sa.sa_family == AF_INET) &&
+ (myaddr.si.sin_addr.s_addr == INADDR_ANY)) {
+ /* myrecvfrom() didn't capture the source address; but we might
+ have bound to a specific address, if so we should use it */
+ memcpy(SOCKADDR_P(&myaddr), &bindaddr4.sin_addr,
+ sizeof(bindaddr4.sin_addr));
+#ifdef HAVE_IPV6
+ } else if ((from.sa.sa_family == AF_INET6) &&
+ IN6_IS_ADDR_UNSPECIFIED(SOCKADDR_P(&myaddr))) {
+ memcpy(SOCKADDR_P(&myaddr), &bindaddr6.sin6_addr,
+ sizeof(bindaddr6.sin6_addr));
+#endif
+ }
}
/*
@@ -609,14 +769,19 @@ int main(int argc, char **argv)
RQ_FILE, fd,
RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0);
sock_methods(&wrap_request);
+
+ tmp_p = (char *)inet_ntop(myaddr.sa.sa_family, SOCKADDR_P(&myaddr),
+ tmpbuf, INET6_ADDRSTRLEN);
+ if (!tmp_p) {
+ tmp_p = tmpbuf;
+ strcpy(tmpbuf, "???");
+ }
if (hosts_access(&wrap_request) == 0) {
if (deny_severity != -1)
- syslog(deny_severity, "connection refused from %s",
- inet_ntoa(from.sin_addr));
+ syslog(deny_severity, "connection refused from %s", tmp_p);
exit(EX_NOPERM); /* Access denied */
} else if (allow_severity != -1) {
- syslog(allow_severity, "connect from %s",
- inet_ntoa(from.sin_addr));
+ syslog(allow_severity, "connect from %s", tmp_p);
}
#endif
@@ -626,7 +791,7 @@ int main(int argc, char **argv)
/* Get a socket. This has to be done before the chroot(), since
some systems require access to /dev to create a socket. */
- peer = socket(AF_INET, SOCK_DGRAM, 0);
+ peer = socket(myaddr.sa.sa_family, SOCK_DGRAM, 0);
if (peer < 0) {
syslog(LOG_ERR, "socket: %m");
exit(EX_IOERR);
@@ -677,16 +842,13 @@ int main(int argc, char **argv)
exit(EX_OSERR);
}
- /* Other basic setup */
- from.sin_family = AF_INET;
-
/* Process the request... */
if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) {
syslog(LOG_ERR, "bind: %m");
exit(EX_IOERR);
}
- if (connect(peer, (struct sockaddr *)&from, sizeof from) < 0) {
+ if (connect(peer, &from.sa, SOCKLEN(&from)) < 0) {
syslog(LOG_ERR, "connect: %m");
exit(EX_IOERR);
}
@@ -776,16 +938,22 @@ int tftp(struct tftphdr *tp, int size)
exit(0);
}
if (verbosity >= 1) {
+ tmp_p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from),
+ tmpbuf, INET6_ADDRSTRLEN);
+ if (!tmp_p) {
+ tmp_p = tmpbuf;
+ strcpy(tmpbuf, "???");
+ }
if (filename == origfilename
|| !strcmp(filename, origfilename))
syslog(LOG_NOTICE, "%s from %s filename %s\n",
tp_opcode == WRQ ? "WRQ" : "RRQ",
- inet_ntoa(from.sin_addr), filename);
+ tmp_p, filename);
else
syslog(LOG_NOTICE,
"%s from %s filename %s remapped to %s\n",
tp_opcode == WRQ ? "WRQ" : "RRQ",
- inet_ntoa(from.sin_addr), origfilename,
+ tmp_p, origfilename,
filename);
}
ecode =
@@ -998,20 +1166,41 @@ int rewrite_macros(char macro, char *output);
int rewrite_macros(char macro, char *output)
{
- char *p;
+ char *p, tb[INET6_ADDRSTRLEN];
+ int l=0;
switch (macro) {
case 'i':
- p = inet_ntoa(from.sin_addr);
- if (output)
+ p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from),
+ tb, INET6_ADDRSTRLEN);
+ if (output && p)
strcpy(output, p);
- return strlen(p);
+ if (!p)
+ return 0;
+ else
+ return strlen(p);
case 'x':
- if (output)
- sprintf(output, "%08lX",
- (unsigned long)ntohl(from.sin_addr.s_addr));
- return 8;
+ if (output) {
+ if (from.sa.sa_family == AF_INET) {
+ sprintf(output, "%08lX",
+ (unsigned long)ntohl(from.si.sin_addr.s_addr));
+ l = 8;
+#ifdef HAVE_IPV6
+ } else {
+ unsigned char *c = (unsigned char *)SOCKADDR_P(&from);
+ p = tb;
+ for (l = 0; l < 16; l++) {
+ sprintf(p, "%02X", *c);
+ c++;
+ p += 2;
+ }
+ strcpy(output, tb);
+ l = strlen(tb);
+#endif
+ }
+ }
+ return l;
default:
return -1;
@@ -1398,8 +1587,14 @@ static void nak(int error, const char *msg)
length += 4; /* Add space for header */
if (verbosity >= 2) {
+ tmp_p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from),
+ tmpbuf, INET6_ADDRSTRLEN);
+ if (!tmp_p) {
+ tmp_p = tmpbuf;
+ strcpy(tmpbuf, "???");
+ }
syslog(LOG_INFO, "sending NAK (%d, %s) to %s",
- error, tp->th_msg, inet_ntoa(from.sin_addr));
+ error, tp->th_msg, tmp_p);
}
if (send(peer, buf, length, 0) != length)