1 /* 2 * Copyright (c) 1997-1999 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.23 2000/02/16 02:09:00 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 (const struct sockaddr *sa, krb5_address *addr) 390 { 391 struct addr_operations *a = find_af(sa->sa_family); 392 if (a == NULL) 393 return KRB5_PROG_ATYPE_NOSUPP; 394 return (*a->sockaddr2addr)(sa, addr); 395 } 396 397 krb5_error_code 398 krb5_sockaddr2port (const struct sockaddr *sa, int16_t *port) 399 { 400 struct addr_operations *a = find_af(sa->sa_family); 401 if (a == NULL) 402 return KRB5_PROG_ATYPE_NOSUPP; 403 return (*a->sockaddr2port)(sa, port); 404 } 405 406 krb5_error_code 407 krb5_addr2sockaddr (const krb5_address *addr, 408 struct sockaddr *sa, 409 int *sa_size, 410 int port) 411 { 412 struct addr_operations *a = find_atype(addr->addr_type); 413 414 if (a == NULL) 415 return KRB5_PROG_ATYPE_NOSUPP; 416 (*a->addr2sockaddr)(addr, sa, sa_size, port); 417 return 0; 418 } 419 420 size_t 421 krb5_max_sockaddr_size (void) 422 { 423 if (max_sockaddr_size == 0) { 424 struct addr_operations *a; 425 426 for(a = at; a < at + num_addrs; ++a) 427 max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size); 428 } 429 return max_sockaddr_size; 430 } 431 432 krb5_boolean 433 krb5_sockaddr_uninteresting(const struct sockaddr *sa) 434 { 435 struct addr_operations *a = find_af(sa->sa_family); 436 if (a == NULL) 437 return TRUE; 438 return (*a->uninteresting)(sa); 439 } 440 441 krb5_error_code 442 krb5_h_addr2sockaddr (int af, 443 const char *addr, struct sockaddr *sa, int *sa_size, 444 int port) 445 { 446 struct addr_operations *a = find_af(af); 447 if (a == NULL) 448 return KRB5_PROG_ATYPE_NOSUPP; 449 (*a->h_addr2sockaddr)(addr, sa, sa_size, port); 450 return 0; 451 } 452 453 krb5_error_code 454 krb5_h_addr2addr (int af, 455 const char *haddr, krb5_address *addr) 456 { 457 struct addr_operations *a = find_af(af); 458 if (a == NULL) 459 return KRB5_PROG_ATYPE_NOSUPP; 460 return (*a->h_addr2addr)(haddr, addr); 461 } 462 463 krb5_error_code 464 krb5_anyaddr (int af, 465 struct sockaddr *sa, 466 int *sa_size, 467 int port) 468 { 469 struct addr_operations *a = find_af (af); 470 471 if (a == NULL) 472 return KRB5_PROG_ATYPE_NOSUPP; 473 474 (*a->anyaddr)(sa, sa_size, port); 475 return 0; 476 } 477 478 krb5_error_code 479 krb5_print_address (const krb5_address *addr, 480 char *str, size_t len, size_t *ret_len) 481 { 482 struct addr_operations *a = find_atype(addr->addr_type); 483 484 if (a == NULL) { 485 char *s; 486 size_t l; 487 int i; 488 s = str; 489 l = snprintf(s, len, "TYPE_%d:", addr->addr_type); 490 s += l; 491 len -= len; 492 for(i = 0; i < addr->address.length; i++) { 493 l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]); 494 len -= l; 495 s += l; 496 } 497 *ret_len = s - str; 498 return 0; 499 } 500 *ret_len = (*a->print_addr)(addr, str, len); 501 return 0; 502 } 503 504 krb5_error_code 505 krb5_parse_address(krb5_context context, 506 const char *string, 507 krb5_addresses *addresses) 508 { 509 int i, n; 510 struct addrinfo *ai, *a; 511 int error; 512 513 for(i = 0; i < num_addrs; i++) { 514 if(at[i].parse_addr) { 515 krb5_address a; 516 if((*at[i].parse_addr)(string, &a) == 0) { 517 ALLOC_SEQ(addresses, 1); 518 addresses->val[0] = a; 519 return 0; 520 } 521 } 522 } 523 524 error = getaddrinfo (string, NULL, NULL, &ai); 525 if (error) 526 return -1; 527 528 n = 0; 529 for (a = ai; a != NULL; a = a->ai_next) 530 ++n; 531 532 ALLOC_SEQ(addresses, n); 533 534 for (a = ai, i = 0; a != NULL; a = a->ai_next, ++i) { 535 krb5_sockaddr2address (ai->ai_addr, &addresses->val[i]); 536 } 537 freeaddrinfo (ai); 538 return 0; 539 } 540