Emeric Vigier | 2f62582 | 2012-08-06 11:09:52 -0400 | [diff] [blame] | 1 | // Copyright (C) 1999-2005 Open Source Telecom Corporation. |
| 2 | // Copyright (C) 2006-2010 David Sugar, Tycho Softworks. |
| 3 | // |
| 4 | // This program is free software; you can redistribute it and/or modify |
| 5 | // it under the terms of the GNU General Public License as published by |
| 6 | // the Free Software Foundation; either version 2 of the License, or |
| 7 | // (at your option) any later version. |
| 8 | // |
| 9 | // This program is distributed in the hope that it will be useful, |
| 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | // GNU General Public License for more details. |
| 13 | // |
| 14 | // You should have received a copy of the GNU General Public License |
| 15 | // along with this program; if not, write to the Free Software |
| 16 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 17 | // |
| 18 | // As a special exception, you may use this file as part of a free software |
| 19 | // library without restriction. Specifically, if other files instantiate |
| 20 | // templates or use macros or inline functions from this file, or you compile |
| 21 | // this file and link it with other files to produce an executable, this |
| 22 | // file does not by itself cause the resulting executable to be covered by |
| 23 | // the GNU General Public License. This exception does not however |
| 24 | // invalidate any other reasons why the executable file might be covered by |
| 25 | // the GNU General Public License. |
| 26 | // |
| 27 | // This exception applies only to the code released under the name GNU |
| 28 | // Common C++. If you copy code from other releases into a copy of GNU |
| 29 | // Common C++, as the General Public License permits, the exception does |
| 30 | // not apply to the code that you add in this way. To avoid misleading |
| 31 | // anyone as to the status of such modified files, you must delete |
| 32 | // this exception notice from them. |
| 33 | // |
| 34 | // If you write modifications of your own for GNU Common C++, it is your choice |
| 35 | // whether to permit this exception to apply to your modifications. |
| 36 | // If you do not wish that, delete this exception notice. |
| 37 | // |
| 38 | |
| 39 | #include <cc++/config.h> |
| 40 | #include <cc++/export.h> |
| 41 | #include <cc++/address.h> |
| 42 | #include "private.h" |
| 43 | #include <cstdlib> |
| 44 | |
| 45 | #ifdef CCXX_IPV6 |
| 46 | |
| 47 | #ifdef CCXX_NAMESPACES |
| 48 | namespace ost { |
| 49 | #endif |
| 50 | |
| 51 | #ifndef WIN32 |
| 52 | Mutex IPV6Address::mutex; |
| 53 | #endif |
| 54 | |
| 55 | const IPV6MulticastValidator IPV6Multicast::validator; |
| 56 | |
| 57 | void IPV6MulticastValidator::operator()(const in6_addr address) const |
| 58 | { |
| 59 | #ifdef CCXX_EXCEPTIONS |
| 60 | // "0000:" is always accepted, as it is an "empty" address. |
| 61 | if ( (address.s6_addr[0] != 0 || address.s6_addr[1] != 0) && |
| 62 | (address.s6_addr[0] != 0xff || address.s6_addr[1] < 0x1f)) { |
| 63 | throw "Multicast address not in the valid prefix ff00-ff1f:"; |
| 64 | } |
| 65 | #endif |
| 66 | } |
| 67 | |
| 68 | IPV6Address::IPV6Address(const IPV6Validator *_validator) |
| 69 | : validator(_validator), hostname(NULL) { |
| 70 | addr_count = 1; |
| 71 | ipaddr = new struct in6_addr[1]; |
| 72 | memcpy(ipaddr, &in6addr_any, sizeof(in6_addr)); |
| 73 | } |
| 74 | |
| 75 | IPV6Address::IPV6Address(const char *address, const IPV6Validator *_validator) : |
| 76 | validator(_validator), ipaddr(NULL), addr_count(0), hostname(NULL) { |
| 77 | if ( this->validator ) |
| 78 | this->validator = validator; |
| 79 | if(address == 0 || !strcmp(address, "*")) |
| 80 | setAddress(NULL); |
| 81 | else |
| 82 | setAddress(address); |
| 83 | } |
| 84 | |
| 85 | IPV6Address::IPV6Address(struct in6_addr addr, const IPV6Validator *_validator) : |
| 86 | validator(_validator), ipaddr(NULL), hostname(NULL) { |
| 87 | if ( this->validator ){ |
| 88 | this->validator = validator; |
| 89 | (*validator)(addr); |
| 90 | } |
| 91 | addr_count = 1; |
| 92 | ipaddr = new struct in6_addr[1]; |
| 93 | memcpy(&ipaddr, &addr, sizeof(struct in6_addr)); |
| 94 | } |
| 95 | |
| 96 | IPV6Address::IPV6Address(const IPV6Address &rhs) : |
| 97 | validator(rhs.validator), addr_count(rhs.addr_count), hostname(NULL) { |
| 98 | ipaddr = new struct in6_addr[addr_count]; |
| 99 | memcpy(ipaddr, rhs.ipaddr, sizeof(struct in6_addr) * addr_count); |
| 100 | } |
| 101 | |
| 102 | IPV6Address::~IPV6Address() |
| 103 | { |
| 104 | if(ipaddr) { |
| 105 | delete[] ipaddr; |
| 106 | ipaddr = NULL; |
| 107 | } |
| 108 | if(hostname) { |
| 109 | delString(hostname); |
| 110 | hostname = NULL; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | struct in6_addr IPV6Address::getAddress(void) const |
| 115 | { |
| 116 | return ipaddr[0]; |
| 117 | } |
| 118 | |
| 119 | struct in6_addr IPV6Address::getAddress(size_t i) const |
| 120 | { |
| 121 | return (i < addr_count ? ipaddr[i] : ipaddr[0]); |
| 122 | } |
| 123 | |
| 124 | bool IPV6Address::isInetAddress(void) const |
| 125 | { |
| 126 | struct in6_addr addr; |
| 127 | memset(&addr, 0, sizeof(addr)); |
| 128 | if(!ipaddr) |
| 129 | return false; |
| 130 | if(memcmp(&addr, &ipaddr[0], sizeof(addr))) |
| 131 | return true; |
| 132 | return false; |
| 133 | } |
| 134 | |
| 135 | IPV6Address &IPV6Address::operator=(const char *str) |
| 136 | { |
| 137 | if(str == 0 || !strcmp(str, "*")) |
| 138 | str = "::"; |
| 139 | |
| 140 | setAddress(str); |
| 141 | |
| 142 | return *this; |
| 143 | } |
| 144 | |
| 145 | IPV6Address &IPV6Address::operator=(struct in6_addr addr) |
| 146 | { |
| 147 | if(ipaddr) |
| 148 | delete[] ipaddr; |
| 149 | if ( validator ) |
| 150 | (*validator)(addr); |
| 151 | addr_count = 1; |
| 152 | ipaddr = new struct in6_addr[1]; |
| 153 | ipaddr[0] = addr; |
| 154 | if(hostname) |
| 155 | delString(hostname); |
| 156 | hostname = NULL; |
| 157 | return *this; |
| 158 | } |
| 159 | |
| 160 | IPV6Address &IPV6Address::operator=(const IPV6Address &rhs) |
| 161 | { |
| 162 | if(this == &rhs) return *this; |
| 163 | |
| 164 | addr_count = rhs.addr_count; |
| 165 | if(ipaddr) |
| 166 | delete[] ipaddr; |
| 167 | ipaddr = new struct in6_addr[addr_count]; |
| 168 | memcpy(ipaddr, rhs.ipaddr, sizeof(struct in6_addr) * addr_count); |
| 169 | validator = rhs.validator; |
| 170 | if(hostname) |
| 171 | delString(hostname); |
| 172 | hostname = NULL; |
| 173 | |
| 174 | return *this; |
| 175 | } |
| 176 | |
| 177 | bool IPV6Address::operator==(const IPV6Address &a) const |
| 178 | { |
| 179 | const IPV6Address *smaller, *larger; |
| 180 | size_t s, l; |
| 181 | |
| 182 | if(addr_count > a.addr_count) { |
| 183 | smaller = &a; |
| 184 | larger = this; |
| 185 | } |
| 186 | else { |
| 187 | smaller = this; |
| 188 | larger = &a; |
| 189 | } |
| 190 | |
| 191 | // Loop through all addr's in the smaller and make sure |
| 192 | // that they are all in the larger |
| 193 | for(s = 0; s < smaller->addr_count; s++) { |
| 194 | // bool found = false; |
| 195 | for(l = 0; l < larger->addr_count && |
| 196 | memcmp((char *)&ipaddr[s], (char *)&a.ipaddr[l], sizeof(struct in6_addr)); l++); |
| 197 | if(l == larger->addr_count) return false; |
| 198 | } |
| 199 | return true; |
| 200 | } |
| 201 | |
| 202 | bool IPV6Address::operator!=(const IPV6Address &a) const |
| 203 | { |
| 204 | // Impliment in terms of operator== |
| 205 | return (*this == a ? false : true); |
| 206 | } |
| 207 | |
| 208 | IPV6Host &IPV6Host::operator&=(const IPV6Mask &ma) |
| 209 | { |
| 210 | for(size_t i = 0; i < addr_count; i++) { |
| 211 | struct in6_addr mask = ma.getAddress(); |
| 212 | unsigned char *a = (unsigned char *)&ipaddr[i]; |
| 213 | unsigned char *m = (unsigned char *)&mask; |
| 214 | |
| 215 | for(size_t j = 0; j < sizeof(struct in6_addr); ++j) |
| 216 | *(a++) &= *(m++); |
| 217 | } |
| 218 | if(hostname) |
| 219 | delString(hostname); |
| 220 | hostname = NULL; |
| 221 | |
| 222 | return *this; |
| 223 | } |
| 224 | |
| 225 | IPV6Host::IPV6Host(struct in6_addr addr) : |
| 226 | IPV6Address(addr) {} |
| 227 | |
| 228 | IPV6Host::IPV6Host(const char *host) : |
| 229 | IPV6Address(host) |
| 230 | { |
| 231 | char namebuf[256]; |
| 232 | |
| 233 | if(!host) { |
| 234 | gethostname(namebuf, 256); |
| 235 | setAddress(namebuf); |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | bool IPV6Address::setIPAddress(const char *host) |
| 240 | { |
| 241 | if(!host) |
| 242 | return false; |
| 243 | |
| 244 | struct in6_addr l_addr; |
| 245 | |
| 246 | #ifdef WIN32 |
| 247 | struct sockaddr saddr; |
| 248 | int slen = sizeof(saddr); |
| 249 | struct sockaddr_in6 *paddr = (struct sockaddr_in6 *)&saddr; |
| 250 | int ok = WSAStringToAddress((LPSTR)host, AF_INET6, NULL, &saddr, &slen); |
| 251 | l_addr = paddr->sin6_addr; |
| 252 | #else |
| 253 | int ok = inet_pton(AF_INET6, host, &l_addr); |
| 254 | #endif |
| 255 | if ( validator ) |
| 256 | (*validator)(l_addr); |
| 257 | if ( !ok ) |
| 258 | return false; |
| 259 | *this = l_addr; |
| 260 | return true; |
| 261 | } |
| 262 | |
| 263 | #if defined(HAVE_GETADDRINFO) && !defined(HAVE_GETHOSTBYNAME2) |
| 264 | |
| 265 | void IPV6Address::setAddress(const char *host) |
| 266 | { |
| 267 | if(hostname) |
| 268 | delString(hostname); |
| 269 | hostname = NULL; |
| 270 | |
| 271 | if(!host) // The way this is currently used, this can never happen |
| 272 | host = "::"; |
| 273 | |
| 274 | #ifdef WIN32 |
| 275 | if(!stricmp(host, "localhost")) |
| 276 | host = "::1"; |
| 277 | #endif |
| 278 | |
| 279 | if(!setIPAddress(host)) { |
| 280 | struct addrinfo hint, *list = NULL, *first; |
| 281 | memset(&hint, 0, sizeof(hint)); |
| 282 | hint.ai_family = AF_INET6; |
| 283 | struct in6_addr *addr; |
| 284 | struct sockaddr_in6 *ip6addr; |
| 285 | |
| 286 | if(getaddrinfo(host, NULL, &hint, &list) || !list) { |
| 287 | if(ipaddr) |
| 288 | delete[] ipaddr; |
| 289 | ipaddr = new struct in6_addr[1]; |
| 290 | memset((void *)&ipaddr[0], 0, sizeof(ipaddr)); |
| 291 | return; |
| 292 | } |
| 293 | |
| 294 | // Count the number of IP addresses returned |
| 295 | addr_count = 0; |
| 296 | first = list; |
| 297 | while(list) { |
| 298 | ++addr_count; |
| 299 | list = list->ai_next; |
| 300 | } |
| 301 | |
| 302 | // Allocate enough memory |
| 303 | if(ipaddr) |
| 304 | delete[] ipaddr; // Cause this was allocated in base |
| 305 | ipaddr = new struct in6_addr[addr_count]; |
| 306 | |
| 307 | // Now go through the list again assigning to |
| 308 | // the member ipaddr; |
| 309 | list = first; |
| 310 | int i = 0; |
| 311 | while(list) { |
| 312 | ip6addr = (struct sockaddr_in6 *)list->ai_addr; |
| 313 | addr = &ip6addr->sin6_addr; |
| 314 | if(validator) |
| 315 | (*validator)(*addr); |
| 316 | ipaddr[i++] = *addr; |
| 317 | list = list->ai_next; |
| 318 | } |
| 319 | freeaddrinfo(first); |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | #else |
| 324 | |
| 325 | void IPV6Address::setAddress(const char *host) |
| 326 | { |
| 327 | if(hostname) |
| 328 | delString(hostname); |
| 329 | hostname = NULL; |
| 330 | |
| 331 | if(!host) // The way this is currently used, this can never happen |
| 332 | host = "::"; |
| 333 | |
| 334 | #ifdef WIN32 |
| 335 | if(!stricmp(host, "localhost")) |
| 336 | host = "::1"; |
| 337 | #endif |
| 338 | |
| 339 | if(!setIPAddress(host)) { |
| 340 | struct hostent *hp; |
| 341 | struct in6_addr **bptr; |
| 342 | #if defined(__GLIBC__) |
| 343 | char hbuf[8192]; |
| 344 | struct hostent hb; |
| 345 | int rtn; |
| 346 | |
| 347 | if(gethostbyname2_r(host, AF_INET6, &hb, hbuf, sizeof(hbuf), &hp, &rtn)) |
| 348 | hp = NULL; |
| 349 | #elif defined(sun) |
| 350 | char hbuf[8192]; |
| 351 | struct hostent hb; |
| 352 | int rtn; |
| 353 | |
| 354 | hp = gethostbyname2_r(host, AF_INET6, &hb, hbuf, sizeof(hbuf), &rtn); |
| 355 | #elif (defined(__osf__) || defined(_OSF_SOURCE) || defined(__hpux)) |
| 356 | hp = gethostbyname(host); |
| 357 | #elif defined(WIN32) && (!defined(_MSC_VER) || _MSC_VER < 1300) |
| 358 | hp = gethostbyname(host); |
| 359 | #elif defined(WIN32) |
| 360 | hp = gethostbyname2(host, AF_INET6); |
| 361 | #else |
| 362 | mutex.enterMutex(); |
| 363 | hp = gethostbyname2(host, AF_INET6); |
| 364 | mutex.leaveMutex(); |
| 365 | #endif |
| 366 | if(!hp) { |
| 367 | if(ipaddr) |
| 368 | delete[] ipaddr; |
| 369 | ipaddr = new struct in6_addr[1]; |
| 370 | memset((void *)&ipaddr[0], 0, sizeof(ipaddr)); |
| 371 | return; |
| 372 | } |
| 373 | |
| 374 | // Count the number of IP addresses returned |
| 375 | addr_count = 0; |
| 376 | for(bptr = (struct in6_addr **)hp->h_addr_list; *bptr != NULL; bptr++) { |
| 377 | addr_count++; |
| 378 | } |
| 379 | |
| 380 | // Allocate enough memory |
| 381 | if(ipaddr) |
| 382 | delete[] ipaddr; // Cause this was allocated in base |
| 383 | ipaddr = new struct in6_addr[addr_count]; |
| 384 | |
| 385 | // Now go through the list again assigning to |
| 386 | // the member ipaddr; |
| 387 | bptr = (struct in6_addr **)hp->h_addr_list; |
| 388 | for(unsigned int i = 0; i < addr_count; i++) { |
| 389 | if ( validator ) |
| 390 | (*validator)(*bptr[i]); |
| 391 | ipaddr[i] = *bptr[i]; |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | #endif |
| 397 | |
| 398 | IPV6Broadcast::IPV6Broadcast(const char *net) : |
| 399 | IPV6Address(net) |
| 400 | { |
| 401 | } |
| 402 | |
| 403 | IPV6Mask::IPV6Mask(const char *mask) : |
| 404 | IPV6Address(mask) |
| 405 | { |
| 406 | } |
| 407 | |
| 408 | const char *IPV6Address::getHostname(void) const |
| 409 | { |
| 410 | struct hostent *hp = NULL; |
| 411 | struct in6_addr addr0; |
| 412 | static char strbuf[64]; |
| 413 | |
| 414 | memset(&addr0, 0, sizeof(addr0)); |
| 415 | if(!memcmp(&addr0, &ipaddr[0], sizeof(addr0))) |
| 416 | return NULL; |
| 417 | |
| 418 | if(!memcmp(&in6addr_loopback, &ipaddr[0], sizeof(addr0))) |
| 419 | return "localhost"; |
| 420 | |
| 421 | #if defined(__GLIBC__) |
| 422 | char hbuf[8192]; |
| 423 | struct hostent hb; |
| 424 | int rtn; |
| 425 | if(gethostbyaddr_r((char *)&ipaddr[0], sizeof(addr0), AF_INET6, &hb, hbuf, sizeof(hbuf), &hp, &rtn)) |
| 426 | hp = NULL; |
| 427 | #elif defined(sun) |
| 428 | char hbuf[8192]; |
| 429 | struct hostent hb; |
| 430 | int rtn; |
| 431 | hp = gethostbyaddr_r((char *)&ipaddr[0], sizeof(addr0), AF_INET6, &hb, hbuf, (int)sizeof(hbuf), &rtn); |
| 432 | #elif defined(__osf__) || defined(WIN32) |
| 433 | hp = gethostbyaddr((char *)&ipaddr[0], sizeof(addr0), AF_INET6); |
| 434 | #else |
| 435 | mutex.enterMutex(); |
| 436 | hp = gethostbyaddr((char *)&ipaddr[0], sizeof(addr0), AF_INET6); |
| 437 | mutex.leaveMutex(); |
| 438 | #endif |
| 439 | if(hp) { |
| 440 | if(hostname) |
| 441 | delString(hostname); |
| 442 | hostname = newString(hp->h_name); |
| 443 | return hostname; |
| 444 | } else { |
| 445 | #ifdef WIN32 |
| 446 | struct sockaddr saddr; |
| 447 | struct sockaddr_in6 *paddr = (struct sockaddr_in6 *)&saddr; |
| 448 | DWORD slen = sizeof(strbuf); |
| 449 | memset(&saddr, 0, sizeof(saddr)); |
| 450 | paddr->sin6_family = AF_INET6; |
| 451 | paddr->sin6_addr = ipaddr[0]; |
| 452 | WSAAddressToString(&saddr, sizeof(saddr), NULL, strbuf, &slen); |
| 453 | return strbuf; |
| 454 | #else |
| 455 | return inet_ntop(AF_INET6, &ipaddr[0], strbuf, sizeof(strbuf)); |
| 456 | #endif |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | IPV6Host operator&(const IPV6Host &addr, const IPV6Mask &mask) |
| 461 | { |
| 462 | IPV6Host temp = addr; |
| 463 | temp &= mask; |
| 464 | return temp; |
| 465 | } |
| 466 | |
| 467 | IPV6Multicast::IPV6Multicast() : |
| 468 | IPV6Address(&validator) |
| 469 | {} |
| 470 | |
| 471 | IPV6Multicast::IPV6Multicast(const char *address) : |
| 472 | IPV6Address(address,&validator) |
| 473 | {} |
| 474 | |
| 475 | #ifdef CCXX_NAMESPACES |
| 476 | } |
| 477 | #endif |
| 478 | |
| 479 | #endif |
| 480 | |
| 481 | /** EMACS ** |
| 482 | * Local variables: |
| 483 | * mode: c++ |
| 484 | * c-basic-offset: 4 |
| 485 | * End: |
| 486 | */ |