Skip to content

Commit 22d646e

Browse files
committed
hunt and peck for an open source port
1 parent 6dca3db commit 22d646e

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

src/lib/bio/fd_open.c

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <freeradius-devel/bio/fd_priv.h>
2626
#include <freeradius-devel/util/file.h>
2727
#include <freeradius-devel/util/cap.h>
28+
#include <freeradius-devel/util/rand.h>
2829

2930
#include <sys/stat.h>
3031
#include <net/if.h>
@@ -723,14 +724,66 @@ static int fr_bio_fd_socket_bind(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
723724
*/
724725
if (fr_ipaddr_to_sockaddr(&salocal, &salen, &my->info.socket.inet.src_ipaddr, my->info.socket.inet.src_port) < 0) return -1;
725726

726-
if (bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen) < 0) {
727-
fr_strerror_printf("Failed binding to socket: %s", fr_syserror(errno));
727+
/*
728+
* If we have a fixed source port, just use that.
729+
*/
730+
if (my->info.cfg->src_port || !my->info.cfg->src_port_start) {
731+
if (bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen) < 0) {
732+
fr_strerror_printf("Failed binding to socket: %s", fr_syserror(errno));
733+
return -1;
734+
}
735+
} else {
736+
uint16_t src_port, current;
737+
struct sockaddr_in *sin = (struct sockaddr_in *) &salocal;
738+
739+
fr_assert(my->info.cfg->src_port_start);
740+
fr_assert(my->info.cfg->src_port_end);
741+
fr_assert(my->info.cfg->src_port_end >= my->info.cfg->src_port_start);
742+
743+
src_port = fr_rand() % (my->info.cfg->src_port_end - my->info.cfg->src_port_start);
744+
745+
/*
746+
* We only care about sin_port, which is in the same location for sockaddr_in and sockaddr_in6.
747+
*/
748+
fr_assert(sin->sin_port == 0);
749+
750+
sin->sin_port = htons(my->info.cfg->src_port_start + src_port);
751+
752+
/*
753+
* We've picked a random port in what is hopefully a large range. If that works, we're
754+
* done.
755+
*/
756+
if (bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen) >= 0) goto done;
757+
758+
/*
759+
* Hunt & peck. Which is horrible.
760+
*/
761+
current = src_port;
762+
while (true) {
763+
if (current == my->info.cfg->src_port_end) {
764+
current = my->info.cfg->src_port_start;
765+
} else {
766+
current++;
767+
}
768+
769+
/*
770+
* We've already checked this, so stop.
771+
*/
772+
if (current == src_port) break;
773+
774+
sin->sin_port = htons(current);
775+
776+
if (bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen) >= 0) goto done;
777+
}
778+
779+
fr_strerror_const("There are no open ports between 'src_port_start' and 'src_port_end'");
728780
return -1;
729781
}
730782

731783
/*
732784
* The source IP may have changed, so get the new one.
733785
*/
786+
done:
734787
return fr_bio_fd_socket_name(my);
735788
}
736789

0 commit comments

Comments
 (0)