/* * Restart transport. * * If option is KEEP_SOCKET, just re-activate ioqueue operation. * * If option is DESTROY_SOCKET: * - if socket is specified, replace. * - if socket is not specified, create and replace. */ PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport, unsigned option, pj_sock_t sock, const pj_sockaddr_in *local, const pjsip_host_port *a_name) { struct udp_transport *tp; pj_status_t status; PJ_ASSERT_RETURN(transport != NULL, PJ_EINVAL); /* Flag must be specified */ PJ_ASSERT_RETURN((option & 0x03) != 0, PJ_EINVAL); tp = (struct udp_transport*) transport; if (option & PJSIP_UDP_TRANSPORT_DESTROY_SOCKET) { char addr_buf[PJ_INET6_ADDRSTRLEN]; pjsip_host_port bound_name; /* Request to recreate transport */ /* Destroy existing socket, if any. */ if (tp->key) { /* This implicitly closes the socket */ pj_ioqueue_unregister(tp->key); tp->key = NULL; } else { /* Close socket. */ if (tp->sock && tp->sock != PJ_INVALID_SOCKET) { pj_sock_close(tp->sock); tp->sock = PJ_INVALID_SOCKET; } } tp->sock = PJ_INVALID_SOCKET; /* Create the socket if it's not specified */ if (sock == PJ_INVALID_SOCKET) { status = create_socket(pj_AF_INET(), local, sizeof(pj_sockaddr_in), &sock); if (status != PJ_SUCCESS) return status; } /* If transport published name is not specified, calculate it * from the bound address. */ if (a_name == NULL) { 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; } /* Init local address. */ status = pj_sock_getsockname(sock, &tp->base.local_addr, &tp->base.addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Assign the socket and published address to transport. */ udp_set_socket(tp, sock, a_name); } else { /* For KEEP_SOCKET, transport must have been paused before */ PJ_ASSERT_RETURN(tp->is_paused, PJ_EINVALIDOP); /* If address name is specified, update it */ if (a_name != NULL) udp_set_pub_name(tp, a_name); } /* Re-register new or existing socket to ioqueue. */ status = register_to_ioqueue(tp); if (status != PJ_SUCCESS) { return status; } /* Restart async read operation. */ status = start_async_read(tp); if (status != PJ_SUCCESS) return status; /* Everything has been set up */ tp->is_paused = PJ_FALSE; PJ_LOG(4,(tp->base.obj_name, "SIP UDP transport restarted, published address is %.*s:%d", (int)tp->base.local_name.host.slen, tp->base.local_name.host.ptr, tp->base.local_name.port)); return PJ_SUCCESS; }
/* * pjsip_udp_transport_attach() * * Attach UDP socket and start 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, *ipv6_quoteb, *ipv6_quotee; 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"; ipv6_quoteb = "["; ipv6_quotee = "]"; } else { format = "udp%p"; ipv6_quoteb = ipv6_quotee = ""; } /* Create pool. */ pool = pjsip_endpt_create_pool(endpt, format, PJSIP_POOL_LEN_TRANSPORT, PJSIP_POOL_INC_TRANSPORT); if (!pool) return PJ_ENOMEM; /* Create the UDP transport object. */ tp = PJ_POOL_ZALLOC_T(pool, struct udp_transport); /* Save pool. */ tp->base.pool = pool; 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); if (status != PJ_SUCCESS) goto on_error; /* Init 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 = type; /* Remote address is left zero (except the family) */ 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(type); /* Length of addressess. */ tp->base.addr_len = sizeof(tp->base.local_addr); /* Init local address. */ status = pj_sock_getsockname(sock, &tp->base.local_addr, &tp->base.addr_len); if (status != PJ_SUCCESS) goto on_error; /* Init remote name. */ 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; /* Init direction */ tp->base.dir = PJSIP_TP_DIR_NONE; /* Set endpoint. */ tp->base.endpt = endpt; /* Transport manager and timer will be initialized by tpmgr */ /* Attach socket and assign name. */ udp_set_socket(tp, sock, a_name); /* Register to ioqueue */ status = register_to_ioqueue(tp); if (status != PJ_SUCCESS) goto on_error; /* Set functions. */ tp->base.send_msg = &udp_send_msg; tp->base.do_shutdown = &udp_shutdown; tp->base.destroy = &udp_destroy; /* This is a permanent transport, so we initialize the ref count * to one so that transport manager don't destroy this transport * when there's no user! */ pj_atomic_inc(tp->base.ref_cnt); /* Register to transport manager. */ tp->base.tpmgr = pjsip_endpt_get_tpmgr(endpt); status = pjsip_transport_register( tp->base.tpmgr, (pjsip_transport*)tp); if (status != PJ_SUCCESS) goto on_error; /* Create rdata and put it in the array. */ tp->rdata_cnt = 0; tp->rdata = (pjsip_rx_data**) pj_pool_calloc(tp->base.pool, async_cnt, sizeof(pjsip_rx_data*)); for (i=0; i<async_cnt; ++i) { pj_pool_t *rdata_pool = pjsip_endpt_create_pool(endpt, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC); if (!rdata_pool) { pj_atomic_set(tp->base.ref_cnt, 0); pjsip_transport_destroy(&tp->base); return PJ_ENOMEM; } init_rdata(tp, i, rdata_pool, NULL); tp->rdata_cnt++; } /* Start reading the ioqueue. */ status = start_async_read(tp); if (status != PJ_SUCCESS) { pjsip_transport_destroy(&tp->base); return status; } /* Done. */ if (p_transport) *p_transport = &tp->base; PJ_LOG(4,(tp->base.obj_name, "SIP %s started, published address is %s%.*s%s:%d", pjsip_transport_get_type_desc((pjsip_transport_type_e)tp->base.key.type), ipv6_quoteb, (int)tp->base.local_name.host.slen, tp->base.local_name.host.ptr, ipv6_quotee, tp->base.local_name.port)); return PJ_SUCCESS; on_error: udp_destroy((pjsip_transport*)tp); return status; }
PJ_DEF(pj_status_t) pjsip_udp_transport_restart2(pjsip_transport *transport, unsigned option, pj_sock_t sock, const pj_sockaddr *local, const pjsip_host_port *a_name) { struct udp_transport *tp; pj_status_t status; char addr[PJ_INET6_ADDRSTRLEN+10]; int i; PJ_ASSERT_RETURN(transport != NULL, PJ_EINVAL); /* Flag must be specified */ PJ_ASSERT_RETURN((option & 0x03) != 0, PJ_EINVAL); tp = (struct udp_transport*) transport; /* Pause the transport first, so that any active read loop spin will * quit as soon as possible. */ tp->is_paused = PJ_TRUE; if (option & PJSIP_UDP_TRANSPORT_DESTROY_SOCKET) { char addr_buf[PJ_INET6_ADDRSTRLEN]; pjsip_host_port bound_name; /* Request to recreate transport */ /* Destroy existing socket, if any. */ if (tp->key) { /* This implicitly closes the socket */ pj_ioqueue_unregister(tp->key); tp->key = NULL; } else { /* Close socket. */ if (tp->sock && tp->sock != PJ_INVALID_SOCKET) { pj_sock_close(tp->sock); tp->sock = PJ_INVALID_SOCKET; } } tp->sock = PJ_INVALID_SOCKET; /* Create the socket if it's not specified */ if (sock == PJ_INVALID_SOCKET) { status = create_socket(local?local->addr.sa_family:pj_AF_UNSPEC(), local, local?pj_sockaddr_get_len(local):0, &sock); if (status != PJ_SUCCESS) return status; } /* If transport published name is not specified, calculate it * from the bound address. */ if (a_name == NULL) { 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; } /* Init local address. */ status = pj_sock_getsockname(sock, &tp->base.local_addr, &tp->base.addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Assign the socket and published address to transport. */ udp_set_socket(tp, sock, a_name); } else { /* For KEEP_SOCKET, transport must have been paused before */ PJ_ASSERT_RETURN(tp->is_paused, PJ_EINVALIDOP); /* If address name is specified, update it */ if (a_name != NULL) udp_set_pub_name(tp, a_name); } /* Make sure all udp_on_read_complete() loop spin are stopped */ do { pj_thread_sleep(1); } while (tp->read_loop_spin); /* Re-register new or existing socket to ioqueue. */ status = register_to_ioqueue(tp); if (status != PJ_SUCCESS) { return status; } /* Re-init op_key. */ for (i = 0; i < tp->rdata_cnt; ++i) { pj_ioqueue_op_key_init(&tp->rdata[i]->tp_info.op_key.op_key, sizeof(pj_ioqueue_op_key_t)); } /* Restart async read operation. */ status = start_async_read(tp); if (status != PJ_SUCCESS) return status; /* Everything has been set up */ tp->is_paused = PJ_FALSE; PJ_LOG(4, (tp->base.obj_name, "SIP UDP transport restarted, published address is %s", pj_addr_str_print(&tp->base.local_name.host, tp->base.local_name.port, addr, sizeof(addr), 1))); return PJ_SUCCESS; }