source: lib/libvarnish/vss.c @ 080f25

Revision 080f25, 7.4 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Push "vas.h", <stdint.h>, and NULL out of libvarnish.h

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2010 Varnish Software AS
4 * All rights reserved.
5 *
6 * Author: Dag-Erling Smørgrav <des@des.no>
7 * Author: Cecilie Fritzvold <cecilihf@linpro.no>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#include <sys/types.h>
34#include <sys/socket.h>
35
36#include <netinet/in.h>
37
38#include <errno.h>
39#include <netdb.h>
40#include <poll.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#include "vas.h"
47#include "vtcp.h"
48#include "vss.h"
49
50/* lightweight addrinfo */
51struct vss_addr {
52        int                      va_family;
53        int                      va_socktype;
54        int                      va_protocol;
55        socklen_t                va_addrlen;
56        struct sockaddr_storage  va_addr;
57};
58
59/*lint -esym(754, _storage) not ref */
60
61/*
62 * Take a string provided by the user and break it up into address and
63 * port parts.  Examples of acceptable input include:
64 *
65 * "localhost" - "localhost:80"
66 * "127.0.0.1" - "127.0.0.1:80"
67 * "0.0.0.0" - "0.0.0.0:80"
68 * "[::1]" - "[::1]:80"
69 * "[::]" - "[::]:80"
70 *
71 * See also RFC5952
72 */
73
74int
75VSS_parse(const char *str, char **addr, char **port)
76{
77        const char *p;
78
79        *addr = *port = NULL;
80
81        if (str[0] == '[') {
82                /* IPv6 address of the form [::1]:80 */
83                if ((p = strchr(str, ']')) == NULL ||
84                    p == str + 1 ||
85                    (p[1] != '\0' && p[1] != ':'))
86                        return (-1);
87                *addr = strdup(str + 1);
88                XXXAN(*addr);
89                (*addr)[p - (str + 1)] = '\0';
90                if (p[1] == ':') {
91                        *port = strdup(p + 2);
92                        XXXAN(*port);
93                }
94        } else {
95                /* IPv4 address of the form 127.0.0.1:80, or non-numeric */
96                p = strchr(str, ' ');
97                if (p == NULL)
98                        p = strchr(str, ':');
99                if (p == NULL) {
100                        *addr = strdup(str);
101                        XXXAN(*addr);
102                } else {
103                        if (p > str) {
104                                *addr = strdup(str);
105                                XXXAN(*addr);
106                                (*addr)[p - str] = '\0';
107                        }
108                        *port = strdup(p + 1);
109                        XXXAN(*port);
110                }
111        }
112        return (0);
113}
114
115/*
116 * For a given host and port, return a list of struct vss_addr, which
117 * contains all the information necessary to open and bind a socket.  One
118 * vss_addr is returned for each distinct address returned by
119 * getaddrinfo().
120 *
121 * The value pointed to by the tap parameter receives a pointer to an
122 * array of pointers to struct vss_addr.  The caller is responsible for
123 * freeing each individual struct vss_addr as well as the array.
124 *
125 * The return value is the number of addresses resoved, or zero.
126 *
127 * If the addr argument contains a port specification, that takes
128 * precedence over the port argument.
129 *
130 * XXX: We need a function to free the allocated addresses.
131 */
132int
133VSS_resolve(const char *addr, const char *port, struct vss_addr ***vap)
134{
135        struct addrinfo hints, *res0, *res;
136        struct vss_addr **va;
137        int i, ret;
138        char *adp, *hop;
139
140        *vap = NULL;
141        memset(&hints, 0, sizeof hints);
142        hints.ai_socktype = SOCK_STREAM;
143        hints.ai_flags = AI_PASSIVE;
144
145        ret = VSS_parse(addr, &hop, &adp);
146        if (ret)
147                return (0);
148
149        if (adp == NULL)
150                ret = getaddrinfo(addr, port, &hints, &res0);
151        else
152                ret = getaddrinfo(hop, adp, &hints, &res0);
153
154        free(hop);
155        free(adp);
156
157        if (ret != 0)
158                return (0);
159
160        XXXAN(res0);
161        for (res = res0, i = 0; res != NULL; res = res->ai_next, ++i)
162                /* nothing */ ;
163        if (i == 0) {
164                freeaddrinfo(res0);
165                return (0);
166        }
167        va = calloc(i, sizeof *va);
168        XXXAN(va);
169        *vap = va;
170        for (res = res0, i = 0; res != NULL; res = res->ai_next, ++i) {
171                va[i] = calloc(1, sizeof(**va));
172                XXXAN(va[i]);
173                va[i]->va_family = res->ai_family;
174                va[i]->va_socktype = res->ai_socktype;
175                va[i]->va_protocol = res->ai_protocol;
176                va[i]->va_addrlen = res->ai_addrlen;
177                xxxassert(va[i]->va_addrlen <= sizeof va[i]->va_addr);
178                memcpy(&va[i]->va_addr, res->ai_addr, va[i]->va_addrlen);
179        }
180        freeaddrinfo(res0);
181        return (i);
182}
183
184/*
185 * Given a struct vss_addr, open a socket of the appropriate type, and bind
186 * it to the requested address.
187 *
188 * If the address is an IPv6 address, the IPV6_V6ONLY option is set to
189 * avoid conflicts between INADDR_ANY and IN6ADDR_ANY.
190 */
191
192int
193VSS_bind(const struct vss_addr *va)
194{
195        int sd, val;
196
197        sd = socket(va->va_family, va->va_socktype, va->va_protocol);
198        if (sd < 0) {
199                perror("socket()");
200                return (-1);
201        }
202        val = 1;
203        if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val) != 0) {
204                perror("setsockopt(SO_REUSEADDR, 1)");
205                (void)close(sd);
206                return (-1);
207        }
208#ifdef IPV6_V6ONLY
209        /* forcibly use separate sockets for IPv4 and IPv6 */
210        val = 1;
211        if (va->va_family == AF_INET6 &&
212            setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof val) != 0) {
213                perror("setsockopt(IPV6_V6ONLY, 1)");
214                (void)close(sd);
215                return (-1);
216        }
217#endif
218        if (bind(sd, (const void*)&va->va_addr, va->va_addrlen) != 0) {
219                perror("bind()");
220                (void)close(sd);
221                return (-1);
222        }
223        return (sd);
224}
225
226/*
227 * Given a struct vss_addr, open a socket of the appropriate type, bind it
228 * to the requested address, and start listening.
229 *
230 * If the address is an IPv6 address, the IPV6_V6ONLY option is set to
231 * avoid conflicts between INADDR_ANY and IN6ADDR_ANY.
232 */
233int
234VSS_listen(const struct vss_addr *va, int depth)
235{
236        int sd;
237
238        sd = VSS_bind(va);
239        if (sd >= 0)  {
240                if (listen(sd, depth) != 0) {
241                        perror("listen()");
242                        (void)close(sd);
243                        return (-1);
244                }
245        }
246        return (sd);
247}
248
249/*
250 * Connect to the socket specified by the address info in va.
251 * Return the socket.
252 */
253int
254VSS_connect(const struct vss_addr *va, int nonblock)
255{
256        int sd, i;
257
258        sd = socket(va->va_family, va->va_socktype, va->va_protocol);
259        if (sd < 0) {
260                if (errno != EPROTONOSUPPORT)
261                        perror("socket()");
262                return (-1);
263        }
264        if (nonblock)
265                (void)VTCP_nonblocking(sd);
266        i = connect(sd, (const void *)&va->va_addr, va->va_addrlen);
267        if (i == 0 || (nonblock && errno == EINPROGRESS))
268                return (sd);
269        perror("connect()");
270        (void)close(sd);
271        return (-1);
272}
273
274/*
275 * And the totally brutal version: Give me connection to this address
276 */
277
278int
279VSS_open(const char *str, double tmo)
280{
281        int retval = -1;
282        int nvaddr, n, i;
283        struct vss_addr **vaddr;
284        struct pollfd pfd;
285
286        nvaddr = VSS_resolve(str, NULL, &vaddr);
287        for (n = 0; n < nvaddr; n++) {
288                retval = VSS_connect(vaddr[n], tmo != 0.0);
289                if (retval >= 0 && tmo != 0.0) {
290                        pfd.fd = retval;
291                        pfd.events = POLLOUT;
292                        i = poll(&pfd, 1, tmo * 1e3);
293                        if (i == 0 || pfd.revents != POLLOUT) {
294                                (void)close(retval);
295                                retval = -1;
296                        }
297                }
298                if (retval >= 0)
299                        break;
300        }
301        for (n = 0; n < nvaddr; n++)
302                free(vaddr[n]);
303        free(vaddr);
304        return (retval);
305}
Note: See TracBrowser for help on using the repository browser.