~ubuntu-branches/ubuntu/saucy/varnish/saucy

« back to all changes in this revision

Viewing changes to bin/varnishd/cache_dir_dns.c

  • Committer: Bazaar Package Importer
  • Author(s): Stig Sandbeck Mathisen
  • Date: 2011-03-21 10:16:07 UTC
  • mfrom: (24.1.2 experimental)
  • Revision ID: james.westby@ubuntu.com-20110321101607-528fzl583fqanas5
Tags: 2.1.5-2
ReleaseĀ forĀ unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-
 
2
 * Copyright (c) 2009 Redpill Linpro AS
 
3
 * Copyright (c) 2010 Varnish Software AS
 
4
 * All rights reserved.
 
5
 *
 
6
 * Author: Kristian Lyngstol <kristian@redpill-linpro.com>
 
7
 *
 
8
 * Redistribution and use in source and binary forms, with or without
 
9
 * modification, are permitted provided that the following conditions
 
10
 * are met:
 
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.
 
16
 *
 
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
 
27
 * SUCH DAMAGE.
 
28
 *
 
29
 */
 
30
 
 
31
#include "config.h"
 
32
 
 
33
#include "svnid.h"
 
34
SVNID("$Id$")
 
35
 
 
36
#include <sys/types.h>
 
37
#include <sys/socket.h>
 
38
 
 
39
#include <errno.h>
 
40
#include <stdlib.h>
 
41
#include <string.h>
 
42
#include <netdb.h>
 
43
#include <unistd.h>
 
44
 
 
45
#include <stdio.h>
 
46
#include <netinet/in.h>
 
47
#include "shmlog.h"
 
48
#include "cache.h"
 
49
#include "cache_backend.h"
 
50
#include "vrt.h"
 
51
 
 
52
/*--------------------------------------------------------------------*/
 
53
 
 
54
/* FIXME: Should eventually be a configurable variable. */
 
55
#define VDI_DNS_MAX_CACHE               1024
 
56
#define VDI_DNS_GROUP_MAX_BACKENDS      1024
 
57
 
 
58
/* DNS Cache entry 
 
59
 */
 
60
struct vdi_dns_hostgroup {
 
61
        unsigned                        magic;
 
62
#define VDI_DNSDIR_MAGIC                0x1bacab21
 
63
        char                            *hostname;
 
64
        struct director                 *hosts[VDI_DNS_GROUP_MAX_BACKENDS];
 
65
        unsigned                        nhosts;
 
66
        unsigned                        next_host; /* Next to use...*/
 
67
        double                          ttl;
 
68
        VTAILQ_ENTRY(vdi_dns_hostgroup) list;
 
69
};
 
70
 
 
71
struct vdi_dns {
 
72
        unsigned                        magic;
 
73
#define VDI_DNS_MAGIC                   0x1337a178
 
74
        struct director                 dir;
 
75
        struct director                 **hosts;
 
76
        unsigned                        nhosts;
 
77
        VTAILQ_HEAD(_cachelist,vdi_dns_hostgroup)       cachelist;
 
78
        unsigned                        ncachelist;
 
79
        pthread_rwlock_t                rwlock;
 
80
        const char                      *suffix;
 
81
        double                  ttl;
 
82
};
 
83
 
 
84
 
 
85
 
 
86
/* Compare an IPv4 backend to a IPv4 addr/len */
 
87
static int
 
88
vdi_dns_comp_addrinfo4(const struct backend *bp, 
 
89
                       const struct sockaddr_in *addr,
 
90
                       const socklen_t len)
 
91
{
 
92
        uint32_t u, p;
 
93
        struct sockaddr_in *bps = (struct sockaddr_in *) bp->ipv4;
 
94
 
 
95
        if (bp->ipv4len != len || len <= 0)
 
96
                return 0;
 
97
 
 
98
        u = addr->sin_addr.s_addr;
 
99
        p = bps->sin_addr.s_addr;
 
100
 
 
101
        return u == p;
 
102
}
 
103
 
 
104
/* Compare an IPv6 backend to a IPv6 addr/len */
 
105
static int
 
106
vdi_dns_comp_addrinfo6(const struct backend *bp,
 
107
                       struct sockaddr_in6 *addr,
 
108
                       const socklen_t len)
 
