forked from mirrors/tftp-hpa-google
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