Merge branch 'listen_v6only' into 'master'

Listen v6only

See merge request slirp/libslirp!77
diff --git a/src/libslirp.h b/src/libslirp.h
index ab446ca..aac182e 100644
--- a/src/libslirp.h
+++ b/src/libslirp.h
@@ -169,6 +169,7 @@
                          int host_port);
 
 #define SLIRP_HOSTFWD_UDP 1
+#define SLIRP_HOSTFWD_V6ONLY 2
 int slirp_add_hostxfwd(Slirp *slirp,
                        const struct sockaddr *haddr, socklen_t haddrlen,
                        const struct sockaddr *gaddr, socklen_t gaddrlen,
diff --git a/src/slirp.c b/src/slirp.c
index fcaa179..ba41843 100644
--- a/src/slirp.c
+++ b/src/slirp.c
@@ -1172,6 +1172,11 @@
                        int flags)
 {
     struct sockaddr_in gdhcp_addr;
+    int fwd_flags = SS_HOSTFWD;
+
+    if (flags & SLIRP_HOSTFWD_V6ONLY)
+        fwd_flags |= SS_HOSTFWD_V6ONLY;
+
     if (gaddr->sa_family == AF_INET) {
         const struct sockaddr_in *gaddr_in = (const struct sockaddr_in *) gaddr;
 
@@ -1208,12 +1213,12 @@
     if (flags & SLIRP_HOSTFWD_UDP) {
         if (!udpx_listen(slirp, haddr, haddrlen,
                                 gaddr, gaddrlen,
-                                SS_HOSTFWD))
+                                fwd_flags))
             return -1;
     } else {
         if (!tcpx_listen(slirp, haddr, haddrlen,
                                 gaddr, gaddrlen,
-                                SS_HOSTFWD))
+                                fwd_flags))
             return -1;
     }
     return 0;
diff --git a/src/socket.c b/src/socket.c
index 2e5fbda..bf50058 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -780,6 +780,7 @@
 
     s = slirp_socket(haddr->sa_family, SOCK_STREAM, 0);
     if ((s < 0) ||
+        (haddr->sa_family == AF_INET6 && slirp_socket_set_v6only(s, (flags & SS_HOSTFWD_V6ONLY) != 0) < 0) ||
         (slirp_socket_set_fast_reuse(s) < 0) ||
         (bind(s, haddr, haddrlen) < 0) ||
         (listen(s, 1) < 0)) {
diff --git a/src/socket.h b/src/socket.h
index 518aebb..932f391 100644
--- a/src/socket.h
+++ b/src/socket.h
@@ -106,6 +106,7 @@
 #define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */
 #define SS_INCOMING \
     0x2000 /* Connection was initiated by a host on the internet */
+#define SS_HOSTFWD_V6ONLY 0x4000 /* Only bind on v6 addresses */
 
 static inline int sockaddr_equal(const struct sockaddr_storage *a,
                                  const struct sockaddr_storage *b)
diff --git a/src/udp.c b/src/udp.c
index bb65ecf..b9694c2 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -370,6 +370,8 @@
         errno = save_errno;
         return NULL;
     }
+    if (haddr->sa_family == AF_INET6)
+        slirp_socket_set_v6only(so->s, (flags & SS_HOSTFWD_V6ONLY) != 0);
     so->so_expire = curtime + SO_EXPIRE;
     insque(so, &slirp->udb);
 
diff --git a/src/util.h b/src/util.h
index 8134db9..07654ec 100644
--- a/src/util.h
+++ b/src/util.h
@@ -39,6 +39,7 @@
 #ifdef _WIN32
 #include <winsock2.h>
 #include <windows.h>
+#include <ws2tcpip.h>
 #else
 #include <sys/socket.h>
 #include <netinet/tcp.h>
@@ -161,6 +162,11 @@
 int slirp_socket(int domain, int type, int protocol);
 void slirp_set_nonblock(int fd);
 
+static inline int slirp_socket_set_v6only(int fd, int v)
+{
+    return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v, sizeof(v));
+}
+
 static inline int slirp_socket_set_nodelay(int fd)
 {
     int v = 1;