mirror of
https://kernel.googlesource.com/pub/scm/network/tftp/tftp-hpa
synced 2025-05-10 00:35:47 +03:00
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:
parent
7fe0fb941c
commit
28f22b6591
9 changed files with 574 additions and 185 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
135
tftp/main.c
135
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
28
tftp/tftp.c
28
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");
|
||||
}
|
||||
|
||||
|
|
140
tftpd/recvfrom.c
140
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;
|
||||
|
@ -106,24 +122,42 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
|||
CMSG_SPACE(sizeof(struct in_pktinfo))];
|
||||
#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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 .
|
||||
|
|
339
tftpd/tftpd.c
339
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);
|
||||
}
|
||||
}
|
||||
memcpy(&bindaddr.sin_addr, hostent->h_addr,
|
||||
hostent->h_length);
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
||||
#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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue