aboutsummaryrefslogtreecommitdiff
path: root/src/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/socket.c')
-rw-r--r--src/socket.c145
1 files changed, 137 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;
}