Changeset 2656 for pjproject/trunk/pjlib/src/pj/sock_common.c
- Timestamp:
- Apr 28, 2009 1:47:45 PM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib/src/pj/sock_common.c
r2394 r2656 26 26 #include <pj/string.h> 27 27 #include <pj/compat/socket.h> 28 29 #if 0 30 /* Enable some tracing */ 31 #include <pj/log.h> 32 #define THIS_FILE "sock_common.c" 33 #define TRACE_(arg) PJ_LOG(4,arg) 34 #else 35 #define TRACE_(arg) 36 #endif 28 37 29 38 … … 446 455 } 447 456 457 static pj_bool_t is_usable_ip(const pj_sockaddr *addr) 458 { 459 if (addr->addr.sa_family==PJ_AF_INET) { 460 /* Only consider if the address is not 127.0.0.0/8 or 0.0.0.0/8. 461 * The 0.0.0.0/8 is a special IP class that doesn't seem to be 462 * practically useful for our purpose. 463 */ 464 if ((pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) 465 return PJ_FALSE; 466 if ((pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==0) 467 return PJ_FALSE; 468 469 return PJ_TRUE; 470 471 } else if (addr->addr.sa_family==PJ_AF_INET6) { 472 pj_sockaddr ipv6_loop; 473 const pj_str_t loop = { "::1", 3}; 474 pj_status_t status; 475 476 status = pj_sockaddr_set_str_addr(PJ_AF_INET6, &ipv6_loop, &loop); 477 if (status != PJ_SUCCESS) 478 return PJ_TRUE; 479 480 if (pj_memcmp(&addr->ipv6.sin6_addr, &ipv6_loop.ipv6.sin6_addr, 16)==0) 481 return PJ_FALSE; 482 483 return PJ_TRUE; 484 } else { 485 return PJ_TRUE; 486 } 487 } 488 448 489 /* Resolve the IP address of local machine */ 449 490 PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr) 450 491 { 451 unsigned count; 492 unsigned i, count, cand_cnt; 493 enum { 494 CAND_CNT = 8, 495 WEIGHT_HOSTNAME = 1, /* hostname IP is not always valid! */ 496 WEIGHT_DEF_ROUTE = 2, 497 WEIGHT_INTERFACE = 1 498 }; 499 /* candidates: */ 500 pj_sockaddr cand_addr[CAND_CNT]; 501 unsigned cand_weight[CAND_CNT]; 502 int selected_cand; 503 char strip[PJ_INET6_ADDRSTRLEN+10]; 452 504 pj_addrinfo ai; 453 505 pj_status_t status; 454 506 507 /* May not be used if TRACE_ is disabled */ 508 PJ_UNUSED_ARG(strip); 455 509 456 510 #ifdef _MSC_VER … … 459 513 #endif 460 514 515 cand_cnt = 0; 516 pj_bzero(cand_addr, sizeof(cand_addr)); 517 pj_bzero(cand_weight, sizeof(cand_weight)); 518 for (i=0; i<PJ_ARRAY_SIZE(cand_addr); ++i) { 519 cand_addr[i].addr.sa_family = (pj_uint16_t)af; 520 PJ_SOCKADDR_RESET_LEN(&cand_addr[i]); 521 } 522 461 523 addr->addr.sa_family = (pj_uint16_t)af; 462 524 PJ_SOCKADDR_RESET_LEN(addr); 463 525 464 /* Try with resolving local hostname first*/526 /* Get hostname's IP address */ 465 527 count = 1; 466 528 status = pj_getaddrinfo(af, pj_gethostname(), &count, &ai); 467 529 if (status == PJ_SUCCESS) { 468 530 pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af); 469 pj_sockaddr_copy_addr(addr, &ai.ai_addr); 470 } 471 472 473 /* If we end up with 127.0.0.0/8 or 0.0.0.0/8, resolve the IP 474 * by getting the default interface to connect to some public host. 475 * The 0.0.0.0/8 is a special IP class that doesn't seem to be 476 * practically useful for our purpose. 477 */ 478 if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(addr) || 479 (af==PJ_AF_INET && (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) || 480 (af==PJ_AF_INET && (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==0)) 481 { 482 status = pj_getdefaultipinterface(af, addr); 483 } 484 485 /* If failed, get the first available interface */ 486 if (status != PJ_SUCCESS) { 487 pj_sockaddr itf[1]; 488 unsigned count = PJ_ARRAY_SIZE(itf); 489 490 status = pj_enum_ip_interface(af, &count, itf); 491 if (status == PJ_SUCCESS) { 492 pj_assert(itf[0].addr.sa_family == (pj_uint16_t)af); 493 pj_sockaddr_copy_addr(addr, &itf[0]); 531 pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr); 532 pj_sockaddr_set_port(&cand_addr[cand_cnt], 0); 533 cand_weight[cand_cnt] += WEIGHT_HOSTNAME; 534 ++cand_cnt; 535 536 TRACE_((THIS_FILE, "hostname IP is %s", 537 pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0))); 538 } 539 540 541 /* Get default interface (interface for default route) */ 542 if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { 543 status = pj_getdefaultipinterface(af, addr); 544 if (status == PJ_SUCCESS) { 545 TRACE_((THIS_FILE, "default IP is %s", 546 pj_sockaddr_print(addr, strip, sizeof(strip), 0))); 547 548 pj_sockaddr_set_port(addr, 0); 549 for (i=0; i<cand_cnt; ++i) { 550 if (pj_sockaddr_cmp(&cand_addr[i], addr)==0) 551 break; 552 } 553 554 cand_weight[i] += WEIGHT_DEF_ROUTE; 555 if (i >= cand_cnt) { 556 pj_sockaddr_copy_addr(&cand_addr[i], addr); 557 ++cand_cnt; 558 } 559 } 560 } 561 562 563 /* Enumerate IP interfaces */ 564 if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { 565 unsigned start_if = cand_cnt; 566 unsigned count = PJ_ARRAY_SIZE(cand_addr) - start_if; 567 568 status = pj_enum_ip_interface(af, &count, &cand_addr[start_if]); 569 if (status == PJ_SUCCESS && count) { 570 /* Clear the port number */ 571 for (i=0; i<count; ++i) 572 pj_sockaddr_set_port(&cand_addr[start_if+i], 0); 573 574 /* For each candidate that we found so far (that is the hostname 575 * address and default interface address, check if they're found 576 * in the interface list. If found, add the weight, and if not, 577 * decrease the weight. 578 */ 579 for (i=0; i<cand_cnt; ++i) { 580 unsigned j; 581 for (j=0; j<count; ++j) { 582 if (pj_sockaddr_cmp(&cand_addr[i], 583 &cand_addr[start_if+j])==0) 584 break; 494 585 } 586 587 if (j == count) { 588 /* Not found */ 589 cand_weight[i] -= WEIGHT_INTERFACE; 590 } else { 591 cand_weight[i] += WEIGHT_INTERFACE; 592 } 593 } 594 595 /* Add remaining interface to candidate list. */ 596 for (i=0; i<count; ++i) { 597 unsigned j; 598 for (j=0; j<cand_cnt; ++j) { 599 if (pj_sockaddr_cmp(&cand_addr[start_if+i], 600 &cand_addr[j])==0) 601 break; 602 } 603 604 if (j == cand_cnt) { 605 pj_sockaddr_copy_addr(&cand_addr[cand_cnt], 606 &cand_addr[start_if+i]); 607 cand_weight[cand_cnt] += WEIGHT_INTERFACE; 608 ++cand_cnt; 609 } 610 } 611 } 612 } 613 614 /* Enumerate candidates to get the best IP address to choose */ 615 selected_cand = -1; 616 for (i=0; i<cand_cnt; ++i) { 617 TRACE_((THIS_FILE, "Checking candidate IP %s, weight=%d", 618 pj_sockaddr_print(&cand_addr[i], strip, sizeof(strip), 0), 619 cand_weight[i])); 620 621 if (!is_usable_ip(&cand_addr[i])) { 622 continue; 623 } 624 625 if (selected_cand == -1) 626 selected_cand = i; 627 else if (cand_weight[i] > cand_weight[selected_cand]) 628 selected_cand = i; 495 629 } 496 630 497 631 /* If else fails, returns loopback interface as the last resort */ 498 if (status != PJ_SUCCESS) { 499 if (af==PJ_AF_INET) { 500 addr->ipv4.sin_addr.s_addr = pj_htonl (0x7f000001); 501 } else { 502 pj_in6_addr *s6_addr; 503 504 s6_addr = (pj_in6_addr*) pj_sockaddr_get_addr(addr); 505 pj_bzero(s6_addr, sizeof(pj_in6_addr)); 506 s6_addr->s6_addr[15] = 1; 507 } 508 status = PJ_SUCCESS; 509 } 510 511 return status; 632 if (selected_cand == -1) { 633 if (af==PJ_AF_INET) { 634 addr->ipv4.sin_addr.s_addr = pj_htonl (0x7f000001); 635 } else { 636 pj_in6_addr *s6_addr; 637 638 s6_addr = (pj_in6_addr*) pj_sockaddr_get_addr(addr); 639 pj_bzero(s6_addr, sizeof(pj_in6_addr)); 640 s6_addr->s6_addr[15] = 1; 641 } 642 TRACE_((THIS_FILE, "Loopback IP %s returned", 643 pj_sockaddr_print(addr, strip, sizeof(strip), 0))); 644 } else { 645 pj_sockaddr_copy_addr(addr, &cand_addr[selected_cand]); 646 TRACE_((THIS_FILE, "Candidate %s selected", 647 pj_sockaddr_print(addr, strip, sizeof(strip), 0))); 648 } 649 650 return PJ_SUCCESS; 512 651 } 513 652
Note: See TracChangeset
for help on using the changeset viewer.