Merge branch 'permit-guestfwd-to-vhost-vnameserver' into 'master'

slirp: permit guestfwd to vhost_addr/vnameserver_addr

Closes #81

See merge request slirp/libslirp!158
diff --git a/meson.build b/meson.build
index f9f8fa4..75a60da 100644
--- a/meson.build
+++ b/meson.build
@@ -238,6 +238,16 @@
 
 test('ncsi', ncsitest)
 
+guestfwdtest = executable('guestfwdtest', 'test/guestfwdtest.c',
+  link_with: [lib],
+  c_args : cargs,
+  link_args : vflag,
+  include_directories: ['src'],
+  dependencies: libslirp_deps
+)
+
+test('guestfwd', guestfwdtest)
+
 if install_devel
   install_headers(['src/libslirp.h'], subdir : 'slirp')
 
diff --git a/src/slirp.c b/src/slirp.c
index 8c7cb5e..0fc51a9 100644
--- a/src/slirp.c
+++ b/src/slirp.c
@@ -1581,9 +1581,12 @@
                              (htonl(0x0204) & ~slirp->vnetwork_mask.s_addr);
     }
     if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) !=
-            slirp->vnetwork_addr.s_addr ||
-        guest_addr->s_addr == slirp->vhost_addr.s_addr ||
-        guest_addr->s_addr == slirp->vnameserver_addr.s_addr) {
+            slirp->vnetwork_addr.s_addr) {
+        return false;
+    }
+    if (!slirp->disable_dns &&
+        guest_addr->s_addr == slirp->vnameserver_addr.s_addr &&
+        guest_port == 53) {
         return false;
     }
 
diff --git a/src/tcp_input.c b/src/tcp_input.c
index 0fe0075..6617d38 100644
--- a/src/tcp_input.c
+++ b/src/tcp_input.c
@@ -600,20 +600,17 @@
         if (af == AF_INET &&
             (so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
                 slirp->vnetwork_addr.s_addr) {
-            if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr &&
-                so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) {
-                /* May be an add exec */
-                for (ex_ptr = slirp->guestfwd_list; ex_ptr;
-                     ex_ptr = ex_ptr->ex_next) {
-                    if (ex_ptr->ex_fport == so->so_fport &&
-                        so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
-                        so->so_state |= SS_CTL;
-                        break;
-                    }
+            /* May be an add exec */
+            for (ex_ptr = slirp->guestfwd_list; ex_ptr;
+                 ex_ptr = ex_ptr->ex_next) {
+                if (ex_ptr->ex_fport == so->so_fport &&
+                    so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
+                    so->so_state |= SS_CTL;
+                    break;
                 }
-                if (so->so_state & SS_CTL) {
-                    goto cont_input;
-                }
+            }
+            if (so->so_state & SS_CTL) {
+                goto cont_input;
             }
             /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */
         }
diff --git a/src/tcp_subr.c b/src/tcp_subr.c
index ff7a330..1542f18 100644
--- a/src/tcp_subr.c
+++ b/src/tcp_subr.c
@@ -992,22 +992,20 @@
     DEBUG_ARG("so = %p", so);
 
     /* TODO: IPv6 */
-    if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
-        /* Check if it's pty_exec */
-        for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
-            if (ex_ptr->ex_fport == so->so_fport &&
-                so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
-                if (ex_ptr->write_cb) {
-                    so->s = SLIRP_INVALID_SOCKET;
-                    so->guestfwd = ex_ptr;
-                    return 1;
-                }
-                DEBUG_MISC(" executing %s", ex_ptr->ex_exec);
-                if (ex_ptr->ex_unix)
-                    return open_unix(so, ex_ptr->ex_unix);
-                else
-                    return fork_exec(so, ex_ptr->ex_exec);
+    /* Check if it's pty_exec */
+    for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+        if (ex_ptr->ex_fport == so->so_fport &&
+            so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
+            if (ex_ptr->write_cb) {
+                so->s = SLIRP_INVALID_SOCKET;
+                so->guestfwd = ex_ptr;
+                return 1;
             }
+            DEBUG_MISC(" executing %s", ex_ptr->ex_exec);
+            if (ex_ptr->ex_unix)
+                return open_unix(so, ex_ptr->ex_unix);
+            else
+                return fork_exec(so, ex_ptr->ex_exec);
         }
     }
     sb->sb_cc = slirp_fmt(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data),
diff --git a/test/guestfwdtest.c b/test/guestfwdtest.c
new file mode 100644
index 0000000..6e321b6
--- /dev/null
+++ b/test/guestfwdtest.c
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+
+#include <assert.h>
+#include <stddef.h>
+
+#include "libslirp.h"
+
+static slirp_ssize_t guestfwd_write(const void *buf, size_t len, void *opaque)
+{
+    return len;
+}
+
+static SlirpCb callbacks;
+
+static struct in_addr in_addr4(uint32_t addr)
+{
+    struct in_addr ret = { .s_addr = htonl(addr) };
+
+    return ret;
+}
+
+static Slirp *guestfwd_slirp(bool disable_dns)
+{
+    SlirpConfig config = {
+        .version = SLIRP_CONFIG_VERSION_MAX,
+        .in_enabled = true,
+        .vnetwork = in_addr4(0x0a000200),
+        .vnetmask = in_addr4(0xffffff00),
+        .vhost = in_addr4(0x0a000202),
+        .vdhcp_start = in_addr4(0x0a00020f),
+        .vnameserver = in_addr4(0x0a000203),
+        .disable_dns = disable_dns,
+    };
+
+    return slirp_new(&config, &callbacks, NULL);
+}
+
+static void assert_guestfwd(Slirp *slirp, struct in_addr addr, int port,
+                            bool expect_ok)
+{
+    int ret;
+
+    ret = slirp_add_guestfwd(slirp, guestfwd_write, NULL, &addr, port);
+    assert((ret == 0) == expect_ok);
+
+    if (ret == 0) {
+        assert(slirp_remove_guestfwd(slirp, addr, port) == 0);
+    }
+}
+
+static void test_guestfwd_validation(void)
+{
+    struct in_addr vhost = in_addr4(0x0a000202);
+    struct in_addr vnameserver = in_addr4(0x0a000203);
+    Slirp *slirp;
+
+    slirp = guestfwd_slirp(false);
+    assert_guestfwd(slirp, vhost, 8080, true);
+    assert_guestfwd(slirp, vnameserver, 8080, true);
+    assert_guestfwd(slirp, vhost, 67, true);
+    assert_guestfwd(slirp, vhost, 69, true);
+    assert_guestfwd(slirp, vnameserver, 53, false);
+    slirp_cleanup(slirp);
+
+    slirp = guestfwd_slirp(true);
+    assert_guestfwd(slirp, vnameserver, 53, true);
+    slirp_cleanup(slirp);
+}
+
+int main(int argc, char *argv[])
+{
+    test_guestfwd_validation();
+}