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 */
|
{ /* socket to flush */
|
||||||
int pktcount = 0;
|
int pktcount = 0;
|
||||||
char rbuf[PKTSIZE];
|
char rbuf[PKTSIZE];
|
||||||
struct sockaddr_in from;
|
union sock_addr from;
|
||||||
socklen_t fromlen;
|
socklen_t fromlen;
|
||||||
fd_set socketset;
|
fd_set socketset;
|
||||||
struct timeval notime;
|
struct timeval notime;
|
||||||
|
@ -253,15 +253,15 @@ int synchnet(int f)
|
||||||
|
|
||||||
/* Otherwise drain the packet */
|
/* Otherwise drain the packet */
|
||||||
pktcount++;
|
pktcount++;
|
||||||
fromlen = sizeof from;
|
fromlen = sizeof(from);
|
||||||
(void)recvfrom(f, rbuf, sizeof(rbuf), 0,
|
(void)recvfrom(f, rbuf, sizeof(rbuf), 0,
|
||||||
(struct sockaddr *)&from, &fromlen);
|
&from.sa, &fromlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pktcount; /* Return packets drained */
|
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_from,
|
||||||
unsigned int port_range_to)
|
unsigned int port_range_to)
|
||||||
{
|
{
|
||||||
|
@ -279,9 +279,8 @@ int pick_port_bind(int sockfd, struct sockaddr_in *myaddr,
|
||||||
port = firstport;
|
port = firstport;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
myaddr->sin_port = htons(port);
|
sa_set_port(myaddr, htons(port));
|
||||||
|
if (bind(sockfd, &myaddr->sa, SOCKLEN(myaddr)) < 0) {
|
||||||
if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) {
|
|
||||||
/* Some versions of Linux return EINVAL instead of EADDRINUSE */
|
/* Some versions of Linux return EINVAL instead of EADDRINUSE */
|
||||||
if (!(port_range && (errno == EINVAL || errno == EADDRINUSE)))
|
if (!(port_range && (errno == EINVAL || errno == EADDRINUSE)))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -299,3 +298,34 @@ int pick_port_bind(int sockfd, struct sockaddr_in *myaddr,
|
||||||
|
|
||||||
return -1;
|
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"
|
#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;
|
||||||
|
|
||||||
struct tftphdr *r_init(void);
|
struct tftphdr *r_init(void);
|
||||||
|
@ -55,7 +107,7 @@ int writeit(FILE *, struct tftphdr **, int, int);
|
||||||
extern int segsize;
|
extern int segsize;
|
||||||
#define MAX_SEGSIZE 65464
|
#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);
|
unsigned int from, unsigned int to);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
135
tftp/main.c
135
tftp/main.c
|
@ -40,7 +40,6 @@
|
||||||
*/
|
*/
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <netdb.h>
|
|
||||||
#ifdef WITH_READLINE
|
#ifdef WITH_READLINE
|
||||||
#include <readline/readline.h>
|
#include <readline/readline.h>
|
||||||
#ifdef HAVE_READLINE_HISTORY_H
|
#ifdef HAVE_READLINE_HISTORY_H
|
||||||
|
@ -72,8 +71,16 @@ static const struct modes modes[] = {
|
||||||
#define MODE_NETASCII (&modes[0])
|
#define MODE_NETASCII (&modes[0])
|
||||||
#define MODE_DEFAULT MODE_NETASCII
|
#define MODE_DEFAULT MODE_NETASCII
|
||||||
|
|
||||||
struct sockaddr_in peeraddr;
|
#ifdef HAVE_IPV6
|
||||||
int f;
|
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;
|
u_short port;
|
||||||
int trace;
|
int trace;
|
||||||
int verbose;
|
int verbose;
|
||||||
|
@ -184,14 +191,18 @@ const char *program;
|
||||||
static inline void usage(int errcode)
|
static inline void usage(int errcode)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
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",
|
"Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n",
|
||||||
|
#endif
|
||||||
program);
|
program);
|
||||||
exit(errcode);
|
exit(errcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct sockaddr_in s_in;
|
union sock_addr sa;
|
||||||
int arg;
|
int arg;
|
||||||
static int pargc, peerargc;
|
static int pargc, peerargc;
|
||||||
static int iscmd = 0;
|
static int iscmd = 0;
|
||||||
|
@ -210,6 +221,14 @@ int main(int argc, char *argv[])
|
||||||
if (argv[arg][0] == '-') {
|
if (argv[arg][0] == '-') {
|
||||||
for (optx = &argv[arg][1]; *optx; optx++) {
|
for (optx = &argv[arg][1]; *optx; optx++) {
|
||||||
switch (*optx) {
|
switch (*optx) {
|
||||||
|
#ifdef HAVE_IPV6
|
||||||
|
case '4':
|
||||||
|
ai_fam = AF_INET;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
ai_fam = AF_INET6;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose = 1;
|
verbose = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -268,6 +287,8 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ai_fam_sock = ai_fam;
|
||||||
|
|
||||||
pargv = argv + arg;
|
pargv = argv + arg;
|
||||||
pargc = argc - arg;
|
pargc = argc - arg;
|
||||||
|
|
||||||
|
@ -283,18 +304,7 @@ int main(int argc, char *argv[])
|
||||||
sp->s_port = htons(IPPORT_TFTP);
|
sp->s_port = htons(IPPORT_TFTP);
|
||||||
sp->s_proto = (char *)"udp";
|
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);
|
bsd_signal(SIGINT, intr);
|
||||||
|
|
||||||
if (peerargc) {
|
if (peerargc) {
|
||||||
|
@ -304,6 +314,21 @@ int main(int argc, char *argv[])
|
||||||
setpeer(peerargc, peerargv);
|
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) {
|
if (iscmd && pargc) {
|
||||||
/* -c specified; execute command and exit */
|
/* -c specified; execute command and exit */
|
||||||
struct cmd *c;
|
struct cmd *c;
|
||||||
|
@ -375,7 +400,7 @@ static void getmoreargs(const char *partial, const char *mprompt)
|
||||||
|
|
||||||
void setpeer(int argc, char *argv[])
|
void setpeer(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct hostent *host;
|
int err;
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
getmoreargs("connect ", "(to) ");
|
getmoreargs("connect ", "(to) ");
|
||||||
|
@ -388,16 +413,34 @@ void setpeer(int argc, char *argv[])
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
host = gethostbyname(argv[1]);
|
peeraddr.sa.sa_family = ai_fam;
|
||||||
if (host == 0) {
|
err = set_sock_addr(argv[1], &peeraddr, &hostname);
|
||||||
|
if (err) {
|
||||||
connected = 0;
|
connected = 0;
|
||||||
printf("%s: unknown host\n", argv[1]);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
peeraddr.sin_family = host->h_addrtype;
|
ai_fam = peeraddr.sa.sa_family;
|
||||||
bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
|
if (f == -1) { /* socket not open */
|
||||||
hostname = xstrdup(host->h_name);
|
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;
|
port = sp->s_port;
|
||||||
if (argc == 3) {
|
if (argc == 3) {
|
||||||
struct servent *usp;
|
struct servent *usp;
|
||||||
|
@ -418,9 +461,13 @@ void setpeer(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) {
|
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",
|
printf("Connected to %s (%s), port %u\n",
|
||||||
hostname, inet_ntoa(peeraddr.sin_addr),
|
hostname, tp, (unsigned int)ntohs(port));
|
||||||
(unsigned int)ntohs(port));
|
|
||||||
}
|
}
|
||||||
connected = 1;
|
connected = 1;
|
||||||
}
|
}
|
||||||
|
@ -484,7 +531,7 @@ static void settftpmode(const struct modes *newmode)
|
||||||
void put(int argc, char *argv[])
|
void put(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
int n;
|
int n, err;
|
||||||
char *cp, *targ;
|
char *cp, *targ;
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
|
@ -499,8 +546,6 @@ void put(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
targ = argv[argc - 1];
|
targ = argv[argc - 1];
|
||||||
if (!literal && strchr(argv[argc - 1], ':')) {
|
if (!literal && strchr(argv[argc - 1], ':')) {
|
||||||
struct hostent *hp;
|
|
||||||
|
|
||||||
for (n = 1; n < argc - 1; n++)
|
for (n = 1; n < argc - 1; n++)
|
||||||
if (strchr(argv[n], ':')) {
|
if (strchr(argv[n], ':')) {
|
||||||
putusage(argv[0]);
|
putusage(argv[0]);
|
||||||
|
@ -509,16 +554,14 @@ void put(int argc, char *argv[])
|
||||||
cp = argv[argc - 1];
|
cp = argv[argc - 1];
|
||||||
targ = strchr(cp, ':');
|
targ = strchr(cp, ':');
|
||||||
*targ++ = 0;
|
*targ++ = 0;
|
||||||
hp = gethostbyname(cp);
|
peeraddr.sa.sa_family = ai_fam;
|
||||||
if (hp == NULL) {
|
err = set_sock_addr(cp, &peeraddr,&hostname);
|
||||||
fprintf(stderr, "tftp: %s: ", cp);
|
if (err) {
|
||||||
herror((char *)NULL);
|
connected = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bcopy(hp->h_addr, &peeraddr.sin_addr, hp->h_length);
|
ai_fam = peeraddr.sa.sa_family;
|
||||||
peeraddr.sin_family = hp->h_addrtype;
|
|
||||||
connected = 1;
|
connected = 1;
|
||||||
hostname = xstrdup(hp->h_name);
|
|
||||||
}
|
}
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
printf("No target machine specified.\n");
|
printf("No target machine specified.\n");
|
||||||
|
@ -535,7 +578,7 @@ void put(int argc, char *argv[])
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("putting %s to %s:%s [%s]\n",
|
printf("putting %s to %s:%s [%s]\n",
|
||||||
cp, hostname, targ, mode->m_mode);
|
cp, hostname, targ, mode->m_mode);
|
||||||
peeraddr.sin_port = port;
|
sa_set_port(&peeraddr, port);
|
||||||
tftp_sendfile(fd, targ, mode->m_mode);
|
tftp_sendfile(fd, targ, mode->m_mode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -554,7 +597,7 @@ void put(int argc, char *argv[])
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("putting %s to %s:%s [%s]\n",
|
printf("putting %s to %s:%s [%s]\n",
|
||||||
argv[n], hostname, targ, mode->m_mode);
|
argv[n], hostname, targ, mode->m_mode);
|
||||||
peeraddr.sin_port = port;
|
sa_set_port(&peeraddr, port);
|
||||||
tftp_sendfile(fd, targ, mode->m_mode);
|
tftp_sendfile(fd, targ, mode->m_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -597,19 +640,15 @@ void get(int argc, char *argv[])
|
||||||
if (literal || src == NULL)
|
if (literal || src == NULL)
|
||||||
src = argv[n];
|
src = argv[n];
|
||||||
else {
|
else {
|
||||||
struct hostent *hp;
|
int err;
|
||||||
|
|
||||||
*src++ = 0;
|
*src++ = 0;
|
||||||
hp = gethostbyname(argv[n]);
|
peeraddr.sa.sa_family = ai_fam;
|
||||||
if (hp == NULL) {
|
err = set_sock_addr(argv[n], &peeraddr, &hostname);
|
||||||
fprintf(stderr, "tftp: %s: ", argv[n]);
|
if (err)
|
||||||
herror((char *)NULL);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
ai_fam = peeraddr.sa.sa_family;
|
||||||
bcopy(hp->h_addr, (caddr_t) & peeraddr.sin_addr, hp->h_length);
|
|
||||||
peeraddr.sin_family = hp->h_addrtype;
|
|
||||||
connected = 1;
|
connected = 1;
|
||||||
hostname = xstrdup(hp->h_name);
|
|
||||||
}
|
}
|
||||||
if (argc < 4) {
|
if (argc < 4) {
|
||||||
cp = argc == 3 ? argv[2] : tail(src);
|
cp = argc == 3 ? argv[2] : tail(src);
|
||||||
|
@ -623,7 +662,7 @@ void get(int argc, char *argv[])
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("getting from %s:%s to %s [%s]\n",
|
printf("getting from %s:%s to %s [%s]\n",
|
||||||
hostname, src, cp, mode->m_mode);
|
hostname, src, cp, mode->m_mode);
|
||||||
peeraddr.sin_port = port;
|
sa_set_port(&peeraddr, port);
|
||||||
tftp_recvfile(fd, src, mode->m_mode);
|
tftp_recvfile(fd, src, mode->m_mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -638,7 +677,7 @@ void get(int argc, char *argv[])
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("getting from %s:%s to %s [%s]\n",
|
printf("getting from %s:%s to %s [%s]\n",
|
||||||
hostname, src, cp, mode->m_mode);
|
hostname, src, cp, mode->m_mode);
|
||||||
peeraddr.sin_port = port;
|
sa_set_port(&peeraddr, port);
|
||||||
tftp_recvfile(fd, src, mode->m_mode);
|
tftp_recvfile(fd, src, mode->m_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
.br
|
.br
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.B tftp
|
.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
|
used to transfer files to and from remote machines, including some
|
||||||
very minimalistic, usually embedded, systems. The remote
|
very minimalistic, usually embedded, systems. The remote
|
||||||
.I host
|
.I host
|
||||||
|
@ -55,6 +55,11 @@ as the default host for future transfers (see the
|
||||||
command below.)
|
command below.)
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
|
.B \-4
|
||||||
|
Connect with IPv4 only, if IPv6 support was compiled in.
|
||||||
|
.TP
|
||||||
|
.B \-6
|
||||||
|
.TP
|
||||||
\fB\-c\fP \fIcommand\fP
|
\fB\-c\fP \fIcommand\fP
|
||||||
Execute \fIcommand\fP as if it had been entered on the tftp prompt.
|
Execute \fIcommand\fP as if it had been entered on the tftp prompt.
|
||||||
Must be specified last on the command line.
|
Must be specified last on the command line.
|
||||||
|
|
28
tftp/tftp.c
28
tftp/tftp.c
|
@ -38,8 +38,8 @@
|
||||||
*/
|
*/
|
||||||
#include "extern.h"
|
#include "extern.h"
|
||||||
|
|
||||||
extern struct sockaddr_in peeraddr; /* filled in by main */
|
extern union sock_addr peeraddr; /* filled in by main */
|
||||||
extern int f; /* the opened socket */
|
extern int f; /* the opened socket */
|
||||||
extern int trace;
|
extern int trace;
|
||||||
extern int verbose;
|
extern int verbose;
|
||||||
extern int rexmtval;
|
extern int rexmtval;
|
||||||
|
@ -71,7 +71,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode)
|
||||||
volatile u_short block;
|
volatile u_short block;
|
||||||
volatile int size, convert;
|
volatile int size, convert;
|
||||||
volatile off_t amount;
|
volatile off_t amount;
|
||||||
struct sockaddr_in from;
|
union sock_addr from;
|
||||||
socklen_t fromlen;
|
socklen_t fromlen;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
u_short ap_opcode, ap_block;
|
u_short ap_opcode, ap_block;
|
||||||
|
@ -105,7 +105,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode)
|
||||||
if (trace)
|
if (trace)
|
||||||
tpacket("sent", dp, size + 4);
|
tpacket("sent", dp, size + 4);
|
||||||
n = sendto(f, dp, size + 4, 0,
|
n = sendto(f, dp, size + 4, 0,
|
||||||
(struct sockaddr *)&peeraddr, sizeof(peeraddr));
|
&peeraddr.sa, SOCKLEN(&peeraddr));
|
||||||
if (n != size + 4) {
|
if (n != size + 4) {
|
||||||
perror("tftp: sendto");
|
perror("tftp: sendto");
|
||||||
goto abort;
|
goto abort;
|
||||||
|
@ -116,14 +116,14 @@ void tftp_sendfile(int fd, const char *name, const char *mode)
|
||||||
do {
|
do {
|
||||||
fromlen = sizeof(from);
|
fromlen = sizeof(from);
|
||||||
n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
|
n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
|
||||||
(struct sockaddr *)&from, &fromlen);
|
&from.sa, &fromlen);
|
||||||
} while (n <= 0);
|
} while (n <= 0);
|
||||||
alarm(0);
|
alarm(0);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
perror("tftp: recvfrom");
|
perror("tftp: recvfrom");
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
peeraddr.sin_port = from.sin_port; /* added */
|
sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */
|
||||||
if (trace)
|
if (trace)
|
||||||
tpacket("received", ap, n);
|
tpacket("received", ap, n);
|
||||||
/* should verify packet came from server */
|
/* 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 u_short block;
|
||||||
volatile int size, firsttrip;
|
volatile int size, firsttrip;
|
||||||
volatile unsigned long amount;
|
volatile unsigned long amount;
|
||||||
struct sockaddr_in from;
|
union sock_addr from;
|
||||||
socklen_t fromlen;
|
socklen_t fromlen;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
volatile int convert; /* true if converting crlf -> lf */
|
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:
|
send_ack:
|
||||||
if (trace)
|
if (trace)
|
||||||
tpacket("sent", ap, size);
|
tpacket("sent", ap, size);
|
||||||
if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
|
if (sendto(f, ackbuf, size, 0, &peeraddr.sa,
|
||||||
sizeof(peeraddr)) != size) {
|
SOCKLEN(&peeraddr)) != size) {
|
||||||
alarm(0);
|
alarm(0);
|
||||||
perror("tftp: sendto");
|
perror("tftp: sendto");
|
||||||
goto abort;
|
goto abort;
|
||||||
|
@ -219,14 +219,14 @@ void tftp_recvfile(int fd, const char *name, const char *mode)
|
||||||
do {
|
do {
|
||||||
fromlen = sizeof(from);
|
fromlen = sizeof(from);
|
||||||
n = recvfrom(f, dp, PKTSIZE, 0,
|
n = recvfrom(f, dp, PKTSIZE, 0,
|
||||||
(struct sockaddr *)&from, &fromlen);
|
&from.sa, &fromlen);
|
||||||
} while (n <= 0);
|
} while (n <= 0);
|
||||||
alarm(0);
|
alarm(0);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
perror("tftp: recvfrom");
|
perror("tftp: recvfrom");
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
peeraddr.sin_port = from.sin_port; /* added */
|
sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */
|
||||||
if (trace)
|
if (trace)
|
||||||
tpacket("received", dp, n);
|
tpacket("received", dp, n);
|
||||||
/* should verify client address */
|
/* 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_opcode = htons((u_short) ACK); /* has seen err msg */
|
||||||
ap->th_block = htons((u_short) block);
|
ap->th_block = htons((u_short) block);
|
||||||
(void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
|
(void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
|
||||||
sizeof(peeraddr));
|
SOCKLEN(&peeraddr));
|
||||||
write_behind(file, convert); /* flush last buffer */
|
write_behind(file, convert); /* flush last buffer */
|
||||||
fclose(file);
|
fclose(file);
|
||||||
stopclock();
|
stopclock();
|
||||||
|
@ -341,8 +341,8 @@ static void nak(int error, const char *msg)
|
||||||
|
|
||||||
if (trace)
|
if (trace)
|
||||||
tpacket("sent", tp, length);
|
tpacket("sent", tp, length);
|
||||||
if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
|
if (sendto(f, ackbuf, length, 0, &peeraddr.sa,
|
||||||
sizeof(peeraddr)) != length)
|
SOCKLEN(&peeraddr)) != length)
|
||||||
perror("nak");
|
perror("nak");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
140
tftpd/recvfrom.c
140
tftpd/recvfrom.c
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h" /* Must be included first! */
|
#include "config.h" /* Must be included first! */
|
||||||
#include "recvfrom.h"
|
|
||||||
#include "common/tftpsubs.h"
|
#include "common/tftpsubs.h"
|
||||||
|
#include "recvfrom.h"
|
||||||
#ifdef HAVE_MACHINE_PARAM_H
|
#ifdef HAVE_MACHINE_PARAM_H
|
||||||
#include <machine/param.h> /* Needed on some versions of FreeBSD */
|
#include <machine/param.h> /* Needed on some versions of FreeBSD */
|
||||||
#endif
|
#endif
|
||||||
|
@ -55,31 +55,47 @@ struct in_pktinfo {
|
||||||
* end up having the same local and remote address when trying to
|
* end up having the same local and remote address when trying to
|
||||||
* bind to it.
|
* 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 sockfd = -1;
|
||||||
int e;
|
int e;
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
socklen_t addrlen;
|
socklen_t addrlen;
|
||||||
|
|
||||||
/* Multicast or universal broadcast address? */
|
/* 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;
|
return 0;
|
||||||
|
|
||||||
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
|
sockfd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
|
||||||
if (sockfd < 0)
|
if (sockfd < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (connect(sockfd, (const struct sockaddr *)addr, sizeof *addr))
|
if (connect(sockfd, &addr->sa, SOCKLEN(addr)))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
addrlen = sizeof sin;
|
addrlen = SOCKLEN(addr);
|
||||||
if (getsockname(sockfd, (struct sockaddr *)&sin, &addrlen))
|
if (getsockname(sockfd, (struct sockaddr *)&sa, &addrlen))
|
||||||
goto err;
|
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:
|
err:
|
||||||
e = errno;
|
e = errno;
|
||||||
|
|
||||||
|
@ -93,7 +109,7 @@ static int address_is_local(const struct sockaddr_in *addr)
|
||||||
int
|
int
|
||||||
myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
||||||
struct sockaddr *from, socklen_t * fromlen,
|
struct sockaddr *from, socklen_t * fromlen,
|
||||||
struct sockaddr_in *myaddr)
|
union sock_addr *myaddr)
|
||||||
{
|
{
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
|
@ -106,24 +122,42 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
||||||
CMSG_SPACE(sizeof(struct in_pktinfo))];
|
CMSG_SPACE(sizeof(struct in_pktinfo))];
|
||||||
#else
|
#else
|
||||||
char control[CMSG_SPACE(sizeof(struct in_addr))];
|
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
|
#endif
|
||||||
} control_un;
|
} control_un;
|
||||||
int on = 1;
|
int on = 1;
|
||||||
#ifdef IP_PKTINFO
|
#ifdef IP_PKTINFO
|
||||||
struct in_pktinfo pktinfo;
|
struct in_pktinfo pktinfo;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_STRUCT_IN6_PKTINFO
|
||||||
|
struct in6_pktinfo pktinfo6;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Try to enable getting the return address */
|
/* Try to enable getting the return address */
|
||||||
#ifdef IP_RECVDSTADDR
|
#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
|
#endif
|
||||||
#ifdef IP_PKTINFO
|
#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
|
#endif
|
||||||
|
|
||||||
bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */
|
bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */
|
||||||
msg.msg_control = control_un.control;
|
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_flags = 0;
|
||||||
|
|
||||||
msg.msg_name = from;
|
msg.msg_name = from;
|
||||||
|
@ -139,8 +173,8 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
||||||
*fromlen = msg.msg_namelen;
|
*fromlen = msg.msg_namelen;
|
||||||
|
|
||||||
if (myaddr) {
|
if (myaddr) {
|
||||||
bzero(myaddr, sizeof(struct sockaddr_in));
|
bzero(myaddr, sizeof(*myaddr));
|
||||||
myaddr->sin_family = AF_INET;
|
myaddr->sa.sa_family = from->sa_family;
|
||||||
|
|
||||||
if (msg.msg_controllen < sizeof(struct cmsghdr) ||
|
if (msg.msg_controllen < sizeof(struct cmsghdr) ||
|
||||||
(msg.msg_flags & MSG_CTRUNC))
|
(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;
|
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
|
||||||
cmptr = CMSG_NXTHDR(&msg, cmptr)) {
|
cmptr = CMSG_NXTHDR(&msg, cmptr)) {
|
||||||
|
|
||||||
|
if (from->sa_family == AF_INET) {
|
||||||
|
myaddr->sa.sa_family = AF_INET;
|
||||||
#ifdef IP_RECVDSTADDR
|
#ifdef IP_RECVDSTADDR
|
||||||
if (cmptr->cmsg_level == IPPROTO_IP &&
|
if (cmptr->cmsg_level == IPPROTO_IP &&
|
||||||
cmptr->cmsg_type == IP_RECVDSTADDR) {
|
cmptr->cmsg_type == IP_RECVDSTADDR) {
|
||||||
memcpy(&myaddr->sin_addr, CMSG_DATA(cmptr),
|
memcpy(&myaddr->si.sin_addr, CMSG_DATA(cmptr),
|
||||||
sizeof(struct in_addr));
|
sizeof(struct in_addr));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef IP_PKTINFO
|
#ifdef IP_PKTINFO
|
||||||
if (cmptr->cmsg_level == IPPROTO_IP &&
|
if (cmptr->cmsg_level == IPPROTO_IP &&
|
||||||
cmptr->cmsg_type == IP_PKTINFO) {
|
cmptr->cmsg_type == IP_PKTINFO) {
|
||||||
memcpy(&pktinfo, CMSG_DATA(cmptr),
|
memcpy(&pktinfo, CMSG_DATA(cmptr),
|
||||||
sizeof(struct in_pktinfo));
|
sizeof(struct in_pktinfo));
|
||||||
memcpy(&myaddr->sin_addr, &pktinfo.ipi_addr,
|
memcpy(&myaddr->si.sin_addr, &pktinfo.ipi_addr,
|
||||||
sizeof(struct in_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
|
#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;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,15 +245,13 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
||||||
|
|
||||||
int
|
int
|
||||||
myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
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 */
|
/* There is no way we can get the local address, fudge it */
|
||||||
|
|
||||||
bzero(myaddr, sizeof(struct sockaddr_in));
|
bzero(myaddr, sizeof(*myaddr));
|
||||||
myaddr->sin_family = AF_INET;
|
myaddr->sa.sa_family = from->sa_family;
|
||||||
|
sa_set_port(myaddr, htons(IPPORT_TFTP));
|
||||||
myaddr->sin_port = htons(IPPORT_TFTP);
|
|
||||||
bzero(&myaddr->sin_addr, sizeof(myaddr->sin_addr));
|
|
||||||
|
|
||||||
return recvfrom(s, buf, len, flags, from, fromlen);
|
return recvfrom(s, buf, len, flags, from, fromlen);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,5 +19,5 @@
|
||||||
|
|
||||||
int
|
int
|
||||||
myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
myrecvfrom(int s, void *buf, int len, unsigned int flags,
|
||||||
struct sockaddr *from, socklen_t * fromlen,
|
struct sockaddr *from, socklen_t *fromlen,
|
||||||
struct sockaddr_in *myaddr);
|
union sock_addr *myaddr);
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
.I directory...
|
.I directory...
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.B tftpd
|
.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
|
protocol is extensively used to support remote booting of diskless
|
||||||
devices. The server is normally started by
|
devices. The server is normally started by
|
||||||
.BR inetd ,
|
.BR inetd ,
|
||||||
|
@ -48,6 +48,12 @@ but can also run standalone.
|
||||||
.PP
|
.PP
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.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
|
.B \-l
|
||||||
Run the server in standalone (listen) mode, rather than run from
|
Run the server in standalone (listen) mode, rather than run from
|
||||||
.BR inetd .
|
.BR inetd .
|
||||||
|
|
339
tftpd/tftpd.c
339
tftpd/tftpd.c
|
@ -42,7 +42,6 @@
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <netdb.h>
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
@ -65,6 +64,12 @@ int allow_severity = -1; /* Don't log at all */
|
||||||
struct request_info wrap_request;
|
struct request_info wrap_request;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_IPV6
|
||||||
|
int ai_fam = AF_UNSPEC;
|
||||||
|
#else
|
||||||
|
int ai_fam = AF_INET;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define TIMEOUT 1000000 /* Default timeout (us) */
|
#define TIMEOUT 1000000 /* Default timeout (us) */
|
||||||
#define TRIES 6 /* Number of attempts to send each packet */
|
#define TRIES 6 /* Number of attempts to send each packet */
|
||||||
#define TIMEOUT_LIMIT ((1 << TRIES)-1)
|
#define TIMEOUT_LIMIT ((1 << TRIES)-1)
|
||||||
|
@ -82,7 +87,9 @@ char buf[PKTSIZE];
|
||||||
char ackbuf[PKTSIZE];
|
char ackbuf[PKTSIZE];
|
||||||
unsigned int max_blksize = MAX_SEGSIZE;
|
unsigned int max_blksize = MAX_SEGSIZE;
|
||||||
|
|
||||||
struct sockaddr_in from;
|
char tmpbuf[INET6_ADDRSTRLEN], *tmp_p;
|
||||||
|
|
||||||
|
union sock_addr from;
|
||||||
socklen_t fromlen;
|
socklen_t fromlen;
|
||||||
off_t tsize;
|
off_t tsize;
|
||||||
int tsize_ok;
|
int tsize_ok;
|
||||||
|
@ -254,10 +261,16 @@ int main(int argc, char **argv)
|
||||||
struct tftphdr *tp;
|
struct tftphdr *tp;
|
||||||
struct passwd *pw;
|
struct passwd *pw;
|
||||||
struct options *opt;
|
struct options *opt;
|
||||||
struct sockaddr_in myaddr;
|
union sock_addr myaddr;
|
||||||
struct sockaddr_in bindaddr;
|
struct sockaddr_in bindaddr4;
|
||||||
|
#ifdef HAVE_IPV6
|
||||||
|
struct sockaddr_in6 bindaddr6;
|
||||||
|
#endif
|
||||||
int n;
|
int n;
|
||||||
int fd = 0;
|
int fd = -1;
|
||||||
|
int fd4 = -1;
|
||||||
|
int fd6 = -1;
|
||||||
|
int fdmax = 0;
|
||||||
int standalone = 0; /* Standalone (listen) mode */
|
int standalone = 0; /* Standalone (listen) mode */
|
||||||
int nodaemon = 0; /* Do not detach process */
|
int nodaemon = 0; /* Do not detach process */
|
||||||
char *address = NULL; /* Address to listen to */
|
char *address = NULL; /* Address to listen to */
|
||||||
|
@ -283,8 +296,16 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
srand(time(NULL) ^ getpid());
|
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) {
|
switch (c) {
|
||||||
|
#ifdef HAVE_IPV6
|
||||||
|
case '4':
|
||||||
|
ai_fam = AF_INET;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
ai_fam = AF_INET6;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case 'c':
|
case 'c':
|
||||||
cancreate = 1;
|
cancreate = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -417,12 +438,6 @@ int main(int argc, char **argv)
|
||||||
if (spec_umask || !unixperms)
|
if (spec_umask || !unixperms)
|
||||||
umask(my_umask);
|
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
|
#ifdef WITH_REGEX
|
||||||
if (rewrite_file)
|
if (rewrite_file)
|
||||||
rewrite_rules = read_remap_rules(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 we're running standalone, set up the input port */
|
||||||
if (standalone) {
|
if (standalone) {
|
||||||
fd = socket(PF_INET, SOCK_DGRAM, 0);
|
#ifdef HAVE_IPV6
|
||||||
|
if (ai_fam != AF_INET6) {
|
||||||
memset(&bindaddr, 0, sizeof bindaddr);
|
#endif
|
||||||
bindaddr.sin_family = AF_INET;
|
fd4 = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
bindaddr.sin_addr.s_addr = INADDR_ANY;
|
if (fd4 < 0) {
|
||||||
bindaddr.sin_port = htons(IPPORT_TFTP);
|
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) {
|
if (address) {
|
||||||
char *portptr, *eportptr;
|
char *portptr, *eportptr;
|
||||||
struct hostent *hostent;
|
int err;
|
||||||
struct servent *servent;
|
struct servent *servent;
|
||||||
unsigned long port;
|
unsigned long port;
|
||||||
|
|
||||||
|
@ -447,17 +491,40 @@ int main(int argc, char **argv)
|
||||||
portptr = strrchr(address, ':');
|
portptr = strrchr(address, ':');
|
||||||
if (portptr)
|
if (portptr)
|
||||||
*portptr++ = '\0';
|
*portptr++ = '\0';
|
||||||
|
else
|
||||||
|
portptr = (char *)"tftp";
|
||||||
if (*address) {
|
if (*address) {
|
||||||
hostent = gethostbyname(address);
|
if (fd4 >= 0) {
|
||||||
if (!hostent || hostent->h_addrtype != AF_INET) {
|
bindaddr4.sin_family = AF_INET;
|
||||||
syslog(LOG_ERR,
|
err = set_sock_addr(address,
|
||||||
"cannot resolve local bind address: %s",
|
(union sock_addr *)&bindaddr4, NULL);
|
||||||
address);
|
if (err) {
|
||||||
exit(EX_NOINPUT);
|
syslog(LOG_ERR,
|
||||||
|
"cannot resolve local IPv4 bind address: %s",
|
||||||
|
address);
|
||||||
|
exit(EX_NOINPUT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
memcpy(&bindaddr.sin_addr, hostent->h_addr,
|
#ifdef HAVE_IPV6
|
||||||
hostent->h_length);
|
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 {
|
} else {
|
||||||
/* Default to using INADDR_ANY */
|
/* Default to using INADDR_ANY */
|
||||||
}
|
}
|
||||||
|
@ -465,10 +532,20 @@ int main(int argc, char **argv)
|
||||||
if (portptr && *portptr) {
|
if (portptr && *portptr) {
|
||||||
servent = getservbyname(portptr, "udp");
|
servent = getservbyname(portptr, "udp");
|
||||||
if (servent) {
|
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))
|
} else if ((port = strtoul(portptr, &eportptr, 0))
|
||||||
&& !*eportptr) {
|
&& !*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")) {
|
} else if (!strcmp(portptr, "tftp")) {
|
||||||
/* It's TFTP, we're OK */
|
/* It's TFTP, we're OK */
|
||||||
} else {
|
} else {
|
||||||
|
@ -479,11 +556,37 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bind(fd, (struct sockaddr *)&bindaddr, sizeof bindaddr) < 0) {
|
if (fd4 >= 0) {
|
||||||
syslog(LOG_ERR, "cannot bind to local socket: %m");
|
if (bind(fd4, (struct sockaddr *)&bindaddr4,
|
||||||
exit(EX_OSERR);
|
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 */
|
/* Daemonize this process */
|
||||||
/* Note: when running in secure mode (-s), we must not chroot, since
|
/* Note: when running in secure mode (-s), we must not chroot, since
|
||||||
we are already in the proper directory. */
|
we are already in the proper directory. */
|
||||||
|
@ -491,10 +594,21 @@ int main(int argc, char **argv)
|
||||||
syslog(LOG_ERR, "cannot daemonize: %m");
|
syslog(LOG_ERR, "cannot daemonize: %m");
|
||||||
exit(EX_OSERR);
|
exit(EX_OSERR);
|
||||||
}
|
}
|
||||||
|
if (fd6 > fd4)
|
||||||
|
fdmax = fd6;
|
||||||
|
else
|
||||||
|
fdmax = fd4;
|
||||||
} else {
|
} else {
|
||||||
/* 0 is our socket descriptor */
|
/* 0 is our socket descriptor */
|
||||||
close(1);
|
close(1);
|
||||||
close(2);
|
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 */
|
/* Disable path MTU discovery */
|
||||||
|
@ -533,29 +647,61 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
FD_ZERO(&readset);
|
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_sec = waittime;
|
||||||
tv_waittime.tv_usec = 0;
|
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 */
|
/* 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);
|
standalone ? NULL : &tv_waittime);
|
||||||
if (rv == -1 && errno == EINTR)
|
if (rv == -1 && errno == EINTR)
|
||||||
continue; /* Signal caught, reloop */
|
continue; /* Signal caught, reloop */
|
||||||
|
|
||||||
if (rv == -1) {
|
if (rv == -1) {
|
||||||
syslog(LOG_ERR, "select loop: %m");
|
syslog(LOG_ERR, "select loop: %m");
|
||||||
exit(EX_IOERR);
|
exit(EX_IOERR);
|
||||||
} else if (rv == 0) {
|
} else if (rv == 0) {
|
||||||
exit(0); /* Timeout, return to inetd */
|
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__
|
#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
|
#endif
|
||||||
|
|
||||||
fromlen = sizeof(from);
|
fromlen = sizeof(from);
|
||||||
|
@ -570,18 +716,32 @@ int main(int argc, char **argv)
|
||||||
exit(EX_IOERR);
|
exit(EX_IOERR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef HAVE_IPV6
|
||||||
if (from.sin_family != AF_INET) {
|
if ((from.sa.sa_family != AF_INET) && (from.sa.sa_family != AF_INET6)) {
|
||||||
syslog(LOG_ERR,
|
syslog(LOG_ERR, "received address was not AF_INET/AF_INET6,"
|
||||||
"received address was not AF_INET, please check your inetd config");
|
" 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);
|
exit(EX_PROTOCOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (standalone && myaddr.sin_addr.s_addr == INADDR_ANY) {
|
if (standalone) {
|
||||||
/* myrecvfrom() didn't capture the source address; but we might
|
if ((from.sa.sa_family == AF_INET) &&
|
||||||
have bound to a specific address, if so we should use it */
|
(myaddr.si.sin_addr.s_addr == INADDR_ANY)) {
|
||||||
memcpy(&myaddr.sin_addr, &bindaddr.sin_addr,
|
/* myrecvfrom() didn't capture the source address; but we might
|
||||||
sizeof bindaddr.sin_addr);
|
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_FILE, fd,
|
||||||
RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0);
|
RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0);
|
||||||
sock_methods(&wrap_request);
|
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 (hosts_access(&wrap_request) == 0) {
|
||||||
if (deny_severity != -1)
|
if (deny_severity != -1)
|
||||||
syslog(deny_severity, "connection refused from %s",
|
syslog(deny_severity, "connection refused from %s", tmp_p);
|
||||||
inet_ntoa(from.sin_addr));
|
|
||||||
exit(EX_NOPERM); /* Access denied */
|
exit(EX_NOPERM); /* Access denied */
|
||||||
} else if (allow_severity != -1) {
|
} else if (allow_severity != -1) {
|
||||||
syslog(allow_severity, "connect from %s",
|
syslog(allow_severity, "connect from %s", tmp_p);
|
||||||
inet_ntoa(from.sin_addr));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -626,7 +791,7 @@ int main(int argc, char **argv)
|
||||||
/* Get a socket. This has to be done before the chroot(), since
|
/* Get a socket. This has to be done before the chroot(), since
|
||||||
some systems require access to /dev to create a socket. */
|
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) {
|
if (peer < 0) {
|
||||||
syslog(LOG_ERR, "socket: %m");
|
syslog(LOG_ERR, "socket: %m");
|
||||||
exit(EX_IOERR);
|
exit(EX_IOERR);
|
||||||
|
@ -677,16 +842,13 @@ int main(int argc, char **argv)
|
||||||
exit(EX_OSERR);
|
exit(EX_OSERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Other basic setup */
|
|
||||||
from.sin_family = AF_INET;
|
|
||||||
|
|
||||||
/* Process the request... */
|
/* Process the request... */
|
||||||
if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) {
|
if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) {
|
||||||
syslog(LOG_ERR, "bind: %m");
|
syslog(LOG_ERR, "bind: %m");
|
||||||
exit(EX_IOERR);
|
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");
|
syslog(LOG_ERR, "connect: %m");
|
||||||
exit(EX_IOERR);
|
exit(EX_IOERR);
|
||||||
}
|
}
|
||||||
|
@ -776,16 +938,22 @@ int tftp(struct tftphdr *tp, int size)
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
if (verbosity >= 1) {
|
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
|
if (filename == origfilename
|
||||||
|| !strcmp(filename, origfilename))
|
|| !strcmp(filename, origfilename))
|
||||||
syslog(LOG_NOTICE, "%s from %s filename %s\n",
|
syslog(LOG_NOTICE, "%s from %s filename %s\n",
|
||||||
tp_opcode == WRQ ? "WRQ" : "RRQ",
|
tp_opcode == WRQ ? "WRQ" : "RRQ",
|
||||||
inet_ntoa(from.sin_addr), filename);
|
tmp_p, filename);
|
||||||
else
|
else
|
||||||
syslog(LOG_NOTICE,
|
syslog(LOG_NOTICE,
|
||||||
"%s from %s filename %s remapped to %s\n",
|
"%s from %s filename %s remapped to %s\n",
|
||||||
tp_opcode == WRQ ? "WRQ" : "RRQ",
|
tp_opcode == WRQ ? "WRQ" : "RRQ",
|
||||||
inet_ntoa(from.sin_addr), origfilename,
|
tmp_p, origfilename,
|
||||||
filename);
|
filename);
|
||||||
}
|
}
|
||||||
ecode =
|
ecode =
|
||||||
|
@ -998,20 +1166,41 @@ int rewrite_macros(char macro, char *output);
|
||||||
|
|
||||||
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) {
|
switch (macro) {
|
||||||
case 'i':
|
case 'i':
|
||||||
p = inet_ntoa(from.sin_addr);
|
p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from),
|
||||||
if (output)
|
tb, INET6_ADDRSTRLEN);
|
||||||
|
if (output && p)
|
||||||
strcpy(output, p);
|
strcpy(output, p);
|
||||||
return strlen(p);
|
if (!p)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return strlen(p);
|
||||||
|
|
||||||
case 'x':
|
case 'x':
|
||||||
if (output)
|
if (output) {
|
||||||
sprintf(output, "%08lX",
|
if (from.sa.sa_family == AF_INET) {
|
||||||
(unsigned long)ntohl(from.sin_addr.s_addr));
|
sprintf(output, "%08lX",
|
||||||
return 8;
|
(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:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1398,8 +1587,14 @@ static void nak(int error, const char *msg)
|
||||||
length += 4; /* Add space for header */
|
length += 4; /* Add space for header */
|
||||||
|
|
||||||
if (verbosity >= 2) {
|
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",
|
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)
|
if (send(peer, buf, length, 0) != length)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue