tftp: fix IPv6 fallout

This commit is contained in:
Denis Vlasenko 2007-02-07 23:20:32 +00:00
parent 8c6c6e955b
commit 0850cdabde
3 changed files with 25 additions and 22 deletions

View File

@ -289,7 +289,7 @@ int setsockopt_broadcast(int fd);
/* NB: returns port in host byte order */ /* NB: returns port in host byte order */
unsigned bb_lookup_port(const char *port, const char *protocol, unsigned default_port); unsigned bb_lookup_port(const char *port, const char *protocol, unsigned default_port);
typedef struct len_and_sockaddr { typedef struct len_and_sockaddr {
int len; socklen_t len;
union { union {
struct sockaddr sa; struct sockaddr sa;
struct sockaddr_in sin; struct sockaddr_in sin;
@ -335,7 +335,7 @@ len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t
* NB: does NOT do htons() internally, just direct assignment. */ * NB: does NOT do htons() internally, just direct assignment. */
void set_nport(len_and_sockaddr *lsa, unsigned port); void set_nport(len_and_sockaddr *lsa, unsigned port);
/* Retrieve sin[6]_port or return -1 for non-INET[6] lsa's */ /* Retrieve sin[6]_port or return -1 for non-INET[6] lsa's */
int get_nport(len_and_sockaddr *lsa); int get_nport(const len_and_sockaddr *lsa);
/* Reverse DNS. Returns NULL on failure. */ /* Reverse DNS. Returns NULL on failure. */
char* xmalloc_sockaddr2host(const struct sockaddr *sa, socklen_t salen); char* xmalloc_sockaddr2host(const struct sockaddr *sa, socklen_t salen);
/* This one doesn't append :PORTNUM */ /* This one doesn't append :PORTNUM */

View File

@ -82,7 +82,7 @@ int xconnect_tcp_v4(struct sockaddr_in *s_addr)
/* "New" networking API */ /* "New" networking API */
int get_nport(len_and_sockaddr *lsa) int get_nport(const len_and_sockaddr *lsa)
{ {
#if ENABLE_FEATURE_IPV6 #if ENABLE_FEATURE_IPV6
if (lsa->sa.sa_family == AF_INET6) { if (lsa->sa.sa_family == AF_INET6) {

View File

@ -132,7 +132,7 @@ static int tftp(
#if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
const int cmd, const int cmd,
#endif #endif
const len_and_sockaddr *peer_lsa, len_and_sockaddr *peer_lsa,
const char *remotefile, const int localfd, const char *remotefile, const int localfd,
unsigned port, int tftp_bufsize) unsigned port, int tftp_bufsize)
{ {
@ -149,6 +149,9 @@ static int tftp(
USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;) USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
unsigned org_port;
len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
/* Can't use RESERVE_CONFIG_BUFFER here since the allocation /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
* size varies meaning BUFFERS_GO_ON_STACK would fail */ * size varies meaning BUFFERS_GO_ON_STACK would fail */
/* We must keep the transmit and receive buffers seperate */ /* We must keep the transmit and receive buffers seperate */
@ -156,7 +159,7 @@ static int tftp(
char *xbuf = xmalloc(tftp_bufsize += 4); char *xbuf = xmalloc(tftp_bufsize += 4);
char *rbuf = xmalloc(tftp_bufsize); char *rbuf = xmalloc(tftp_bufsize);
port = htons(port); port = org_port = htons(port);
socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0); socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
@ -167,10 +170,10 @@ static int tftp(
} }
while (1) { while (1) {
cp = xbuf; cp = xbuf;
/* first create the opcode part */ /* first create the opcode part */
/* (this 16bit store is aligned) */
*((uint16_t*)cp) = htons(opcode); *((uint16_t*)cp) = htons(opcode);
cp += 2; cp += 2;
@ -222,6 +225,7 @@ static int tftp(
/* add ack and data */ /* add ack and data */
if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) { if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) {
/* TODO: unaligned access! */
*((uint16_t*)cp) = htons(block_nr); *((uint16_t*)cp) = htons(block_nr);
cp += 2; cp += 2;
block_nr++; block_nr++;
@ -273,28 +277,26 @@ static int tftp(
FD_SET(socketfd, &rfds); FD_SET(socketfd, &rfds);
switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) { switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
struct sockaddr *from; unsigned from_port;
socklen_t fromlen;
case 1: case 1:
fromlen = peer_lsa->len; from->len = peer_lsa->len;
from = alloca(fromlen); memset(from, 0, peer_lsa->len);
memset(from, 0, fromlen);
len = recvfrom(socketfd, rbuf, tftp_bufsize, 0, len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
from, &fromlen); &from->sa, &from->len);
if (len < 0) { if (len < 0) {
bb_perror_msg("recvfrom"); bb_perror_msg("recvfrom");
break; break;
} }
#if ENABLE_FEATURE_IPV6 from_port = get_nport(from);
if (from->sa_family == AF_INET6) if (port == org_port) {
if (((struct sockaddr_in6*)from)->sin6_port != port) /* Our first query went to port 69
goto recv_again; * but reply will come from different one.
#endif * Remember and use this new port */
if (from->sa_family == AF_INET) port = from_port;
if (((struct sockaddr_in*)from)->sin_port != port) set_nport(peer_lsa, from_port);
goto recv_again; }
if (port != from_port)
goto recv_again;
timeout = 0; timeout = 0;
break; break;
case 0: case 0:
@ -317,6 +319,7 @@ static int tftp(
} }
/* process received packet */ /* process received packet */
/* (both accesses seems to be aligned) */
opcode = ntohs( ((uint16_t*)rbuf)[0] ); opcode = ntohs( ((uint16_t*)rbuf)[0] );
tmp = ntohs( ((uint16_t*)rbuf)[1] ); tmp = ntohs( ((uint16_t*)rbuf)[1] );