Ignore:
Timestamp:
Jun 4, 2009 3:11:25 PM (11 years ago)
Author:
bennylp
Message:

Ticket #878: New PJLIB API to parse socket address string

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib/src/pj/sock_common.c

    r2656 r2743  
    2020#include <pj/sock.h> 
    2121#include <pj/assert.h> 
     22#include <pj/ctype.h> 
    2223#include <pj/errno.h> 
    2324#include <pj/ip_helper.h> 
     
    455456} 
    456457 
     458/* 
     459 * Parse address 
     460 */ 
     461PJ_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 
    457643static pj_bool_t is_usable_ip(const pj_sockaddr *addr) 
    458644{ 
Note: See TracChangeset for help on using the changeset viewer.