aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2021-12-12 01:03:37 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2021-12-12 01:03:37 +0000
commit74756eb75cc70ed948e791bbc1843a86f64ba3ea (patch)
tree796fe214f02e66135a195ac86c715f17ab796020
parent5bf953e2f92d5c426c2a6b5db5e95b6bdf1f439b (diff)
parent32bea29d04c24be75c3169ea6ad107e4c3828691 (diff)
downloadslirp-74756eb75cc70ed948e791bbc1843a86f64ba3ea.zip
slirp-74756eb75cc70ed948e791bbc1843a86f64ba3ea.tar.gz
slirp-74756eb75cc70ed948e791bbc1843a86f64ba3ea.tar.bz2
Merge branch 'hostfwd-unix' into 'master'
Support Unix sockets in hostfwd See merge request slirp/libslirp!103
-rw-r--r--src/socket.c145
-rw-r--r--src/socket.h19
2 files changed, 156 insertions, 8 deletions
diff --git a/src/socket.c b/src/socket.c
index 9e1e47e..f071d69 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -51,6 +51,7 @@ struct socket *socreate(Slirp *slirp, int type)
so->so_type = type;
so->so_state = SS_NOFDREF;
so->s = -1;
+ so->s_aux = -1;
so->slirp = slirp;
so->pollfds_idx = -1;
@@ -83,6 +84,10 @@ void sofree(struct socket *so)
{
Slirp *slirp = so->slirp;
+ if (so->s_aux != -1) {
+ closesocket(so->s_aux);
+ }
+
soqfree(so, &slirp->if_fastq);
soqfree(so, &slirp->if_batchq);
@@ -774,14 +779,35 @@ struct socket *tcpx_listen(Slirp *slirp,
char addrstr[INET6_ADDRSTRLEN];
char portstr[6];
int ret;
- ret = getnameinfo(haddr, haddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
- g_assert(ret == 0);
- DEBUG_ARG("haddr = %s", addrstr);
- DEBUG_ARG("hport = %s", portstr);
- ret = getnameinfo(laddr, laddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
- g_assert(ret == 0);
- DEBUG_ARG("laddr = %s", addrstr);
- DEBUG_ARG("lport = %s", portstr);
+ switch (haddr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ ret = getnameinfo(haddr, haddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
+ g_assert(ret == 0);
+ DEBUG_ARG("hfamily = INET");
+ DEBUG_ARG("haddr = %s", addrstr);
+ DEBUG_ARG("hport = %s", portstr);
+ break;
+#ifndef _WIN32
+ case AF_UNIX:
+ DEBUG_ARG("hfamily = UNIX");
+ DEBUG_ARG("hpath = %s", ((struct sockaddr_un *) haddr)->sun_path);
+ break;
+#endif
+ default:
+ g_assert_not_reached();
+ }
+ switch (laddr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ ret = getnameinfo(laddr, laddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
+ g_assert(ret == 0);
+ DEBUG_ARG("laddr = %s", addrstr);
+ DEBUG_ARG("lport = %s", portstr);
+ break;
+ default:
+ g_assert_not_reached();
+ }
DEBUG_ARG("flags = %x", flags);
/*
@@ -1042,6 +1068,109 @@ void sotranslate_accept(struct socket *so)
}
break;
+ 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 = 0;
+
+ // TODO Is there a better way of checking socket type?
+ switch (so->so_type) {
+ case IPPROTO_TCP:
+ s = slirp_socket(PF_INET, SOCK_STREAM, 0);
+ break;
+ case IPPROTO_UDP:
+ s = slirp_socket(PF_INET, SOCK_DGRAM, 0);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ 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_aux = 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 = 0;
+
+ switch (so->so_type) {
+ case IPPROTO_TCP:
+ s = slirp_socket(PF_INET6, SOCK_STREAM, 0);
+ break;
+ case IPPROTO_UDP:
+ s = slirp_socket(PF_INET6, SOCK_DGRAM, 0);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ 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_aux = 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;
}
diff --git a/src/socket.h b/src/socket.h
index acafff0..ca8c103 100644
--- a/src/socket.h
+++ b/src/socket.h
@@ -6,6 +6,12 @@
#ifndef SLIRP_SOCKET_H
#define SLIRP_SOCKET_H
+#include <string.h>
+
+#ifndef _WIN32
+#include <sys/un.h>
+#endif
+
#include "misc.h"
#include "sbuf.h"
@@ -34,6 +40,8 @@ struct socket {
struct socket *so_next, *so_prev; /* For a linked list of sockets */
int s; /* The actual socket */
+ int s_aux; /* An auxiliary socket for miscellaneous use. Currently used to
+ * reserve OS ports in UNIX-to-inet translation. */
struct gfwd_list *guestfwd;
int pollfds_idx; /* GPollFD GArray index */
@@ -129,6 +137,13 @@ static inline int sockaddr_equal(const struct sockaddr_storage *a,
return (in6_equal(&a6->sin6_addr, &b6->sin6_addr) &&
a6->sin6_port == b6->sin6_port);
}
+#ifndef _WIN32
+ case AF_UNIX: {
+ const struct sockaddr_un *aun = (const struct sockaddr_un *)a;
+ const struct sockaddr_un *bun = (const struct sockaddr_un *)b;
+ return strncmp(aun->sun_path, bun->sun_path, sizeof(aun->sun_path)) == 0;
+ }
+#endif
default:
g_assert_not_reached();
}
@@ -143,6 +158,10 @@ static inline socklen_t sockaddr_size(const struct sockaddr_storage *a)
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
+#ifndef _WIN32
+ case AF_UNIX:
+ return sizeof(struct sockaddr_un);
+#endif
default:
g_assert_not_reached();
}