aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Ngai <nicholas@ngai.me>2021-11-27 14:22:42 -0800
committerNicholas Ngai <nicholas@ngai.me>2021-11-27 14:22:42 -0800
commit87ea58f2f5a99515fd6544aa1a66bbfb26af2b6f (patch)
tree526f69ad0c177e808c9cd4219c071b76e91b9445
parent559addbcf0807fcf5f9ec664d96bc4c8faa2d850 (diff)
downloadslirp-87ea58f2f5a99515fd6544aa1a66bbfb26af2b6f.zip
slirp-87ea58f2f5a99515fd6544aa1a66bbfb26af2b6f.tar.gz
slirp-87ea58f2f5a99515fd6544aa1a66bbfb26af2b6f.tar.bz2
socket: Allocate Unix-to-TCP hostfwd port from OS by binding to port 0
Signed-off-by: Nicholas Ngai <nicholas@ngai.me>
-rw-r--r--src/socket.c77
1 files changed, 73 insertions, 4 deletions
diff --git a/src/socket.c b/src/socket.c
index c24fe28..7869e6a 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -1062,20 +1062,89 @@ void sotranslate_accept(struct socket *so)
}
break;
- case AF_UNIX:
- /* Translate Unix socket to random ephemeral source port. */
+ case AF_UNIX: {
+ /* Translate Unix socket to random ephemeral source port. We obtain
+ * this source port by binding to port 0 so that the OS allocates a
+ * port for us. If this fails, we fall back to choosing a random port
+ * with a random number generator. */
+ int s;
+ struct sockaddr_in in_addr;
+ struct sockaddr_in6 in6_addr;
+ socklen_t in_addr_len;
+
if (so->slirp->in_enabled) {
so->so_ffamily = AF_INET;
so->so_faddr = slirp->vhost_addr;
- so->so_fport = g_rand_int_range(slirp->grand, 49152, 65536);
+ so->so_fport = 0;
+
+ // TODO Is there a better way of checking socket type?
+ s = slirp_socket(PF_INET, so->so_tcpcb ? SOCK_STREAM : SOCK_DGRAM, 0);
+ if (s < 0) {
+ g_error("Ephemeral slirp_socket() allocation failed");
+ goto unix2inet_cont;
+ }
+ memset(&in_addr, 0, sizeof(in_addr));
+ in_addr.sin_family = AF_INET;
+ in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ in_addr.sin_port = htons(0);
+ if (bind(s, (struct sockaddr *) &in_addr, sizeof(in_addr))) {
+ g_error("Ephemeral bind() failed");
+ closesocket(s);
+ goto unix2inet_cont;
+ }
+ in_addr_len = sizeof(in_addr);
+ if (getsockname(s, (struct sockaddr *) &in_addr, &in_addr_len)) {
+ g_error("Ephemeral getsockname() failed");
+ closesocket(s);
+ goto unix2inet_cont;
+ }
+ so->s = s;
+ so->so_fport = in_addr.sin_port;
+
+unix2inet_cont:
+ if (!so->so_fport) {
+ g_warning("Falling back to random port allocation");
+ so->so_fport = htons(g_rand_int_range(slirp->grand, 49152, 65536));
+ }
} else if (so->slirp->in6_enabled) {
so->so_ffamily = AF_INET6;
so->so_faddr6 = slirp->vhost_addr6;
- so->so_fport6 = g_rand_int_range(slirp->grand, 49152, 65536);
+ so->so_fport6 = 0;
+
+ // TODO Is there a better way of checking socket type?
+ s = slirp_socket(PF_INET6, so->so_tcpcb ? SOCK_STREAM : SOCK_DGRAM, 0);
+ if (s < 0) {
+ g_error("Ephemeral slirp_socket() allocation failed");
+ goto unix2inet6_cont;
+ }
+ memset(&in6_addr, 0, sizeof(in6_addr));
+ in6_addr.sin6_family = AF_INET6;
+ in6_addr.sin6_addr = in6addr_loopback;
+ in6_addr.sin6_port = htons(0);
+ if (bind(s, (struct sockaddr *) &in6_addr, sizeof(in6_addr))) {
+ g_error("Ephemeral bind() failed");
+ closesocket(s);
+ goto unix2inet6_cont;
+ }
+ in_addr_len = sizeof(in6_addr);
+ if (getsockname(s, (struct sockaddr *) &in6_addr, &in_addr_len)) {
+ g_error("Ephemeral getsockname() failed");
+ closesocket(s);
+ goto unix2inet6_cont;
+ }
+ so->s = s;
+ so->so_fport6 = in6_addr.sin6_port;
+
+unix2inet6_cont:
+ if (!so->so_fport6) {
+ g_warning("Falling back to random port allocation");
+ so->so_fport6 = htons(g_rand_int_range(slirp->grand, 49152, 65536));
+ }
} else {
g_assert_not_reached();
}
break;
+ } /* case AF_UNIX */
default:
break;