Implement `portfwd-loopback-choice'. Works on local side in Unix as
[u/mdw/putty] / winnet.c
index b5ca741..9d3f66c 100644 (file)
--- a/winnet.c
+++ b/winnet.c
@@ -55,6 +55,9 @@
 #include "network.h"
 #include "tree234.h"
 
+#define ipv4_is_loopback(addr) \
+       ((ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L)
+
 struct Socket_tag {
     struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
@@ -654,7 +657,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     return (Socket) ret;
 }
 
-Socket sk_newlistener(int port, Plug plug, int local_host_only)
+Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
 {
     static struct socket_function_table fn_table = {
        sk_tcp_plug,
@@ -716,6 +719,8 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only)
        if (addr->family == AF_INET6) {
            memset(&a6, 0, sizeof(a6));
            a6.sin6_family = AF_INET6;
+           /* FIXME: srcaddr is ignored for IPv6, because I (SGT) don't
+            * know how to do it. :-) */
            if (local_host_only)
                a6.sin6_addr = in6addr_loopback;
            else
@@ -724,11 +729,32 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only)
        } else
 #endif
        {
+           int got_addr = 0;
            a.sin_family = AF_INET;
-           if (local_host_only)
-               a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-           else
-               a.sin_addr.s_addr = htonl(INADDR_ANY);
+
+           /*
+            * Bind to source address. First try an explicitly
+            * specified one...
+            */
+           if (srcaddr) {
+               a.sin_addr.s_addr = inet_addr(srcaddr);
+               if (a.sin_addr.s_addr != INADDR_NONE) {
+                   /* Override localhost_only with specified listen addr. */
+                   ret->localhost_only = ipv4_is_loopback(a.sin_addr);
+                   got_addr = 1;
+               }
+           }
+
+           /*
+            * ... and failing that, go with one of the standard ones.
+            */
+           if (!got_addr) {
+               if (local_host_only)
+                   a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+               else
+                   a.sin_addr.s_addr = htonl(INADDR_ANY);
+           }
+
            a.sin_port = htons((short)port);
        }
 #ifdef IPV6
@@ -1047,8 +1073,7 @@ int select_result(WPARAM wParam, LPARAM lParam)
                    break;
            }
 
-           if (s->localhost_only &&
-               ntohl(isa.sin_addr.s_addr) != INADDR_LOOPBACK) {
+           if (s->localhost_only && !ipv4_is_loopback(isa.sin_addr)) {
                closesocket(t);        /* dodgy WinSock let nonlocal through */
            } else if (plug_accepting(s->plug, (void*)t)) {
                closesocket(t);        /* denied or error */