109
{
 
110
        uint8_t *u, *p;
 
111
        int i;
 
112
        struct sockaddr_in6 *bps = (struct sockaddr_in6 *) bp->ipv6;
 
113
 
 
114
        if (bp->ipv6len != len || len <= 0)
 
115
                return 0;
 
116
 
 
117
        u = addr->sin6_addr.s6_addr;
 
118
        p = bps->sin6_addr.s6_addr;
 
119
 
 
120
        for (i=0; i < 16; i++) {
 
121
                if (u[i] != p[i])
 
122
                        return 0;
 
123
        }
 
124
 
 
125
        return 1;
 
126
}
 
127
 
 
128
/* Check if a backends socket is the same as addr */
 
129
static int
 
130
vdi_dns_comp_addrinfo(const struct director *dir,
 
131
                      struct sockaddr *addr,
 
132
                      const socklen_t len)
 
133
{
 
134
        struct backend *bp;
 
135
 
 
136
        bp = vdi_get_backend_if_simple(dir);
 
137
        AN(bp);
 
138
        if (addr->sa_family == PF_INET && bp->ipv4) {
 
139
                return (vdi_dns_comp_addrinfo4(bp, (struct sockaddr_in *)
 
140
                        addr, len));
 
141
        } else if (addr->sa_family == PF_INET6 && bp->ipv6) {
 
142
                return (vdi_dns_comp_addrinfo6(bp, (struct sockaddr_in6 *)
 
143
                        addr, len));
 
144
        }
 
145
        return 0;
 
146
}
 
147
 
 
148
/* Pick a host from an existing hostgroup.
 
149
 * Balance on round-robin if multiple backends are available and only pick
 
150
 * healthy ones.
 
151
 */
 
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;
 
159
 
 
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;
 
166
                else
 
167
                        current = i + initial;
 
168
                if (VBE_Healthy_sp(sp, group->hosts[current])) {
 
169
                        group->next_host = current+1;
 
170
                        return group->hosts[current];
 
171
                }
 
172
        }
 
173
 
 
174
        return NULL;
 
175
}
 
176
 
 
177
/* Remove an item from the dns cache.
 
178
 * If *group is NULL, the head is popped.
 
179
 * Remember locking.
 
180
 */
 
181
static void
 
182
vdi_dns_pop_cache(struct vdi_dns *vs,
 
183
                  struct vdi_dns_hostgroup *group)
 
184
{
 
185
        if (group == NULL)
 
186
                group = VTAILQ_LAST( &vs->cachelist, _cachelist );
 
187
        assert(group != NULL);
 
188
        free(group->hostname);
 
189
        VTAILQ_REMOVE(&vs->cachelist, group, list);
 
190
        FREE_OBJ(group);
 
191
        vs->ncachelist--;
 
192
}
 
193
 
 
194
/* Dummy in case someone feels like optimizing it? meh...
 
195
 */
 
196
static inline int
 
197
vdi_dns_groupmatch(const struct vdi_dns_hostgroup *group, const char *hostname)
 
198
{
 
199
        return !strcmp(group->hostname, hostname);
 
200
}
 
201
 
 
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)
 
205
 *
 
206
 * if rwlock is true, the first timed out object found (if any) is popped
 
207
 * and freed.
 
208
 */
 
209
static int
 
210
vdi_dns_cache_has(const struct sess *sp,
 
211
                  struct vdi_dns *vs,
 
212
                  const char *hostname,
 
213
                  struct director **backend,
 
214
                  int rwlock)
 
215
{
 
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) {
 
222
                        if (rwlock)
 
223
                                vdi_dns_pop_cache(vs, hostgr);
 
224
                        return 0;
 
225
                }
 
226
                if (vdi_dns_groupmatch(hostgr, hostname)) {
 
227
                        ret = (vdi_dns_pick_host(sp, hostgr));
 
228
                        *backend = ret;
 
229
                        if (*backend != NULL) 
 
230
                                CHECK_OBJ_NOTNULL(*backend, DIRECTOR_MAGIC);
 
231
                        return 1;
 
232
                }
 
233
        }
 
234
        return 0;
 
235
}
 
236
 
 
237
/* Add a newly cached item to the dns cache list.
 
238
 * (Sorry for the list_add/_add confusion...)
 
239
 */
 
240
static void
 
241
vdi_dns_cache_list_add(const struct sess *sp,
 
242
                       struct vdi_dns *vs,
 
243
                       struct vdi_dns_hostgroup *new)
 
