/* * 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 #include #include #include #include #include #include #include #include #include #if defined(__APPLE__) #include #endif #if defined(DMALLOC) #include #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; }