]> err.no Git - varnish/commitdiff
Attempt to fix the bind-to-any problem:
authordes <des@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Tue, 15 May 2007 11:35:44 +0000 (11:35 +0000)
committerdes <des@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Tue, 15 May 2007 11:35:44 +0000 (11:35 +0000)
 - Introduce a "struct tcp_addr" which is a lightweight form of struct
   addrinfo for our own internal use.

 - Add a TCP_resolve() function which takes the output from TCP_parse()
   and fills in a list of pointers to struct tcp_addr, one for each
   address returned by getaddrinfo().

 - Modify all TCP_open() callers to use TCP_resolve() and call TCP_open()
   once for every address returned.

 - Add some explanatory comments to tcp.c.

git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@1423 d4fa192b-c00b-0410-8231-f00ffab90ce4

varnish-cache/bin/varnishd/common.h
varnish-cache/bin/varnishd/heritage.h
varnish-cache/bin/varnishd/mgt_child.c
varnish-cache/bin/varnishd/mgt_cli.c
varnish-cache/bin/varnishd/mgt_param.c
varnish-cache/bin/varnishd/tcp.c

index 70719aa9074092dd784b678388ffd5f19cb470e9..13a5099ad4253a5f942fc64228521744d3e1960c 100644 (file)
@@ -41,8 +41,10 @@ extern struct varnish_stats *VSL_stats;
 #define TCP_ADDRBUFSIZE                64
 #define TCP_PORTBUFSIZE                16
 
+struct tcp_addr;
+
 void TCP_name(struct sockaddr *addr, unsigned l, char *abuf, unsigned alen, char *pbuf, unsigned plen);
 void TCP_myname(int sock, char *abuf, unsigned alen, char *pbuf, unsigned plen);
 int TCP_parse(const char *str, char **addr, char **port);
-int TCP_open(const char *addr, const char *port, int http);
-void TCP_check(struct cli *cli, const char *addr, const char *port);
+int TCP_resolve(const char *addr, const char *port, struct tcp_addr ***ta);
+int TCP_open(const struct tcp_addr *addr, int http);
index 8853951186ca029d00e787854e6964b0e2d464d8..95660943af817a08c6f98f66a7d60941308ec349 100644 (file)
@@ -36,8 +36,7 @@
 struct listen_sock {
        TAILQ_ENTRY(listen_sock)        list;
        int                             sock;
-       char                            *host;
-       char                            *port;
+       struct tcp_addr                 *addr;
 };
 
 TAILQ_HEAD(listen_sock_head, listen_sock);
index 4f8cece44f10d6133abefaf6f454c087b0c07503..5eba38f7e3435f569e43855701dad8596d0c4ef5 100644 (file)
@@ -130,7 +130,7 @@ open_sockets(void)
        TAILQ_FOREACH(ls, &heritage.socks, list) {
                if (ls->sock >= 0)
                        continue;
-               ls->sock = TCP_open(ls->host, ls->port, 1);
+               ls->sock = TCP_open(ls->addr, 1);
                if (ls->sock < 0)
                        return (1);
        }
index d7eb3dfd9e277740244ef1404a31fe719c8fa7f6..160eb8fd6895cefbfca1f7d808b3eef6837be09f 100644 (file)
@@ -55,8 +55,6 @@
 #include "shmlog.h"
 
 static int             cli_i = -1, cli_o = -1;
-static int             telnet_sock;
-static struct ev       *telnet_ev;
 
 /*--------------------------------------------------------------------*/
 
