| @@ -26,12 +26,6 @@ |
| #define TFTP_TIMEOUT 5 /* seconds */ |
| #define TFTP_NUM_RETRIES 5 /* number of retries */ |
| |
| - | static const char * const MODE_OCTET = "octet"; |
| - | #define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/ |
| - | |
| - | static const char * const OPTION_BLOCKSIZE = "blksize"; |
| - | #define OPTION_BLOCKSIZE_LEN 8 /* sizeof(OPTION_BLOCKSIZE) */ |
| - | |
| /* opcodes we support */ |
| #define TFTP_RRQ 1 |
| #define TFTP_WRQ 2 |
| @@ -51,13 +45,23 @@ static const char *const tftp_bb_error_msg[] = { |
| "No such user" |
| }; |
| |
| - | #define tftp_cmd_get ENABLE_FEATURE_TFTP_GET |
| - | |
| - | #if ENABLE_FEATURE_TFTP_PUT |
| - | # define tftp_cmd_put (tftp_cmd_get+ENABLE_FEATURE_TFTP_PUT) |
| + | #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT |
| + | #define USE_GETPUT(a) |
| + | #define CMD_GET(cmd) 1 |
| + | #define CMD_PUT(cmd) 0 |
| + | #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT |
| + | #define USE_GETPUT(a) |
| + | #define CMD_GET(cmd) 0 |
| + | #define CMD_PUT(cmd) 1 |
| #else |
| - | # define tftp_cmd_put 0 |
| + | #define USE_GETPUT(a) a |
| + | /* masks coming from getpot32 */ |
| + | #define CMD_GET(cmd) (cmd & 1) |
| + | #define CMD_PUT(cmd) (cmd & 2) |
| #endif |
| + | /* NB: in the code below |
| + | * CMD_GET(cmd) and CMD_GET(cmd) are mutually exclusive |
| + | */ |
| |
| |
| #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
| @@ -70,8 +74,9 @@ static int tftp_blocksize_check(int blocksize, int bufsize) |
| * to use blocksizes smaller than 22 octets. |
| */ |
| |
| - | if ((bufsize && (blocksize > bufsize)) || |
| - | (blocksize < 8) || (blocksize > 65564)) { |
| + | if ((bufsize && (blocksize > bufsize)) |
| + | || (blocksize < 8) || (blocksize > 65564) |
| + | ) { |
| bb_error_msg("bad blocksize"); |
| return 0; |
| } |
| @@ -86,7 +91,6 @@ static char *tftp_option_get(char *buf, int len, const char * const option) |
| int k; |
| |
| while (len > 0) { |
| - | |
| /* Make sure the options are terminated correctly */ |
| |
| for (k = 0; k < len; k++) { |
| @@ -122,22 +126,23 @@ static char *tftp_option_get(char *buf, int len, const char * const option) |
| |
| #endif |
| |
| - | static int tftp(const int cmd, const struct hostent *host, |
| - | const char *remotefile, const int localfd, |
| - | const unsigned short port, int tftp_bufsize) |
| + | static int tftp( |
| + | #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT |
| + | const int cmd, |
| + | #endif |
| + | const len_and_sockaddr *peer_lsa, |
| + | const char *remotefile, const int localfd, |
| + | const unsigned port, int tftp_bufsize) |
| { |
| - | struct sockaddr_in sa; |
| - | struct sockaddr_in from; |
| struct timeval tv; |
| - | socklen_t fromlen; |
| fd_set rfds; |
| int socketfd; |
| int len; |
| int opcode = 0; |
| int finished = 0; |
| int timeout = TFTP_NUM_RETRIES; |
| - | unsigned short block_nr = 1; |
| - | unsigned short tmp; |
| + | uint16_t block_nr = 1; |
| + | uint16_t tmp; |
| char *cp; |
| |
| USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;) |
| @@ -149,42 +154,24 @@ static int tftp(const int cmd, const struct hostent *host, |
| char *xbuf = xmalloc(tftp_bufsize += 4); |
| char *rbuf = xmalloc(tftp_bufsize); |
| |
| - | if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { |
| - | /* need to unlink the localfile, so don't use xsocket here. */ |
| - | bb_perror_msg("socket"); |
| - | return EXIT_FAILURE; |
| - | } |
| - | |
| - | len = sizeof(sa); |
| - | |
| - | memset(&sa, 0, len); |
| - | xbind(socketfd, (struct sockaddr *)&sa, len); |
| - | |
| - | sa.sin_family = host->h_addrtype; |
| - | sa.sin_port = port; |
| - | memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, |
| - | sizeof(sa.sin_addr)); |
| + | socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0); |
| |
| /* build opcode */ |
| - | if (cmd & tftp_cmd_get) { |
| + | opcode = TFTP_WRQ; |
| + | if (CMD_GET(cmd)) { |
| opcode = TFTP_RRQ; |
| } |
| - | if (cmd & tftp_cmd_put) { |
| - | opcode = TFTP_WRQ; |
| - | } |
| |
| while (1) { |
| |
| cp = xbuf; |
| |
| /* first create the opcode part */ |
| - | *((unsigned short *) cp) = htons(opcode); |
| + | *((uint16_t*)cp) = htons(opcode); |
| cp += 2; |
| |
| /* add filename and mode */ |
| - | if (((cmd & tftp_cmd_get) && (opcode == TFTP_RRQ)) || |
| - | ((cmd & tftp_cmd_put) && (opcode == TFTP_WRQ))) |
| - | { |
| + | if (CMD_GET(cmd) ? (opcode == TFTP_RRQ) : (opcode == TFTP_WRQ)) { |
| int too_long = 0; |
| |
| /* see if the filename fits into xbuf |
| @@ -198,14 +185,14 @@ static int tftp(const int cmd, const struct hostent *host, |
| cp += len; |
| } |
| |
| - | if (too_long || ((&xbuf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) { |
| + | if (too_long || (&xbuf[tftp_bufsize - 1] - cp) < sizeof("octet")) { |
| bb_error_msg("remote filename too long"); |
| break; |
| } |
| |
| /* add "mode" part of the package */ |
| - | memcpy(cp, MODE_OCTET, MODE_OCTET_LEN); |
| - | cp += MODE_OCTET_LEN; |
| + | memcpy(cp, "octet", sizeof("octet")); |
| + | cp += sizeof("octet"); |
| |
| #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
| |
| @@ -219,8 +206,8 @@ static int tftp(const int cmd, const struct hostent *host, |
| } |
| |
| /* add "blksize" + number of blocks */ |
| - | memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN); |
| - | cp += OPTION_BLOCKSIZE_LEN; |
| + | memcpy(cp, "blksize", sizeof("blksize")); |
| + | cp += sizeof("blksize"); |
| cp += snprintf(cp, 6, "%d", len) + 1; |
| |
| want_option_ack = 1; |
| @@ -230,16 +217,12 @@ static int tftp(const int cmd, const struct hostent *host, |
| |
| /* add ack and data */ |
| |
| - | if (((cmd & tftp_cmd_get) && (opcode == TFTP_ACK)) || |
| - | ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA))) { |
| - | |
| - | *((unsigned short *) cp) = htons(block_nr); |
| - | |
| + | if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) { |
| + | *((uint16_t*)cp) = htons(block_nr); |
| cp += 2; |
| - | |
| block_nr++; |
| |
| - | if ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA)) { |
| + | if (CMD_PUT(cmd) && (opcode == TFTP_DATA)) { |
| len = full_read(localfd, cp, tftp_bufsize - 4); |
| |
| if (len < 0) { |
| @@ -261,9 +244,7 @@ static int tftp(const int cmd, const struct hostent *host, |
| |
| timeout = TFTP_NUM_RETRIES; /* re-initialize */ |
| do { |
| - | |
| len = cp - xbuf; |
| - | |
| #if ENABLE_DEBUG_TFTP |
| fprintf(stderr, "sending %u bytes\n", len); |
| for (cp = xbuf; cp < &xbuf[len]; cp++) |
| @@ -271,7 +252,7 @@ static int tftp(const int cmd, const struct hostent *host, |
| fprintf(stderr, "\n"); |
| #endif |
| if (sendto(socketfd, xbuf, len, 0, |
| - | (struct sockaddr *) &sa, sizeof(sa)) < 0) { |
| + | &peer_lsa->sa, peer_lsa->len) < 0) { |
| bb_perror_msg("send"); |
| len = -1; |
| break; |
| @@ -284,9 +265,6 @@ static int tftp(const int cmd, const struct hostent *host, |
| |
| /* receive packet */ |
| |
| - | memset(&from, 0, sizeof(from)); |
| - | fromlen = sizeof(from); |
| - | |
| tv.tv_sec = TFTP_TIMEOUT; |
| tv.tv_usec = 0; |
| |
| @@ -294,30 +272,41 @@ static int tftp(const int cmd, const struct hostent *host, |
| FD_SET(socketfd, &rfds); |
| |
| switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) { |
| + | struct sockaddr *from; |
| + | socklen_t fromlen; |
| + | |
| case 1: |
| - | len = recvfrom(socketfd, rbuf, tftp_bufsize, 0, |
| - | (struct sockaddr *) &from, &fromlen); |
| + | fromlen = peer_lsa->len; |
| + | from = alloca(fromlen); |
| + | memset(from, 0, fromlen); |
| |
| + | len = recvfrom(socketfd, rbuf, tftp_bufsize, 0, |
| + | from, &fromlen); |
| if (len < 0) { |
| bb_perror_msg("recvfrom"); |
| break; |
| } |
| - | |
| timeout = 0; |
| - | |
| - | if (sa.sin_port == port) { |
| - | sa.sin_port = from.sin_port; |
| - | } |
| - | if (sa.sin_port == from.sin_port) { |
| - | break; |
| + | if (from->sa_family == peer_lsa->sa.sa_family) { |
| + | #if ENABLE_FEATURE_IPV6 |
| + | if (from->sa_family == AF_INET6 |
| + | && ((struct sockaddr_in6*)from)->sin6_port == port |
| + | ) |
| + | break; |
| + | #endif |
| + | /* Non-internet sockets are ok */ |
| + | if (from->sa_family != AF_INET) |
| + | break; |
| + | if (((struct sockaddr_in*)from)->sin_port == port) |
| + | break; |
| } |
| - | |
| - | /* fall-through for bad packets! */ |
| - | /* discard the packet - treat as timeout */ |
| + | /* family doesn't match, or |
| + | * it is INET[v6] and port doesn't match - |
| + | * fall-through for bad packets! |
| + | * (discard the packet - treat as timeout) */ |
| timeout = TFTP_NUM_RETRIES; |
| case 0: |
| bb_error_msg("timeout"); |
| - | |
| timeout--; |
| if (timeout == 0) { |
| len = -1; |
| @@ -331,14 +320,14 @@ static int tftp(const int cmd, const struct hostent *host, |
| |
| } while (timeout && (len >= 0)); |
| |
| - | if ((finished) || (len < 0)) { |
| + | if (finished || (len < 0)) { |
| break; |
| } |
| |
| /* process received packet */ |
| |
| - | opcode = ntohs(*((unsigned short *) rbuf)); |
| - | tmp = ntohs(*((unsigned short *) &rbuf[2])); |
| + | opcode = ntohs( ((uint16_t*)rbuf)[0] ); |
| + | tmp = ntohs( ((uint16_t*)rbuf)[1] ); |
| |
| #if ENABLE_DEBUG_TFTP |
| fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp); |
| @@ -352,7 +341,6 @@ static int tftp(const int cmd, const struct hostent *host, |
| rbuf[tftp_bufsize - 1] = '\0'; |
| } else if (tmp < (sizeof(tftp_bb_error_msg) |
| / sizeof(char *))) { |
| - | |
| msg = tftp_bb_error_msg[tmp]; |
| } |
| |
| @@ -368,25 +356,22 @@ static int tftp(const int cmd, const struct hostent *host, |
| want_option_ack = 0; |
| |
| if (opcode == TFTP_OACK) { |
| - | |
| /* server seems to support options */ |
| - | |
| char *res; |
| |
| - | res = tftp_option_get(&rbuf[2], len - 2, OPTION_BLOCKSIZE); |
| + | res = tftp_option_get(&rbuf[2], len - 2, "blksize"); |
| |
| if (res) { |
| int blksize = xatoi_u(res); |
| |
| if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) { |
| - | |
| - | if (cmd & tftp_cmd_put) { |
| + | if (CMD_PUT(cmd)) { |
| opcode = TFTP_DATA; |
| } else { |
| opcode = TFTP_ACK; |
| } |
| #if ENABLE_DEBUG_TFTP |
| - | fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE, |
| + | fprintf(stderr, "using blksize %u\n", |
| blksize); |
| #endif |
| tftp_bufsize = blksize + 4; |
| @@ -407,10 +392,8 @@ static int tftp(const int cmd, const struct hostent *host, |
| } |
| #endif |
| |
| - | if ((cmd & tftp_cmd_get) && (opcode == TFTP_DATA)) { |
| - | |
| + | if (CMD_GET(cmd) && (opcode == TFTP_DATA)) { |
| if (tmp == block_nr) { |
| - | |
| len = full_write(localfd, &rbuf[4], len - 4); |
| |
| if (len < 0) { |
| @@ -430,6 +413,7 @@ static int tftp(const int cmd, const struct hostent *host, |
| --block_nr; |
| opcode = TFTP_ACK; |
| continue; |
| + | // tmp==(block_nr-1) and (tmp+1)==block_nr is always same, I think. wtf? |
| } else if (tmp + 1 == block_nr) { |
| /* Server lost our TFTP_ACK. Resend it */ |
| block_nr = tmp; |
| @@ -438,9 +422,8 @@ static int tftp(const int cmd, const struct hostent *host, |
| } |
| } |
| |
| - | if ((cmd & tftp_cmd_put) && (opcode == TFTP_ACK)) { |
| - | |
| - | if (tmp == (unsigned short) (block_nr - 1)) { |
| + | if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) { |
| + | if (tmp == (uint16_t) (block_nr - 1)) { |
| if (finished) { |
| break; |
| } |
| @@ -462,61 +445,32 @@ static int tftp(const int cmd, const struct hostent *host, |
| |
| int tftp_main(int argc, char **argv) |
| { |
| - | struct hostent *host = NULL; |
| + | len_and_sockaddr *peer_lsa; |
| const char *localfile = NULL; |
| const char *remotefile = NULL; |
| + | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
| + | const char *sblocksize = NULL; |
| + | #endif |
| int port; |
| - | int cmd = 0; |
| + | USE_GETPUT(int cmd;) |
| int fd = -1; |
| int flags = 0; |
| int result; |
| int blocksize = TFTP_BLOCKSIZE_DEFAULT; |
| |
| - | /* figure out what to pass to getopt */ |
| - | |
| - | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
| - | char *sblocksize = NULL; |
| - | |
| - | #define BS "b:" |
| - | #define BS_ARG , &sblocksize |
| - | #else |
| - | #define BS |
| - | #define BS_ARG |
| - | #endif |
| - | |
| - | #if ENABLE_FEATURE_TFTP_GET |
| - | #define GET "g" |
| - | #define GET_COMPL ":g" |
| - | #else |
| - | #define GET |
| - | #define GET_COMPL |
| - | #endif |
| + | /* -p or -g is mandatory, and they are mutually exclusive */ |
| + | opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:") |
| + | USE_GETPUT("?g--p:p--g"); |
| |
| - | #if ENABLE_FEATURE_TFTP_PUT |
| - | #define PUT "p" |
| - | #define PUT_COMPL ":p" |
| - | #else |
| - | #define PUT |
| - | #define PUT_COMPL |
| - | #endif |
| + | USE_GETPUT(cmd =) getopt32(argc, argv, |
| + | USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p") |
| + | "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"), |
| + | &localfile, &remotefile |
| + | USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize)); |
| |
| - | #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT) |
| - | opt_complementary = GET_COMPL PUT_COMPL ":?g--p:p--g"; |
| - | #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT) |
| - | opt_complementary = GET_COMPL PUT_COMPL; |
| - | #endif |
| - | |
| - | cmd = getopt32(argc, argv, GET PUT "l:r:" BS, &localfile, &remotefile BS_ARG); |
| - | |
| - | cmd &= (tftp_cmd_get | tftp_cmd_put); |
| - | #if ENABLE_FEATURE_TFTP_GET |
| - | if (cmd == tftp_cmd_get) |
| + | flags = O_RDONLY; |
| + | if (CMD_GET(cmd)) |
| flags = O_WRONLY | O_CREAT | O_TRUNC; |
| - | #endif |
| - | #if ENABLE_FEATURE_TFTP_PUT |
| - | if (cmd == tftp_cmd_put) |
| - | flags = O_RDONLY; |
| - | #endif |
| |
| #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
| if (sblocksize) { |
| @@ -535,30 +489,31 @@ int tftp_main(int argc, char **argv) |
| bb_show_usage(); |
| |
| if (localfile == NULL || LONE_DASH(localfile)) { |
| - | fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO; |
| + | fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO; |
| } else { |
| - | fd = open(localfile, flags, 0644); /* fail below */ |
| - | } |
| - | if (fd < 0) { |
| - | bb_perror_msg_and_die("local file"); |
| + | fd = xopen3(localfile, flags, 0644); |
| } |
| |
| - | host = xgethostbyname(argv[optind]); |
| port = bb_lookup_port(argv[optind + 1], "udp", 69); |
| + | peer_lsa = host2sockaddr(argv[optind], port); |
| |
| #if ENABLE_DEBUG_TFTP |
| - | fprintf(stderr, "using server \"%s\", remotefile \"%s\", " |
| - | "localfile \"%s\".\n", |
| - | inet_ntoa(*((struct in_addr *) host->h_addr)), |
| + | fprintf(stderr, "using server \"%s\", " |
| + | "remotefile \"%s\", localfile \"%s\".\n", |
| + | xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len), |
| remotefile, localfile); |
| #endif |
| |
| - | result = tftp(cmd, host, remotefile, fd, port, blocksize); |
| + | result = tftp( |
| + | #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT |
| + | cmd, |
| + | #endif |
| + | peer_lsa, remotefile, fd, port, blocksize); |
| |
| - | if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) { |
| + | if (fd > 1) { |
| if (ENABLE_FEATURE_CLEAN_UP) |
| close(fd); |
| - | if (cmd == tftp_cmd_get && result != EXIT_SUCCESS) |
| + | if (CMD_GET(cmd) && result != EXIT_SUCCESS) |
| unlink(localfile); |
| } |
| return result; |