/*
* scamper_addr.c
*
* $Id: scamper_addr.c,v 1.29 2007/05/14 03:27:30 mjl Exp $
*
* Copyright (C) 2004-2007 The University of Waikato
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#if defined(__APPLE__)
#include <stdint.h>
#endif
#if defined(DMALLOC)
#include <dmalloc.h>
#endif
#include "mjl_splaytree.h"
#include "scamper_addr.h"
#include "utils.h"
#if defined(__sun__)
# define s6_addr32 _S6_un._S6_u32
#elif !defined(s6_addr32)
# define s6_addr32 __u6_addr.__u6_addr32
#endif
static int ipv4_cmp(const scamper_addr_t *, const scamper_addr_t *);
static int ipv6_cmp(const scamper_addr_t *, const scamper_addr_t *);
static int ethernet_cmp(const scamper_addr_t *, const scamper_addr_t *);
static int firewire_cmp(const scamper_addr_t *, const scamper_addr_t *);
static void ipv4_tostr(const scamper_addr_t *, char *, const size_t);
static void ipv6_tostr(const scamper_addr_t *, char *, const size_t);
static void ethernet_tostr(const scamper_addr_t *, char *, const size_t);
static void firewire_tostr(const scamper_addr_t *, char *, const size_t);
struct handler
{
int type;
size_t size;
int (*cmp)(const scamper_addr_t *sa, const scamper_addr_t *sb);
void (*tostr)(const scamper_addr_t *addr, char *buf, const size_t len);
};
static const struct handler handlers[] = {
{
SCAMPER_ADDR_TYPE_IPV4,
4,
ipv4_cmp,
ipv4_tostr
},
{
SCAMPER_ADDR_TYPE_IPV6,
16,
ipv6_cmp,
ipv6_tostr
},
{
SCAMPER_ADDR_TYPE_ETHERNET,
6,
ethernet_cmp,
ethernet_tostr
},
{
SCAMPER_ADDR_TYPE_FIREWIRE,
8,
firewire_cmp,
firewire_tostr
}
};
struct scamper_addrcache
{
splaytree_t *tree[sizeof(handlers)/sizeof(struct handler)];
};
#ifndef NDEBUG
#if 0
static void scamper_addr_debug(const scamper_addr_t *sa)
{
char buf[128];
fprintf(stderr, "scamper_addr_t: %s %d\n",
scamper_addr_tostr(sa,buf,sizeof(buf)), sa->refcnt);
return;
}
#endif
#endif
#define scamper_addr_debug(sa) ((void)0)
static int ipv4_cmp(const scamper_addr_t *sa, const scamper_addr_t *sb)
{
struct in_addr *a, *b;
assert(sa->type == SCAMPER_ADDR_TYPE_IPV4);
assert(sb->type == SCAMPER_ADDR_TYPE_IPV4);
a = (struct in_addr *)sa->addr;
b = (struct in_addr *)sb->addr;
if(a->s_addr < b->s_addr) return -1;
if(a->s_addr > b->s_addr) return 1;
return 0;
}
static void ipv4_tostr(const scamper_addr_t *addr, char *buf, const size_t len)
{
inet_ntop(AF_INET, addr->addr, buf, len);
return;
}
static int ipv6_cmp(const scamper_addr_t *sa, const scamper_addr_t *sb)
{
struct in6_addr *a, *b;
int i;
assert(sa->type == SCAMPER_ADDR_TYPE_IPV6);
assert(sb->type == SCAMPER_ADDR_TYPE_IPV6);
a = (struct in6_addr *)sa->addr;
b = (struct in6_addr *)sb->addr;
for(i=0; i<4; i++)
{
if(a->s6_addr32[i] < b->s6_addr32[i]) return -1;
if(a->s6_addr32[i] > b->s6_addr32[i]) return 1;
}
return 0;
}
static void ipv6_tostr(const scamper_addr_t *addr, char *buf, const size_t len)
{
inet_ntop(AF_INET6, addr->addr, buf, len);
return;
}
static int ethernet_cmp(const scamper_addr_t *sa, const scamper_addr_t *sb)
{
assert(sa->type == SCAMPER_ADDR_TYPE_ETHERNET);
assert(sb->type == SCAMPER_ADDR_TYPE_ETHERNET);
return memcmp(sa->addr, sb->addr, 6);
}
static void ethernet_tostr(const scamper_addr_t *addr,
char *buf, const size_t len)
{
uint8_t *mac = (uint8_t *)addr->addr;
snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return;
}
static int firewire_cmp(const scamper_addr_t *sa, const scamper_addr_t *sb)
{
assert(sa->type == SCAMPER_ADDR_TYPE_FIREWIRE);
assert(sb->type == SCAMPER_ADDR_TYPE_FIREWIRE);
return memcmp(sa->addr, sb->addr, 8);
}
static void firewire_tostr(const scamper_addr_t *addr,
char *buf, const size_t len)
{
uint8_t *lla = (uint8_t *)addr->addr;
snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
lla[0], lla[1], lla[2], lla[3], lla[4], lla[5], lla[6], lla[7]);
return;
}
size_t scamper_addr_size(const scamper_addr_t *sa)
{
return handlers[sa->type-1].size;
}
const char *scamper_addr_tostr(const scamper_addr_t *sa,
char *dst, const size_t size)
{
handlers[sa->type-1].tostr(sa, dst, size);
return dst;
}
scamper_addr_t *scamper_addr_alloc(const int type, const void *addr)
{
scamper_addr_t *sa;
assert(addr != NULL);
assert(type-1 >= 0);
assert((size_t)(type-1) < sizeof(handlers)/sizeof(struct handler));
if((sa = malloc(sizeof(scamper_addr_t))) != NULL)
{
if((sa->addr = memdup(addr, handlers[type-1].size)) == NULL)
{
free(sa);
return NULL;
}
sa->type = type;
sa->refcnt = 1;
sa->internal = NULL;
}
return sa;
}
/*
* scamper_addr_resolve:
*
* resolve the address contained in addr to a sockaddr that
* tells us what family the address belongs to, and has a binary
* representation of the address
*/
scamper_addr_t *scamper_addr_resolve(const int af, const char *addr)
{
struct addrinfo hints, *res, *res0;
scamper_addr_t *sa;
void *va;
int type;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_family = af;
if(getaddrinfo(addr, NULL, &hints, &res0) != 0 || res0 == NULL)
{
return NULL;
}
for(res = res0; res != NULL; res = res->ai_next)
{
if(res->ai_family == PF_INET)
{
va = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
type = SCAMPER_ADDR_TYPE_IPV4;
break;
}
else if(res->ai_family == PF_INET6)
{
va = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
type = SCAMPER_ADDR_TYPE_IPV6;
break;
}
}
if(res == NULL)
{
freeaddrinfo(res0);
return NULL;
}
sa = scamper_addr_alloc(type, va);
freeaddrinfo(res0);
return sa;
}
scamper_addr_t *scamper_addrcache_get(scamper_addrcache_t *ac,
const int type, const void *addr)
{
scamper_addr_t *sa, findme;
findme.type = type;
findme.addr = (void *)addr;
if((sa = splaytree_find(ac->tree[type-1], &findme)) != NULL)
{
assert(sa->internal = ac);
sa->refcnt++;
scamper_addr_debug(sa);
return sa;
}
if((sa = scamper_addr_alloc(type, addr)) != NULL)
{
if(splaytree_insert(ac->tree[type-1], sa) == NULL)
{
goto err;
}
sa->internal = ac;
}
scamper_addr_debug(sa);
return sa;
err:
scamper_addr_free(sa);
return NULL;
}
/*
* scamper_addr_resolve:
*
* resolve the address contained in addr to a sockaddr that
* tells us what family the address belongs to, and has a binary
* representation of the address
*/
scamper_addr_t *scamper_addrcache_resolve(scamper_addrcache_t *addrcache,
const int af, const char *addr)
{
struct addrinfo hints, *res, *res0;
scamper_addr_t *sa;
void *va;
int type;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_family = af;
if(getaddrinfo(addr, NULL, &hints, &res0) != 0 || res0 == NULL)
{
return NULL;
}
for(res = res0; res != NULL; res = res->ai_next)
{
if(res->ai_family == PF_INET)
{
va = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
type = SCAMPER_ADDR_TYPE_IPV4;
break;
}
else if(res->ai_family == PF_INET6)
{
va = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
type = SCAMPER_ADDR_TYPE_IPV6;
break;
}
}
if(res == NULL)
{
freeaddrinfo(res0);
return NULL;
}
sa = scamper_addrcache_get(addrcache, type, va);
freeaddrinfo(res0);
return sa;
}
scamper_addr_t *scamper_addr_use(scamper_addr_t *sa)
{
if(sa != NULL)
{
sa->refcnt++;
scamper_addr_debug(sa);
}
return sa;
}
void scamper_addr_free(scamper_addr_t *sa)
{
scamper_addrcache_t *ac;
if(sa == NULL)
{
return;
}
assert(sa->refcnt > 0);
if(--sa->refcnt > 0)
{
scamper_addr_debug(sa);
return;
}
if((ac = sa->internal) != NULL)
{
splaytree_remove_item(ac->tree[sa->type-1], sa);
}
scamper_addr_debug(sa);
free(sa->addr);
free(sa);
return;
}
int scamper_addr_cmp(const scamper_addr_t *a, const scamper_addr_t *b)
{
/*
* if the two address structures point to the same memory, then they are
* a match
*/
if(a == b)
{
return 0;
}
/*
* if the two address types are the same, then do a comparison on the
* underlying addresses
*/
if(a->type == b->type)
{
return handlers[a->type-1].cmp(a, b);
}
/* otherwise, return a code based on the difference between the types */
if(a->type < b->type)
{
return -1;
}
else
{
return 1;
}
}
static void free_cb(void *node)
{
((scamper_addr_t *)node)->internal = NULL;
return;
}
void scamper_addrcache_free(scamper_addrcache_t *ac)
{
int i;
for(i=(sizeof(handlers)/sizeof(struct handler))-1; i>=0; i--)
{
if(ac->tree[i] != NULL) splaytree_free(ac->tree[i], free_cb);
}
free(ac);
return;
}
scamper_addrcache_t *scamper_addrcache_alloc()
{
scamper_addrcache_t *ac;
int i;
if((ac = malloc(sizeof(scamper_addrcache_t))) == NULL)
{
return NULL;
}
memset(ac, 0, sizeof(scamper_addrcache_t));
for(i=(sizeof(handlers)/sizeof(struct handler))-1; i>=0; i--)
{
ac->tree[i] = splaytree_alloc((splaytree_cmp_t)handlers[i].cmp);
if(ac->tree[i] == NULL) goto err;
}
return ac;
err:
scamper_addrcache_free(ac);
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1