diff options
Diffstat (limited to 'networking/tftp.c')
-rw-r--r-- | networking/tftp.c | 251 |
1 files changed, 103 insertions, 148 deletions
diff --git a/networking/tftp.c b/networking/tftp.c index 2d28973..eaeb808 100644 --- a/networking/tftp.c +++ b/networking/tftp.c @@ -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; |