diff --git a/common/tftpsubs.c b/common/tftpsubs.c index 9103106..b2eae71 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -272,3 +272,41 @@ synchnet(int f) /* socket to flush */ return pktcount; /* Return packets drained */ } + + +int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, unsigned int port_range_from, unsigned int port_range_to) +{ + unsigned int port, firstport; + int port_range = 0; + + if (port_range_from != 0 && port_range_to != 0) { + port_range = 1; + } + + firstport = port_range + ? port_range_from + rand() % (port_range_to-port_range_from+1) + : 0; + + port = firstport; + + do { + myaddr->sin_port = htons(port); + + if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { + /* Some versions of Linux return EINVAL instead of EADDRINUSE */ + if ( !(port_range && (errno == EINVAL || errno == EADDRINUSE)) ) + return -1; + + /* Normally, we shouldn't have to loop, but some situations involving + aborted transfers make it possible. */ + } else { + return 0; + } + + port++; + if ( port > port_range_to ) + port = port_range_from; + } while ( port != firstport ); + + return -1; +} diff --git a/common/tftpsubs.h b/common/tftpsubs.h index 645c6b9..359ba1a 100644 --- a/common/tftpsubs.h +++ b/common/tftpsubs.h @@ -74,3 +74,6 @@ extern char *xstrdup(const char *); void (*bsd_signal(int, void (*)(int)))(int); #endif + + +int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, unsigned int from, unsigned int to); diff --git a/tftp/main.c b/tftp/main.c index 3a8315f..c3ec3a0 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -102,6 +102,9 @@ const char *prompt = "tftp> "; sigjmp_buf toplevel; void intr(int); struct servent *sp; +int portrange = 0; +unsigned int portrange_from = 0; +unsigned int portrange_to = 0; void get (int, char **); void help (int, char **); @@ -241,6 +244,16 @@ main(int argc, char *argv[]) case 'c': iscmd = 1; break; + case 'R': + if ( ++arg >= argc ) + usage(EX_USAGE); + if ( sscanf(argv[arg], "%u:%u", &portrange_from, &portrange_to) != 2 || + portrange_from > portrange_to || portrange_to > 65535 ) { + fprintf(stderr, "Bad port range: %s\n", argv[arg]); + exit(EX_USAGE); + } + portrange = 1; + break; case 'h': default: usage(*optx == 'h' ? 0 : EX_USAGE); @@ -276,7 +289,7 @@ main(int argc, char *argv[]) } bzero((char *)&s_in, sizeof (s_in)); s_in.sin_family = AF_INET; - if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { + if (pick_port_bind(f, &s_in, portrange_from, portrange_to)) { perror("tftp: bind"); exit(EX_OSERR); } diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 6018850..09d8733 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -273,38 +273,6 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags, } } -static int -pick_port_bind(int sockfd, struct sockaddr_in *myaddr) -{ - unsigned int port, firstport; - - firstport = portrange - ? portrange_from + rand() % (portrange_to-portrange_from+1) - : 0; - - port = firstport; - - do { - myaddr->sin_port = htons(port); - - if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { - /* Some versions of Linux return EINVAL instead of EADDRINUSE */ - if ( !(portrange && (errno == EINVAL || errno == EADDRINUSE)) ) - return -1; - - /* Normally, we shouldn't have to loop, but some situations involving - aborted transfers make it possible. */ - } else { - return 0; - } - - port++; - if ( port > portrange_to ) - port = portrange_from; - } while ( port != firstport ); - - return -1; -} int main(int argc, char **argv) @@ -750,7 +718,7 @@ main(int argc, char **argv) from.sin_family = AF_INET; /* Process the request... */ - if (pick_port_bind(peer, &myaddr) < 0) { + if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) { syslog(LOG_ERR, "bind: %m"); exit(EX_IOERR); }