Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

345 lines (269 sloc) 7.571 kb
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* Cherokee
*
* Authors:
* Alvaro Lopez Ortega <alvaro@alobbs.com>
*
* Copyright (C) 2001-2011 Alvaro Lopez Ortega
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "common-internal.h"
#ifndef _WIN32
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
#endif
#include "resolv_cache.h"
#include "socket_lowlevel.h"
#include "util.h"
#include "avl.h"
#include "socket.h"
#include "bogotime.h"
#define ENTRIES "resolve"
typedef struct {
struct addrinfo *addr;
cherokee_buffer_t ip_str;
cherokee_buffer_t ip_str_all;
} cherokee_resolv_cache_entry_t;
struct cherokee_resolv_cache {
cherokee_avl_t table;
CHEROKEE_RWLOCK_T (lock);
};
static cherokee_resolv_cache_t *__global_resolv = NULL;
/* Entries
*/
static ret_t
entry_new (cherokee_resolv_cache_entry_t **entry)
{
CHEROKEE_NEW_STRUCT(n, resolv_cache_entry);
n->addr = NULL;
cherokee_buffer_init (&n->ip_str);
cherokee_buffer_init (&n->ip_str_all);
*entry = n;
return ret_ok;
}
static void
entry_free (void *entry)
{
cherokee_resolv_cache_entry_t *e = entry;
if (e->addr) {
freeaddrinfo (e->addr);
}
cherokee_buffer_mrproper (&e->ip_str);
cherokee_buffer_mrproper (&e->ip_str_all);
free(entry);
}
static ret_t
entry_fill_up (cherokee_resolv_cache_entry_t *entry,
cherokee_buffer_t *domain)
{
ret_t ret;
char tmp[46]; // Max IPv6 length is 45
struct addrinfo *addr;
time_t eagain_at = 0;
while (true) {
ret = cherokee_gethostbyname (domain, &entry->addr);
if (ret == ret_ok) {
break;
} else if (ret == ret_eagain) {
if (eagain_at == 0) {
eagain_at = cherokee_bogonow_now;
} else if (cherokee_bogonow_now > eagain_at + 3) {
LOG_WARNING (CHEROKEE_ERROR_RESOLVE_TIMEOUT, domain->buf);
return ret_error;
}
CHEROKEE_THREAD_YIELD;
continue;
} else {
return ret_error;
}
}
if (unlikely (entry->addr == NULL)) {
return ret_error;
}
/* Render the text representation
*/
ret = cherokee_ntop (entry->addr->ai_family, entry->addr->ai_addr, tmp, sizeof(tmp));
if (ret != ret_ok) {
return ret_error;
}
cherokee_buffer_add (&entry->ip_str, tmp, strlen(tmp));
/* Render the text representation (all the IPs)
*/
cherokee_buffer_add_buffer (&entry->ip_str_all, &entry->ip_str);
addr = entry->addr;
while (addr != NULL) {
ret = cherokee_ntop (entry->addr->ai_family, addr->ai_addr, tmp, sizeof(tmp));
if (ret != ret_ok) {
return ret_error;
}
cherokee_buffer_add_char (&entry->ip_str_all, ',');
cherokee_buffer_add (&entry->ip_str_all, tmp, strlen(tmp));
addr = addr->ai_next;
}
return ret_ok;
}
/* Table
*/
ret_t
cherokee_resolv_cache_init (cherokee_resolv_cache_t *resolv)
{
ret_t ret;
ret = cherokee_avl_init (&resolv->table);
if (unlikely (ret != ret_ok)) return ret;
ret = cherokee_avl_set_case (&resolv->table, true);
if (unlikely (ret != ret_ok)) return ret;
CHEROKEE_RWLOCK_INIT (&resolv->lock, NULL);
return ret_ok;
}
ret_t
cherokee_resolv_cache_mrproper (cherokee_resolv_cache_t *resolv)
{
cherokee_avl_mrproper (&resolv->table, entry_free);
CHEROKEE_RWLOCK_DESTROY (&resolv->lock);
return ret_ok;
}
ret_t
cherokee_resolv_cache_get_default (cherokee_resolv_cache_t **resolv)
{
ret_t ret;
if (unlikely (__global_resolv == NULL)) {
CHEROKEE_NEW_STRUCT (n, resolv_cache);
ret = cherokee_resolv_cache_init (n);
if (ret != ret_ok) return ret;
__global_resolv = n;
}
*resolv = __global_resolv;
return ret_ok;
}
ret_t
cherokee_resolv_cache_clean (cherokee_resolv_cache_t *resolv)
{
CHEROKEE_RWLOCK_WRITER (&resolv->lock);
cherokee_avl_mrproper (&resolv->table, entry_free);
CHEROKEE_RWLOCK_UNLOCK (&resolv->lock);
return ret_ok;
}
static ret_t
table_add_new_entry (cherokee_resolv_cache_t *resolv,
cherokee_buffer_t *domain,
cherokee_resolv_cache_entry_t **entry)
{
ret_t ret;
cherokee_resolv_cache_entry_t *n = NULL;
/* Instance the entry
*/
ret = entry_new (&n);
if (unlikely (ret != ret_ok)) {
return ret;
}
/* Fill it up
*/
ret = entry_fill_up (n, domain);
if (unlikely (ret != ret_ok)) {
entry_free (n);
return ret;
}
/* Add it to the table
*/
CHEROKEE_RWLOCK_WRITER (&resolv->lock);
ret = cherokee_avl_add (&resolv->table, domain, (void **)n);
CHEROKEE_RWLOCK_UNLOCK (&resolv->lock);
*entry = n;
return ret_ok;
}
ret_t
cherokee_resolv_cache_get_ipstr (cherokee_resolv_cache_t *resolv,
cherokee_buffer_t *domain,
const char **ip)
{
ret_t ret;
cherokee_resolv_cache_entry_t *entry = NULL;
/* Look for the name in the cache
*/
CHEROKEE_RWLOCK_READER (&resolv->lock);
ret = cherokee_avl_get (&resolv->table, domain, (void **)&entry);
CHEROKEE_RWLOCK_UNLOCK (&resolv->lock);
if (ret != ret_ok) {
TRACE (ENTRIES, "Resolve '%s': missed.\n", domain->buf);
/* Bad luck: it wasn't cached
*/
ret = table_add_new_entry (resolv, domain, &entry);
if (ret != ret_ok) {
return ret;
}
TRACE (ENTRIES, "Resolve '%s': added succesfuly as '%s'.\n", domain->buf, entry->ip_str_all.buf);
} else {
TRACE (ENTRIES, "Resolve '%s': hit: %s\n", domain->buf, entry->ip_str_all.buf);
}
/* Return the ip string
*/
if (ip != NULL) {
*ip = entry->ip_str.buf;
}
return ret_ok;
}
ret_t
cherokee_resolv_cache_get_host (cherokee_resolv_cache_t *resolv,
cherokee_buffer_t *domain,
void *sock_)
{
ret_t ret;
const struct addrinfo *addr = NULL;
cherokee_socket_t *sock = sock_;
/* Get addrinfo
*/
ret = cherokee_resolv_cache_get_addrinfo (resolv, domain, &addr);
if (ret != ret_ok) {
return ret;
}
/* Copy it to the socket object
*/
ret = cherokee_socket_update_from_addrinfo (sock, addr, 0);
if (ret != ret_ok) {
return ret;
}
return ret_ok;
}
ret_t
cherokee_resolv_cache_get_addrinfo (cherokee_resolv_cache_t *resolv,
cherokee_buffer_t *domain,
const struct addrinfo **addr_info)
{
ret_t ret;
cherokee_resolv_cache_entry_t *entry = NULL;
/* Look for the name in the cache
*/
CHEROKEE_RWLOCK_READER (&resolv->lock);
ret = cherokee_avl_get (&resolv->table, domain, (void **)&entry);
CHEROKEE_RWLOCK_UNLOCK (&resolv->lock);
if (ret != ret_ok) {
TRACE (ENTRIES, "Resolve '%s': missed.\n", domain->buf);
/* Bad luck: it wasn't cached
*/
ret = table_add_new_entry (resolv, domain, &entry);
if (ret != ret_ok) {
TRACE (ENTRIES, "Resolve '%s': error ret=%d.\n", domain->buf, ret);
return ret;
}
TRACE (ENTRIES, "Resolve '%s': added succesfuly as '%s'.\n", domain->buf, entry->ip_str.buf);
} else {
TRACE (ENTRIES, "Resolve '%s': hit.\n", domain->buf);
}
*addr_info = entry->addr;
return ret_ok;
}
Jump to Line
Something went wrong with that request. Please try again.