From 50e350cc66e7053ccfd1ca523d89dfe8ddb8e1c4 Mon Sep 17 00:00:00 2001 From: des Date: Wed, 6 Jun 2007 11:24:06 +0000 Subject: [PATCH] Move parts of tcp.c out into libvarnish. Rename the API from "TCP" to "VSS" (Varnish Stream Sockets) as I intend to eventually add support for AF_UNIX sockets. This also moves the accept filter code out from VSS_listen() (previously TCP_open()) and into a separate function in tcp.c git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@1500 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- varnish-cache/bin/varnishd/common.h | 4 +- varnish-cache/bin/varnishd/heritage.h | 2 +- varnish-cache/bin/varnishd/mgt_child.c | 4 +- varnish-cache/bin/varnishd/mgt_cli.c | 10 +- varnish-cache/bin/varnishd/mgt_param.c | 8 +- varnish-cache/bin/varnishd/mgt_vcc.c | 4 +- varnish-cache/bin/varnishd/tcp.c | 168 +---------------- varnish-cache/include/Makefile.am | 4 +- varnish-cache/include/vss.h | 36 ++++ varnish-cache/lib/libvarnish/Makefile.am | 3 +- varnish-cache/lib/libvarnish/vss.c | 223 +++++++++++++++++++++++ 11 files changed, 291 insertions(+), 175 deletions(-) create mode 100644 varnish-cache/include/vss.h create mode 100644 varnish-cache/lib/libvarnish/vss.c diff --git a/varnish-cache/bin/varnishd/common.h b/varnish-cache/bin/varnishd/common.h index be83743f..59eb63d4 100644 --- a/varnish-cache/bin/varnishd/common.h +++ b/varnish-cache/bin/varnishd/common.h @@ -45,6 +45,4 @@ 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_resolve(const char *addr, const char *port, struct tcp_addr ***ta); -int TCP_open(const struct tcp_addr *addr, int http); +int TCP_filter_http(int sock); diff --git a/varnish-cache/bin/varnishd/heritage.h b/varnish-cache/bin/varnishd/heritage.h index a89cdff4..65985d64 100644 --- a/varnish-cache/bin/varnishd/heritage.h +++ b/varnish-cache/bin/varnishd/heritage.h @@ -36,7 +36,7 @@ struct listen_sock { TAILQ_ENTRY(listen_sock) list; int sock; - struct tcp_addr *addr; + struct vss_addr *addr; }; TAILQ_HEAD(listen_sock_head, listen_sock); diff --git a/varnish-cache/bin/varnishd/mgt_child.c b/varnish-cache/bin/varnishd/mgt_child.c index dd09d482..726a4684 100644 --- a/varnish-cache/bin/varnishd/mgt_child.c +++ b/varnish-cache/bin/varnishd/mgt_child.c @@ -54,6 +54,7 @@ #include "cli_priv.h" #include "mgt_cli.h" #include "mgt_event.h" +#include "vss.h" pid_t mgt_pid; pid_t child_pid = -1; @@ -130,7 +131,8 @@ open_sockets(void) TAILQ_FOREACH(ls, &heritage.socks, list) { if (ls->sock >= 0) continue; - ls->sock = TCP_open(ls->addr, 1); + ls->sock = VSS_listen(ls->addr, params->listen_depth); + TCP_filter_http(ls->sock); if (ls->sock < 0) return (1); } diff --git a/varnish-cache/bin/varnishd/mgt_cli.c b/varnish-cache/bin/varnishd/mgt_cli.c index 32cc3768..c86ac79b 100644 --- a/varnish-cache/bin/varnishd/mgt_cli.c +++ b/varnish-cache/bin/varnishd/mgt_cli.c @@ -54,6 +54,8 @@ #include "mgt_event.h" #include "shmlog.h" +#include "vss.h" + static int cli_i = -1, cli_o = -1; /*--------------------------------------------------------------------*/ @@ -389,12 +391,12 @@ telnet_accept(struct ev *ev, int what) int mgt_cli_telnet(const char *T_arg) { - struct tcp_addr **ta; + struct vss_addr **ta; char *addr, *port; int i, n; - XXXAZ(TCP_parse(T_arg, &addr, &port)); - XXXAN(n = TCP_resolve(addr, port, &ta)); + XXXAZ(VSS_parse(T_arg, &addr, &port)); + XXXAN(n = VSS_resolve(addr, port, &ta)); free(addr); free(port); if (n == 0) { @@ -402,7 +404,7 @@ mgt_cli_telnet(const char *T_arg) exit(2); } for (i = 0; i < n; ++i) { - int sock = TCP_open(ta[i], 0); + int sock = VSS_listen(ta[i], 1); struct ev *ev = ev_new(); XXXAN(ev); ev->fd = sock; diff --git a/varnish-cache/bin/varnishd/mgt_param.c b/varnish-cache/bin/varnishd/mgt_param.c index 1a8b230a..648fe0ec 100644 --- a/varnish-cache/bin/varnishd/mgt_param.c +++ b/varnish-cache/bin/varnishd/mgt_param.c @@ -47,6 +47,8 @@ #include "heritage.h" +#include "vss.h" + struct parspec; typedef void tweak_t(struct cli *, struct parspec *, const char *arg); @@ -392,16 +394,16 @@ tweak_listen_address(struct cli *cli, struct parspec *par, const char *arg) } TAILQ_INIT(&lsh); for (i = 1; av[i] != NULL; i++) { - struct tcp_addr **ta; + struct vss_addr **ta; char *host, *port; int j, n; - if (TCP_parse(av[i], &host, &port) != 0) { + if (VSS_parse(av[i], &host, &port) != 0) { cli_out(cli, "Invalid listen address \"%s\"", av[i]); cli_result(cli, CLIS_PARAM); break; } - n = TCP_resolve(host, port ? port : "http", &ta); + n = VSS_resolve(host, port ? port : "http", &ta); free(host); free(port); if (n == 0) { diff --git a/varnish-cache/bin/varnishd/mgt_vcc.c b/varnish-cache/bin/varnishd/mgt_vcc.c index fb062491..0241e744 100644 --- a/varnish-cache/bin/varnishd/mgt_vcc.c +++ b/varnish-cache/bin/varnishd/mgt_vcc.c @@ -53,6 +53,8 @@ #include "mgt.h" #include "mgt_cli.h" +#include "vss.h" + struct vclprog { TAILQ_ENTRY(vclprog) list; char *name; @@ -320,7 +322,7 @@ mgt_vcc_default(const char *b_arg, const char *f_arg, int C_flag) * XXX: a bug for a backend to not reply at that time, so then * XXX: again: we should check it here in the "trivial" case. */ - if (TCP_parse(b_arg, &addr, &port) != 0 || addr == NULL) { + if (VSS_parse(b_arg, &addr, &port) != 0 || addr == NULL) { fprintf(stderr, "invalid backend address\n"); return (1); } diff --git a/varnish-cache/bin/varnishd/tcp.c b/varnish-cache/bin/varnishd/tcp.c index 1056237f..c116a3f2 100644 --- a/varnish-cache/bin/varnishd/tcp.c +++ b/varnish-cache/bin/varnishd/tcp.c @@ -53,15 +53,6 @@ #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 @@ -100,166 +91,25 @@ TCP_myname(int sock, char *abuf, unsigned alen, char *pbuf, unsigned plen) /*--------------------------------------------------------------------*/ -#ifdef HAVE_ACCEPT_FILTERS -static void -accept_filter(int fd) +int +TCP_filter_http(int sock) { +#ifdef HAVE_ACCEPT_FILTERS struct accept_filter_arg afa; int i; bzero(&afa, sizeof(afa)); strcpy(afa.af_name, "httpready"); errno = 0; - i = setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, + i = setsockopt(sock, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)); + /* XXX ugly */ if (i) printf("Acceptfilter(%d, httpready): %d %s\n", - fd, i, strerror(errno)); -} -#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) -{ - const char *p; - - *addr = *port = NULL; - - if (str[0] == '[') { - /* IPv6 address of the form [::1]:80 */ - if ((p = strchr(str, ']')) == NULL || - p == str + 1 || - (p[1] != '\0' && p[1] != ':')) - return (-1); - *addr = strndup(str + 1, p - (str + 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) { - *addr = strndup(str, p - str); - XXXAN(*addr); - } - *port = strdup(p + 1); - XXXAN(*port); - } - } - return (0); -} - -/* - * 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, *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); - } - freeaddrinfo(res0); + sock, i, strerror(errno)); 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 struct tcp_addr *ta, int http) -{ - int sd, val; - - sd = socket(ta->ta_family, ta->ta_socktype, ta->ta_protocol); - if (sd < 0) { - perror("socket()"); - return (-1); - } - val = 1; - if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val) != 0) { - perror("setsockopt(SO_REUSEADDR, 1)"); - close(sd); - return (-1); - } -#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()"); - close(sd); - return (-1); - } - if (listen(sd, http ? params->listen_depth : 16) != 0) { - perror("listen()"); - close(sd); - return (-1); - } -#ifdef HAVE_ACCEPT_FILTERS - if (http) - accept_filter(sd); +#else + (void)sock; + return (0); #endif - return (sd); } diff --git a/varnish-cache/include/Makefile.am b/varnish-cache/include/Makefile.am index aaa8d2a8..5b62b62e 100644 --- a/varnish-cache/include/Makefile.am +++ b/varnish-cache/include/Makefile.am @@ -30,5 +30,5 @@ noinst_HEADERS = \ vcl.h \ vcl_returns.h \ vrt.h \ - vrt_obj.h - + vrt_obj.h \ + vss.h diff --git a/varnish-cache/include/vss.h b/varnish-cache/include/vss.h new file mode 100644 index 00000000..7302a81c --- /dev/null +++ b/varnish-cache/include/vss.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2006 Verdens Gang AS + * Copyright (c) 2006 Linpro AS + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +/* vss.c */ +struct vss_addr; + +int VSS_parse(const char *str, char **addr, char **port); +int VSS_resolve(const char *addr, const char *port, struct vss_addr ***ta); +int VSS_listen(const struct vss_addr *addr, int depth); +int VSS_connect(const struct vss_addr *addr); diff --git a/varnish-cache/lib/libvarnish/Makefile.am b/varnish-cache/lib/libvarnish/Makefile.am index d2d1e0cd..f68a4fd1 100644 --- a/varnish-cache/lib/libvarnish/Makefile.am +++ b/varnish-cache/lib/libvarnish/Makefile.am @@ -15,6 +15,7 @@ libvarnish_la_SOURCES = \ time.c \ version.c \ vpf.c \ - vsb.c + vsb.c \ + vss.c libvarnish_la_CFLAGS = -include config.h diff --git a/varnish-cache/lib/libvarnish/vss.c b/varnish-cache/lib/libvarnish/vss.c new file mode 100644 index 00000000..03cf8a48 --- /dev/null +++ b/varnish-cache/lib/libvarnish/vss.c @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 2006 Verdens Gang AS + * Copyright (c) 2006 Linpro AS + * All rights reserved. + * + * Author: Dag-Erling Smørgrav + * Author: Cecilie Fritzvold + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_STRLCPY +#include "compat/strlcpy.h" +#endif +#ifndef HAVE_STRNDUP +#include "compat/strndup.h" +#endif + +#include "libvarnish.h" +#include "vss.h" + +/* lightweight addrinfo */ +struct vss_addr { + int va_family; + int va_socktype; + int va_protocol; + socklen_t va_addrlen; + struct sockaddr_storage va_addr; +}; + +/* + * 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 +VSS_parse(const char *str, char **addr, char **port) +{ + const char *p; + + *addr = *port = NULL; + + if (str[0] == '[') { + /* IPv6 address of the form [::1]:80 */ + if ((p = strchr(str, ']')) == NULL || + p == str + 1 || + (p[1] != '\0' && p[1] != ':')) + return (-1); + *addr = strndup(str + 1, p - (str + 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) { + *addr = strndup(str, p - str); + XXXAN(*addr); + } + *port = strdup(p + 1); + XXXAN(*port); + } + } + return (0); +} + +/* + * For a given host and port, return a list of struct vss_addr, which + * contains all the information necessary to open and bind a socket. One + * vss_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 vss_addr. The caller is responsible for + * freeing each individual struct vss_addr as well as the array. + * + * The return value is the number of addresses resoved, or zero. + */ +int +VSS_resolve(const char *addr, const char *port, struct vss_addr ***vap) +{ + struct addrinfo hints, *res0, *res; + struct vss_addr **va; + 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; + va = calloc(i, sizeof *va); + XXXAN(va); + *vap = va; + for (res = res0, i = 0; res != NULL; res = res->ai_next, ++i) { + va[i] = calloc(1, sizeof *va[i]); + XXXAN(va[i]); + va[i]->va_family = res->ai_family; + va[i]->va_socktype = res->ai_socktype; + va[i]->va_protocol = res->ai_protocol; + va[i]->va_addrlen = res->ai_addrlen; + xxxassert(va[i]->va_addrlen <= sizeof va[i]->va_addr); + memcpy(&va[i]->va_addr, res->ai_addr, va[i]->va_addrlen); + } + freeaddrinfo(res0); + return (i); +} + +/* + * Given a struct vss_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. + */ +int +VSS_listen(const struct vss_addr *va, int depth) +{ + int sd, val; + + sd = socket(va->va_family, va->va_socktype, va->va_protocol); + if (sd < 0) { + perror("socket()"); + return (-1); + } + val = 1; + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val) != 0) { + perror("setsockopt(SO_REUSEADDR, 1)"); + close(sd); + return (-1); + } +#ifdef IPV6_V6ONLY + /* forcibly use separate sockets for IPv4 and IPv6 */ + val = 1; + if (va->va_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 *)&va->va_addr, va->va_addrlen) != 0) { + perror("bind()"); + close(sd); + return (-1); + } + if (listen(sd, depth) != 0) { + perror("listen()"); + close(sd); + return (-1); + } + return (sd); +} + +/* + * Connect to the socket specified by the address info in va. + * Return the socket. + */ +int +VSS_connect(const struct vss_addr *va) +{ + int sd; + + sd = socket(va->va_family, va->va_socktype, va->va_protocol); + if (sd < 0) { + perror("socket()"); + return (-1); + } + if (connect(sd, (const struct sockaddr *)&va->va_addr, va->va_addrlen) != 0) { + perror("connect()"); + close(sd); + return (-1); + } + return (sd); +} -- 2.39.5