Add ipv4/ipv6-agnostic host forwarding functions

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
diff --git a/src/libslirp.h b/src/libslirp.h
index b52b63d..a0f0eb1 100644
--- a/src/libslirp.h
+++ b/src/libslirp.h
@@ -8,6 +8,7 @@
 
 #ifdef _WIN32
 #include <winsock2.h>
+#include <ws2tcpip.h>
 #include <in6addr.h>
 #else
 #include <netinet/in.h>
@@ -166,7 +167,16 @@
                       int host_port, struct in_addr guest_addr, int guest_port);
 int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
                          int host_port);
-/* TODO: rather introduce a function that takes two sockaddr */
+
+#define SLIRP_HOSTFWD_UDP 1
+int slirp_add_hostxfwd(Slirp *slirp,
+                       const struct sockaddr *haddr, socklen_t haddrlen,
+                       const struct sockaddr *gaddr, socklen_t gaddrlen,
+                       int flags);
+int slirp_remove_hostxfwd(Slirp *slirp,
+                          const struct sockaddr *haddr, socklen_t haddrlen,
+                          int flags);
+
 int slirp_add_ipv6_hostfwd(Slirp *slirp, int is_udp,
                            struct in6_addr host_addr, int host_port,
                            struct in6_addr guest_addr, int guest_port);
diff --git a/src/libslirp.map b/src/libslirp.map
index 20a17ac..cae6cf8 100644
--- a/src/libslirp.map
+++ b/src/libslirp.map
@@ -30,6 +30,8 @@
 } SLIRP_4.1;
 
 SLIRP_4.5 {
+    slirp_add_hostxfwd;
+    slirp_remove_hostxfwd;
     slirp_add_ipv6_hostfwd;
     slirp_remove_ipv6_hostfwd;
     slirp_neighbor_info;
diff --git a/src/slirp.c b/src/slirp.c
index 110e6fc..98cfd48 100644
--- a/src/slirp.c
+++ b/src/slirp.c
@@ -1141,6 +1141,78 @@
     return 0;
 }
 
+int slirp_remove_hostxfwd(Slirp *slirp,
+                          const struct sockaddr *haddr, socklen_t haddrlen,
+                          int flags)
+{
+    struct socket *so;
+    struct socket *head = (flags & SLIRP_HOSTFWD_UDP ? &slirp->udb : &slirp->tcb);
+    struct sockaddr_storage addr;
+    socklen_t addr_len;
+
+    for (so = head->so_next; so != head; so = so->so_next) {
+        addr_len = sizeof(addr);
+        if ((so->so_state & SS_HOSTFWD) &&
+            getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&
+            sockaddr_equal(&addr, (const struct sockaddr_storage *) haddr)) {
+            so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);
+            closesocket(so->s);
+            sofree(so);
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+int slirp_add_hostxfwd(Slirp *slirp,
+                       const struct sockaddr *haddr, socklen_t haddrlen,
+                       const struct sockaddr *gaddr, socklen_t gaddrlen,
+                       int flags)
+{
+    struct sockaddr_in gdhcp_addr;
+    if (gaddr->sa_family == AF_INET) {
+        const struct sockaddr_in *gaddr_in = (const struct sockaddr_in *) gaddr;
+
+        if (gaddrlen < sizeof(struct sockaddr_in))
+            return -1;
+
+        if (!gaddr_in->sin_addr.s_addr) {
+            gdhcp_addr = *gaddr_in;
+            gdhcp_addr.sin_addr = slirp->vdhcp_startaddr;
+            gaddr = (struct sockaddr *) &gdhcp_addr;
+            gaddrlen = sizeof(gdhcp_addr);
+        }
+    } else {
+        const struct sockaddr_in6 *gaddr_in6 = (const struct sockaddr_in6 *) gaddr;
+
+        if (gaddrlen < sizeof(struct sockaddr_in6))
+            return -1;
+
+        if (in6_zero(&gaddr_in6->sin6_addr)) {
+            /*
+             * Libslirp currently only provides a stateless DHCPv6 server, thus
+             * we can't translate "addr-any" to the guest. Instead, for now,
+             * reject it.
+             */
+            return -1;
+        }
+    }
+
+    if (flags & SLIRP_HOSTFWD_UDP) {
+        if (!udpx_listen(slirp, haddr, haddrlen,
+                                gaddr, gaddrlen,
+                                SS_HOSTFWD))
+            return -1;
+    } else {
+        if (!tcpx_listen(slirp, haddr, haddrlen,
+                                gaddr, gaddrlen,
+                                SS_HOSTFWD))
+            return -1;
+    }
+    return 0;
+}
+
 int slirp_remove_ipv6_hostfwd(Slirp *slirp, int is_udp,
                               struct in6_addr host_addr, int host_port)
 {