1 /* 2 * Copyright (c) 1997-2001 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 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 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "krb5_locl.h" 35 36 RCSID("$Id: addr_families.c,v 1.26 2001/05/14 22:49:55 assar Exp $"); 37 38 struct addr_operations { 39 int af; 40 krb5_address_type atype; 41 size_t max_sockaddr_size; 42 krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *); 43 krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *); 44 void (*addr2sockaddr)(const krb5_address *, struct sockaddr *, 45 int *sa_size, int port); 46 void (*h_addr2sockaddr)(const char *, struct sockaddr *, int *, int); 47 krb5_error_code (*h_addr2addr)(const char *, krb5_address *); 48 krb5_boolean (*uninteresting)(const struct sockaddr *); 49 void (*anyaddr)(struct sockaddr *, int *, int); 50 int (*print_addr)(const krb5_address *, char *, size_t); 51 int (*parse_addr)(const char*, krb5_address *); 52 }; 53 54 /* 55 * AF_INET - aka IPv4 implementation 56 */ 57 58 static krb5_error_code 59 ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a) 60 { 61 const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; 62 unsigned char buf[4]; 63 64 a->addr_type = KRB5_ADDRESS_INET; 65 memcpy (buf, &sin->sin_addr, 4); 66 return krb5_data_copy(&a->address, buf, 4); 67 } 68 69 static krb5_error_code 70 ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port) 71 { 72 const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; 73 74 *port = sin->sin_port; 75 return 0; 76 } 77 78 static void 79 ipv4_addr2sockaddr (const krb5_address *a, 80 struct sockaddr *sa, 81 int *sa_size, 82 int port) 83 { 84 struct sockaddr_in *sin = (struct sockaddr_in *)sa; 85 86 memset (sin, 0, sizeof(*sin)); 87 sin->sin_family = AF_INET; 88 memcpy (&sin->sin_addr, a->address.data, 4); 89 sin->sin_port = port; 90 *sa_size = sizeof(*sin); 91 } 92 93 static void 94 ipv4_h_addr2sockaddr(const char *addr, 95 struct sockaddr *sa, int *sa_size, int port) 96 { 97 struct sockaddr_in *sin = (struct sockaddr_in *)sa; 98 99 memset (sin, 0, sizeof(*sin)); 100 *sa_size = sizeof(*sin); 101 sin->sin_family = AF_INET; 102 sin->sin_port = port; 103 sin->sin_addr = *((const struct in_addr *)addr); 104 } 105 106 static krb5_error_code 107 ipv4_h_addr2addr (const char *addr, 108 krb5_address *a) 109 { 110 unsigned char buf[4]; 111 112 a->addr_type = KRB5_ADDRESS_INET; 113 memcpy(buf, addr, 4); 114 return krb5_data_copy(&a->address, buf, 4); 115 } 116 117 /* 118 * Are there any addresses that should be considered `uninteresting'? 119 */ 120 121 static krb5_boolean 122 ipv4_uninteresting (const struct sockaddr *sa) 123 { 124 const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; 125 126 if (sin->sin_addr.s_addr == INADDR_ANY) 127 return TRUE; 128 129 return FALSE; 130 } 131 132 static void 133 ipv4_anyaddr (struct sockaddr *sa, int *sa_size, int port) 134 { 135 struct sockaddr_in *sin = (struct sockaddr_in *)sa; 136 137 memset (sin, 0, sizeof(*sin)); 138 *sa_size = sizeof(*sin); 139 sin->sin_family = AF_INET; 140 sin->sin_port = port; 141 sin->sin_addr.s_addr = INADDR_ANY; 142 } 143 144 static int 145 ipv4_print_addr (const krb5_address *addr, char *str, size_t len) 146 { 147 struct in_addr ia; 148 149 memcpy (&ia, addr->address.data, 4); 150 151 return snprintf (str, len, "IPv4:%s", inet_ntoa(ia)); 152 } 153 154 static int 155 ipv4_parse_addr (const char *address, krb5_address *addr) 156 { 157 const char *p; 158 struct in_addr a; 159 160 p = strchr(address, ':'); 161 if(p) { 162 p++; 163 if(strncasecmp(address, "ip:", p - address) != 0 && 164 strncasecmp(address, "ip4:", p - address) != 0 && 165 strncasecmp(address, "ipv4:", p - address) != 0 && 166 strncasecmp(address, "inet:", p - address) != 0) 167 return -1; 168 } else 169 p = address; 170 #ifdef HAVE_INET_ATON 171 if(inet_aton(p, &a) == 0) 172 return -1; 173 #elif defined(HAVE_INET_ADDR) 174 a.s_addr = inet_addr(p); 175 if(a.s_addr == INADDR_NONE) 176 return -1; 177 #else 178 return -1; 179 #endif 180 addr->addr_type = KRB5_ADDRESS_INET; 181 if(krb5_data_alloc(&addr->address, 4) != 0) 182 return -1; 183 _krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length); 184 return 0; 185 } 186 187 /* 188 * AF_INET6 - aka IPv6 implementation 189 */ 190 191 #ifdef HAVE_IPV6 192 193 static krb5_error_code 194 ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a) 195 { 196 const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; 197 198 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 199 unsigned char buf[4]; 200 201 a->addr_type = KRB5_ADDRESS_INET; 202 #ifndef IN6_ADDR_V6_TO_V4 203 #ifdef IN6_EXTRACT_V4ADDR 204 #define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x)) 205 #else 206 #define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12]) 207 #endif 208 #endif 209 memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4); 210 return krb5_data_copy(&a->address, buf, 4); 211 } else { 212 a->addr_type = KRB5_ADDRESS_INET6; 213 return krb5_data_copy(&a->address, 214 &sin6->sin6_addr, 215 sizeof(sin6->sin6_addr)); 216 } 217 } 218 219 static krb5_error_code 220 ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port) 221 { 222 const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; 223 224 *port = sin6->sin6_port; 225 return 0; 226 } 227 228 static void 229 ipv6_addr2sockaddr (const krb5_address *a, 230 struct sockaddr *sa, 231 int *sa_size, 232 int port) 233 { 234 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 235 236 memset (sin6, 0, sizeof(*sin6)); 237 sin6->sin6_family = AF_INET6; 238 memcpy (&sin6->sin6_addr, a->address.data, sizeof(sin6->sin6_addr)); 239 sin6->sin6_port = port; 240 *sa_size = sizeof(*sin6); 241 } 242 243 static void 244 ipv6_h_addr2sockaddr(const char *addr, 245 struct sockaddr *sa, 246 int *sa_size, 247 int port) 248 { 249 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 250 251 memset (sin6, 0, sizeof(*sin6)); 252 *sa_size = sizeof(*sin6); 253 sin6->sin6_family = AF_INET6; 254 sin6->sin6_port = port; 255 sin6->sin6_addr = *((const struct in6_addr *)addr); 256 } 257 258 static krb5_error_code 259 ipv6_h_addr2addr (const char *addr, 260 krb5_address *a) 261 { 262 a->addr_type = KRB5_ADDRESS_INET6; 263 return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr)); 264 } 265 266 /* 267 * 268 */ 269 270 static krb5_boolean 271 ipv6_uninteresting (const struct sockaddr *sa) 272 { 273 const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; 274 const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr; 275 276 return 277 IN6_IS_ADDR_LINKLOCAL(in6) 278 || IN6_IS_ADDR_V4COMPAT(in6); 279 } 280 281 static void 282 ipv6_anyaddr (struct sockaddr *sa, int *sa_size, int port) 283 { 284 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 285 286 memset (sin6, 0, sizeof(*sin6)); 287 *sa_size = sizeof(*sin6); 288 sin6->sin6_family = AF_INET6; 289 sin6->sin6_port = port; 290 sin6->sin6_addr = in6addr_any; 291 } 292 293 static int 294 ipv6_print_addr (const krb5_address *addr, char *str, size_t len) 295 { 296 char buf[128], buf2[3]; 297 #ifdef HAVE_INET_NTOP 298 if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL) 299 #endif 300 { 301 /* XXX this is pretty ugly, but better than abort() */ 302 int i; 303 unsigned char *p = addr->address.data; 304 buf[0] = '\0'; 305 for(i = 0; i < addr->address.length; i++) { 306 snprintf(buf2, sizeof(buf2), "%02x", p[i]); 307 if(i > 0 && (i & 1) == 0) 308 strlcat(buf, ":", sizeof(buf)); 309 strlcat(buf, buf2, sizeof(buf)); 310 } 311 } 312 return snprintf(str, len, "IPv6:%s", buf); 313 } 314 315 static int 316 ipv6_parse_addr (const char *address, krb5_address *addr) 317 { 318 int ret; 319 struct in6_addr in6; 320 321 ret = inet_pton(AF_INET6, address, &in6.s6_addr); 322 if(ret == 1) { 323 addr->addr_type = KRB5_ADDRESS_INET6; 324 ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr)); 325 if (ret) 326 return -1; 327 memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr)); 328 return 0; 329 } 330 return -1; 331 } 332 333 #endif /* IPv6 */ 334 335 /* 336 * table 337 */ 338 339 static struct addr_operations at[] = { 340 {AF_INET, KRB5_ADDRESS_INET, sizeof(struct sockaddr_in), 341 ipv4_sockaddr2addr, 342 ipv4_sockaddr2port, 343 ipv4_addr2sockaddr, 344 ipv4_h_addr2sockaddr, 345 ipv4_h_addr2addr, 346 ipv4_uninteresting, ipv4_anyaddr, ipv4_print_addr, ipv4_parse_addr}, 347 #ifdef HAVE_IPV6 348 {AF_INET6, KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6), 349 ipv6_sockaddr2addr, 350 ipv6_sockaddr2port, 351 ipv6_addr2sockaddr, 352 ipv6_h_addr2sockaddr, 353 ipv6_h_addr2addr, 354 ipv6_uninteresting, ipv6_anyaddr, ipv6_print_addr, ipv6_parse_addr} 355 #endif 356 }; 357 358 static int num_addrs = sizeof(at) / sizeof(at[0]); 359 360 static size_t max_sockaddr_size = 0; 361 362 /* 363 * generic functions 364 */ 365 366 static struct addr_operations * 367 find_af(int af) 368 { 369 struct addr_operations *a; 370 371 for (a = at; a < at + num_addrs; ++a) 372 if (af == a->af) 373 return a; 374 return NULL; 375 } 376 377 static struct addr_operations * 378 find_atype(int atype) 379 { 380 struct addr_operations *a; 381 382 for (a = at; a < at + num_addrs; ++a) 383 if (atype == a->atype) 384 return a; 385 return NULL; 386 } 387 388 krb5_error_code 389 krb5_sockaddr2address (krb5_context context, 390 const struct sockaddr *sa, krb5_address *addr) 391 { 392 struct addr_operations *a = find_af(sa->sa_family); 393 if (a == NULL) { 394 krb5_set_error_string (context, "Address family %d not supported", 395 sa->sa_family); 396 return KRB5_PROG_ATYPE_NOSUPP; 397 } 398 return (*a->sockaddr2addr)(sa, addr); 399 } 400 401 krb5_error_code 402 krb5_sockaddr2port (krb5_context context, 403 const struct sockaddr *sa, int16_t *port) 404 { 405 struct addr_operations *a = find_af(sa->sa_family); 406 if (a == NULL) { 407 krb5_set_error_string (context, "Address family %d not supported", 408 sa->sa_family); 409 return KRB5_PROG_ATYPE_NOSUPP; 410 } 411 return (*a->sockaddr2port)(sa, port); 412 } 413 414 krb5_error_code 415 krb5_addr2sockaddr (krb5_context context, 416 const krb5_address *addr, 417 struct sockaddr *sa, 418 int *sa_size, 419 int port) 420 { 421 struct addr_operations *a = find_atype(addr->addr_type); 422 423 if (a == NULL) { 424 krb5_set_error_string (context, "Address type %d not supported", 425 addr->addr_type); 426 return KRB5_PROG_ATYPE_NOSUPP; 427 } 428 (*a->addr2sockaddr)(addr, sa, sa_size, port); 429 return 0; 430 } 431 432 size_t 433 krb5_max_sockaddr_size (void) 434 { 435 if (max_sockaddr_size == 0) { 436 struct addr_operations *a; 437 438 for(a = at; a < at + num_addrs; ++a) 439 max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size); 440 } 441 return max_sockaddr_size; 442 } 443 444 krb5_boolean 445 krb5_sockaddr_uninteresting(const struct sockaddr *sa) 446 { 447 struct addr_operations *a = find_af(sa->sa_family); 448 if (a == NULL) 449 return TRUE; 450 return (*a->uninteresting)(sa); 451 } 452 453 krb5_error_code 454 krb5_h_addr2sockaddr (krb5_context context, 455 int af, 456 const char *addr, struct sockaddr *sa, int *sa_size, 457 int port) 458 { 459 struct addr_operations *a = find_af(af); 460 if (a == NULL) { 461 krb5_set_error_string (context, "Address family %d not supported", af); 462 return KRB5_PROG_ATYPE_NOSUPP; 463 } 464 (*a->h_addr2sockaddr)(addr, sa, sa_size, port); 465 return 0; 466 } 467 468 krb5_error_code 469 krb5_h_addr2addr (krb5_context context, 470 int af, 471 const char *haddr, krb5_address *addr) 472 { 473 struct addr_operations *a = find_af(af); 474 if (a == NULL) { 475 krb5_set_error_string (context, "Address family %d not supported", af); 476 return KRB5_PROG_ATYPE_NOSUPP; 477 } 478 return (*a->h_addr2addr)(haddr, addr); 479 } 480 481 krb5_error_code 482 krb5_anyaddr (krb5_context context, 483 int af, 484 struct sockaddr *sa, 485 int *sa_size, 486 int port) 487 { 488 struct addr_operations *a = find_af (af); 489 490 if (a == NULL) { 491 krb5_set_error_string (context, "Address family %d not supported", af); 492 return KRB5_PROG_ATYPE_NOSUPP; 493 } 494 495 (*a->anyaddr)(sa, sa_size, port); 496 return 0; 497 } 498 499 krb5_error_code 500 krb5_print_address (const krb5_address *addr, 501 char *str, size_t len, size_t *ret_len) 502 { 503 struct addr_operations *a = find_atype(addr->addr_type); 504 505 if (a == NULL) { 506 char *s; 507 size_t l; 508 int i; 509 s = str; 510 l = snprintf(s, len, "TYPE_%d:", addr->addr_type); 511 s += l; 512 len -= len; 513 for(i = 0; i < addr->address.length; i++) { 514 l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]); 515 len -= l; 516 s += l; 517 } 518 *ret_len = s - str; 519 return 0; 520 } 521 *ret_len = (*a->print_addr)(addr, str, len); 522 return 0; 523 } 524 525 krb5_error_code 526 krb5_parse_address(krb5_context context, 527 const char *string, 528 krb5_addresses *addresses) 529 { 530 int i, n; 531 struct addrinfo *ai, *a; 532 int error; 533 int save_errno; 534 535 for(i = 0; i < num_addrs; i++) { 536 if(at[i].parse_addr) { 537 krb5_address a; 538 if((*at[i].parse_addr)(string, &a) == 0) { 539 ALLOC_SEQ(addresses, 1); 540 addresses->val[0] = a; 541 return 0; 542 } 543 } 544 } 545 546 error = getaddrinfo (string, NULL, NULL, &ai); 547 if (error) { 548 save_errno = errno; 549 krb5_set_error_string (context, "%s: %s", string, gai_strerror(error)); 550 return krb5_eai_to_heim_errno(error, save_errno); 551 } 552 553 n = 0; 554 for (a = ai; a != NULL; a = a->ai_next) 555 ++n; 556 557 ALLOC_SEQ(addresses, n); 558 559 for (a = ai, i = 0; a != NULL; a = a->ai_next, ++i) { 560 krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i]); 561 } 562 freeaddrinfo (ai); 563 return 0; 564 } 565