Changeset 2743 for pjproject/trunk
- Timestamp:
- Jun 4, 2009 3:11:25 PM (15 years ago)
- Location:
- pjproject/trunk/pjlib
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib/include/pj/sock.h
r2394 r2743 941 941 pj_uint16_t hostport); 942 942 943 /** 944 * Parse string containing IP address and optional port into socket address, 945 * possibly also with address family detection. This function supports both 946 * IPv4 and IPv6 parsing, however IPv6 parsing may only be done if IPv6 is 947 * enabled during compilation. 948 * 949 * This function supports parsing several formats. Sample IPv4 inputs and 950 * their default results:: 951 * - "10.0.0.1:80": address 10.0.0.1 and port 80. 952 * - "10.0.0.1": address 10.0.0.1 and port zero. 953 * - "10.0.0.1:": address 10.0.0.1 and port zero. 954 * - "10.0.0.1:0": address 10.0.0.1 and port zero. 955 * - ":80": address 0.0.0.0 and port 80. 956 * - ":": address 0.0.0.0 and port 0. 957 * - "localhost": address 127.0.0.1 and port 0. 958 * - "localhost:": address 127.0.0.1 and port 0. 959 * - "localhost:80": address 127.0.0.1 and port 80. 960 * 961 * Sample IPv6 inputs and their default results: 962 * - "[fec0::01]:80": address fec0::01 and port 80 963 * - "[fec0::01]": address fec0::01 and port 0 964 * - "[fec0::01]:": address fec0::01 and port 0 965 * - "[fec0::01]:0": address fec0::01 and port 0 966 * - "fec0::01": address fec0::01 and port 0 967 * - "fec0::01:80": address fec0::01:80 and port 0 968 * - "::": address zero (::) and port 0 969 * - "[::]": address zero (::) and port 0 970 * - "[::]:": address zero (::) and port 0 971 * - ":::": address zero (::) and port 0 972 * - "[::]:80": address zero (::) and port 0 973 * - ":::80": address zero (::) and port 80 974 * 975 * Note: when the IPv6 socket address contains port number, the IP 976 * part of the socket address should be enclosed with square brackets, 977 * otherwise the port number will be included as part of the IP address 978 * (see "fec0::01:80" example above). 979 * 980 * @param af Optionally specify the address family to be used. If the 981 * address family is to be deducted from the input, specify 982 * pj_AF_UNSPEC() here. 983 * @param options Additional options to assist the parsing, must be zero 984 * for now. 985 * @param str The input string to be parsed. 986 * @param addr Pointer to store the result. 987 * 988 * @return PJ_SUCCESS if the parsing is successful. 989 */ 990 PJ_DECL(pj_status_t) pj_sockaddr_parse(int af, unsigned options, 991 const pj_str_t *str, 992 pj_sockaddr *addr); 993 943 994 /***************************************************************************** 944 995 * -
pjproject/trunk/pjlib/src/pj/sock_common.c
r2656 r2743 20 20 #include <pj/sock.h> 21 21 #include <pj/assert.h> 22 #include <pj/ctype.h> 22 23 #include <pj/errno.h> 23 24 #include <pj/ip_helper.h> … … 455 456 } 456 457 458 /* 459 * Parse address 460 */ 461 PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, 462 const pj_str_t *str, 463 pj_sockaddr *addr) 464 { 465 const char *end = str->ptr + str->slen; 466 const char *last_colon_pos = NULL; 467 468 PJ_ASSERT_RETURN(addr, PJ_EINVAL); 469 PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || 470 af==PJ_AF_INET || 471 af==PJ_AF_INET6, PJ_EINVAL); 472 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); 473 474 /* Deduce address family if it's not given */ 475 if (af == PJ_AF_UNSPEC) { 476 unsigned colon_cnt = 0; 477 const char *p; 478 479 /* Can't accept NULL or empty input if address family is unknown */ 480 PJ_ASSERT_RETURN(str && str->slen, PJ_EINVAL); 481 482 for (p=str->ptr; p!=end; ++p) { 483 if (*p == ':') { 484 ++colon_cnt; 485 last_colon_pos = p; 486 } 487 } 488 489 if (colon_cnt > 1) 490 af = PJ_AF_INET6; 491 else 492 af = PJ_AF_INET; 493 } else { 494 /* Input may be NULL or empty as long as address family is given */ 495 if (str == NULL || str->slen == 0) 496 return pj_sockaddr_init(af, addr, NULL, 0); 497 } 498 499 if (af == PJ_AF_INET) { 500 /* Parse as IPv4. Supported formats: 501 * - "10.0.0.1:80" 502 * - "10.0.0.1" 503 * - "10.0.0.1:" 504 * - ":80" 505 * - ":" 506 */ 507 pj_str_t ip_part; 508 unsigned long port; 509 510 if (last_colon_pos == NULL) 511 last_colon_pos = pj_strchr(str, ':'); 512 513 ip_part.ptr = (char*)str->ptr; 514 515 if (last_colon_pos) { 516 pj_str_t port_part; 517 int i; 518 519 ip_part.slen = last_colon_pos - str->ptr; 520 521 port_part.ptr = (char*)last_colon_pos + 1; 522 port_part.slen = end - port_part.ptr; 523 524 /* Make sure port number is valid */ 525 for (i=0; i<port_part.slen; ++i) { 526 if (!pj_isdigit(port_part.ptr[i])) 527 return PJ_EINVAL; 528 } 529 port = pj_strtoul(&port_part); 530 if (port > 65535) 531 return PJ_EINVAL; 532 } else { 533 ip_part.slen = str->slen; 534 port = 0; 535 } 536 537 return pj_sockaddr_in_init(&addr->ipv4, &ip_part, (pj_uint16_t)port); 538 } 539 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 540 else if (af == PJ_AF_INET6) { 541 /* Parse as IPv4. Supported formats: 542 * - "fe::01:80" ==> note: port number is zero in this case, not 80! 543 * - "[fe::01]:80" 544 * - "fe::01" 545 * - "fe::01:" 546 * - "[fe::01]" 547 * - "[fe::01]:" 548 * - "[::]:80" 549 * - ":::80" 550 * - "[::]" 551 * - "[::]:" 552 * - ":::" 553 * - "::" 554 */ 555 pj_str_t ip_part, port_part; 556 557 if (*str->ptr == '[') { 558 char *end_bracket = pj_strchr(str, ']'); 559 int i; 560 unsigned long port; 561 562 if (end_bracket == NULL) 563 return PJ_EINVAL; 564 565 ip_part.ptr = (char*)str->ptr + 1; 566 ip_part.slen = end_bracket - ip_part.ptr; 567 568 if (last_colon_pos == NULL) { 569 const char *p; 570 for (p=str->ptr; p!=end; ++p) { 571 if (*p == ':') 572 last_colon_pos = p; 573 } 574 } 575 576 if (last_colon_pos == NULL) 577 return PJ_EINVAL; 578 579 if (last_colon_pos < end_bracket) { 580 port_part.ptr = NULL; 581 port_part.slen = 0; 582 } else { 583 port_part.ptr = (char*)last_colon_pos + 1; 584 port_part.slen = end - port_part.ptr; 585 } 586 587 /* Make sure port number is valid */ 588 for (i=0; i<port_part.slen; ++i) { 589 if (!pj_isdigit(port_part.ptr[i])) 590 return PJ_EINVAL; 591 } 592 port = pj_strtoul(&port_part); 593 if (port > 65535) 594 return PJ_EINVAL; 595 596 return pj_sockaddr_init(PJ_AF_INET6, addr, &ip_part, 597 (pj_uint16_t)port); 598 } else { 599 int i; 600 unsigned long port; 601 602 /* First lets try to parse everything as IPv6 address */ 603 if (pj_sockaddr_init(PJ_AF_INET6, addr, str, 0)==PJ_SUCCESS) 604 return PJ_SUCCESS; 605 606 /* Parse as IPv6:port */ 607 if (last_colon_pos == NULL) { 608 const char *p; 609 for (p=str->ptr; p!=end; ++p) { 610 if (*p == ':') 611 last_colon_pos = p; 612 } 613 } 614 615 if (last_colon_pos == NULL) 616 return PJ_EINVAL; 617 618 ip_part.ptr = (char*)str->ptr; 619 ip_part.slen = last_colon_pos - str->ptr; 620 621 port_part.ptr = (char*)last_colon_pos + 1; 622 port_part.slen = end - port_part.ptr; 623 624 /* Make sure port number is valid */ 625 for (i=0; i<port_part.slen; ++i) { 626 if (!pj_isdigit(port_part.ptr[i])) 627 return PJ_EINVAL; 628 } 629 port = pj_strtoul(&port_part); 630 if (port > 65535) 631 return PJ_EINVAL; 632 633 return pj_sockaddr_init(PJ_AF_INET6, addr, &ip_part, 634 (pj_uint16_t)port); 635 } 636 } 637 #endif 638 else { 639 return PJ_EIPV6NOTSUP; 640 } 641 } 642 457 643 static pj_bool_t is_usable_ip(const pj_sockaddr *addr) 458 644 { -
pjproject/trunk/pjlib/src/pjlib-test/sock.c
r2394 r2743 168 168 /* pj_gethostaddr() */ 169 169 170 171 return 0; 172 } 173 174 static int parse_test(void) 175 { 176 #define IPv4 1 177 #define IPv6 2 178 179 struct test_t { 180 const char *input; 181 int result_af; 182 const char *result_ip; 183 pj_uint16_t result_port; 184 }; 185 struct test_t valid_tests[] = 186 { 187 /* IPv4 */ 188 { "10.0.0.1:80", IPv4, "10.0.0.1", 80}, 189 { "10.0.0.1", IPv4, "10.0.0.1", 0}, 190 { "10.0.0.1:", IPv4, "10.0.0.1", 0}, 191 { "10.0.0.1:0", IPv4, "10.0.0.1", 0}, 192 { ":80", IPv4, "0.0.0.0", 80}, 193 { ":", IPv4, "0.0.0.0", 0}, 194 { "localhost", IPv4, "127.0.0.1", 0}, 195 { "localhost:", IPv4, "127.0.0.1", 0}, 196 { "localhost:80", IPv4, "127.0.0.1", 80}, 197 198 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 199 { "fe::01:80", IPv6, "fe::01:80", 0}, 200 { "[fe::01]:80", IPv6, "fe::01", 80}, 201 { "fe::01", IPv6, "fe::01", 0}, 202 { "[fe::01]", IPv6, "fe::01", 0}, 203 { "fe::01:", IPv6, "fe::01", 0}, 204 { "[fe::01]:", IPv6, "fe::01", 0}, 205 { "::", IPv6, "::0", 0}, 206 { "[::]", IPv6, "::", 0}, 207 { ":::", IPv6, "::", 0}, 208 { "[::]:", IPv6, "::", 0}, 209 { ":::80", IPv6, "::", 80}, 210 { "[::]:80", IPv6, "::", 80}, 211 #endif 212 }; 213 struct test_t invalid_tests[] = 214 { 215 /* IPv4 */ 216 { "10.0.0.1:abcd", IPv4}, /* port not numeric */ 217 { "10.0.0.1:-1", IPv4}, /* port contains illegal character */ 218 { "10.0.0.1:123456", IPv4}, /* port too big */ 219 { "1.2.3.4.5:80", IPv4}, /* invalid IP */ 220 221 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 222 { "[fe::01]:abcd", IPv6}, /* port not numeric */ 223 { "[fe::01]:-1", IPv6}, /* port contains illegal character */ 224 { "[fe::01]:123456", IPv6}, /* port too big */ 225 { "fe::01:02::03:04:80", IPv6}, /* invalid IP */ 226 { "[fe::01:02::03:04]:80", IPv6}, /* invalid IP */ 227 { "[fe:01", IPv6}, /* Unterminated bracket */ 228 #endif 229 }; 230 231 unsigned i; 232 233 PJ_LOG(3,("test", "...IP address parsing")); 234 235 for (i=0; i<PJ_ARRAY_SIZE(valid_tests); ++i) { 236 pj_status_t status; 237 pj_str_t input; 238 pj_sockaddr addr, result; 239 240 switch (valid_tests[i].result_af) { 241 case IPv4: 242 valid_tests[i].result_af = PJ_AF_INET; 243 break; 244 case IPv6: 245 valid_tests[i].result_af = PJ_AF_INET6; 246 break; 247 default: 248 pj_assert(!"Invalid AF!"); 249 continue; 250 } 251 252 /* Try parsing with PJ_AF_UNSPEC */ 253 status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, 254 pj_cstr(&input, valid_tests[i].input), 255 &addr); 256 if (status != PJ_SUCCESS) { 257 PJ_LOG(1,("test", ".... failed when parsing %s", 258 valid_tests[i].input)); 259 return -10; 260 } 261 262 /* Build the correct result */ 263 status = pj_sockaddr_init(valid_tests[i].result_af, 264 &result, 265 pj_cstr(&input, valid_tests[i].result_ip), 266 valid_tests[i].result_port); 267 if (status != PJ_SUCCESS) { 268 PJ_LOG(1,("test", ".... error building IP address %s", 269 valid_tests[i].input)); 270 return -30; 271 } 272 273 /* Compare the result */ 274 if (pj_sockaddr_cmp(&addr, &result) != 0) { 275 PJ_LOG(1,("test", ".... parsed result mismatched for %s", 276 valid_tests[i].input)); 277 return -40; 278 } 279 280 /* Parse again with the specified af */ 281 status = pj_sockaddr_parse(valid_tests[i].result_af, 0, 282 pj_cstr(&input, valid_tests[i].input), 283 &addr); 284 if (status != PJ_SUCCESS) { 285 PJ_LOG(1,("test", ".... failed when parsing %s", 286 valid_tests[i].input)); 287 return -50; 288 } 289 290 /* Compare the result again */ 291 if (pj_sockaddr_cmp(&addr, &result) != 0) { 292 PJ_LOG(1,("test", ".... parsed result mismatched for %s", 293 valid_tests[i].input)); 294 return -60; 295 } 296 } 297 298 for (i=0; i<PJ_ARRAY_SIZE(invalid_tests); ++i) { 299 pj_status_t status; 300 pj_str_t input; 301 pj_sockaddr addr; 302 303 switch (invalid_tests[i].result_af) { 304 case IPv4: 305 invalid_tests[i].result_af = PJ_AF_INET; 306 break; 307 case IPv6: 308 invalid_tests[i].result_af = PJ_AF_INET6; 309 break; 310 default: 311 pj_assert(!"Invalid AF!"); 312 continue; 313 } 314 315 /* Try parsing with PJ_AF_UNSPEC */ 316 status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, 317 pj_cstr(&input, invalid_tests[i].input), 318 &addr); 319 if (status == PJ_SUCCESS) { 320 PJ_LOG(1,("test", ".... expecting failure when parsing %s", 321 invalid_tests[i].input)); 322 return -100; 323 } 324 } 170 325 171 326 return 0; … … 577 732 return rc; 578 733 734 rc = parse_test(); 735 if (rc != 0) 736 return rc; 737 579 738 rc = gethostbyname_test(); 580 739 if (rc != 0)
Note: See TracChangeset
for help on using the changeset viewer.