@@ -374,14 +372,13 @@ mgt_cli_setup(int fdi, int fdo, int verbose)
 static int
 telnet_accept(struct ev *ev, int what)
 {
-       socklen_t l;
-       struct sockaddr addr[2];        /* XXX IPv6 hack */
+       struct sockaddr_storage addr;
+       socklen_t addrlen;
        int i;
 
-       (void)ev;
        (void)what;
-       l = sizeof addr;
-       i = accept(telnet_sock, addr, &l);
+       addrlen = sizeof addr;
+       i = accept(ev->fd, (struct sockaddr *)&addr, &addrlen);
        if (i < 0)
                return (0);
 
@@ -392,21 +389,29 @@ telnet_accept(struct ev *ev, int what)
 int
 mgt_cli_telnet(const char *T_arg)
 {
+       struct tcp_addr **ta;
        char *addr, *port;
+       int i, n;
 
-       TCP_parse(T_arg, &addr, &port);
-       telnet_sock = TCP_open(addr, port, 0);
+       XXXAZ(TCP_parse(T_arg, &addr, &port));
+       XXXAN(n = TCP_resolve(addr, port, &ta));
        free(addr);
        free(port);
-       if (telnet_sock < 0) {
+       if (n == 0) {
                fprintf(stderr, "Could not open TELNET port\n");
-               exit (2);
+               exit(2);
        }
-       telnet_ev = ev_new();
-       XXXAN(telnet_ev);
-       telnet_ev->fd = telnet_sock;
-       telnet_ev->fd_flags = POLLIN;
-       telnet_ev->callback = telnet_accept;
-       ev_add(mgt_evb, telnet_ev);
+       for (i = 0; i < n; ++i) {
+               int sock = TCP_open(ta[i], 0);
+               struct ev *ev = ev_new();
+               XXXAN(ev);
+               ev->fd = sock;
+               ev->fd_flags = POLLIN;
+               ev->callback = telnet_accept;
+               ev_add(mgt_evb, ev);
+               free(ta[i]);
+               ta[i] = NULL;
+       }
+       free(ta);
        return (0);
 }
index 32372c5af52047934403120da49bde7a7807f8ad..8f08e5a15f4c85f51c8e2ecd8f7ac100edb3cd65 100644 (file)
  * $Id$
  */
 
+#include <limits.h>
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
-#include <limits.h>
 
 #include "cli.h"
 #include "cli_priv.h"
@@ -284,8 +284,7 @@ clean_listen_sock_head(struct listen_sock_head *lsh)
 
        TAILQ_FOREACH_SAFE(ls, lsh, list, ls2) {
                TAILQ_REMOVE(lsh, ls, list);
-               free(ls->host);
-               free(ls->port);
+               free(ls->addr);
                free(ls);
        }
 }
@@ -323,21 +322,31 @@ tweak_listen_address(struct cli *cli, struct parspec *par, const char *arg)
        }
        TAILQ_INIT(&lsh);
        for (i = 1; av[i] != NULL; i++) {
-               ls = calloc(sizeof *ls, 1);
-               AN(ls);
-               ls->sock = -1;
-               TAILQ_INSERT_TAIL(&lsh, ls, list);
-               if (TCP_parse(av[i], &ls->host, &ls->port) != 0) {
+               struct tcp_addr **ta;
+               char *host, *port;
+               int j, n;
+
+               if (TCP_parse(av[i], &host, &port) != 0) {
                        cli_out(cli, "Invalid listen address \"%s\"", av[i]);
                        cli_result(cli, CLIS_PARAM);
                        break;
                }
-               if (ls->port == NULL)
-                       ls->port = strdup("http");
-               AN(ls->port);
-               TCP_check(cli, ls->host, ls->port);
-               if (cli->result != CLIS_OK)
+               n = TCP_resolve(host, port ? port : "http", &ta);
+               free(host);
+               free(port);
+               if (n == 0) {
+                       cli_out(cli, "Invalid listen address \"%s\"", av[i]);
+                       cli_result(cli, CLIS_PARAM);
                        break;
+               }
+               for (j = 0; j < n; ++j) {
+                       ls = calloc(sizeof *ls, 1);
+                       AN(ls);
+                       ls->sock = -1;
+                       ls->addr = ta[j];
+                       TAILQ_INSERT_TAIL(&lsh, ls, list);
+               }
+               free(ta);
        }
        FreeArgv(av);
        if (cli->result != CLIS_OK) {
index ea8b4a94829b76e6f7ea444dedb249115b6b2dba..b8108c8e3070873ad0cd27fb85202e6e750cf2a8 100644 (file)
 #include "cli.h"
 #include "cli_priv.h"
 
+/* lightweight addrinfo */
+struct tcp_addr {
+       int                      ta_family;
+       int                      ta_socktype;
+       int                      ta_protocol;
+       socklen_t                ta_addrlen;
+       struct sockaddr_storage  ta_addr;
+};
+
 /*--------------------------------------------------------------------*/
 
 void
@@ -109,6 +118,16 @@ accept_filter(int fd)
 }
 #endif
 
+/*
+ * Take a string provided by the user and break it up into address and
+ * port parts.  Examples of acceptable input include:
+ *
+ * "localhost" - "localhost:80"
+ * "127.0.0.1" - "127.0.0.1:80"
+ * "0.0.0.0" - "0.0.0.0:80"
+ * "[::1]" - "[::1]:80"
+ * "[::]" - "[::]:80"
+ */
 int
 TCP_parse(const char *str, char **addr, char **port)
 {
@@ -123,88 +142,124 @@ TCP_parse(const char *str, char **addr, char **port)
                    (p[1] != '\0' && p[1] != ':'))
                        return (-1);
                *addr = strndup(str + 1, p - (str + 1));
-               if (p[1] == ':')
+               XXXAN(*addr);
+               if (p[1] == ':') {
                        *port = strdup(p + 2);
+                       XXXAN(*port);
+               }
        } else {
                /* IPv4 address of the form 127.0.0.1:80, or non-numeric */
                p = strchr(str, ':');
                if (p == NULL) {
                        *addr = strdup(str);
+                       XXXAN(*addr);
                } else {
-                       if (p > str)
+                       if (p > str) {
                                *addr = strndup(str, p - str);
+                               XXXAN(*addr);
+                       }
                        *port = strdup(p + 1);
+                       XXXAN(*port);
                }
        }
        return (0);
 }
 
