From 223903c913565d00e54b55f9685a174f1f31ed49 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Sat, 1 Dec 2007 08:59:25 +0000 Subject: Ticket #421: initial IPv6 support: UDP transport git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1602 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsip/sip_transport.h | 22 +++- pjsip/include/pjsip/sip_transport_udp.h | 35 ++++- pjsip/include/pjsip/sip_types.h | 11 +- pjsip/src/pjsip/sip_resolve.c | 70 ++++++---- pjsip/src/pjsip/sip_transport.c | 227 +++++++++++++++++++++----------- pjsip/src/pjsip/sip_transport_tcp.c | 6 +- pjsip/src/pjsip/sip_transport_udp.c | 211 ++++++++++++++++++++++------- pjsip/src/pjsua-lib/pjsua_core.c | 114 +++++++++------- pjsip/src/pjsua-lib/pjsua_media.c | 6 +- 9 files changed, 495 insertions(+), 207 deletions(-) diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h index 9f1ff044..6097abab 100644 --- a/pjsip/include/pjsip/sip_transport.h +++ b/pjsip/include/pjsip/sip_transport.h @@ -132,6 +132,15 @@ pjsip_transport_get_type_from_name(const pj_str_t *name); PJ_DECL(pjsip_transport_type_e) pjsip_transport_get_type_from_flag(unsigned flag); +/** + * Get the socket address family of a given transport type. + * + * @param type Transport type. + * + * @return Transport type. + */ +PJ_DECL(int) pjsip_transport_type_get_af(pjsip_transport_type_e type); + /** * Get transport flag from type. * @@ -162,6 +171,15 @@ pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type); */ PJ_DECL(const char*) pjsip_transport_get_type_name(pjsip_transport_type_e t); +/** + * Get longer description for the specified transport type. + * + * @param t Transport type. + * + * @return Transport description. + */ +PJ_DECL(const char*) pjsip_transport_get_type_desc(pjsip_transport_type_e t); + /***************************************************************************** @@ -307,7 +325,7 @@ struct pjsip_rx_data int src_addr_len; /** The IP source address string (NULL terminated). */ - char src_name[16]; + char src_name[PJ_INET6_ADDRSTRLEN]; /** The IP source port number. */ int src_port; @@ -506,7 +524,7 @@ struct pjsip_tx_data pjsip_transport *transport; /**< Transport being used. */ pj_sockaddr dst_addr; /**< Destination address. */ int dst_addr_len; /**< Length of address. */ - char dst_name[16]; /**< Destination address. */ + char dst_name[PJ_INET6_ADDRSTRLEN]; /**< Destination address. */ int dst_port; /**< Destination port. */ } tp_info; diff --git a/pjsip/include/pjsip/sip_transport_udp.h b/pjsip/include/pjsip/sip_transport_udp.h index 5fdf9dcf..74bb5ff3 100644 --- a/pjsip/include/pjsip/sip_transport_udp.h +++ b/pjsip/include/pjsip/sip_transport_udp.h @@ -82,7 +82,17 @@ PJ_DECL(pj_status_t) pjsip_udp_transport_start(pjsip_endpoint *endpt, pjsip_transport **p_transport); /** - * Attach UDP socket as a new transport and start the transport. + * Start IPv6 UDP transport. + */ +PJ_DECL(pj_status_t) pjsip_udp_transport_start6(pjsip_endpoint *endpt, + const pj_sockaddr_in6 *local, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_transport **p_transport); + + +/** + * Attach IPv4 UDP socket as a new transport and start the transport. * * @param endpt The SIP endpoint. * @param sock UDP socket to use. @@ -102,6 +112,29 @@ PJ_DECL(pj_status_t) pjsip_udp_transport_attach(pjsip_endpoint *endpt, pjsip_transport **p_transport); +/** + * Attach IPv4 or IPv6 UDP socket as a new transport and start the transport. + * + * @param endpt The SIP endpoint. + * @param type Transport type, which is PJSIP_TRANSPORT_UDP for IPv4 + * or PJSIP_TRANSPORT_UDP6 for IPv6 socket. + * @param sock UDP socket to use. + * @param a_name Published address (only the host and port portion is + * used). + * @param async_cnt Number of simultaneous async operations. + * @param p_transport Pointer to receive the transport. + * + * @return PJ_SUCCESS when the transport has been successfully + * started and registered to transport manager, or + * the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_udp_transport_attach2(pjsip_endpoint *endpt, + pjsip_transport_type_e type, + pj_sock_t sock, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_transport **p_transport); + /** * Retrieve the internal socket handle used by the UDP transport. Note * that this socket normally is registered to ioqueue, so if application diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h index d248b358..404dc8d4 100644 --- a/pjsip/include/pjsip/sip_types.h +++ b/pjsip/include/pjsip/sip_types.h @@ -82,7 +82,16 @@ typedef enum pjsip_transport_type_e PJSIP_TRANSPORT_LOOP_DGRAM, /** Start of user defined transport */ - PJSIP_TRANSPORT_START_OTHER + PJSIP_TRANSPORT_START_OTHER, + + /** Start of IPv6 transports */ + PJSIP_TRANSPORT_IPV6 = 128, + + /** UDP over IPv6 */ + PJSIP_TRANSPORT_UDP6 = PJSIP_TRANSPORT_UDP + PJSIP_TRANSPORT_IPV6, + + /** TCP over IPv6 */ + PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6 } pjsip_transport_type_e; diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index ef0f4414..41b892b3 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -137,21 +138,24 @@ PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver) /* * Internal: - * determine if an address is a valid IP address. + * determine if an address is a valid IP address, and if it is, + * return the IP version (4 or 6). */ -static int is_str_ip(const pj_str_t *host) +static int get_ip_addr_ver(const pj_str_t *host) { - const char *p = host->ptr; - const char *end = ((const char*)host->ptr) + host->slen; + pj_in_addr dummy; + pj_in6_addr dummy6; - while (p != end) { - if (pj_isdigit(*p) || *p=='.') { - ++p; - } else { - return 0; - } - } - return 1; + /* First check with inet_aton() */ + if (pj_inet_aton(host, &dummy) > 0) + return 4; + + /* Then check if this is an IPv6 address */ + if (pj_inet_pton(pj_AF_INET6(), host, &dummy6) == PJ_SUCCESS) + return 6; + + /* Not an IP address */ + return 0; } @@ -166,18 +170,18 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, { pjsip_server_addresses svr_addr; pj_status_t status = PJ_SUCCESS; - int is_ip_addr; + int ip_addr_ver; struct query *query; pjsip_transport_type_e type = target->type; - /* Is it IP address or hostname?. */ - is_ip_addr = is_str_ip(&target->addr.host); + /* Is it IP address or hostname? And if it's an IP, which version? */ + ip_addr_ver = get_ip_addr_ver(&target->addr.host); /* Set the transport type if not explicitly specified. * RFC 3263 section 4.1 specify rules to set up this. */ if (type == PJSIP_TRANSPORT_UNSPECIFIED) { - if (is_ip_addr || (target->addr.port != 0)) { + if (ip_addr_ver || (target->addr.port != 0)) { #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { @@ -209,18 +213,25 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, type = PJSIP_TRANSPORT_UDP; } } + + /* Add IPv6 flag for IPv6 address */ + if (ip_addr_ver == 6) + type = (pjsip_transport_type_e)((int)type + PJSIP_TRANSPORT_IPV6); } /* If target is an IP address, or if resolver is not configured, * we can just finish the resolution now using pj_gethostbyname() */ - if (is_ip_addr || resolver->res == NULL) { + if (ip_addr_ver || resolver->res == NULL) { pj_in_addr ip_addr; + int af; + pj_addrinfo ai; + unsigned count; pj_uint16_t srv_port; - if (!is_ip_addr) { + if (!ip_addr_ver) { PJ_LOG(5,(THIS_FILE, "DNS resolver not available, target '%.*s:%d' type=%s " "will be resolved with gethostbyname()", @@ -238,14 +249,27 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, srv_port = (pj_uint16_t)target->addr.port; } - /* This will eventually call pj_gethostbyname() if the host - * is not an IP address. - */ - status = pj_sockaddr_in_init((pj_sockaddr_in*)&svr_addr.entry[0].addr, - &target->addr.host, srv_port); + if (type & PJSIP_TRANSPORT_IPV6) { + af = pj_AF_INET6(); + } else { + af = pj_AF_INET(); + } + + /* Resolve */ + count = 1; + status = pj_getaddrinfo(af, &target->addr.host, &count, &ai); if (status != PJ_SUCCESS) goto on_error; + svr_addr.entry[0].addr.addr.sa_family = (pj_uint16_t)af; + pj_memcpy(&svr_addr.entry[0].addr, &ai.ai_addr, sizeof(pj_sockaddr)); + + if (af == pj_AF_INET6()) { + svr_addr.entry[0].addr.ipv6.sin6_port = pj_htons(srv_port); + } else { + svr_addr.entry[0].addr.ipv4.sin_port = pj_htons(srv_port); + } + /* Call the callback. */ ip_addr = ((pj_sockaddr_in*)&svr_addr.entry[0].addr)->sin_addr; PJ_LOG(5,(THIS_FILE, diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 11191c54..2c85f44d 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -93,22 +93,103 @@ struct pjsip_tpmgr */ struct transport_names_t { - pjsip_transport_type_e type; - pj_uint16_t port; - pj_str_t name; - unsigned flag; - char name_buf[16]; + pjsip_transport_type_e type; /* Transport type */ + pj_uint16_t port; /* Default port number */ + pj_str_t name; /* Id tag */ + const char *description; /* Longer description */ + unsigned flag; /* Flags */ + char name_buf[16]; /* For user's transport */ } transport_names[16] = { - { PJSIP_TRANSPORT_UNSPECIFIED, 0, {"Unspecified", 11}, 0}, - { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM}, - { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE}, - { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE}, - { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE}, - { PJSIP_TRANSPORT_LOOP, 15060, {"LOOP", 4}, PJSIP_TRANSPORT_RELIABLE}, - { PJSIP_TRANSPORT_LOOP_DGRAM, 15060, {"LOOP-DGRAM", 10}, PJSIP_TRANSPORT_DATAGRAM}, + { + PJSIP_TRANSPORT_UNSPECIFIED, + 0, + {"Unspecified", 11}, + "Unspecified", + 0 + }, + { + PJSIP_TRANSPORT_UDP, + 5060, + {"UDP", 3}, + "UDP transport", + PJSIP_TRANSPORT_DATAGRAM + }, + { + PJSIP_TRANSPORT_TCP, + 5060, + {"TCP", 3}, + "TCP transport", + PJSIP_TRANSPORT_RELIABLE + }, + { + PJSIP_TRANSPORT_TLS, + 5061, + {"TLS", 3}, + "TLS transport", + PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE + }, + { + PJSIP_TRANSPORT_SCTP, + 5060, + {"SCTP", 4}, + "SCTP transport", + PJSIP_TRANSPORT_RELIABLE + }, + { + PJSIP_TRANSPORT_LOOP, + 15060, + {"LOOP", 4}, + "Loopback transport", + PJSIP_TRANSPORT_RELIABLE + }, + { + PJSIP_TRANSPORT_LOOP_DGRAM, + 15060, + {"LOOP-DGRAM", 10}, + "Loopback datagram transport", + PJSIP_TRANSPORT_DATAGRAM + }, + { + PJSIP_TRANSPORT_UDP6, + 5060, + {"UDP", 3}, + "UDP IPv6 transport", + PJSIP_TRANSPORT_DATAGRAM + }, + { + PJSIP_TRANSPORT_TCP6, + 5060, + {"TCP", 3}, + "TCP IPv6 transport", + PJSIP_TRANSPORT_RELIABLE + }, }; +struct transport_names_t *get_tpname(pjsip_transport_type_e type) +{ + unsigned i; + for (i=0; iaddr.sa_family, + pj_sockaddr_get_addr(addr), + str, sizeof(str)); + return str; +} + /* * Register new transport type to PJSIP. @@ -153,12 +234,6 @@ PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type_from_name(const pj_str_t { unsigned i; - /* Sanity check. - * Check that transport_names[] are indexed on transport type. - */ - PJ_ASSERT_RETURN(transport_names[PJSIP_TRANSPORT_UDP].type == - PJSIP_TRANSPORT_UDP, PJSIP_TRANSPORT_UNSPECIFIED); - if (name->slen == 0) return PJSIP_TRANSPORT_UNSPECIFIED; @@ -181,12 +256,6 @@ PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type_from_flag(unsigned flag) { unsigned i; - /* Sanity check. - * Check that transport_names[] are indexed on transport type. - */ - PJ_ASSERT_RETURN(transport_names[PJSIP_TRANSPORT_UDP].type == - PJSIP_TRANSPORT_UDP, PJSIP_TRANSPORT_UNSPECIFIED); - /* Get the transport type for the specified flags. */ for (i=0; iflag; } /* @@ -218,17 +289,8 @@ PJ_DEF(unsigned) pjsip_transport_get_flag_from_type(pjsip_transport_type_e type) */ PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type) { - /* Sanity check. - * Check that transport_names[] are indexed on transport type. - */ - PJ_ASSERT_RETURN(transport_names[PJSIP_TRANSPORT_UDP].type == - PJSIP_TRANSPORT_UDP, 0); - - /* Check that argument is valid. */ - PJ_ASSERT_RETURN((unsigned)type < PJ_ARRAY_SIZE(transport_names), 5060); - /* Return the port. */ - return transport_names[type].port; + return get_tpname(type)->port; } /* @@ -236,17 +298,17 @@ PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e typ */ PJ_DEF(const char*) pjsip_transport_get_type_name(pjsip_transport_type_e type) { - /* Sanity check. - * Check that transport_names[] are indexed on transport type. - */ - PJ_ASSERT_RETURN(transport_names[PJSIP_TRANSPORT_UDP].type == - PJSIP_TRANSPORT_UDP, "Unknown"); - - /* Check that argument is valid. */ - PJ_ASSERT_RETURN((unsigned)typename.ptr; +} - /* Return the port. */ - return transport_names[type].name.ptr; +/* + * Get transport description. + */ +PJ_DEF(const char*) pjsip_transport_get_type_desc(pjsip_transport_type_e type) +{ + /* Return the description. */ + return get_tpname(type)->description; } @@ -556,15 +618,12 @@ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, tdata->tp_info.transport = tr; pj_memcpy(&tdata->tp_info.dst_addr, addr, addr_len); tdata->tp_info.dst_addr_len = addr_len; - if (((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET()) { - const char *str_addr; - str_addr = pj_inet_ntoa(((pj_sockaddr_in*)addr)->sin_addr); - pj_ansi_strcpy(tdata->tp_info.dst_name, str_addr); - tdata->tp_info.dst_port = pj_ntohs(((pj_sockaddr_in*)addr)->sin_port); - } else { - pj_ansi_strcpy(tdata->tp_info.dst_name, ""); - tdata->tp_info.dst_port = 0; - } + + pj_inet_ntop(((pj_sockaddr*)addr)->addr.sa_family, + pj_sockaddr_get_addr(addr), + tdata->tp_info.dst_name, + sizeof(tdata->tp_info.dst_name)); + tdata->tp_info.dst_port = pj_sockaddr_get_port(addr); /* Distribute to modules. * When the message reach mod_msg_print, the contents of the message will @@ -803,8 +862,8 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, TRACE_((THIS_FILE,"Transport %s registered: type=%s, remote=%s:%d", tp->obj_name, pjsip_transport_get_type_name(tp->key.type), - pj_inet_ntoa(((pj_sockaddr_in*)&tp->key.rem_addr)->sin_addr), - pj_ntohs(((pj_sockaddr_in*)&tp->key.rem_addr)->sin_port))); + addr_string(&tp->key.rem_addr), + pj_sockaddr_get_port(&tp->key.rem_addr))); return PJ_SUCCESS; } @@ -1054,12 +1113,21 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, } else if ((flag & PJSIP_TRANSPORT_DATAGRAM) != 0) { - pj_sockaddr_in remote; + pj_sockaddr remote; + int addr_len; pjsip_transport *tp; - pj_sockaddr_in_init(&remote, NULL, 0); + pj_bzero(&remote, sizeof(remote)); + if (type & PJSIP_TRANSPORT_IPV6) { + addr_len = sizeof(pj_sockaddr_in6); + remote.addr.sa_family = pj_AF_INET6(); + } else { + addr_len = sizeof(pj_sockaddr_in); + remote.addr.sa_family = pj_AF_INET(); + } + status = pjsip_tpmgr_acquire_transport(tpmgr, type, &remote, - sizeof(remote), NULL, &tp); + addr_len, NULL, &tp); if (status == PJ_SUCCESS) { pj_strdup(pool, ip_addr, &tp->local_name.host); @@ -1381,8 +1449,8 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr, TRACE_((THIS_FILE,"Acquiring transport type=%s, remote=%s:%d", pjsip_transport_get_type_name(type), - pj_inet_ntoa(((pj_sockaddr_in*)remote)->sin_addr), - pj_ntohs(((pj_sockaddr_in*)remote)->sin_port))); + addr_string(remote), + pj_sockaddr_get_port(remote))); pj_lock_acquire(mgr->lock); @@ -1462,24 +1530,23 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr, if (type == PJSIP_TRANSPORT_LOOP || type == PJSIP_TRANSPORT_LOOP_DGRAM) { - pj_sockaddr_in *addr = (pj_sockaddr_in*)&key.rem_addr; + pj_sockaddr *addr = &key.rem_addr; - pj_bzero(addr, sizeof(pj_sockaddr_in)); - key_len = sizeof(key.type) + sizeof(pj_sockaddr_in); + pj_bzero(addr, addr_len); + key_len = sizeof(key.type) + addr_len; transport = (pjsip_transport*) pj_hash_get(mgr->table, &key, key_len, NULL); } - /* For datagram INET transports, try lookup with zero address. + /* For datagram transports, try lookup with zero address. */ - else if ((flag & PJSIP_TRANSPORT_DATAGRAM) && - (remote_addr->addr.sa_family == pj_AF_INET())) + else if (flag & PJSIP_TRANSPORT_DATAGRAM) { - pj_sockaddr_in *addr = (pj_sockaddr_in*)&key.rem_addr; + pj_sockaddr *addr = &key.rem_addr; - pj_bzero(addr, sizeof(pj_sockaddr_in)); - addr->sin_family = pj_AF_INET(); + pj_bzero(addr, addr_len); + addr->addr.sa_family = remote_addr->addr.sa_family; - key_len = sizeof(key.type) + sizeof(pj_sockaddr_in); + key_len = sizeof(key.type) + addr_len; transport = (pjsip_transport*) pj_hash_get(mgr->table, &key, key_len, NULL); } diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c index 4a8024d2..dff02a39 100644 --- a/pjsip/src/pjsip/sip_transport_tcp.c +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -289,13 +289,13 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, * interface address as the transport's address. */ if (listener_addr->sin_addr.s_addr == 0) { - pj_in_addr hostip; + pj_sockaddr hostip; - status = pj_gethostip(&hostip); + status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) goto on_error; - listener_addr->sin_addr = hostip; + listener_addr->sin_addr.s_addr = hostip.ipv4.sin_addr.s_addr; } /* Save the address name */ diff --git a/pjsip/src/pjsip/sip_transport_udp.c b/pjsip/src/pjsip/sip_transport_udp.c index 307dc038..29b3c513 100644 --- a/pjsip/src/pjsip/sip_transport_udp.c +++ b/pjsip/src/pjsip/sip_transport_udp.c @@ -142,16 +142,23 @@ static void udp_on_read_complete( pj_ioqueue_key_t *key, */ if (bytes_read > MIN_SIZE) { pj_size_t size_eaten; - const pj_sockaddr_in *src_addr = - (pj_sockaddr_in*)&rdata->pkt_info.src_addr; + const pj_sockaddr *src_addr = &rdata->pkt_info.src_addr; /* Init pkt_info part. */ rdata->pkt_info.len = bytes_read; rdata->pkt_info.zero = 0; pj_gettimeofday(&rdata->pkt_info.timestamp); - pj_ansi_strcpy(rdata->pkt_info.src_name, - pj_inet_ntoa(src_addr->sin_addr)); - rdata->pkt_info.src_port = pj_ntohs(src_addr->sin_port); + if (src_addr->addr.sa_family == pj_AF_INET()) { + pj_ansi_strcpy(rdata->pkt_info.src_name, + pj_inet_ntoa(src_addr->ipv4.sin_addr)); + rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv4.sin_port); + } else { + pj_inet_ntop(pj_AF_INET6(), + pj_sockaddr_get_addr(&rdata->pkt_info.src_addr), + rdata->pkt_info.src_name, + sizeof(rdata->pkt_info.src_name)); + rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv6.sin6_port); + } size_eaten = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, @@ -412,23 +419,32 @@ static pj_status_t udp_shutdown(pjsip_transport *transport) /* Create socket */ -static pj_status_t create_socket(const pj_sockaddr_in *local_a, - pj_sock_t *p_sock) +static pj_status_t create_socket(int af, const pj_sockaddr_t *local_a, + int addr_len, pj_sock_t *p_sock) { pj_sock_t sock; pj_sockaddr_in tmp_addr; + pj_sockaddr_in6 tmp_addr6; pj_status_t status; - status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock); + status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) return status; if (local_a == NULL) { - pj_sockaddr_in_init(&tmp_addr, NULL, 0); - local_a = &tmp_addr; + if (af == pj_AF_INET6()) { + pj_bzero(&tmp_addr6, sizeof(tmp_addr6)); + tmp_addr6.sin6_family = (pj_uint16_t)af; + local_a = &tmp_addr6; + addr_len = sizeof(tmp_addr6); + } else { + pj_sockaddr_in_init(&tmp_addr, NULL, 0); + local_a = &tmp_addr; + addr_len = sizeof(tmp_addr); + } } - status = pj_sock_bind(sock, local_a, sizeof(*local_a)); + status = pj_sock_bind(sock, local_a, addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; @@ -442,9 +458,10 @@ static pj_status_t create_socket(const pj_sockaddr_in *local_a, /* Generate transport's published address */ static pj_status_t get_published_name(pj_sock_t sock, char hostbuf[], + int hostbufsz, pjsip_host_port *bound_name) { - pj_sockaddr_in tmp_addr; + pj_sockaddr tmp_addr; int addr_len; pj_status_t status; @@ -454,25 +471,36 @@ static pj_status_t get_published_name(pj_sock_t sock, return status; bound_name->host.ptr = hostbuf; - bound_name->port = pj_ntohs(tmp_addr.sin_port); + if (tmp_addr.addr.sa_family == pj_AF_INET()) { + bound_name->port = pj_ntohs(tmp_addr.ipv4.sin_port); - /* If bound address specifies "0.0.0.0", get the IP address - * of local hostname. - */ - if (tmp_addr.sin_addr.s_addr == PJ_INADDR_ANY) { - pj_in_addr hostip; + /* If bound address specifies "0.0.0.0", get the IP address + * of local hostname. + */ + if (tmp_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) { + pj_sockaddr hostip; - status = pj_gethostip(&hostip); - if (status != PJ_SUCCESS) - return status; + status = pj_gethostip(pj_AF_INET(), &hostip); + if (status != PJ_SUCCESS) + return status; + + pj_strcpy2(&bound_name->host, pj_inet_ntoa(hostip.ipv4.sin_addr)); + } else { + /* Otherwise use bound address. */ + pj_strcpy2(&bound_name->host, + pj_inet_ntoa(tmp_addr.ipv4.sin_addr)); + status = PJ_SUCCESS; + } - pj_strcpy2(&bound_name->host, pj_inet_ntoa(hostip)); } else { - /* Otherwise use bound address. */ - pj_strcpy2(&bound_name->host, pj_inet_ntoa(tmp_addr.sin_addr)); + bound_name->port = pj_ntohs(tmp_addr.ipv6.sin6_port); + status = pj_inet_ntop(tmp_addr.addr.sa_family, + pj_sockaddr_get_addr(&tmp_addr), + hostbuf, hostbufsz); } - return PJ_SUCCESS; + + return status; } /* Set the published address of the transport */ @@ -480,6 +508,7 @@ static void udp_set_pub_name(struct udp_transport *tp, const pjsip_host_port *a_name) { enum { INFO_LEN = 80 }; + char local_addr[PJ_INET6_ADDRSTRLEN]; pj_assert(a_name->host.slen != 0); pj_strdup_with_null(tp->base.pool, &tp->base.local_name.host, @@ -490,10 +519,15 @@ static void udp_set_pub_name(struct udp_transport *tp, if (tp->base.info == NULL) { tp->base.info = (char*) pj_pool_alloc(tp->base.pool, INFO_LEN); } + + pj_inet_ntop(tp->base.local_addr.addr.sa_family, + pj_sockaddr_get_addr(&tp->base.local_addr), + local_addr, sizeof(local_addr)); + pj_ansi_snprintf( tp->base.info, INFO_LEN, "udp %s:%d [published as %s:%d]", - pj_inet_ntoa(((pj_sockaddr_in*)&tp->base.local_addr)->sin_addr), - pj_ntohs(((pj_sockaddr_in*)&tp->base.local_addr)->sin_port), + local_addr, + pj_sockaddr_get_port(&tp->base.local_addr), tp->base.local_name.host.ptr, tp->base.local_name.port); } @@ -595,22 +629,30 @@ static pj_status_t start_async_read(struct udp_transport *tp) * * Attach UDP socket and start transport. */ -PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, - pj_sock_t sock, - const pjsip_host_port *a_name, - unsigned async_cnt, - pjsip_transport **p_transport) +static pj_status_t transport_attach( pjsip_endpoint *endpt, + pjsip_transport_type_e type, + pj_sock_t sock, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_transport **p_transport) { pj_pool_t *pool; struct udp_transport *tp; + const char *format; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(endpt && sock!=PJ_INVALID_SOCKET && a_name && async_cnt>0, PJ_EINVAL); + /* Object name. */ + if (type & PJSIP_TRANSPORT_IPV6) + format = "udpv6%p"; + else + format = "udp%p"; + /* Create pool. */ - pool = pjsip_endpt_create_pool(endpt, "udp%p", PJSIP_POOL_LEN_TRANSPORT, + pool = pjsip_endpt_create_pool(endpt, format, PJSIP_POOL_LEN_TRANSPORT, PJSIP_POOL_INC_TRANSPORT); if (!pool) return PJ_ENOMEM; @@ -621,9 +663,7 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, /* Save pool. */ tp->base.pool = pool; - /* Object name. */ - pj_ansi_snprintf(tp->base.obj_name, sizeof(tp->base.obj_name), - "udp%p", tp); + pj_memcpy(tp->base.obj_name, pool->obj_name, PJ_MAX_OBJ_NAME); /* Init reference counter. */ status = pj_atomic_create(pool, 0, &tp->base.ref_cnt); @@ -631,25 +671,27 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, goto on_error; /* Init lock. */ - status = pj_lock_create_recursive_mutex(pool, "udp%p", &tp->base.lock); + status = pj_lock_create_recursive_mutex(pool, pool->obj_name, + &tp->base.lock); if (status != PJ_SUCCESS) goto on_error; /* Set type. */ - tp->base.key.type = PJSIP_TRANSPORT_UDP; + tp->base.key.type = type; /* Remote address is left zero (except the family) */ - tp->base.key.rem_addr.addr.sa_family = pj_AF_INET(); + tp->base.key.rem_addr.addr.sa_family = (pj_uint16_t) + ((type & PJSIP_TRANSPORT_IPV6) ? pj_AF_INET6() : pj_AF_INET()); /* Type name. */ tp->base.type_name = "UDP"; /* Transport flag */ - tp->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP); + tp->base.flag = pjsip_transport_get_flag_from_type(type); /* Length of addressess. */ - tp->base.addr_len = sizeof(pj_sockaddr_in); + tp->base.addr_len = sizeof(tp->base.local_addr); /* Init local address. */ status = pj_sock_getsockname(sock, &tp->base.local_addr, @@ -658,7 +700,10 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, goto on_error; /* Init remote name. */ - tp->base.remote_name.host = pj_str("0.0.0.0"); + if (type == PJSIP_TRANSPORT_UDP) + tp->base.remote_name.host = pj_str("0.0.0.0"); + else + tp->base.remote_name.host = pj_str("::0"); tp->base.remote_name.port = 0; /* Set endpoint. */ @@ -723,7 +768,8 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, *p_transport = &tp->base; PJ_LOG(4,(tp->base.obj_name, - "SIP UDP transport started, published address is %.*s:%d", + "SIP %s started, published address is %.*s:%d", + pjsip_transport_get_type_desc((pjsip_transport_type_e)tp->base.key.type), (int)tp->base.local_name.host.slen, tp->base.local_name.host.ptr, tp->base.local_name.port)); @@ -735,6 +781,28 @@ on_error: return status; } + +PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, + pj_sock_t sock, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_transport **p_transport) +{ + return transport_attach(endpt, PJSIP_TRANSPORT_UDP, sock, a_name, + async_cnt, p_transport); +} + +PJ_DEF(pj_status_t) pjsip_udp_transport_attach2( pjsip_endpoint *endpt, + pjsip_transport_type_e type, + pj_sock_t sock, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_transport **p_transport) +{ + return transport_attach(endpt, type, sock, a_name, + async_cnt, p_transport); +} + /* * pjsip_udp_transport_start() * @@ -748,12 +816,13 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt, { pj_sock_t sock; pj_status_t status; - char addr_buf[16]; + char addr_buf[PJ_INET6_ADDRSTRLEN]; pjsip_host_port bound_name; PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); - status = create_socket(local_a, &sock); + status = create_socket(pj_AF_INET(), local_a, sizeof(pj_sockaddr_in), + &sock); if (status != PJ_SUCCESS) return status; @@ -761,7 +830,8 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt, /* Address name is not specified. * Build a name based on bound address. */ - status = get_published_name(sock, addr_buf, &bound_name); + status = get_published_name(sock, addr_buf, sizeof(addr_buf), + &bound_name); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; @@ -775,6 +845,47 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt, } +/* + * pjsip_udp_transport_start() + * + * Create a UDP socket in the specified address and start a transport. + */ +PJ_DEF(pj_status_t) pjsip_udp_transport_start6(pjsip_endpoint *endpt, + const pj_sockaddr_in6 *local_a, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_transport **p_transport) +{ + pj_sock_t sock; + pj_status_t status; + char addr_buf[PJ_INET_ADDRSTRLEN]; + pjsip_host_port bound_name; + + PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); + + status = create_socket(pj_AF_INET6(), local_a, sizeof(pj_sockaddr_in6), + &sock); + if (status != PJ_SUCCESS) + return status; + + if (a_name == NULL) { + /* Address name is not specified. + * Build a name based on bound address. + */ + status = get_published_name(sock, addr_buf, sizeof(addr_buf), + &bound_name); + if (status != PJ_SUCCESS) { + pj_sock_close(sock); + return status; + } + + a_name = &bound_name; + } + + return pjsip_udp_transport_attach( endpt, sock, a_name, async_cnt, + p_transport); +} + /* * Retrieve the internal socket handle used by the UDP transport. */ @@ -869,7 +980,7 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport, tp = (struct udp_transport*) transport; if (option & PJSIP_UDP_TRANSPORT_DESTROY_SOCKET) { - char addr_buf[16]; + char addr_buf[PJ_INET_ADDRSTRLEN]; pjsip_host_port bound_name; /* Request to recreate transport */ @@ -890,7 +1001,8 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport, /* Create the socket if it's not specified */ if (sock == PJ_INVALID_SOCKET) { - status = create_socket(local, &sock); + status = create_socket(pj_AF_INET(), local, + sizeof(pj_sockaddr_in), &sock); if (status != PJ_SUCCESS) return status; } @@ -899,7 +1011,8 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport, * from the bound address. */ if (a_name == NULL) { - status = get_published_name(sock, addr_buf, &bound_name); + status = get_published_name(sock, addr_buf, sizeof(addr_buf), + &bound_name); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 578425e2..39a71f8e 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -1181,18 +1181,33 @@ PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void) * PJSUA SIP Transport API. */ +/* + * Tools to get address string. + */ +static const char *addr_string(const pj_sockaddr_t *addr) +{ + static char str[128]; + str[0] = '\0'; + pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, + pj_sockaddr_get_addr(addr), + str, sizeof(str)); + return str; +} + /* * Create and initialize SIP socket (and possibly resolve public * address via STUN, depending on config). */ -static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, +static pj_status_t create_sip_udp_sock(int af, + const pj_str_t *bind_param, int port, pj_sock_t *p_sock, - pj_sockaddr_in *p_pub_addr) + pj_sockaddr *p_pub_addr) { - char ip_addr[32]; + char stun_ip_addr[PJ_INET6_ADDRSTRLEN]; pj_str_t stun_srv; pj_sock_t sock; + pj_sockaddr bind_addr; pj_status_t status; /* Make sure STUN server resolution has completed */ @@ -1202,14 +1217,27 @@ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, return status; } - status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock); + /* Initialize bound address */ + if (bind_param->slen) { + status = pj_sockaddr_init(af, &bind_addr, bind_param, + (pj_uint16_t)port); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Unable to resolve transport bound address", + status); + return status; + } + } else { + pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port); + } + + status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "socket() error", status); return status; } - status = pj_sock_bind_in(sock, pj_ntohl(bound_addr.s_addr), - (pj_uint16_t)port); + status = pj_sock_bind(sock, &bind_addr, sizeof(bind_addr)); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "bind() error", status); pj_sock_close(sock); @@ -1218,7 +1246,7 @@ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, /* If port is zero, get the bound port */ if (port == 0) { - pj_sockaddr_in bound_addr; + pj_sockaddr bound_addr; int namelen = sizeof(bound_addr); status = pj_sock_getsockname(sock, &bound_addr, &namelen); if (status != PJ_SUCCESS) { @@ -1227,12 +1255,12 @@ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, return status; } - port = pj_ntohs(bound_addr.sin_port); + port = pj_sockaddr_get_port(&bound_addr); } if (pjsua_var.stun_srv.addr.sa_family != 0) { - pj_ansi_strcpy(ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr)); - stun_srv = pj_str(ip_addr); + pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr)); + stun_srv = pj_str(stun_ip_addr); } else { stun_srv.slen = 0; } @@ -1240,22 +1268,28 @@ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, /* Get the published address, either by STUN or by resolving * the name of local host. */ - if (p_pub_addr->sin_addr.s_addr != 0) { + if (pj_sockaddr_has_addr(p_pub_addr)) { /* * Public address is already specified, no need to resolve the * address, only set the port. */ - if (p_pub_addr->sin_port == 0) - p_pub_addr->sin_port = pj_htons((pj_uint16_t)port); + if (pj_sockaddr_get_port(p_pub_addr) == 0) + pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port); } else if (stun_srv.slen) { /* * STUN is specified, resolve the address with STUN. */ + if (af != pj_AF_INET()) { + pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP); + pj_sock_close(sock); + return PJ_EAFNOTSUP; + } + status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock, &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port), &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port), - p_pub_addr); + &p_pub_addr->ipv4); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error contacting STUN server", status); pj_sock_close(sock); @@ -1263,25 +1297,24 @@ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, } } else { + pj_bzero(p_pub_addr, sizeof(pj_sockaddr)); - pj_bzero(p_pub_addr, sizeof(pj_sockaddr_in)); - - status = pj_gethostip(&p_pub_addr->sin_addr); + status = pj_gethostip(af, p_pub_addr); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to get local host IP", status); pj_sock_close(sock); return status; } - p_pub_addr->sin_family = pj_AF_INET(); - p_pub_addr->sin_port = pj_htons((pj_uint16_t)port); + p_pub_addr->addr.sa_family = (pj_uint16_t)af; + pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port); } *p_sock = sock; PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d", - pj_inet_ntoa(p_pub_addr->sin_addr), - (int)pj_ntohs(p_pub_addr->sin_port))); + addr_string(p_pub_addr), + (int)pj_sockaddr_get_port(p_pub_addr))); return PJ_SUCCESS; } @@ -1313,14 +1346,14 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, } /* Create the transport */ - if (type == PJSIP_TRANSPORT_UDP) { + if (type & PJSIP_TRANSPORT_UDP) { /* - * Create UDP transport. + * Create UDP transport (IPv4 or IPv6). */ pjsua_transport_config config; + char hostbuf[PJ_INET6_ADDRSTRLEN]; pj_sock_t sock = PJ_INVALID_SOCKET; - pj_sockaddr_in bound_addr; - pj_sockaddr_in pub_addr; + pj_sockaddr pub_addr; pjsip_host_port addr_name; /* Supply default config if it's not specified */ @@ -1329,22 +1362,12 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, cfg = &config; } - /* Initialize bound address, if any */ - bound_addr.sin_addr.s_addr = PJ_INADDR_ANY; - if (cfg->bound_addr.slen) { - status = pj_sockaddr_in_set_str_addr(&bound_addr,&cfg->bound_addr); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to resolve transport bound address", - status); - goto on_return; - } - } - /* Initialize the public address from the config, if any */ - pj_sockaddr_in_init(&pub_addr, NULL, (pj_uint16_t)cfg->port); + pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr, + NULL, (pj_uint16_t)cfg->port); if (cfg->public_addr.slen) { - status = pj_sockaddr_in_set_str_addr(&pub_addr, &cfg->public_addr); + status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type), + &pub_addr, &cfg->public_addr); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to resolve transport public address", @@ -1356,18 +1379,19 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, /* Create the socket and possibly resolve the address with STUN * (only when public address is not specified). */ - status = create_sip_udp_sock(bound_addr.sin_addr, cfg->port, + status = create_sip_udp_sock(pjsip_transport_type_get_af(type), + &cfg->bound_addr, cfg->port, &sock, &pub_addr); if (status != PJ_SUCCESS) goto on_return; - addr_name.host = pj_str(pj_inet_ntoa(pub_addr.sin_addr)); - addr_name.port = pj_ntohs(pub_addr.sin_port); + pj_ansi_strcpy(hostbuf, addr_string(&pub_addr)); + addr_name.host = pj_str(hostbuf); + addr_name.port = pj_sockaddr_get_port(&pub_addr); /* Create UDP transport */ - status = pjsip_udp_transport_attach( pjsua_var.endpt, sock, - &addr_name, 1, - &tp); + status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock, + &addr_name, 1, &tp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating SIP UDP transport", status); diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 16dcbce8..6738284a 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -346,15 +346,15 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, break; } else { - pj_in_addr addr; + pj_sockaddr addr; /* Get local IP address. */ - status = pj_gethostip(&addr); + status = pj_gethostip(pj_AF_INET(), &addr); if (status != PJ_SUCCESS) goto on_error; for (i=0; i<2; ++i) - mapped_addr[i].sin_addr = addr; + mapped_addr[i].sin_addr.s_addr = addr.ipv4.sin_addr.s_addr; mapped_addr[0].sin_port=pj_htons((pj_uint16_t)next_rtp_port); mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(next_rtp_port+1)); -- cgit v1.2.3