From c4851558e0c60e8a8929473631b825e1945f239b Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Wed, 12 Aug 2009 10:56:06 +0000 Subject: Ticket #935: new pj_sockaddr_parse2() API git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2863 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib/include/pj/sock.h | 39 +++++++- pjlib/src/pj/sock_common.c | 217 ++++++++++++++++++++++++++------------------ pjlib/src/pjlib-test/sock.c | 1 + 3 files changed, 170 insertions(+), 87 deletions(-) (limited to 'pjlib') diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h index b8ddb02d..ecc23691 100644 --- a/pjlib/include/pj/sock.h +++ b/pjlib/include/pj/sock.h @@ -979,18 +979,55 @@ PJ_DECL(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, * * @param af Optionally specify the address family to be used. If the * address family is to be deducted from the input, specify - * pj_AF_UNSPEC() here. + * pj_AF_UNSPEC() here. Other supported values are + * #pj_AF_INET() and #pj_AF_INET6() * @param options Additional options to assist the parsing, must be zero * for now. * @param str The input string to be parsed. * @param addr Pointer to store the result. * * @return PJ_SUCCESS if the parsing is successful. + * + * @see pj_sockaddr_parse2() */ PJ_DECL(pj_status_t) pj_sockaddr_parse(int af, unsigned options, const pj_str_t *str, pj_sockaddr *addr); +/** + * This function is similar to #pj_sockaddr_parse(), except that it will not + * convert the hostpart into IP address (thus possibly resolving the hostname + * into a #pj_sockaddr. + * + * Unlike #pj_sockaddr_parse(), this function has a limitation that if port + * number is specified in an IPv6 input string, the IP part of the IPv6 socket + * address MUST be enclosed in square brackets, otherwise the port number will + * be considered as part of the IPv6 IP address. + * + * @param af Optionally specify the address family to be used. If the + * address family is to be deducted from the input, specify + * #pj_AF_UNSPEC() here. Other supported values are + * #pj_AF_INET() and #pj_AF_INET6() + * @param options Additional options to assist the parsing, must be zero + * for now. + * @param str The input string to be parsed. + * @param hostpart Optional pointer to store the host part of the socket + * address, with any brackets removed. + * @param port Optional pointer to store the port number. If port number + * is not found, this will be set to zero upon return. + * @param raf Optional pointer to store the detected address family of + * the input address. + * + * @return PJ_SUCCESS if the parsing is successful. + * + * @see pj_sockaddr_parse() + */ +PJ_DECL(pj_status_t) pj_sockaddr_parse2(int af, unsigned options, + const pj_str_t *str, + pj_str_t *hostpart, + pj_uint16_t *port, + int *raf); + /***************************************************************************** * * HOST NAME AND ADDRESS. diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c index 0317fc66..899bbd0f 100644 --- a/pjlib/src/pj/sock_common.c +++ b/pjlib/src/pj/sock_common.c @@ -458,43 +458,51 @@ PJ_DEF(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr, /* * Parse address */ -PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, +PJ_DEF(pj_status_t) pj_sockaddr_parse2(int af, unsigned options, const pj_str_t *str, - pj_sockaddr *addr) + pj_str_t *p_hostpart, + pj_uint16_t *p_port, + int *raf) { const char *end = str->ptr + str->slen; const char *last_colon_pos = NULL; + unsigned colon_cnt = 0; + const char *p; + + PJ_ASSERT_RETURN((af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && + options==0 && + str!=NULL, PJ_EINVAL); + + /* Special handling for empty input */ + if (str->slen==0 || str->ptr==NULL) { + if (p_hostpart) + p_hostpart->slen = 0; + if (p_port) + *p_port = 0; + if (*raf) + *raf = PJ_AF_INET; + return PJ_SUCCESS; + } - PJ_ASSERT_RETURN(addr, PJ_EINVAL); - PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || - af==PJ_AF_INET || - af==PJ_AF_INET6, PJ_EINVAL); - PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); + /* Count the colon and get the last colon */ + for (p=str->ptr; p!=end; ++p) { + if (*p == ':') { + ++colon_cnt; + last_colon_pos = p; + } + } /* Deduce address family if it's not given */ if (af == PJ_AF_UNSPEC) { - unsigned colon_cnt = 0; - const char *p; - - /* Can't accept NULL or empty input if address family is unknown */ - PJ_ASSERT_RETURN(str && str->slen, PJ_EINVAL); - - for (p=str->ptr; p!=end; ++p) { - if (*p == ':') { - ++colon_cnt; - last_colon_pos = p; - } - } - if (colon_cnt > 1) af = PJ_AF_INET6; else af = PJ_AF_INET; - } else { - /* Input may be NULL or empty as long as address family is given */ - if (str == NULL || str->slen == 0) - return pj_sockaddr_init(af, addr, NULL, 0); - } + } else if (af == PJ_AF_INET && colon_cnt > 1) + return PJ_EINVAL; + + if (raf) + *raf = af; if (af == PJ_AF_INET) { /* Parse as IPv4. Supported formats: @@ -504,19 +512,16 @@ PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, * - ":80" * - ":" */ - pj_str_t ip_part; + pj_str_t hostpart; unsigned long port; - if (last_colon_pos == NULL) - last_colon_pos = pj_strchr(str, ':'); - - ip_part.ptr = (char*)str->ptr; + hostpart.ptr = (char*)str->ptr; if (last_colon_pos) { pj_str_t port_part; int i; - ip_part.slen = last_colon_pos - str->ptr; + hostpart.slen = last_colon_pos - str->ptr; port_part.ptr = (char*)last_colon_pos + 1; port_part.slen = end - port_part.ptr; @@ -530,15 +535,20 @@ PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, if (port > 65535) return PJ_EINVAL; } else { - ip_part.slen = str->slen; + hostpart.slen = str->slen; port = 0; } - return pj_sockaddr_in_init(&addr->ipv4, &ip_part, (pj_uint16_t)port); - } -#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 - else if (af == PJ_AF_INET6) { - /* Parse as IPv4. Supported formats: + if (p_hostpart) + *p_hostpart = hostpart; + if (p_port) + *p_port = (pj_uint16_t)port; + + return PJ_SUCCESS; + + } else if (af == PJ_AF_INET6) { + + /* Parse as IPv6. Supported formats: * - "fe::01:80" ==> note: port number is zero in this case, not 80! * - "[fe::01]:80" * - "fe::01" @@ -552,30 +562,23 @@ PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, * - ":::" * - "::" */ - pj_str_t ip_part, port_part; + pj_str_t hostpart, port_part; if (*str->ptr == '[') { - char *end_bracket = pj_strchr(str, ']'); + char *end_bracket; int i; unsigned long port; - if (end_bracket == NULL) + if (last_colon_pos == NULL) return PJ_EINVAL; - ip_part.ptr = (char*)str->ptr + 1; - ip_part.slen = end_bracket - ip_part.ptr; - - if (last_colon_pos == NULL) { - const char *p; - for (p=str->ptr; p!=end; ++p) { - if (*p == ':') - last_colon_pos = p; - } - } - - if (last_colon_pos == NULL) + end_bracket = pj_strchr(str, ']'); + if (end_bracket == NULL) return PJ_EINVAL; + hostpart.ptr = (char*)str->ptr + 1; + hostpart.slen = end_bracket - hostpart.ptr; + if (last_colon_pos < end_bracket) { port_part.ptr = NULL; port_part.slen = 0; @@ -593,51 +596,93 @@ PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, if (port > 65535) return PJ_EINVAL; - return pj_sockaddr_init(PJ_AF_INET6, addr, &ip_part, - (pj_uint16_t)port); + if (p_hostpart) + *p_hostpart = hostpart; + if (p_port) + *p_port = (pj_uint16_t)port; + + return PJ_SUCCESS; + } else { - int i; - unsigned long port; + /* Treat everything as part of the IPv6 IP address */ + if (p_hostpart) + *p_hostpart = *str; + if (p_port) + *p_port = 0; - /* First lets try to parse everything as IPv6 address */ - if (pj_sockaddr_init(PJ_AF_INET6, addr, str, 0)==PJ_SUCCESS) - return PJ_SUCCESS; + return PJ_SUCCESS; + } - /* Parse as IPv6:port */ - if (last_colon_pos == NULL) { - const char *p; - for (p=str->ptr; p!=end; ++p) { - if (*p == ':') - last_colon_pos = p; - } - } + } else { + return PJ_EAFNOTSUP; + } - if (last_colon_pos == NULL) - return PJ_EINVAL; +} - ip_part.ptr = (char*)str->ptr; - ip_part.slen = last_colon_pos - str->ptr; +/* + * Parse address + */ +PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, + const pj_str_t *str, + pj_sockaddr *addr) +{ + pj_str_t hostpart; + pj_uint16_t port; + pj_status_t status; - port_part.ptr = (char*)last_colon_pos + 1; - port_part.slen = end - port_part.ptr; + PJ_ASSERT_RETURN(addr, PJ_EINVAL); + PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || + af==PJ_AF_INET || + af==PJ_AF_INET6, PJ_EINVAL); + PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); - /* Make sure port number is valid */ - for (i=0; i 65535) - return PJ_EINVAL; + status = pj_sockaddr_parse2(af, options, str, &hostpart, &port, &af); + if (status != PJ_SUCCESS) + return status; + + status = pj_sockaddr_init(af, addr, &hostpart, port); +#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 + if (status != PJ_SUCCESS && af == PJ_AF_INET6) { + /* Parsing does not yield valid address. Try to treat the last + * portion after the colon as port number. + */ + const char *last_colon_pos=NULL, *p; + const char *end = str->ptr + str->slen; + unsigned long long_port; + pj_str_t port_part; + int i; + + /* Parse as IPv6:port */ + for (p=str->ptr; p!=end; ++p) { + if (*p == ':') + last_colon_pos = p; + } + + if (last_colon_pos == NULL) + return status; + + hostpart.ptr = (char*)str->ptr; + hostpart.slen = last_colon_pos - str->ptr; + + port_part.ptr = (char*)last_colon_pos + 1; + port_part.slen = end - port_part.ptr; - return pj_sockaddr_init(PJ_AF_INET6, addr, &ip_part, - (pj_uint16_t)port); + /* Make sure port number is valid */ + for (i=0; i 65535) + return status; + + port = (pj_uint16_t)long_port; + + status = pj_sockaddr_init(PJ_AF_INET6, addr, &hostpart, port); } #endif - else { - return PJ_EIPV6NOTSUP; - } + + return status; } static pj_bool_t is_usable_ip(const pj_sockaddr *addr) diff --git a/pjlib/src/pjlib-test/sock.c b/pjlib/src/pjlib-test/sock.c index b25d574f..ec911177 100644 --- a/pjlib/src/pjlib-test/sock.c +++ b/pjlib/src/pjlib-test/sock.c @@ -219,6 +219,7 @@ static int parse_test(void) { "10.0.0.1:-1", IPv4}, /* port contains illegal character */ { "10.0.0.1:123456", IPv4}, /* port too big */ { "1.2.3.4.5:80", IPv4}, /* invalid IP */ + { "10:0:80", IPv4}, /* hostname has colon */ #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 { "[fe::01]:abcd", IPv6}, /* port not numeric */ -- cgit v1.2.3