-/*--------------------------------------------------------------------*/
-
-void
-TCP_check(struct cli *cli, const char *addr, const char *port)
+/*
+ * For a given host and port, return a list of struct tcp_addr, which
+ * contains all the information necessary to open and bind a socket.  One
+ * tcp_addr is returned for each distinct address returned by
+ * getaddrinfo().
+ *
+ * The value pointed to by the tap parameter receives a pointer to an
+ * array of pointers to struct tcp_addr.  The caller is responsible for
+ * freeing each individual struct tcp_addr as well as the array.
+ *
+ * The return value is the number of addresses resoved, or zero.
+ */
+int
+TCP_resolve(const char *addr, const char *port, struct tcp_addr ***tap)
 {
-       struct addrinfo hints, *res;
-       int ret;
-
-       memset(&hints, 0, sizeof hints);
-       hints.ai_socktype = SOCK_STREAM;
-       hints.ai_flags = AI_PASSIVE;
-       ret = getaddrinfo(addr, port, &hints, &res);
-       if (ret == 0) {
-               freeaddrinfo(res);
-               return;
+       struct addrinfo hints, *res0, *res;
+       struct tcp_addr **ta;
+       int i, ret;
+
+        memset(&hints, 0, sizeof hints);
+        hints.ai_socktype = SOCK_STREAM;
+        hints.ai_flags = AI_PASSIVE;
+        ret = getaddrinfo(addr, port, &hints, &res0);
+        if (ret != 0) {
+                fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(ret));
+                return (0);
+        }
+       for (res = res0, i = 0; res != NULL; res = res->ai_next)
+               ++i;
+       ta = calloc(i, sizeof *ta);
+       XXXAN(ta);
+       *tap = ta;
+       for (res = res0, i = 0; res != NULL; res = res->ai_next, ++i) {
+               ta[i] = calloc(1, sizeof *ta[i]);
+               XXXAN(ta[i]);
+               ta[i]->ta_family = res->ai_family;
+               ta[i]->ta_socktype = res->ai_socktype;
+               ta[i]->ta_protocol = res->ai_protocol;
+               ta[i]->ta_addrlen = res->ai_addrlen;
+               xxxassert(ta[i]->ta_addrlen <= sizeof ta[i]->ta_addr);
+               memcpy(&ta[i]->ta_addr, res->ai_addr, ta[i]->ta_addrlen);
        }
-       cli_out(cli, "getaddrinfo(%s, %s): %s\n",
-           addr, port, gai_strerror(ret));
-       cli_result(cli, CLIS_PARAM);
+       freeaddrinfo(res0);
+       return (i);
 }
 
+/*
+ * Given a struct tcp_addr, open a socket of the appropriate type, bind it
+ * to the requested address, and start listening.
+ *
+ * If the address is an IPv6 address, the IPV6_V6ONLY option is set to
+ * avoid conflicts between INADDR_ANY and IN6ADDR_ANY.
+ *
+ * If the http parameter is non-zero and accept filters are available,
+ * install an HTTP accept filter on the socket.
+ */
 int
-TCP_open(const char *addr, const char *port, int http)
+TCP_open(const struct tcp_addr *ta, int http)
 {
-       struct addrinfo hints, *res;
-       int ret, sd, val;
-
-       memset(&hints, 0, sizeof hints);
-       hints.ai_socktype = SOCK_STREAM;
-       hints.ai_flags = AI_PASSIVE;
-       ret = getaddrinfo(addr, port, &hints, &res);
-       if (ret != 0) {
-               fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(ret));
-               return (-1);
-       }
-       sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+       int sd, val;
+
+       sd = socket(ta->ta_family, ta->ta_socktype, ta->ta_protocol);
        if (sd < 0) {
                perror("socket()");
-               freeaddrinfo(res);
                return (-1);
        }
        val = 1;
        if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val) != 0) {
                perror("setsockopt(SO_REUSEADDR, 1)");
-               freeaddrinfo(res);
                close(sd);
                return (-1);
        }
-       if (bind(sd, res->ai_addr, res->ai_addrlen) != 0) {
+#ifdef IPV6_V6ONLY
+       /* forcibly use separate sockets for IPv4 and IPv6 */
+       val = 1;
+       if (ta->ta_family == AF_INET6 &&
+           setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof val) != 0) {
+               perror("setsockopt(IPV6_V6ONLY, 1)");
+               close(sd);
+               return (-1);
+       }
+#endif
+       if (bind(sd, (const struct sockaddr *)&ta->ta_addr, ta->ta_addrlen) != 0) {
                perror("bind()");
-               freeaddrinfo(res);
                close(sd);
                return (-1);
        }
        if (listen(sd, http ? params->listen_depth : 16) != 0) {
                perror("listen()");
-               freeaddrinfo(res);
                close(sd);
                return (-1);
        }
 #ifdef HAVE_ACCEPT_FILTERS
        if (http)
                accept_filter(sd);
-#else
-       (void)http;
 #endif
-       freeaddrinfo(res);
        return (sd);
 }