mirror of
https://kernel.googlesource.com/pub/scm/network/tftp/tftp-hpa
synced 2025-05-10 16:55:46 +03:00
Add support for IPv6 in the server and client.
Add support for IPv6 in the server and client. You can force the use of IPv4 or IPv6 only with new -4 and -6 commandline options, if IPv6 support was compiled in. Signed-off-by: Karsten Keil <kkeil@suse.de> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
parent
7fe0fb941c
commit
28f22b6591
9 changed files with 574 additions and 185 deletions
|
@ -237,7 +237,7 @@ int synchnet(int f)
|
||||||
{ /* socket to flush */
|
{ /* 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