244
{
 
245
        if (vs->ncachelist >= VDI_DNS_MAX_CACHE) {
 
246
                VSL_stats->dir_dns_cache_full++;
 
247
                vdi_dns_pop_cache(vs, NULL);
 
248
        }
 
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);
 
253
        vs->ncachelist++;
 
254
}
 
255
 
 
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...
 
259
 */
 
260
static int
 
261
vdi_dns_cache_add(const struct sess *sp,
 
262
                  struct vdi_dns *vs,
 
263
                  const char *hostname,
 
264
                  struct director **backend)
 
265
{
 
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).
 
273
         */
 
274
 
 
275
        if (vdi_dns_cache_has(sp, vs, hostname, backend, 1))
 
276
                return 1;
 
277
        
 
278
        memset(&hint, 0, sizeof hint);
 
279
        hint.ai_family = PF_UNSPEC;
 
280
        hint.ai_socktype = SOCK_STREAM;
 
281
 
 
282
        ALLOC_OBJ(new, VDI_DNSDIR_MAGIC);
 
283
        XXXAN(new);
 
284
        new->hostname = calloc(sizeof(char), strlen(hostname)+1);
 
285
        XXXAN(new->hostname);
 
286
        strcpy(new->hostname, hostname);
 
287
 
 
288
        error = getaddrinfo(hostname, "80", &hint, &res0);
 
289
        VSL_stats->dir_dns_lookups++;
 
290
        if (error) {
 
291
                vdi_dns_cache_list_add(sp, vs, new);
 
292
                VSL_stats->dir_dns_failed++;
 
293
                return 0;
 
294
        }
 
295
 
 
296
        for (res = res0; res; res = res->ai_next) {
 
297
                if (res->ai_family != PF_INET && res->ai_family != PF_INET6)
 
298
                        continue;
 
299
 
 
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],
 
305
                                    DIRECTOR_MAGIC);
 
306
                                host++;
 
307
                        }
 
308
                }
 
309
        }
 
310
        freeaddrinfo(res0);
 
311
 
 
312
        new->nhosts = host;
 
313
        vdi_dns_cache_list_add(sp, vs, new);
 
314
        *backend = vdi_dns_pick_host(sp, new);  
 
315
        return 1;
 
316
}
 
317
 
 
318
/* Walk through the cached lookups looking for the relevant host, add one
 
319
 * if it isn't already cached.
 
320
 *
 
321
 * Returns a backend or NULL.
 
322
 */
 
323
static struct director *
 
324
vdi_dns_walk_cache(const struct sess *sp,
 
325
                   struct vdi_dns *vs,
 
326
                   const char *hostname)
 
327
{
 
328
        struct director *backend = NULL;
 
329
        int ret;
 
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));
 
333
        if (!ret) {
 
334
                AZ(pthread_rwlock_wrlock(&vs->rwlock));
 
335
                ret = vdi_dns_cache_add(sp, vs, hostname, &backend);
 
336
                AZ(pthread_rwlock_unlock(&vs->rwlock));
 
337
        } else
 
338
                VSL_stats->dir_dns_hit++;
 
339
 
 
340
        /* Bank backend == cached a failure, so to speak */
 
341
        if (backend != NULL)
 
342
                CHECK_OBJ_NOTNULL(backend, DIRECTOR_MAGIC);
 
343
        return backend;
 
344
}
 
345
 
 
346
/* Parses the Host:-header and heads out to find a backend.
 
347
 */
 
348
static struct director *
 
349
vdi_dns_find_backend(const struct sess *sp, struct vdi_dns *vs)
 
350
{
 
351
        struct director *ret;
 
352
        struct http *hp;
 
353
        char *p;
 
354
        char hostname[NI_MAXHOST];
 
355
        int i;
 
356
 
 
357
        /* bereq is only present after recv et. al, otherwise use req (ie:
 
358
         * use req for health checks in vcl_recv and such).
 
359
         */
 
360
        if (sp->wrk->bereq)
 
361
                hp = sp->wrk->bereq;
 
362
        else
 
363
                hp = sp->http;
 
364
 
 
365
 
 
366
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
 
367
        if (http_GetHdr(hp, H_Host, &p) == 0)
 
368
                return (NULL);
 
369
 
 
370
        /* We need a working copy since it's going to be modified */    
 
371
        strncpy(hostname, p, sizeof(hostname));
 
372
 
 
373
        /* remove port-portion of the Host-header, if present. */
 
374
        for (i = 0; i < strlen(hostname); i++) {
 
375
                if (hostname[i] == ':') {
 
376
                        hostname[i] = '\0';
 
377
                        break;
 
378
                }
 
379
        }
 
380
 
 
381
        if (vs->suffix)
 
382
                strncat(hostname, vs->suffix, sizeof(hostname) - strlen(hostname));
 
383
 
 
384
        ret = vdi_dns_walk_cache(sp, vs, hostname);
 
385
        return ret;
 
386
}
 
