Commit b37014de authored by Dag Erling Smørgrav's avatar Dag Erling Smørgrav

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: http://www.varnish-cache.org/svn/trunk/varnish-cache@1500 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 4bd7291a
......@@ -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);
......@@ -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);
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -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) {
......
......@@ -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);
}
......
......@@ -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);
}
......@@ -30,5 +30,5 @@ noinst_HEADERS = \
vcl.h \
vcl_returns.h \
vrt.h \
vrt_obj.h
vrt_obj.h \
vss.h
/*-
* 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);
......@@ -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
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006 Linpro AS
* All rights reserved.
*
* Author: Dag-Erling Smørgrav <des@linpro.no>
* Author: Cecilie Fritzvold <cecilihf@linpro.no>
*
* 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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment