tftp: fix wrong check for port numbers
authorDenis Vlasenko <vda.linux@googlemail.com>
Sun, 18 Mar 2007 18:00:12 +0000 (18:00 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sun, 18 Mar 2007 18:00:12 +0000 (18:00 -0000)
include/libbb.h
networking/tftp.c

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