387
 
 
388
static struct vbe_conn *
 
389
vdi_dns_getfd(const struct director *director, struct sess *sp)
 
390
{
 
391
        struct vdi_dns *vs;
 
392
        struct director *dir;
 
393
        struct vbe_conn *vbe;
 
394
 
 
395
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
 
396
        CHECK_OBJ_NOTNULL(director, DIRECTOR_MAGIC);
 
397
        CAST_OBJ_NOTNULL(vs, director->priv, VDI_DNS_MAGIC);
 
398
 
 
399
        dir = vdi_dns_find_backend(sp, vs);
 
400
        if (!dir || !VBE_Healthy_sp(sp, dir))
 
401
                return (NULL);
 
402
        
 
403
        vbe = VBE_GetFd(dir, sp);
 
404
        return (vbe);
 
405
}
 
406
 
 
407
static unsigned
 
408
vdi_dns_healthy(double now, const struct director *dir, uintptr_t target)
 
409
{
 
410
        /* XXX: Fooling -Werror for a bit until it's actually implemented.
 
411
         */
 
412
        if (now || dir || target)
 
413
                return 1;
 
414
        else
 
415
                return 1;
 
416
        return 1;
 
417
        /*
 
418
        struct vdi_dns *vs;
 
419
        struct director *dir;
 
420
        int i;
 
421
 
 
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);
 
425
 
 
426
        dir = vdi_dns_find_backend(sp, vs);
 
427
 
 
428
        if (dir)
 
429
                return 1;
 
430
        return 0;
 
431
        */
 
432
}
 
433
 
 
434
/*lint -e{818} not const-able */
 
435
static void
 
436
vdi_dns_fini(struct director *d)
 
437
{
 
438
        struct vdi_dns *vs;
 
439
 
 
440
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
 
441
        CAST_OBJ_NOTNULL(vs, d->priv, VDI_DNS_MAGIC);
 
442
 
 
443
        free(vs->hosts);
 
444
        free(vs->dir.vcl_name);
 
445
        vs->dir.magic = 0;
 
446
        /* FIXME: Free the cache */
 
447
        AZ(pthread_rwlock_destroy(&vs->rwlock));
 
448
        FREE_OBJ(vs);
 
449
}
 
450
 
 
451
void
 
452
VRT_init_dir_dns(struct cli *cli, struct director **bp, int idx,
 
453
    const void *priv)
 
454
{
 
455
        const struct vrt_dir_dns *t;
 
456
        struct vdi_dns *vs;
 
457
        const struct vrt_dir_dns_entry *te;
 
458
        int i;
 
459
 
 
460
        ASSERT_CLI();
 
461
        (void)cli;
 
462
        t = priv;
 
463
        ALLOC_OBJ(vs, VDI_DNS_MAGIC);
 
464
        XXXAN(vs);
 
465
        vs->hosts = calloc(sizeof(struct director *), t->nmember);
 
466
        XXXAN(vs->hosts);
 
467
 
 
468
        vs->dir.magic = DIRECTOR_MAGIC;
 
469
        vs->dir.priv = vs;
 
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;
 
475
 
 
476
        vs->suffix = t->suffix;
 
477
        vs->ttl = t->ttl;
 
478
 
 
479
        te = t->members;
 
480
        for (i = 0; i < t->nmember; i++, te++)
 
481
                vs->hosts[i] = bp[te->host];
 
482
        vs->nhosts = t->nmember;
 
483
        vs->ttl = t->ttl;
 
484
        VTAILQ_INIT(&vs->cachelist);
 
485
        AZ(pthread_rwlock_init(&vs->rwlock, NULL));
 
486
        bp[idx] = &vs->dir;
 
487
}