2
* Copyright (c) 2009 Redpill Linpro AS
3
* Copyright (c) 2010 Varnish Software AS
6
* Author: Kristian Lyngstol <kristian@redpill-linpro.com>
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36
#include <sys/types.h>
37
#include <sys/socket.h>
46
#include <netinet/in.h>
49
#include "cache_backend.h"
52
/*--------------------------------------------------------------------*/
54
/* FIXME: Should eventually be a configurable variable. */
55
#define VDI_DNS_MAX_CACHE 1024
56
#define VDI_DNS_GROUP_MAX_BACKENDS 1024
60
struct vdi_dns_hostgroup {
62
#define VDI_DNSDIR_MAGIC 0x1bacab21
64
struct director *hosts[VDI_DNS_GROUP_MAX_BACKENDS];
66
unsigned next_host; /* Next to use...*/
68
VTAILQ_ENTRY(vdi_dns_hostgroup) list;
73
#define VDI_DNS_MAGIC 0x1337a178
75
struct director **hosts;
77
VTAILQ_HEAD(_cachelist,vdi_dns_hostgroup) cachelist;
79
pthread_rwlock_t rwlock;
86
/* Compare an IPv4 backend to a IPv4 addr/len */
88
vdi_dns_comp_addrinfo4(const struct backend *bp,
89
const struct sockaddr_in *addr,
93
struct sockaddr_in *bps = (struct sockaddr_in *) bp->ipv4;
95
if (bp->ipv4len != len || len <= 0)
98
u = addr->sin_addr.s_addr;
99
p = bps->sin_addr.s_addr;
104
/* Compare an IPv6 backend to a IPv6 addr/len */
106
vdi_dns_comp_addrinfo6(const struct backend *bp,
107
struct sockaddr_in6 *addr,
112
struct sockaddr_in6 *bps = (struct sockaddr_in6 *) bp->ipv6;
114
if (bp->ipv6len != len || len <= 0)
117
u = addr->sin6_addr.s6_addr;
118
p = bps->sin6_addr.s6_addr;
120
for (i=0; i < 16; i++) {
128
/* Check if a backends socket is the same as addr */
130
vdi_dns_comp_addrinfo(const struct director *dir,
131
struct sockaddr *addr,
136
bp = vdi_get_backend_if_simple(dir);
138
if (addr->sa_family == PF_INET && bp->ipv4) {
139
return (vdi_dns_comp_addrinfo4(bp, (struct sockaddr_in *)
141
} else if (addr->sa_family == PF_INET6 && bp->ipv6) {
142
return (vdi_dns_comp_addrinfo6(bp, (struct sockaddr_in6 *)
148
/* Pick a host from an existing hostgroup.
149
* Balance on round-robin if multiple backends are available and only pick
152
static struct director *
153
vdi_dns_pick_host(const struct sess *sp, struct vdi_dns_hostgroup *group) {
154
int initial, i, nhosts, current;
155
if (group->nhosts == 0)
156
return (NULL); // In case of error.
157
if (group->next_host >= group->nhosts)
158
group->next_host = 0;
160
/* Pick a healthy backend */
161
initial = group->next_host;
162
nhosts = group->nhosts;
163
for (i=0; i < nhosts; i++) {
164
if (i + initial >= nhosts)
165
current = i + initial - nhosts;
167
current = i + initial;
168
if (VBE_Healthy_sp(sp, group->hosts[current])) {
169
group->next_host = current+1;
170
return group->hosts[current];
177
/* Remove an item from the dns cache.
178
* If *group is NULL, the head is popped.
182
vdi_dns_pop_cache(struct vdi_dns *vs,
183
struct vdi_dns_hostgroup *group)
186
group = VTAILQ_LAST( &vs->cachelist, _cachelist );
187
assert(group != NULL);
188
free(group->hostname);
189
VTAILQ_REMOVE(&vs->cachelist, group, list);
194
/* Dummy in case someone feels like optimizing it? meh...
197
vdi_dns_groupmatch(const struct vdi_dns_hostgroup *group, const char *hostname)
199
return !strcmp(group->hostname, hostname);
202
/* Search the cache for 'hostname' and put a backend-pointer as necessary,
203
* return true for cache hit. This could still be a NULL backend if we did
204
* a lookup earlier and didn't find a host (ie: cache failed too)
206
* if rwlock is true, the first timed out object found (if any) is popped
210
vdi_dns_cache_has(const struct sess *sp,
212
const char *hostname,
213
struct director **backend,
216
struct director *ret;
217
struct vdi_dns_hostgroup *hostgr;
218
struct vdi_dns_hostgroup *hostgr2;
219
VTAILQ_FOREACH_SAFE(hostgr, &vs->cachelist, list, hostgr2) {
220
CHECK_OBJ_NOTNULL(hostgr, VDI_DNSDIR_MAGIC);
221
if (hostgr->ttl <= sp->t_req) {
223
vdi_dns_pop_cache(vs, hostgr);
226
if (vdi_dns_groupmatch(hostgr, hostname)) {
227
ret = (vdi_dns_pick_host(sp, hostgr));
229
if (*backend != NULL)
230
CHECK_OBJ_NOTNULL(*backend, DIRECTOR_MAGIC);
237
/* Add a newly cached item to the dns cache list.
238
* (Sorry for the list_add/_add confusion...)
241
vdi_dns_cache_list_add(const struct sess *sp,
243
struct vdi_dns_hostgroup *new)
245
if (vs->ncachelist >= VDI_DNS_MAX_CACHE) {
246
VSL_stats->dir_dns_cache_full++;
247
vdi_dns_pop_cache(vs, NULL);
249
CHECK_OBJ_NOTNULL(new, VDI_DNSDIR_MAGIC);
250
assert(new->hostname != 0);
251
new->ttl = sp->t_req + vs->ttl;
252
VTAILQ_INSERT_HEAD(&vs->cachelist, new, list);
256
/* Add an item to the dns cache.
257
* XXX: Might want to factor the getaddrinfo() out of the lock and do the
258
* cache_has() afterwards to do multiple dns lookups in parallel...
261
vdi_dns_cache_add(const struct sess *sp,
263
const char *hostname,
264
struct director **backend)
266
int error, i, host = 0;
267
struct addrinfo *res0, *res, hint;
268
struct vdi_dns_hostgroup *new;
269
/* Due to possible race while upgrading the lock, we have to
270
* recheck if the result is already looked up. The overhead for
271
* this is insignificant unless dns isn't cached properly (all
272
* unique names or something equally troublesome).
275
if (vdi_dns_cache_has(sp, vs, hostname, backend, 1))
278
memset(&hint, 0, sizeof hint);
279
hint.ai_family = PF_UNSPEC;
280
hint.ai_socktype = SOCK_STREAM;
282
ALLOC_OBJ(new, VDI_DNSDIR_MAGIC);
284
new->hostname = calloc(sizeof(char), strlen(hostname)+1);
285
XXXAN(new->hostname);
286
strcpy(new->hostname, hostname);
288
error = getaddrinfo(hostname, "80", &hint, &res0);
289
VSL_stats->dir_dns_lookups++;
291
vdi_dns_cache_list_add(sp, vs, new);
292
VSL_stats->dir_dns_failed++;
296
for (res = res0; res; res = res->ai_next) {
297
if (res->ai_family != PF_INET && res->ai_family != PF_INET6)
300
for (i = 0; i < vs->nhosts; i++) {
301
if (vdi_dns_comp_addrinfo(vs->hosts[i],
302
res->ai_addr, res->ai_addrlen)) {
303
new->hosts[host] = vs->hosts[i];
304
CHECK_OBJ_NOTNULL(new->hosts[host],
313
vdi_dns_cache_list_add(sp, vs, new);
314
*backend = vdi_dns_pick_host(sp, new);
318
/* Walk through the cached lookups looking for the relevant host, add one
319
* if it isn't already cached.
321
* Returns a backend or NULL.
323
static struct director *
324
vdi_dns_walk_cache(const struct sess *sp,
326
const char *hostname)
328
struct director *backend = NULL;
330
AZ(pthread_rwlock_rdlock(&vs->rwlock));
331
ret = vdi_dns_cache_has(sp, vs, hostname, &backend, 0);
332
AZ(pthread_rwlock_unlock(&vs->rwlock));
334
AZ(pthread_rwlock_wrlock(&vs->rwlock));
335
ret = vdi_dns_cache_add(sp, vs, hostname, &backend);
336
AZ(pthread_rwlock_unlock(&vs->rwlock));
338
VSL_stats->dir_dns_hit++;
340
/* Bank backend == cached a failure, so to speak */
342
CHECK_OBJ_NOTNULL(backend, DIRECTOR_MAGIC);
346
/* Parses the Host:-header and heads out to find a backend.
348
static struct director *
349
vdi_dns_find_backend(const struct sess *sp, struct vdi_dns *vs)
351
struct director *ret;
354
char hostname[NI_MAXHOST];
357
/* bereq is only present after recv et. al, otherwise use req (ie:
358
* use req for health checks in vcl_recv and such).
366
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
367
if (http_GetHdr(hp, H_Host, &p) == 0)
370
/* We need a working copy since it's going to be modified */
371
strncpy(hostname, p, sizeof(hostname));
373
/* remove port-portion of the Host-header, if present. */
374
for (i = 0; i < strlen(hostname); i++) {
375
if (hostname[i] == ':') {
382
strncat(hostname, vs->suffix, sizeof(hostname) - strlen(hostname));
384
ret = vdi_dns_walk_cache(sp, vs, hostname);
388
static struct vbe_conn *
389
vdi_dns_getfd(const struct director *director, struct sess *sp)
392
struct director *dir;
393
struct vbe_conn *vbe;
395
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
396
CHECK_OBJ_NOTNULL(director, DIRECTOR_MAGIC);
397
CAST_OBJ_NOTNULL(vs, director->priv, VDI_DNS_MAGIC);
399
dir = vdi_dns_find_backend(sp, vs);
400
if (!dir || !VBE_Healthy_sp(sp, dir))
403
vbe = VBE_GetFd(dir, sp);
408
vdi_dns_healthy(double now, const struct director *dir, uintptr_t target)
410
/* XXX: Fooling -Werror for a bit until it's actually implemented.
412
if (now || dir || target)
419
struct director *dir;
422
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
423
CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC);
424
CAST_OBJ_NOTNULL(vs, sp->director->priv, VDI_DNS_MAGIC);
426
dir = vdi_dns_find_backend(sp, vs);
434
/*lint -e{818} not const-able */
436
vdi_dns_fini(struct director *d)
440
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
441
CAST_OBJ_NOTNULL(vs, d->priv, VDI_DNS_MAGIC);
444
free(vs->dir.vcl_name);
446
/* FIXME: Free the cache */
447
AZ(pthread_rwlock_destroy(&vs->rwlock));
452
VRT_init_dir_dns(struct cli *cli, struct director **bp, int idx,
455
const struct vrt_dir_dns *t;
457
const struct vrt_dir_dns_entry *te;
463
ALLOC_OBJ(vs, VDI_DNS_MAGIC);
465
vs->hosts = calloc(sizeof(struct director *), t->nmember);
468
vs->dir.magic = DIRECTOR_MAGIC;
470
vs->dir.name = "dns";
471
REPLACE(vs->dir.vcl_name, t->name);
472
vs->dir.getfd = vdi_dns_getfd;
473
vs->dir.fini = vdi_dns_fini;
474
vs->dir.healthy = vdi_dns_healthy;
476
vs->suffix = t->suffix;
480
for (i = 0; i < t->nmember; i++, te++)
481
vs->hosts[i] = bp[te->host];
482
vs->nhosts = t->nmember;
484
VTAILQ_INIT(&vs->cachelist);
485
AZ(pthread_rwlock_init(&vs->rwlock, NULL));