static pj_status_t init_stack() { pj_sockaddr addr; pjsip_inv_callback inv_cb; pj_status_t status; pj_log_set_level(5); status = pj_init(); CHECK_STATUS(); pj_log_set_level(3); status = pjlib_util_init(); CHECK_STATUS(); pj_caching_pool_init(&app.cp, NULL, 0); app.pool = pj_pool_create( &app.cp.factory, "sipecho", 512, 512, 0); status = pjsip_endpt_create(&app.cp.factory, NULL, &app.sip_endpt); CHECK_STATUS(); pj_log_set_level(4); pj_sockaddr_init(AF, &addr, NULL, (pj_uint16_t)SIP_PORT); if (AF == pj_AF_INET()) { status = pjsip_udp_transport_start( app.sip_endpt, &addr.ipv4, NULL, 1, NULL); } else if (AF == pj_AF_INET6()) { status = pjsip_udp_transport_start6(app.sip_endpt, &addr.ipv6, NULL, 1, NULL); } else { status = PJ_EAFNOTSUP; } pj_log_set_level(3); CHECK_STATUS(); status = pjsip_tsx_layer_init_module(app.sip_endpt) || pjsip_ua_init_module( app.sip_endpt, NULL ); CHECK_STATUS(); pj_bzero(&inv_cb, sizeof(inv_cb)); inv_cb.on_state_changed = &call_on_state_changed; inv_cb.on_new_session = &call_on_forked; inv_cb.on_media_update = &call_on_media_update; inv_cb.on_rx_offer = &call_on_rx_offer; status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb) || pjsip_100rel_init_module(app.sip_endpt) || pjsip_endpt_register_module( app.sip_endpt, &mod_sipecho) || pjsip_endpt_register_module( app.sip_endpt, &msg_logger) || //pjmedia_endpt_create(&app.cp.factory, // pjsip_endpt_get_ioqueue(app.sip_endpt), // 0, &app.med_endpt) || pj_thread_create(app.pool, "sipecho", &worker_proc, NULL, 0, 0, &app.worker_thread); CHECK_STATUS(); return PJ_SUCCESS; }
/* Get the default IP interface */ PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr) { pj_sock_t fd; pj_str_t cp; pj_sockaddr a; int len; pj_uint8_t zero[64]; pj_status_t status; addr->addr.sa_family = (pj_uint16_t)af; status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd); if (status != PJ_SUCCESS) { return status; } PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() pj_sock_socket.")); if (af == PJ_AF_INET) { cp = pj_str("1.1.1.1"); } else { cp = pj_str("1::1"); } status = pj_sockaddr_init(af, &a, &cp, 53); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() pj_sockaddr_init.")); status = pj_sock_connect(fd, &a, pj_sockaddr_get_len(&a)); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() pj_sock_connect.")); len = sizeof(a); status = pj_sock_getsockname(fd, &a, &len); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() pj_sock_getsockname.")); pj_sock_close(fd); /* Check that the address returned is not zero */ pj_bzero(zero, sizeof(zero)); if (pj_memcmp(pj_sockaddr_get_addr(&a), zero, pj_sockaddr_get_addr_len(&a))==0) { PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() interface not found.")); return PJ_ENOTFOUND; } pj_sockaddr_copy_addr(addr, &a); /* Success */ return PJ_SUCCESS; }
/* * Initialize pjsip_udp_transport_cfg structure with default values. */ PJ_DEF(void) pjsip_udp_transport_cfg_default(pjsip_udp_transport_cfg *cfg, int af) { pj_bzero(cfg, sizeof(*cfg)); cfg->af = af; pj_sockaddr_init(cfg->af, &cfg->bind_addr, NULL, 0); cfg->async_cnt = 1; }
/* * This callback is called when target is resolved with DNS AAAA query. */ static void dns_aaaa_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *pkt) { struct query *query = (struct query*) user_data; pjsip_server_addresses *srv = &query->server; /* Reset outstanding job */ query->object6 = NULL; if (status == PJ_SUCCESS) { pj_dns_addr_record rec; unsigned i; /* Parse the response */ rec.addr_count = 0; status = pj_dns_parse_addr_response(pkt, &rec); /* Build server addresses and call callback */ for (i = 0; i < rec.addr_count && srv->count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i) { /* Should not happen, just in case */ if (rec.addr[i].af != pj_AF_INET6()) continue; srv->entry[srv->count].type = query->naptr[0].type | PJSIP_TRANSPORT_IPV6; srv->entry[srv->count].priority = 0; srv->entry[srv->count].weight = 0; srv->entry[srv->count].addr_len = sizeof(pj_sockaddr_in6); pj_sockaddr_init(pj_AF_INET6(), &srv->entry[srv->count].addr, 0, (pj_uint16_t)query->req.def_port); srv->entry[srv->count].addr.ipv6.sin6_addr = rec.addr[i].ip.v6; ++srv->count; } } if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query->objname, "DNS AAAA record resolution failed: %s", errmsg)); query->last_error = status; } /* Call the callback if all DNS queries have been completed */ if (query->object == NULL && query->object6 == NULL) { if (srv->count > 0) (*query->cb)(PJ_SUCCESS, query->token, &query->server); else (*query->cb)(query->last_error, query->token, NULL); } }
/* * Initialize pjsip_tcp_transport_cfg structure with default values. */ PJ_DEF(void) pjsip_tcp_transport_cfg_default(pjsip_tcp_transport_cfg *cfg, int af) { pj_bzero(cfg, sizeof(*cfg)); cfg->af = af; pj_sockaddr_init(cfg->af, &cfg->bind_addr, NULL, 0); cfg->async_cnt = 1; cfg->reuse_addr = PJSIP_TCP_TRANSPORT_REUSEADDR; }
/* Callback to be called by DNS SRV resolution */ static void srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { struct query *query = (struct query*) user_data; pjsip_server_addresses srv; unsigned i; if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query->objname, "DNS A/AAAA record resolution failed: %s", errmsg)); /* Call the callback */ (*query->cb)(status, query->token, NULL); return; } /* Build server addresses and call callback */ srv.count = 0; for (i=0; i<rec->count; ++i) { const pj_dns_addr_record *s = &rec->entry[i].server; unsigned j; for (j = 0; j < s->addr_count && srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++j) { srv.entry[srv.count].type = query->naptr[0].type; srv.entry[srv.count].priority = rec->entry[i].priority; srv.entry[srv.count].weight = rec->entry[i].weight; pj_sockaddr_init(s->addr[j].af, &srv.entry[srv.count].addr, 0, (pj_uint16_t)rec->entry[i].port); if (s->addr[j].af == pj_AF_INET6()) srv.entry[srv.count].addr.ipv6.sin6_addr = s->addr[j].ip.v6; else srv.entry[srv.count].addr.ipv4.sin_addr = s->addr[j].ip.v4; srv.entry[srv.count].addr_len = pj_sockaddr_get_len(&srv.entry[srv.count].addr); /* Update transport type if this is IPv6 */ if (s->addr[j].af == pj_AF_INET6()) srv.entry[srv.count].type |= PJSIP_TRANSPORT_IPV6; ++srv.count; } } /* Call the callback */ (*query->cb)(PJ_SUCCESS, query->token, &srv); }
PJ_DEF(pj_status_t) pj_activesock_create_udp( pj_pool_t *pool, const pj_sockaddr *addr, const pj_activesock_cfg *opt, pj_ioqueue_t *ioqueue, const pj_activesock_cb *cb, void *user_data, pj_activesock_t **p_asock, pj_sockaddr *bound_addr) { pj_sock_t sock_fd; pj_sockaddr default_addr; pj_status_t status; if (addr == NULL) { pj_sockaddr_init(pj_AF_INET(), &default_addr, NULL, 0); addr = &default_addr; } status = pj_sock_socket(addr->addr.sa_family, pj_SOCK_DGRAM(), 0, &sock_fd); if (status != PJ_SUCCESS) { return status; } status = pj_sock_bind(sock_fd, addr, pj_sockaddr_get_len(addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock_fd); return status; } status = pj_activesock_create(pool, sock_fd, pj_SOCK_DGRAM(), opt, ioqueue, cb, user_data, p_asock); if (status != PJ_SUCCESS) { pj_sock_close(sock_fd); return status; } if (bound_addr) { int addr_len = sizeof(*bound_addr); status = pj_sock_getsockname(sock_fd, bound_addr, &addr_len); if (status != PJ_SUCCESS) { pj_activesock_close(*p_asock); return status; } } return PJ_SUCCESS; }
static pj_stun_msg* create_msgint3(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_sockaddr mapped_addr; pj_str_t s1; pj_status_t status; status = pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, pj_cstr(&s1, "test vector")); if (status != PJ_SUCCESS) goto on_error; status = pj_sockaddr_init(pj_AF_INET6(), &mapped_addr, pj_cstr(&s1, "2001:db8:1234:5678:11:2233:4455:6677"), 32853); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &mapped_addr, sizeof(pj_sockaddr)); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_msgint_attr(pool, msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); if (status != PJ_SUCCESS) goto on_error; return msg; on_error: app_perror(" error: create_msgint3()", status); return NULL; }
static pj_status_t get_local_interface(const pj_sockaddr *server, pj_sockaddr *local_addr) { pj_sock_t sock; pj_sockaddr tmp, local; int addr_len; pj_status_t status; status = pj_sock_socket(server->addr.sa_family, pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) return status; addr_len = pj_sockaddr_get_len(server); pj_sockaddr_init(server->addr.sa_family, &local, NULL, 0); status = pj_sock_bind(sock, &local, addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } status = pj_sock_connect(sock, server, addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } status = pj_sock_getsockname(sock, &tmp, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } pj_sockaddr_cp(local_addr, &tmp); pj_sock_close(sock); return PJ_SUCCESS; }
/* DNS resolver callback */ static void dns_srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { pj_stun_sock *stun_sock = (pj_stun_sock*) user_data; pj_grp_lock_acquire(stun_sock->grp_lock); /* Clear query */ stun_sock->q = NULL; /* Handle error */ if (status != PJ_SUCCESS) { sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status); pj_grp_lock_release(stun_sock->grp_lock); return; } pj_assert(rec->count); pj_assert(rec->entry[0].server.addr_count); pj_assert(rec->entry[0].server.addr[0].af == stun_sock->af); /* Set the address */ pj_sockaddr_init(stun_sock->af, &stun_sock->srv_addr, NULL, rec->entry[0].port); if (stun_sock->af == pj_AF_INET6()) { stun_sock->srv_addr.ipv6.sin6_addr = rec->entry[0].server.addr[0].ip.v6; } else { stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0].ip.v4; } /* Start sending Binding request */ get_mapped_addr(stun_sock); pj_grp_lock_release(stun_sock->grp_lock); }
/* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pjsip_tcp_transport_cfg cfg; pjsip_tcp_transport_cfg_default(&cfg, pj_AF_INET()); if (local) pj_sockaddr_cp(&cfg.bind_addr, local); else pj_sockaddr_init(cfg.af, &cfg.bind_addr, NULL, 0); if (a_name) pj_memcpy(&cfg.addr_name, a_name, sizeof(*a_name)); if (async_cnt) cfg.async_cnt = async_cnt; return pjsip_tcp_transport_start3(endpt, &cfg, p_factory); }
/* Test will perform multiple clients trying to connect to single server. * Once SSL connection established, echo test will be performed. */ static int perf_test(unsigned clients, unsigned ms_handshake_timeout) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_timer_heap_t *timer = NULL; pj_ssl_sock_t *ssock_serv = NULL; pj_ssl_sock_t **ssock_cli = NULL; pj_ssl_sock_param param; struct test_state state_serv = { 0 }; struct test_state *state_cli = NULL; pj_sockaddr addr, listen_addr; pj_ssl_cert_t *cert = NULL; pj_status_t status; unsigned i, cli_err = 0, tot_sent = 0, tot_recv = 0; pj_time_val start; pool = pj_pool_create(mem, "ssl_perf", 256, 256, NULL); status = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } status = pj_timer_heap_create(pool, PJ_IOQUEUE_MAX_HANDLES, &timer); if (status != PJ_SUCCESS) { goto on_return; } /* Set cert */ { pj_str_t tmp1, tmp2, tmp3, tmp4; status = pj_ssl_cert_load_from_files(pool, pj_strset2(&tmp1, (char*)CERT_CA_FILE), pj_strset2(&tmp2, (char*)CERT_FILE), pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), &cert); if (status != PJ_SUCCESS) { goto on_return; } } pj_ssl_sock_param_default(¶m); param.cb.on_accept_complete = &ssl_on_accept_complete; param.cb.on_connect_complete = &ssl_on_connect_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.timer_heap = timer; param.timeout.sec = 0; param.timeout.msec = ms_handshake_timeout; pj_time_val_normalize(¶m.timeout); /* Init default bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } /* SERVER */ param.user_data = &state_serv; state_serv.pool = pool; state_serv.echo = PJ_TRUE; state_serv.is_server = PJ_TRUE; status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); if (status != PJ_SUCCESS) { goto on_return; } status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr)); if (status != PJ_SUCCESS) { goto on_return; } /* Get listening address for clients to connect to */ { pj_ssl_sock_info info; char buf[64]; pj_ssl_sock_get_info(ssock_serv, &info); pj_sockaddr_cp(&listen_addr, &info.local_addr); pj_sockaddr_print((pj_sockaddr_t*)&listen_addr, buf, sizeof(buf), 1); PJ_LOG(3, ("", "...Listener ready at %s", buf)); } /* CLIENTS */ clients_num = clients; param.timeout.sec = 0; param.timeout.msec = 0; /* Init random seed */ { pj_time_val now; pj_gettimeofday(&now); pj_srand((unsigned)now.sec); } /* Allocate SSL socket pointers and test state */ ssock_cli = pj_pool_calloc(pool, clients, sizeof(pj_ssl_sock_t*)); state_cli = pj_pool_calloc(pool, clients, sizeof(struct test_state)); /* Get start timestamp */ pj_gettimeofday(&start); /* Setup clients */ for (i = 0; i < clients; ++i) { param.user_data = &state_cli[i]; state_cli[i].pool = pool; state_cli[i].check_echo = PJ_TRUE; state_cli[i].send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024; state_cli[i].send_str = pj_pool_alloc(pool, state_cli[i].send_str_len); { unsigned j; for (j = 0; j < state_cli[i].send_str_len; ++j) state_cli[i].send_str[j] = (char)(pj_rand() % 256); } status = pj_ssl_sock_create(pool, ¶m, &ssock_cli[i]); if (status != PJ_SUCCESS) { app_perror("...ERROR pj_ssl_sock_create()", status); cli_err++; clients_num--; continue; } status = pj_ssl_sock_start_connect(ssock_cli[i], pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr)); if (status == PJ_SUCCESS) { ssl_on_connect_complete(ssock_cli[i], PJ_SUCCESS); } else if (status == PJ_EPENDING) { status = PJ_SUCCESS; } else { app_perror("...ERROR pj_ssl_sock_create()", status); pj_ssl_sock_close(ssock_cli[i]); ssock_cli[i] = NULL; clients_num--; cli_err++; continue; } /* Give chance to server to accept this client */ { unsigned n = 5; #ifdef PJ_SYMBIAN while(n && pj_symbianos_poll(-1, 1000)) n--; #else pj_time_val delay = {0, 100}; while(n && pj_ioqueue_poll(ioqueue, &delay) > 0) n--; #endif } } /* Wait until everything has been sent/received or error */ while (clients_num) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer, &delay); #endif } /* Clean up sockets */ { pj_time_val delay = {0, 500}; while (pj_ioqueue_poll(ioqueue, &delay) > 0); } if (state_serv.err != PJ_SUCCESS) { status = state_serv.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); /* SSL setup and data transfer duration */ { pj_time_val stop; pj_gettimeofday(&stop); PJ_TIME_VAL_SUB(stop, start); PJ_LOG(3, ("", ".....Setup & data transfer duration: %d.%03ds", stop.sec, stop.msec)); } /* Check clients status */ for (i = 0; i < clients; ++i) { if (state_cli[i].err != PJ_SUCCESS) cli_err++; tot_sent += state_cli[1].sent; tot_recv += state_cli[1].recv; } PJ_LOG(3, ("", ".....Clients: %d (%d errors)", clients, cli_err)); PJ_LOG(3, ("", ".....Total sent/recv: %d/%d bytes", tot_sent, tot_recv)); on_return: if (ssock_serv) pj_ssl_sock_close(ssock_serv); for (i = 0; i < clients; ++i) { if (ssock_cli[i] && !state_cli[i].err && !state_cli[i].done) pj_ssl_sock_close(ssock_cli[i]); } if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }
/* * Create stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp( pjmedia_vid_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; int rem_af, local_af; pj_sockaddr local_addr; pj_status_t status; PJ_UNUSED_ARG(endpt); /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Media type must be video */ if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) != 0) return PJMEDIA_EINVALIMEDIATYPE; /* Reset: */ pj_bzero(si, sizeof(*si)); /* Media type: */ si->type = PJMEDIA_TYPE_VIDEO; /* Transport protocol */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, &local_m->desc.transport); if (status != PJ_SUCCESS) return PJMEDIA_SDPNEG_EINVANSTP; if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_AVP; } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; } else { si->proto = PJMEDIA_TP_PROTO_UNKNOWN; return PJ_SUCCESS; } /* Check address family in remote SDP */ rem_af = pj_AF_UNSPEC(); if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { rem_af = pj_AF_INET(); } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { rem_af = pj_AF_INET6(); } } if (rem_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_EAFNOTSUP; } /* Set remote address: */ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Check address family of local info */ local_af = pj_AF_UNSPEC(); if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { local_af = pj_AF_INET(); } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { local_af = pj_AF_INET6(); } } if (local_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_SUCCESS; } /* Set remote address: */ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, local_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Local and remote address family must match */ if (local_af != rem_af) return PJ_EAFNOTSUP; /* Media direction: */ if (local_m->desc.port == 0 || pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* No need to do anything else if stream is rejected */ if (local_m->desc.port == 0) { return PJ_SUCCESS; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), pj_sockaddr_get_addr(&si->rem_addr), pj_sockaddr_get_addr_len(&si->rem_addr)); } } } if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); } /* Get codec info and param */ status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m); /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; return status; }
/* * Create a new listener on the specified port. */ PJ_DEF(pj_status_t) pj_turn_listener_create_udp( pj_turn_srv *srv, int af, const pj_str_t *bound_addr, unsigned port, unsigned concurrency_cnt, unsigned flags, pj_turn_listener **p_listener) { pj_pool_t *pool; struct udp_listener *udp; pj_ioqueue_callback ioqueue_cb; unsigned i; pj_status_t status; /* Create structure */ pool = pj_pool_create(srv->core.pf, "udp%p", 1000, 1000, NULL); udp = PJ_POOL_ZALLOC_T(pool, struct udp_listener); udp->base.pool = pool; udp->base.obj_name = pool->obj_name; udp->base.server = srv; udp->base.tp_type = PJ_TURN_TP_UDP; udp->base.sock = PJ_INVALID_SOCKET; udp->base.destroy = &udp_destroy; udp->read_cnt = concurrency_cnt; udp->base.flags = flags; udp->tp.obj_name = udp->base.obj_name; udp->tp.info = udp->base.info; udp->tp.listener = &udp->base; udp->tp.sendto = &udp_sendto; udp->tp.add_ref = &udp_add_ref; udp->tp.dec_ref = &udp_dec_ref; /* Create socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &udp->base.sock); if (status != PJ_SUCCESS) goto on_error; /* Init bind address */ status = pj_sockaddr_init(af, &udp->base.addr, bound_addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; /* Create info */ pj_ansi_strcpy(udp->base.info, "UDP:"); pj_sockaddr_print(&udp->base.addr, udp->base.info+4, sizeof(udp->base.info)-4, 3); /* Bind socket */ status = pj_sock_bind(udp->base.sock, &udp->base.addr, pj_sockaddr_get_len(&udp->base.addr)); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = on_read_complete; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, udp->base.sock, udp, &ioqueue_cb, &udp->key); /* Create op keys */ udp->read_op = (struct read_op**)pj_pool_calloc(pool, concurrency_cnt, sizeof(struct read_op*)); /* Create each read_op and kick off read operation */ for (i=0; i<concurrency_cnt; ++i) { pj_pool_t *rpool = pj_pool_create(srv->core.pf, "rop%p", 1000, 1000, NULL); udp->read_op[i] = PJ_POOL_ZALLOC_T(pool, struct read_op); udp->read_op[i]->pkt.pool = rpool; on_read_complete(udp->key, &udp->read_op[i]->op_key, 0); } /* Done */ PJ_LOG(4,(udp->base.obj_name, "Listener %s created", udp->base.info)); *p_listener = &udp->base; return PJ_SUCCESS; on_error: udp_destroy(&udp->base); return status; }
/* * 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; 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); status = pj_sockaddr_parse2(af, options, str, &hostpart, &port, &af); if (status != PJ_SUCCESS) return status; #if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6 if (af==PJ_AF_INET6) return PJ_EIPV6NOTSUP; #endif 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; /* Make sure port number is valid */ for (i=0; i<port_part.slen; ++i) { if (!pj_isdigit(port_part.ptr[i])) return status; } long_port = pj_strtoul(&port_part); if (long_port > 65535) return status; port = (pj_uint16_t)long_port; status = pj_sockaddr_init(PJ_AF_INET6, addr, &hostpart, port); } #endif return status; }
static int stun_destroy_test(void) { enum { LOOP = 500 }; struct stun_test_session test_sess; pj_sockaddr bind_addr; int addr_len; pj_caching_pool cp; pj_pool_t *pool; unsigned i; pj_status_t status; int rc = 0; PJ_LOG(3,(THIS_FILE, " STUN destroy concurrency test")); pj_bzero(&test_sess, sizeof(test_sess)); pj_caching_pool_init(&cp, NULL, 0); pool = pj_pool_create(&cp.factory, "testsess", 512, 512, NULL); pj_stun_config_init(&test_sess.stun_cfg, &cp.factory, 0, NULL, NULL); status = pj_timer_heap_create(pool, 1023, &test_sess.stun_cfg.timer_heap); pj_assert(status == PJ_SUCCESS); status = pj_lock_create_recursive_mutex(pool, NULL, &test_sess.lock); pj_assert(status == PJ_SUCCESS); pj_timer_heap_set_lock(test_sess.stun_cfg.timer_heap, test_sess.lock, PJ_TRUE); pj_assert(status == PJ_SUCCESS); status = pj_ioqueue_create(pool, 512, &test_sess.stun_cfg.ioqueue); pj_assert(status == PJ_SUCCESS); pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &test_sess.server_sock); pj_sockaddr_init(pj_AF_INET(), &bind_addr, NULL, 0); status = pj_sock_bind(test_sess.server_sock, &bind_addr, pj_sockaddr_get_len(&bind_addr)); pj_assert(status == PJ_SUCCESS); addr_len = sizeof(bind_addr); status = pj_sock_getsockname(test_sess.server_sock, &bind_addr, &addr_len); pj_assert(status == PJ_SUCCESS); test_sess.server_port = pj_sockaddr_get_port(&bind_addr); status = pj_event_create(pool, NULL, PJ_TRUE, PJ_FALSE, &test_sess.server_event); pj_assert(status == PJ_SUCCESS); for (i=0; i<SERVER_THREAD_CNT; ++i) { status = pj_thread_create(pool, NULL, &server_thread_proc, &test_sess, 0, 0, &test_sess.server_threads[i]); pj_assert(status == PJ_SUCCESS); } for (i=0; i<WORKER_THREAD_CNT; ++i) { status = pj_thread_create(pool, NULL, &worker_thread_proc, &test_sess, 0, 0, &test_sess.worker_threads[i]); pj_assert(status == PJ_SUCCESS); } /* Test 1: Main thread calls destroy while callback is processing response */ PJ_LOG(3,(THIS_FILE, " Destroy in main thread while callback is running")); for (i=0; i<LOOP; ++i) { int sleep = pj_rand() % 5; PJ_LOG(3,(THIS_FILE, " Try %-3d of %d", i+1, LOOP)); /* Test 1: destroy at the same time when receiving response */ pj_bzero(&test_sess.param, sizeof(test_sess.param)); test_sess.param.client_sleep_after_start = 20; test_sess.param.client_sleep_before_destroy = sleep; test_sess.param.server_wait_for_event = PJ_TRUE; stun_destroy_test_session(&test_sess); PJ_LOG(3,(THIS_FILE, " stun test a: sleep delay:%d: clients with response: %d", sleep, test_sess.param.client_got_response)); /* Test 2: destroy at the same time with STUN retransmit timer */ test_sess.param.server_drop_request = PJ_TRUE; test_sess.param.client_sleep_after_start = 0; test_sess.param.client_sleep_before_destroy = PJ_STUN_RTO_VALUE; test_sess.param.server_wait_for_event = PJ_FALSE; stun_destroy_test_session(&test_sess); PJ_LOG(3,(THIS_FILE, " stun test b: retransmit concurrency")); /* Test 3: destroy at the same time with receiving response * AND STUN retransmit timer */ test_sess.param.client_got_response = 0; test_sess.param.server_drop_request = PJ_FALSE; test_sess.param.client_sleep_after_start = PJ_STUN_RTO_VALUE; test_sess.param.client_sleep_before_destroy = 0; test_sess.param.server_wait_for_event = PJ_TRUE; stun_destroy_test_session(&test_sess); PJ_LOG(3,(THIS_FILE, " stun test c: clients with response: %d", test_sess.param.client_got_response)); pj_thread_sleep(10); ice_one_conc_test(&test_sess.stun_cfg, PJ_FALSE); pj_thread_sleep(10); } /* Avoid compiler warning */ goto on_return; on_return: test_sess.thread_quit_flag = PJ_TRUE; for (i=0; i<SERVER_THREAD_CNT; ++i) { pj_thread_join(test_sess.server_threads[i]); } for (i=0; i<WORKER_THREAD_CNT; ++i) { pj_thread_join(test_sess.worker_threads[i]); } pj_event_destroy(test_sess.server_event); pj_sock_close(test_sess.server_sock); pj_ioqueue_destroy(test_sess.stun_cfg.ioqueue); pj_timer_heap_destroy(test_sess.stun_cfg.timer_heap); pj_pool_release(pool); pj_caching_pool_destroy(&cp); PJ_LOG(3,(THIS_FILE, " Done. rc=%d", rc)); return rc; }
static int parse_test(void) { #define IPv4 1 #define IPv6 2 struct test_t { const char *input; int result_af; const char *result_ip; pj_uint16_t result_port; }; struct test_t valid_tests[] = { /* IPv4 */ { "10.0.0.1:80", IPv4, "10.0.0.1", 80}, { "10.0.0.1", IPv4, "10.0.0.1", 0}, { "10.0.0.1:", IPv4, "10.0.0.1", 0}, { "10.0.0.1:0", IPv4, "10.0.0.1", 0}, { ":80", IPv4, "0.0.0.0", 80}, { ":", IPv4, "0.0.0.0", 0}, #if !PJ_SYMBIAN { "localhost", IPv4, "127.0.0.1", 0}, { "localhost:", IPv4, "127.0.0.1", 0}, { "localhost:80", IPv4, "127.0.0.1", 80}, #endif #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 { "fe::01:80", IPv6, "fe::01:80", 0}, { "[fe::01]:80", IPv6, "fe::01", 80}, { "fe::01", IPv6, "fe::01", 0}, { "[fe::01]", IPv6, "fe::01", 0}, { "fe::01:", IPv6, "fe::01", 0}, { "[fe::01]:", IPv6, "fe::01", 0}, { "::", IPv6, "::0", 0}, { "[::]", IPv6, "::", 0}, { ":::", IPv6, "::", 0}, { "[::]:", IPv6, "::", 0}, { ":::80", IPv6, "::", 80}, { "[::]:80", IPv6, "::", 80}, #endif }; struct test_t invalid_tests[] = { /* IPv4 */ { "10.0.0.1:abcd", IPv4}, /* port not numeric */ { "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 */ { "[fe::01]:-1", IPv6}, /* port contains illegal character */ { "[fe::01]:123456", IPv6}, /* port too big */ { "fe::01:02::03:04:80", IPv6}, /* invalid IP */ { "[fe::01:02::03:04]:80", IPv6}, /* invalid IP */ { "[fe:01", IPv6}, /* Unterminated bracket */ #endif }; unsigned i; PJ_LOG(3,("test", "...IP address parsing")); for (i=0; i<PJ_ARRAY_SIZE(valid_tests); ++i) { pj_status_t status; pj_str_t input; pj_sockaddr addr, result; switch (valid_tests[i].result_af) { case IPv4: valid_tests[i].result_af = PJ_AF_INET; break; case IPv6: valid_tests[i].result_af = PJ_AF_INET6; break; default: pj_assert(!"Invalid AF!"); continue; } /* Try parsing with PJ_AF_UNSPEC */ status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, pj_cstr(&input, valid_tests[i].input), &addr); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... failed when parsing %s (i=%d)", valid_tests[i].input, i)); return -10; } /* Check "sin_len" member of parse result */ CHECK_SA_ZERO_LEN(&addr, -20); /* Build the correct result */ status = pj_sockaddr_init(valid_tests[i].result_af, &result, pj_cstr(&input, valid_tests[i].result_ip), valid_tests[i].result_port); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... error building IP address %s", valid_tests[i].input)); return -30; } /* Compare the result */ if (pj_sockaddr_cmp(&addr, &result) != 0) { PJ_LOG(1,("test", ".... parsed result mismatched for %s", valid_tests[i].input)); return -40; } /* Parse again with the specified af */ status = pj_sockaddr_parse(valid_tests[i].result_af, 0, pj_cstr(&input, valid_tests[i].input), &addr); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... failed when parsing %s", valid_tests[i].input)); return -50; } /* Check "sin_len" member of parse result */ CHECK_SA_ZERO_LEN(&addr, -55); /* Compare the result again */ if (pj_sockaddr_cmp(&addr, &result) != 0) { PJ_LOG(1,("test", ".... parsed result mismatched for %s", valid_tests[i].input)); return -60; } } for (i=0; i<PJ_ARRAY_SIZE(invalid_tests); ++i) { pj_status_t status; pj_str_t input; pj_sockaddr addr; switch (invalid_tests[i].result_af) { case IPv4: invalid_tests[i].result_af = PJ_AF_INET; break; case IPv6: invalid_tests[i].result_af = PJ_AF_INET6; break; default: pj_assert(!"Invalid AF!"); continue; } /* Try parsing with PJ_AF_UNSPEC */ status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, pj_cstr(&input, invalid_tests[i].input), &addr); if (status == PJ_SUCCESS) { PJ_LOG(1,("test", ".... expecting failure when parsing %s", invalid_tests[i].input)); return -100; } } return 0; }
/* * main() * * If called with argument, treat argument as SIP URL to be called. * Otherwise wait for incoming calls. */ int main(int argc, char *argv[]) { pj_pool_t *pool = NULL; pj_status_t status; unsigned i; /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); pj_log_set_level(5); /* Then init PJLIB-UTIL: */ status = pjlib_util_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* Create global endpoint: */ { const pj_str_t *hostname; const char *endpt_name; /* Endpoint MUST be assigned a globally unique name. * The name will be used as the hostname in Warning header. */ /* For this implementation, we'll use hostname for simplicity */ hostname = pj_gethostname(); endpt_name = hostname->ptr; /* Create the endpoint: */ status = pjsip_endpt_create(&cp.factory, endpt_name, &g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* * Add UDP transport, with hard-coded port * Alternatively, application can use pjsip_udp_transport_attach() to * start UDP transport, if it already has an UDP socket (e.g. after it * resolves the address with STUN). */ { pj_sockaddr addr; pj_sockaddr_init(AF, &addr, NULL, (pj_uint16_t)SIP_PORT); if (AF == pj_AF_INET()) { status = pjsip_udp_transport_start( g_endpt, &addr.ipv4, NULL, 1, NULL); } else if (AF == pj_AF_INET6()) { status = pjsip_udp_transport_start6(g_endpt, &addr.ipv6, NULL, 1, NULL); } else { status = PJ_EAFNOTSUP; } if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to start UDP transport", status); return 1; } } /* * Init transaction layer. * This will create/initialize transaction hash tables etc. */ status = pjsip_tsx_layer_init_module(g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Initialize UA layer module. * This will create/initialize dialog hash tables etc. */ status = pjsip_ua_init_module( g_endpt, NULL ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Init invite session module. * The invite session module initialization takes additional argument, * i.e. a structure containing callbacks to be called on specific * occurence of events. * * The on_state_changed and on_new_session callbacks are mandatory. * Application must supply the callback function. * * We use on_media_update() callback in this application to start * media transmission. */ { pjsip_inv_callback inv_cb; /* Init the callback for INVITE session: */ pj_bzero(&inv_cb, sizeof(inv_cb)); inv_cb.on_state_changed = &call_on_state_changed; inv_cb.on_new_session = &call_on_forked; inv_cb.on_media_update = &call_on_media_update; /* Initialize invite session module: */ status = pjsip_inv_usage_init(g_endpt, &inv_cb); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* Initialize 100rel support */ status = pjsip_100rel_init_module(g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* * Register our module to receive incoming requests. */ status = pjsip_endpt_register_module( g_endpt, &mod_simpleua); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Register message logger module. */ status = pjsip_endpt_register_module( g_endpt, &msg_logger); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ #if PJ_HAS_THREADS status = pjmedia_endpt_create(&cp.factory, NULL, 1, &g_med_endpt); #else status = pjmedia_endpt_create(&cp.factory, pjsip_endpt_get_ioqueue(g_endpt), 0, &g_med_endpt); #endif PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Add PCMA/PCMU codec to the media endpoint. */ #if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0 status = pjmedia_codec_g711_init(g_med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); #endif #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) /* Init video subsystem */ pool = pjmedia_endpt_create_pool(g_med_endpt, "Video subsystem", 512, 512); status = pjmedia_video_format_mgr_create(pool, 64, 0, NULL); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_converter_mgr_create(pool, NULL); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_vid_codec_mgr_create(pool, NULL); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_vid_dev_subsys_init(&cp.factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); # if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0 /* Init ffmpeg video codecs */ status = pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); # endif /* PJMEDIA_HAS_FFMPEG_VID_CODEC */ #endif /* PJMEDIA_HAS_VIDEO */ /* * Create media transport used to send/receive RTP/RTCP socket. * One media transport is needed for each call. Application may * opt to re-use the same media transport for subsequent calls. */ for (i = 0; i < PJ_ARRAY_SIZE(g_med_transport); ++i) { status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL, RTP_PORT + i*2, 0, &g_med_transport[i]); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create media transport", status); return 1; } /* * Get socket info (address, port) of the media transport. We will * need this info to create SDP (i.e. the address and port info in * the SDP). */ pjmedia_transport_info_init(&g_med_tpinfo[i]); pjmedia_transport_get_info(g_med_transport[i], &g_med_tpinfo[i]); pj_memcpy(&g_sock_info[i], &g_med_tpinfo[i].sock_info, sizeof(pjmedia_sock_info)); } /* * If URL is specified, then make call immediately. */ if (argc > 1) { pj_sockaddr hostaddr; char hostip[PJ_INET6_ADDRSTRLEN+2]; char temp[80]; pj_str_t dst_uri = pj_str(argv[1]); pj_str_t local_uri; pjsip_dialog *dlg; pjmedia_sdp_session *local_sdp; pjsip_tx_data *tdata; if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to retrieve local host IP", status); return 1; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:simpleuac@%s:%d>", hostip, SIP_PORT); local_uri = pj_str(temp); /* Create UAC dialog */ status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, /* local URI */ &local_uri, /* local Contact */ &dst_uri, /* remote URI */ &dst_uri, /* remote target */ &dlg); /* dialog */ if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create UAC dialog", status); return 1; } /* If we expect the outgoing INVITE to be challenged, then we should * put the credentials in the dialog here, with something like this: * { pjsip_cred_info cred[1]; cred[0].realm = pj_str("sip.server.realm"); cred[0].scheme = pj_str("digest"); cred[0].username = pj_str("theuser"); cred[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; cred[0].data = pj_str("thepassword"); pjsip_auth_clt_set_credentials( &dlg->auth_sess, 1, cred); } * */ /* Get the SDP body to be put in the outgoing INVITE, by asking * media endpoint to create one for us. */ status = pjmedia_endpt_create_sdp( g_med_endpt, /* the media endpt */ dlg->pool, /* pool. */ MAX_MEDIA_CNT, /* # of streams */ g_sock_info, /* RTP sock info */ &local_sdp); /* the SDP result */ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create the INVITE session, and pass the SDP returned earlier * as the session's initial capability. */ status = pjsip_inv_create_uac( dlg, local_sdp, 0, &g_inv); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* If we want the initial INVITE to travel to specific SIP proxies, * then we should put the initial dialog's route set here. The final * route set will be updated once a dialog has been established. * To set the dialog's initial route set, we do it with something * like this: * { pjsip_route_hdr route_set; pjsip_route_hdr *route; const pj_str_t hname = { "Route", 5 }; char *uri = "sip:proxy.server;lr"; pj_list_init(&route_set); route = pjsip_parse_hdr( dlg->pool, &hname, uri, strlen(uri), NULL); PJ_ASSERT_RETURN(route != NULL, 1); pj_list_push_back(&route_set, route); pjsip_dlg_set_route_set(dlg, &route_set); } * * Note that Route URI SHOULD have an ";lr" parameter! */ /* Create initial INVITE request. * This INVITE request will contain a perfectly good request and * an SDP body as well. */ status = pjsip_inv_invite(g_inv, &tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Send initial INVITE request. * From now on, the invite session's state will be reported to us * via the invite session callbacks. */ status = pjsip_inv_send_msg(g_inv, tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } else { /* No URL to make call to */ PJ_LOG(3,(THIS_FILE, "Ready to accept incoming calls...")); } /* Loop until one call is completed */ for (;!g_complete;) { pj_time_val timeout = {0, 10}; pjsip_endpt_handle_events(g_endpt, &timeout); } /* On exit, dump current memory usage: */ dump_pool_usage(THIS_FILE, &cp); /* Destroy audio ports. Destroy the audio port first * before the stream since the audio port has threads * that get/put frames to the stream. */ if (g_snd_port) pjmedia_snd_port_destroy(g_snd_port); #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) /* Destroy video ports */ if (g_vid_capturer) pjmedia_vid_port_destroy(g_vid_capturer); if (g_vid_renderer) pjmedia_vid_port_destroy(g_vid_renderer); #endif /* Destroy streams */ if (g_med_stream) pjmedia_stream_destroy(g_med_stream); #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) if (g_med_vstream) pjmedia_vid_stream_destroy(g_med_vstream); /* Deinit ffmpeg codec */ # if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0 pjmedia_codec_ffmpeg_vid_deinit(); # endif #endif /* Destroy media transports */ for (i = 0; i < MAX_MEDIA_CNT; ++i) { if (g_med_transport[i]) pjmedia_transport_close(g_med_transport[i]); } /* Deinit pjmedia endpoint */ if (g_med_endpt) pjmedia_endpt_destroy(g_med_endpt); /* Deinit pjsip endpoint */ if (g_endpt) pjsip_endpt_destroy(g_endpt); /* Release pool */ if (pool) pj_pool_release(pool); return 0; }
/* Get IP interface for sending to the specified destination */ PJ_DEF(pj_status_t) pj_getipinterface(int af, const pj_str_t *dst, pj_sockaddr *itf_addr, pj_bool_t allow_resolve, pj_sockaddr *p_dst_addr) { pj_sockaddr dst_addr; pj_sock_t fd; int len; pj_uint8_t zero[64]; pj_status_t status; pj_sockaddr_init(af, &dst_addr, NULL, 53); status = pj_inet_pton(af, dst, pj_sockaddr_get_addr(&dst_addr)); if (status != PJ_SUCCESS) { /* "dst" is not an IP address. */ if (allow_resolve) { status = pj_sockaddr_init(af, &dst_addr, dst, 53); } else { pj_str_t cp; if (af == PJ_AF_INET) { cp = pj_str("1.1.1.1"); } else { cp = pj_str("1::1"); } status = pj_sockaddr_init(af, &dst_addr, &cp, 53); } if (status != PJ_SUCCESS) return status; } /* Create UDP socket and connect() to the destination IP */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd); if (status != PJ_SUCCESS) { return status; } status = pj_sock_connect(fd, &dst_addr, pj_sockaddr_get_len(&dst_addr)); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } len = sizeof(*itf_addr); status = pj_sock_getsockname(fd, itf_addr, &len); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } pj_sock_close(fd); /* Check that the address returned is not zero */ pj_bzero(zero, sizeof(zero)); if (pj_memcmp(pj_sockaddr_get_addr(itf_addr), zero, pj_sockaddr_get_addr_len(itf_addr))==0) { return PJ_ENOTFOUND; } if (p_dst_addr) *p_dst_addr = dst_addr; return PJ_SUCCESS; }
/* Raw TCP socket try to connect to SSL socket server, once * connection established, it will just do nothing, SSL socket * server should be able to close the connection after specified * timeout period (set ms_timeout to 0 to disable timer). */ static int client_non_ssl(unsigned ms_timeout) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_timer_heap_t *timer = NULL; pj_ssl_sock_t *ssock_serv = NULL; pj_activesock_t *asock_cli = NULL; pj_activesock_cb asock_cb = { 0 }; pj_sock_t sock = PJ_INVALID_SOCKET; pj_ssl_sock_param param; struct test_state state_serv = { 0 }; struct test_state state_cli = { 0 }; pj_sockaddr listen_addr; pj_ssl_cert_t *cert = NULL; pj_status_t status; pool = pj_pool_create(mem, "ssl_accept_raw_tcp", 256, 256, NULL); status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } status = pj_timer_heap_create(pool, 4, &timer); if (status != PJ_SUCCESS) { goto on_return; } /* Set cert */ { pj_str_t tmp1, tmp2, tmp3, tmp4; status = pj_ssl_cert_load_from_files(pool, pj_strset2(&tmp1, (char*)CERT_CA_FILE), pj_strset2(&tmp2, (char*)CERT_FILE), pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), &cert); if (status != PJ_SUCCESS) { goto on_return; } } pj_ssl_sock_param_default(¶m); param.cb.on_accept_complete = &ssl_on_accept_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.timer_heap = timer; param.timeout.sec = 0; param.timeout.msec = ms_timeout; pj_time_val_normalize(¶m.timeout); /* SERVER */ param.user_data = &state_serv; state_serv.pool = pool; state_serv.is_server = PJ_TRUE; state_serv.is_verbose = PJ_TRUE; status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); if (status != PJ_SUCCESS) { goto on_return; } status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } /* Init bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } status = pj_ssl_sock_start_accept(ssock_serv, pool, &listen_addr, pj_sockaddr_get_len(&listen_addr)); if (status != PJ_SUCCESS) { goto on_return; } /* Update listener address */ { pj_ssl_sock_info info; pj_ssl_sock_get_info(ssock_serv, &info); pj_sockaddr_cp(&listen_addr, &info.local_addr); } /* CLIENT */ state_cli.pool = pool; status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) { goto on_return; } asock_cb.on_connect_complete = &asock_on_connect_complete; asock_cb.on_data_read = &asock_on_data_read; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL, ioqueue, &asock_cb, &state_cli, &asock_cli); if (status != PJ_SUCCESS) { goto on_return; } status = pj_activesock_start_connect(asock_cli, pool, (pj_sockaddr_t*)&listen_addr, pj_sockaddr_get_len(&listen_addr)); if (status == PJ_SUCCESS) { asock_on_connect_complete(asock_cli, PJ_SUCCESS); } else if (status == PJ_EPENDING) { status = PJ_SUCCESS; } else { goto on_return; } /* Wait until everything has been sent/received or error */ while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer, &delay); #endif } if (state_serv.err || state_cli.err) { if (state_serv.err != PJ_SUCCESS) status = state_serv.err; else status = state_cli.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); on_return: if (ssock_serv) pj_ssl_sock_close(ssock_serv); if (asock_cli && !state_cli.err && !state_cli.done) pj_activesock_close(asock_cli); if (timer) pj_timer_heap_destroy(timer); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }
/* * Create stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { pjmedia_codec_mgr *mgr; const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; int rem_af, local_af; pj_sockaddr local_addr; pjmedia_sdp_rtpmap *rtpmap; unsigned i, pt, fmti; pj_status_t status; /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Get codec manager. */ mgr = pjmedia_endpt_get_codec_mgr(endpt); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Reset: */ pj_bzero(si, sizeof(*si)); #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR /* Set default RTCP XR enabled/disabled */ si->rtcp_xr_enabled = PJ_TRUE; #endif /* Media type: */ if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) { si->type = PJMEDIA_TYPE_AUDIO; } else if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0) { si->type = PJMEDIA_TYPE_VIDEO; } else { si->type = PJMEDIA_TYPE_UNKNOWN; /* Avoid rejecting call because of unrecognized media, * just return PJ_SUCCESS, this media will be deactivated later. */ //return PJMEDIA_EINVALIMEDIATYPE; return PJ_SUCCESS; } /* Transport protocol */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, &local_m->desc.transport); if (status != PJ_SUCCESS) return PJMEDIA_SDPNEG_EINVANSTP; if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_AVP; } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; } else { si->proto = PJMEDIA_TP_PROTO_UNKNOWN; return PJ_SUCCESS; } /* Check address family in remote SDP */ rem_af = pj_AF_UNSPEC(); if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { rem_af = pj_AF_INET(); } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { rem_af = pj_AF_INET6(); } } if (rem_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_EAFNOTSUP; } /* Set remote address: */ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Check address family of local info */ local_af = pj_AF_UNSPEC(); if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { local_af = pj_AF_INET(); } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { local_af = pj_AF_INET6(); } } if (local_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_SUCCESS; } /* Set remote address: */ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, local_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Local and remote address family must match */ if (local_af != rem_af) return PJ_EAFNOTSUP; /* Media direction: */ if (local_m->desc.port == 0 || pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* No need to do anything else if stream is rejected */ if (local_m->desc.port == 0) { return PJ_SUCCESS; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), pj_sockaddr_get_addr(&si->rem_addr), pj_sockaddr_get_addr_len(&si->rem_addr)); } } } if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); } /* Get the payload number for receive channel. */ /* Previously we used to rely on fmt[0] being the selected codec, but some UA sends telephone-event as fmt[0] and this would cause assert failure below. Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch. // And codec must be numeric! if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || !pj_isdigit(*rem_m->desc.fmt[0].ptr)) { return PJMEDIA_EINVALIDPT; } pt = pj_strtoul(&local_m->desc.fmt[0]); pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS); */ /* This is to suppress MSVC warning about uninitialized var */ pt = 0; /* Find the first codec which is not telephone-event */ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) return PJMEDIA_EINVALIDPT; pt = pj_strtoul(&local_m->desc.fmt[fmti]); if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS ) break; } if ( fmti >= local_m->desc.fmt_count ) return PJMEDIA_EINVALIDPT; /* Get codec info. * For static payload types, get the info from codec manager. * For dynamic payload types, MUST get the rtpmap. */ if (pt < 96) { pj_bool_t has_rtpmap; rtpmap = NULL; has_rtpmap = PJ_TRUE; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) { has_rtpmap = PJ_FALSE; } if (attr != NULL) { status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) has_rtpmap = PJ_FALSE; } /* Build codec format info: */ if (has_rtpmap) { si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) /* The session info should have the actual clock rate, because * this info is used for calculationg buffer size, etc in stream */ if (si->fmt.pt == PJMEDIA_RTP_PT_G722) si->fmt.clock_rate = 16000; #endif /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } } else { const pjmedia_codec_info *p_info; status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); } /* For static payload type, pt's are symetric */ si->tx_pt = pt; } else { attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) return status; /* Build codec format info: */ si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ si->tx_pt = 0xFFFF; for (i=0; i<rem_m->desc.fmt_count; ++i) { unsigned rpt; pjmedia_sdp_attr *r_attr; pjmedia_sdp_rtpmap r_rtpmap; rpt = pj_strtoul(&rem_m->desc.fmt[i]); if (rpt < 96) continue; r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP, &rem_m->desc.fmt[i]); if (!r_attr) continue; if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS) continue; if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) && rtpmap->clock_rate == r_rtpmap.clock_rate) { /* Found matched codec. */ si->tx_pt = rpt; break; } } if (si->tx_pt == 0xFFFF) return PJMEDIA_EMISSINGRTPMAP; } /* Now that we have codec info, get the codec param. */ si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param); status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param); /* Get remote fmtp for our encoder. */ parse_fmtp(pool, rem_m, si->tx_pt, &si->param->setting.enc_fmtp); /* Get local fmtp for our decoder. */ parse_fmtp(pool, local_m, si->fmt.pt, &si->param->setting.dec_fmtp); /* Get remote maxptime for our encoder. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "maxptime", NULL); if (attr) { pj_str_t tmp_val = attr->value; pj_strltrim(&tmp_val); si->tx_maxptime = pj_strtoul(&tmp_val); } /* When direction is NONE (it means SDP negotiation has failed) we don't * need to return a failure here, as returning failure will cause * the whole SDP to be rejected. See ticket #: * http:// * * Thanks Alain Totouom */ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) return status; /* Get incomming payload type for telephone-events */ si->rx_event_pt = -1; for (i=0; i<local_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = local_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->rx_event_pt = pj_strtoul(&r.pt); break; } } /* Get outgoing payload type for telephone-events */ si->tx_event_pt = -1; for (i=0; i<rem_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->tx_event_pt = pj_strtoul(&r.pt); break; } } /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; return PJ_SUCCESS; }
/* * Create stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { const pj_str_t STR_INACTIVE = { "inactive", 8 }; const pj_str_t STR_SENDONLY = { "sendonly", 8 }; const pj_str_t STR_RECVONLY = { "recvonly", 8 }; pjmedia_codec_mgr *mgr; const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; int rem_af, local_af; pj_sockaddr local_addr; pj_status_t status; /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Media type must be audio */ if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) != 0) return PJMEDIA_EINVALIMEDIATYPE; /* Get codec manager. */ mgr = pjmedia_endpt_get_codec_mgr(endpt); /* Reset: */ pj_bzero(si, sizeof(*si)); #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR /* Set default RTCP XR enabled/disabled */ si->rtcp_xr_enabled = PJ_TRUE; #endif /* Media type: */ si->type = PJMEDIA_TYPE_AUDIO; /* Transport protocol */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, &local_m->desc.transport); if (status != PJ_SUCCESS) return PJMEDIA_SDPNEG_EINVANSTP; if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_AVP; } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; } else { si->proto = PJMEDIA_TP_PROTO_UNKNOWN; return PJ_SUCCESS; } /* Check address family in remote SDP */ rem_af = pj_AF_UNSPEC(); if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { rem_af = pj_AF_INET(); } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { rem_af = pj_AF_INET6(); } } if (rem_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_EAFNOTSUP; } /* Set remote address: */ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Check address family of local info */ local_af = pj_AF_UNSPEC(); if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { local_af = pj_AF_INET(); } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { local_af = pj_AF_INET6(); } } if (local_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_SUCCESS; } /* Set remote address: */ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, local_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Local and remote address family must match */ if (local_af != rem_af) return PJ_EAFNOTSUP; /* Media direction: */ if (local_m->desc.port == 0 || pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* No need to do anything else if stream is rejected */ if (local_m->desc.port == 0) { return PJ_SUCCESS; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), pj_sockaddr_get_addr(&si->rem_addr), pj_sockaddr_get_addr_len(&si->rem_addr)); } } } if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); } /* Get the payload number for receive channel. */ /* Previously we used to rely on fmt[0] being the selected codec, but some UA sends telephone-event as fmt[0] and this would cause assert failure below. Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch. // And codec must be numeric! if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || !pj_isdigit(*rem_m->desc.fmt[0].ptr)) { return PJMEDIA_EINVALIDPT; } pt = pj_strtoul(&local_m->desc.fmt[0]); pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS); */ /* Get codec info and param */ status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m); /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; return status; }
static int https_client_test(unsigned ms_timeout) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_timer_heap_t *timer = NULL; pj_ssl_sock_t *ssock = NULL; pj_ssl_sock_param param; pj_status_t status; struct test_state state = {0}; pj_sockaddr local_addr, rem_addr; pj_str_t tmp_st; pool = pj_pool_create(mem, "https_get", 256, 256, NULL); status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } status = pj_timer_heap_create(pool, 4, &timer); if (status != PJ_SUCCESS) { goto on_return; } state.pool = pool; state.send_str = HTTP_REQ; state.send_str_len = pj_ansi_strlen(state.send_str); state.is_verbose = PJ_TRUE; pj_ssl_sock_param_default(¶m); param.cb.on_connect_complete = &ssl_on_connect_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.user_data = &state; param.server_name = pj_str((char*)HTTP_SERVER_ADDR); param.timer_heap = timer; param.timeout.sec = 0; param.timeout.msec = ms_timeout; param.proto = PJ_SSL_SOCK_PROTO_SSL23; pj_time_val_normalize(¶m.timeout); status = pj_ssl_sock_create(pool, ¶m, &ssock); if (status != PJ_SUCCESS) { goto on_return; } pj_sockaddr_init(PJ_AF_INET, &local_addr, pj_strset2(&tmp_st, "0.0.0.0"), 0); pj_sockaddr_init(PJ_AF_INET, &rem_addr, pj_strset2(&tmp_st, HTTP_SERVER_ADDR), HTTP_SERVER_PORT); status = pj_ssl_sock_start_connect(ssock, pool, &local_addr, &rem_addr, sizeof(rem_addr)); if (status == PJ_SUCCESS) { ssl_on_connect_complete(ssock, PJ_SUCCESS); } else if (status == PJ_EPENDING) { status = PJ_SUCCESS; } else { goto on_return; } /* Wait until everything has been sent/received */ while (state.err == PJ_SUCCESS && !state.done) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer, &delay); #endif } if (state.err) { status = state.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state.sent, state.recv)); on_return: if (ssock && !state.err && !state.done) pj_ssl_sock_close(ssock); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (timer) pj_timer_heap_destroy(timer); if (pool) pj_pool_release(pool); return status; }
/* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( pjsip_endpoint *endpt, const pjsip_tcp_transport_cfg *cfg, pjsip_tpfactory **p_factory ) { pj_pool_t *pool; pj_sock_t sock = PJ_INVALID_SOCKET; struct tcp_listener *listener; pj_activesock_cfg asock_cfg; pj_activesock_cb listener_cb; pj_sockaddr *listener_addr; int addr_len; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ if (cfg->addr_name.host.slen) { pj_sockaddr tmp; status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, (pj_uint16_t)cfg->addr_name.port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || (cfg->af==pj_AF_INET() && tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener); listener->factory.pool = pool; listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP : PJSIP_TRANSPORT_TCP6; listener->factory.type_name = (char*) pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = pjsip_transport_get_flag_from_type(listener->factory.type); listener->qos_type = cfg->qos_type; pj_memcpy(&listener->qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); pj_ansi_strcpy(listener->factory.obj_name, "tcplis"); if (listener->factory.type==PJSIP_TRANSPORT_TCP6) pj_ansi_strcat(listener->factory.obj_name, "6"); status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; /* Create socket */ status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, 2, listener->factory.obj_name, "SIP TCP listener socket"); /* Bind address may be different than factory.local_addr because * factory.local_addr will be resolved below. */ pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr); /* Bind socket */ listener_addr = &listener->factory.local_addr; pj_sockaddr_cp(listener_addr, &cfg->bind_addr); status = pj_sock_bind(sock, listener_addr, pj_sockaddr_get_len(listener_addr)); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the bound address */ addr_len = pj_sockaddr_get_len(listener_addr); status = pj_sock_getsockname(sock, listener_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (cfg->addr_name.host.slen) { /* Copy the address */ listener->factory.addr_name = cfg->addr_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &cfg->addr_name.host); listener->factory.addr_name.port = cfg->addr_name.port; } else { /* No published address is given, use the bound address */ /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; status = pj_gethostip(listener->bound_addr.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_sockaddr_copy_addr(listener_addr, &hostip); } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tcplis:%d", listener->factory.addr_name.port); /* Start listening to the address */ status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG); if (status != PJ_SUCCESS) goto on_error; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); if (cfg->async_cnt > MAX_ASYNC_CNT) asock_cfg.async_cnt = MAX_ASYNC_CNT; else asock_cfg.async_cnt = cfg->async_cnt; pj_bzero(&listener_cb, sizeof(listener_cb)); listener_cb.on_accept_complete = &on_accept_complete; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, pjsip_endpt_get_ioqueue(endpt), &listener_cb, listener, &listener->asock); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport = lis_create_transport; listener->factory.destroy = lis_destroy; listener->is_registered = PJ_TRUE; status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, &listener->factory); if (status != PJ_SUCCESS) { listener->is_registered = PJ_FALSE; goto on_error; } /* Start pending accept() operations */ status = pj_activesock_start_accept(listener->asock, pool); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(4,(listener->factory.obj_name, "SIP TCP listener ready for incoming connections at %.*s:%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port)); /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET) pj_sock_close(sock); lis_destroy(&listener->factory); return status; }
static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher, pj_bool_t req_client_cert, pj_bool_t client_provide_cert) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_ssl_sock_t *ssock_serv = NULL; pj_ssl_sock_t *ssock_cli = NULL; pj_ssl_sock_param param; struct test_state state_serv = { 0 }; struct test_state state_cli = { 0 }; pj_sockaddr addr, listen_addr; pj_ssl_cipher ciphers[1]; pj_ssl_cert_t *cert = NULL; pj_status_t status; pool = pj_pool_create(mem, "ssl_echo", 256, 256, NULL); status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } pj_ssl_sock_param_default(¶m); param.cb.on_accept_complete = &ssl_on_accept_complete; param.cb.on_connect_complete = &ssl_on_connect_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.ciphers = ciphers; /* Init default bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } /* === SERVER === */ param.proto = srv_proto; param.user_data = &state_serv; param.ciphers_num = (srv_cipher == -1)? 0 : 1; param.require_client_cert = req_client_cert; ciphers[0] = srv_cipher; state_serv.pool = pool; state_serv.echo = PJ_TRUE; state_serv.is_server = PJ_TRUE; state_serv.is_verbose = PJ_TRUE; status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); if (status != PJ_SUCCESS) { goto on_return; } /* Set server cert */ { pj_str_t tmp1, tmp2, tmp3, tmp4; status = pj_ssl_cert_load_from_files(pool, pj_strset2(&tmp1, (char*)CERT_CA_FILE), pj_strset2(&tmp2, (char*)CERT_FILE), pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), &cert); if (status != PJ_SUCCESS) { goto on_return; } status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } } status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr)); if (status != PJ_SUCCESS) { goto on_return; } /* Get listener address */ { pj_ssl_sock_info info; pj_ssl_sock_get_info(ssock_serv, &info); pj_sockaddr_cp(&listen_addr, &info.local_addr); } /* === CLIENT === */ param.proto = cli_proto; param.user_data = &state_cli; param.ciphers_num = (cli_cipher == -1)? 0 : 1; ciphers[0] = cli_cipher; state_cli.pool = pool; state_cli.check_echo = PJ_TRUE; state_cli.is_verbose = PJ_TRUE; { pj_time_val now; pj_gettimeofday(&now); pj_srand((unsigned)now.sec); state_cli.send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024; } state_cli.send_str = pj_pool_alloc(pool, state_cli.send_str_len); { unsigned i; for (i = 0; i < state_cli.send_str_len; ++i) state_cli.send_str[i] = (char)(pj_rand() % 256); } status = pj_ssl_sock_create(pool, ¶m, &ssock_cli); if (status != PJ_SUCCESS) { goto on_return; } /* Set cert for client */ { if (!client_provide_cert) { pj_str_t tmp1, tmp2; pj_strset2(&tmp1, (char*)CERT_CA_FILE); pj_strset2(&tmp2, NULL); status = pj_ssl_cert_load_from_files(pool, &tmp1, &tmp2, &tmp2, &tmp2, &cert); if (status != PJ_SUCCESS) { goto on_return; } } status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } } status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr)); if (status == PJ_SUCCESS) { ssl_on_connect_complete(ssock_cli, PJ_SUCCESS); } else if (status == PJ_EPENDING) { status = PJ_SUCCESS; } else { goto on_return; } /* Wait until everything has been sent/received or error */ while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); #endif } /* Clean up sockets */ { pj_time_val delay = {0, 100}; while (pj_ioqueue_poll(ioqueue, &delay) > 0); } if (state_serv.err || state_cli.err) { if (state_serv.err != PJ_SUCCESS) status = state_serv.err; else status = state_cli.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state_cli.sent, state_cli.recv)); on_return: if (ssock_serv) pj_ssl_sock_close(ssock_serv); if (ssock_cli && !state_cli.err && !state_cli.done) pj_ssl_sock_close(ssock_cli); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }
/** * Create UDP stream transport. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_create3(pjmedia_endpt *endpt, int af, const char *name, const pj_str_t *addr, int port, unsigned options, pjmedia_transport **p_tp) { pjmedia_sock_info si; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && port && p_tp, PJ_EINVAL); pj_bzero(&si, sizeof(pjmedia_sock_info)); si.rtp_sock = si.rtcp_sock = PJ_INVALID_SOCKET; /* Create RTP socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &si.rtp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind RTP socket */ status = pj_sockaddr_init(af, &si.rtp_addr_name, addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; status = pj_sock_bind(si.rtp_sock, &si.rtp_addr_name, pj_sockaddr_get_len(&si.rtp_addr_name)); if (status != PJ_SUCCESS) goto on_error; /* Create RTCP socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &si.rtcp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind RTCP socket */ status = pj_sockaddr_init(af, &si.rtcp_addr_name, addr, (pj_uint16_t)(port+1)); if (status != PJ_SUCCESS) goto on_error; status = pj_sock_bind(si.rtcp_sock, &si.rtcp_addr_name, pj_sockaddr_get_len(&si.rtcp_addr_name)); if (status != PJ_SUCCESS) goto on_error; /* Create UDP transport by attaching socket info */ return pjmedia_transport_udp_attach( endpt, name, &si, options, p_tp); on_error: if (si.rtp_sock != PJ_INVALID_SOCKET) pj_sock_close(si.rtp_sock); if (si.rtcp_sock != PJ_INVALID_SOCKET) pj_sock_close(si.rtcp_sock); return status; }
/* SSL socket try to connect to raw TCP socket server, once * connection established, SSL socket will try to perform SSL * handshake. SSL client socket should be able to close the * connection after specified timeout period (set ms_timeout to * 0 to disable timer). */ static int server_non_ssl(unsigned ms_timeout) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_timer_heap_t *timer = NULL; pj_activesock_t *asock_serv = NULL; pj_ssl_sock_t *ssock_cli = NULL; pj_activesock_cb asock_cb = { 0 }; pj_sock_t sock = PJ_INVALID_SOCKET; pj_ssl_sock_param param; struct test_state state_serv = { 0 }; struct test_state state_cli = { 0 }; pj_sockaddr addr, listen_addr; pj_status_t status; pool = pj_pool_create(mem, "ssl_connect_raw_tcp", 256, 256, NULL); status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } status = pj_timer_heap_create(pool, 4, &timer); if (status != PJ_SUCCESS) { goto on_return; } /* SERVER */ state_serv.pool = pool; state_serv.ioqueue = ioqueue; status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) { goto on_return; } /* Init bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } status = pj_sock_bind(sock, (pj_sockaddr_t*)&listen_addr, pj_sockaddr_get_len((pj_sockaddr_t*)&listen_addr)); if (status != PJ_SUCCESS) { goto on_return; } status = pj_sock_listen(sock, PJ_SOMAXCONN); if (status != PJ_SUCCESS) { goto on_return; } asock_cb.on_accept_complete = &asock_on_accept_complete; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL, ioqueue, &asock_cb, &state_serv, &asock_serv); if (status != PJ_SUCCESS) { goto on_return; } status = pj_activesock_start_accept(asock_serv, pool); if (status != PJ_SUCCESS) goto on_return; /* Update listener address */ { int addr_len; addr_len = sizeof(listen_addr); pj_sock_getsockname(sock, (pj_sockaddr_t*)&listen_addr, &addr_len); } /* CLIENT */ pj_ssl_sock_param_default(¶m); param.cb.on_connect_complete = &ssl_on_connect_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.timer_heap = timer; param.timeout.sec = 0; param.timeout.msec = ms_timeout; pj_time_val_normalize(¶m.timeout); param.user_data = &state_cli; state_cli.pool = pool; state_cli.is_server = PJ_FALSE; state_cli.is_verbose = PJ_TRUE; status = pj_ssl_sock_create(pool, ¶m, &ssock_cli); if (status != PJ_SUCCESS) { goto on_return; } /* Init default bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } status = pj_ssl_sock_start_connect(ssock_cli, pool, (pj_sockaddr_t*)&addr, (pj_sockaddr_t*)&listen_addr, pj_sockaddr_get_len(&listen_addr)); if (status != PJ_EPENDING) { goto on_return; } /* Wait until everything has been sent/received or error */ while ((!state_serv.err && !state_serv.done) || (!state_cli.err && !state_cli.done)) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer, &delay); #endif } if (state_serv.err || state_cli.err) { if (state_cli.err != PJ_SUCCESS) status = state_cli.err; else status = state_serv.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); on_return: if (asock_serv) pj_activesock_close(asock_serv); if (ssock_cli && !state_cli.err && !state_cli.done) pj_ssl_sock_close(ssock_cli); if (timer) pj_timer_heap_destroy(timer); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }
/* * This is the main function for performing server resolution. */ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target, void *token, pjsip_resolver_callback *cb) { pjsip_server_addresses svr_addr; pj_status_t status = PJ_SUCCESS; int ip_addr_ver; struct query *query; pjsip_transport_type_e type = target->type; /* 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 (ip_addr_ver || (target->addr.port != 0)) { #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } else { /* No type or explicit port is specified, and the address is * not IP address. * In this case, full NAPTR resolution must be performed. * But we don't support it (yet). */ #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { 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 (ip_addr_ver || resolver->res == NULL) { char addr_str[PJ_INET6_ADDRSTRLEN+10]; pj_uint16_t srv_port; if (ip_addr_ver != 0) { /* Target is an IP address, no need to resolve */ if (ip_addr_ver == 4) { pj_sockaddr_init(pj_AF_INET(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_aton(&target->addr.host, &svr_addr.entry[0].addr.ipv4.sin_addr); } else { pj_sockaddr_init(pj_AF_INET6(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_pton(pj_AF_INET6(), &target->addr.host, &svr_addr.entry[0].addr.ipv6.sin6_addr); } } else { pj_addrinfo ai; unsigned count; int af; PJ_LOG(5,(THIS_FILE, "DNS resolver not available, target '%.*s:%d' type=%s " "will be resolved with getaddrinfo()", target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type))); 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)); } /* Set the port number */ if (target->addr.port == 0) { srv_port = (pj_uint16_t) pjsip_transport_get_default_port_for_type(type); } else { srv_port = (pj_uint16_t)target->addr.port; } pj_sockaddr_set_port(&svr_addr.entry[0].addr, srv_port); /* Call the callback. */ PJ_LOG(5,(THIS_FILE, "Target '%.*s:%d' type=%s resolved to " "'%s' type=%s (%s)", (int)target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type), pj_sockaddr_print(&svr_addr.entry[0].addr, addr_str, sizeof(addr_str), 3), pjsip_transport_get_type_name(type), pjsip_transport_get_type_desc(type))); svr_addr.count = 1; svr_addr.entry[0].priority = 0; svr_addr.entry[0].weight = 0; svr_addr.entry[0].type = type; svr_addr.entry[0].addr_len = pj_sockaddr_get_len(&svr_addr.entry[0].addr); (*cb)(status, token, &svr_addr); /* Done. */ return; } /* Target is not an IP address so we need to resolve it. */ #if PJSIP_HAS_RESOLVER /* Build the query state */ query = PJ_POOL_ZALLOC_T(pool, struct query); query->objname = THIS_FILE; query->token = token; query->cb = cb; query->req.target = *target; pj_strdup(pool, &query->req.target.addr.host, &target->addr.host); /* If port is not specified, start with SRV resolution * (should be with NAPTR, but we'll do that later) */ PJ_TODO(SUPPORT_DNS_NAPTR); /* Build dummy NAPTR entry */ query->naptr_cnt = 1; pj_bzero(&query->naptr[0], sizeof(query->naptr[0])); query->naptr[0].order = 0; query->naptr[0].pref = 0; query->naptr[0].type = type; pj_strdup(pool, &query->naptr[0].name, &target->addr.host); /* Start DNS SRV or A resolution, depending on whether port is specified */ if (target->addr.port == 0) { query->query_type = PJ_DNS_TYPE_SRV; query->req.def_port = 5060; if (type == PJSIP_TRANSPORT_TLS) { query->naptr[0].res_type = pj_str("_sips._tcp."); query->req.def_port = 5061; } else if (type == PJSIP_TRANSPORT_TCP) query->naptr[0].res_type = pj_str("_sip._tcp."); else if (type == PJSIP_TRANSPORT_UDP) query->naptr[0].res_type = pj_str("_sip._udp."); else { pj_assert(!"Unknown transport type"); query->naptr[0].res_type = pj_str("_sip._udp."); } } else { /* Otherwise if port is specified, start with A (or AAAA) host * resolution */ query->query_type = PJ_DNS_TYPE_A; query->naptr[0].res_type.slen = 0; query->req.def_port = target->addr.port; } /* Start the asynchronous query */ PJ_LOG(5, (query->objname, "Starting async DNS %s query: target=%.*s%.*s, transport=%s, " "port=%d", pj_dns_get_type_name(query->query_type), (int)query->naptr[0].res_type.slen, query->naptr[0].res_type.ptr, (int)query->naptr[0].name.slen, query->naptr[0].name.ptr, pjsip_transport_get_type_name(target->type), target->addr.port)); if (query->query_type == PJ_DNS_TYPE_SRV) { unsigned option = PJ_TRUE; if (type & PJSIP_TRANSPORT_IPV6) { option |= PJ_DNS_SRV_FALLBACK_GETADDRINFO_IPV6; } else { option |= PJ_DNS_SRV_FALLBACK_GETADDRINFO_IPV4; } status = pj_dns_srv_resolve(&query->naptr[0].name, &query->naptr[0].res_type, query->req.def_port, pool, resolver->res, option, query, &srv_resolver_cb, NULL); } else if (query->query_type == PJ_DNS_TYPE_A) { status = pj_dns_resolver_start_query(resolver->res, &query->naptr[0].name, PJ_DNS_TYPE_A, 0, &dns_a_callback, query, &query->object); } else { pj_assert(!"Unexpected"); status = PJ_EBUG; } if (status != PJ_SUCCESS) goto on_error; return; #else /* PJSIP_HAS_RESOLVER */ PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(query); PJ_UNUSED_ARG(srv_name); #endif /* PJSIP_HAS_RESOLVER */ on_error: if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; PJ_LOG(4,(THIS_FILE, "Failed to resolve '%.*s'. Err=%d (%s)", (int)target->addr.host.slen, target->addr.host.ptr, status, pj_strerror(status,errmsg,sizeof(errmsg)).ptr)); (*cb)(status, token, NULL); return; } }
/* * Create relay. */ static pj_status_t create_relay(pj_turn_srv *srv, pj_turn_allocation *alloc, const pj_stun_msg *msg, const alloc_request *req, pj_turn_relay_res *relay) { enum { RETRY = 40 }; pj_pool_t *pool = alloc->pool; int retry, retry_max, sock_type; pj_ioqueue_callback icb; int af, namelen; pj_stun_string_attr *sa; pj_status_t status; pj_bzero(relay, sizeof(*relay)); relay->allocation = alloc; relay->tp.sock = PJ_INVALID_SOCKET; /* TODO: get the requested address family from somewhere */ af = alloc->transport->listener->addr.addr.sa_family; /* Save realm */ sa = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP); pj_strdup(pool, &relay->realm, &sa->value); /* Save username */ sa = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP); pj_strdup(pool, &relay->user, &sa->value); /* Lifetime and timeout */ relay->lifetime = req->lifetime; pj_timer_entry_init(&relay->timer, TIMER_ID_NONE, relay, &relay_timeout_cb); resched_timeout(alloc); /* Transport type */ relay->hkey.tp_type = req->tp_type; /* Create the socket */ if (req->tp_type == PJ_TURN_TP_UDP) { sock_type = pj_SOCK_DGRAM(); } else if (req->tp_type == PJ_TURN_TP_TCP) { sock_type = pj_SOCK_STREAM(); } else { pj_assert(!"Unknown transport"); return PJ_EINVALIDOP; } status = pj_sock_socket(af, sock_type, 0, &relay->tp.sock); if (status != PJ_SUCCESS) { pj_bzero(relay, sizeof(*relay)); return status; } /* Find suitable port for this allocation */ if (req->rpp_port) { retry_max = 1; } else { retry_max = RETRY; } for (retry=0; retry<retry_max; ++retry) { pj_uint16_t port; pj_sockaddr bound_addr; pj_lock_acquire(srv->core.lock); if (req->rpp_port) { port = (pj_uint16_t) req->rpp_port; } else if (req->tp_type == PJ_TURN_TP_UDP) { port = (pj_uint16_t) srv->ports.next_udp++; if (srv->ports.next_udp > srv->ports.max_udp) srv->ports.next_udp = srv->ports.min_udp; } else if (req->tp_type == PJ_TURN_TP_TCP) { port = (pj_uint16_t) srv->ports.next_tcp++; if (srv->ports.next_tcp > srv->ports.max_tcp) srv->ports.next_tcp = srv->ports.min_tcp; } else { pj_assert(!"Invalid transport"); port = 0; } pj_lock_release(srv->core.lock); pj_sockaddr_init(af, &bound_addr, NULL, port); status = pj_sock_bind(relay->tp.sock, &bound_addr, pj_sockaddr_get_len(&bound_addr)); if (status == PJ_SUCCESS) break; } if (status != PJ_SUCCESS) { /* Unable to allocate port */ PJ_LOG(4,(THIS_FILE, "Unable to allocate relay, giving up: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } /* Init relay key */ namelen = sizeof(relay->hkey.addr); status = pj_sock_getsockname(relay->tp.sock, &relay->hkey.addr, &namelen); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "pj_sock_getsockname() failed: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } if (!pj_sockaddr_has_addr(&relay->hkey.addr)) { pj_sockaddr_copy_addr(&relay->hkey.addr, &alloc->transport->listener->addr); } if (!pj_sockaddr_has_addr(&relay->hkey.addr)) { pj_sockaddr tmp_addr; pj_gethostip(af, &tmp_addr); pj_sockaddr_copy_addr(&relay->hkey.addr, &tmp_addr); } /* Init ioqueue */ pj_bzero(&icb, sizeof(icb)); icb.on_read_complete = &on_rx_from_peer; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, relay->tp.sock, relay, &icb, &relay->tp.key); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "pj_ioqueue_register_sock() failed: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } /* Kick off pending read operation */ pj_ioqueue_op_key_init(&relay->tp.read_key, sizeof(relay->tp.read_key)); on_rx_from_peer(relay->tp.key, &relay->tp.read_key, 0); /* Done */ return PJ_SUCCESS; }
/* * Callback from TURN session when state has changed */ static void turn_on_state(pj_turn_session *sess, pj_turn_state_t old_state, pj_turn_state_t new_state) { pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); pj_status_t status; if (turn_sock == NULL) { /* We've been destroyed */ return; } /* Notify app first */ if (turn_sock->cb.on_state) { (*turn_sock->cb.on_state)(turn_sock, old_state, new_state); } /* Make sure user hasn't destroyed us in the callback */ if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) { pj_turn_session_info info; pj_turn_session_get_info(turn_sock->sess, &info); new_state = info.state; } if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) { /* * Once server has been resolved, initiate outgoing TCP * connection to the server. */ pj_turn_session_info info; char addrtxt[PJ_INET6_ADDRSTRLEN+8]; int sock_type; pj_sock_t sock; pj_activesock_cfg asock_cfg; pj_activesock_cb asock_cb; pj_sockaddr bound_addr, *cfg_bind_addr; pj_uint16_t max_bind_retry; /* Close existing connection, if any. This happens when * we're switching to alternate TURN server when either TCP * connection or ALLOCATE request failed. */ if (turn_sock->active_sock) { pj_activesock_close(turn_sock->active_sock); turn_sock->active_sock = NULL; } /* Get server address from session info */ pj_turn_session_get_info(sess, &info); if (turn_sock->conn_type == PJ_TURN_TP_UDP) sock_type = pj_SOCK_DGRAM(); else sock_type = pj_SOCK_STREAM(); /* Init socket */ status = pj_sock_socket(turn_sock->af, sock_type, 0, &sock); if (status != PJ_SUCCESS) { pj_turn_sock_destroy(turn_sock); return; } /* Bind socket */ cfg_bind_addr = &turn_sock->setting.bound_addr; max_bind_retry = MAX_BIND_RETRY; if (turn_sock->setting.port_range && turn_sock->setting.port_range < max_bind_retry) { max_bind_retry = turn_sock->setting.port_range; } pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0); if (cfg_bind_addr->addr.sa_family == pj_AF_INET() || cfg_bind_addr->addr.sa_family == pj_AF_INET6()) { pj_sockaddr_cp(&bound_addr, cfg_bind_addr); } status = pj_sock_bind_random(sock, &bound_addr, turn_sock->setting.port_range, max_bind_retry); if (status != PJ_SUCCESS) { pj_turn_sock_destroy(turn_sock); return; } /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, turn_sock->setting.qos_type, &turn_sock->setting.qos_params, (turn_sock->setting.qos_ignore_error?2:1), turn_sock->pool->obj_name, NULL); if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) { pj_turn_sock_destroy(turn_sock); return; } /* Apply socket buffer size */ if (turn_sock->setting.so_rcvbuf_size > 0) { unsigned sobuf_size = turn_sock->setting.so_rcvbuf_size; status = pj_sock_setsockopt_sobuf(sock, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, turn_sock->obj_name, status, "Failed setting SO_RCVBUF"); } else { if (sobuf_size < turn_sock->setting.so_rcvbuf_size) { PJ_LOG(4, (turn_sock->obj_name, "Warning! Cannot set SO_RCVBUF as configured," " now=%d, configured=%d", sobuf_size, turn_sock->setting.so_rcvbuf_size)); } else { PJ_LOG(5, (turn_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); } } } if (turn_sock->setting.so_sndbuf_size > 0) { unsigned sobuf_size = turn_sock->setting.so_sndbuf_size; status = pj_sock_setsockopt_sobuf(sock, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, turn_sock->obj_name, status, "Failed setting SO_SNDBUF"); } else { if (sobuf_size < turn_sock->setting.so_sndbuf_size) { PJ_LOG(4, (turn_sock->obj_name, "Warning! Cannot set SO_SNDBUF as configured," " now=%d, configured=%d", sobuf_size, turn_sock->setting.so_sndbuf_size)); } else { PJ_LOG(5, (turn_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); } } } /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.grp_lock = turn_sock->grp_lock; pj_bzero(&asock_cb, sizeof(asock_cb)); asock_cb.on_data_read = &on_data_read; asock_cb.on_connect_complete = &on_connect_complete; status = pj_activesock_create(turn_sock->pool, sock, sock_type, &asock_cfg, turn_sock->cfg.ioqueue, &asock_cb, turn_sock, &turn_sock->active_sock); if (status != PJ_SUCCESS) { pj_turn_sock_destroy(turn_sock); return; } PJ_LOG(5,(turn_sock->pool->obj_name, "Connecting to %s", pj_sockaddr_print(&info.server, addrtxt, sizeof(addrtxt), 3))); /* Initiate non-blocking connect */ #if PJ_HAS_TCP status=pj_activesock_start_connect(turn_sock->active_sock, turn_sock->pool, &info.server, pj_sockaddr_get_len(&info.server)); if (status == PJ_SUCCESS) { on_connect_complete(turn_sock->active_sock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { pj_turn_sock_destroy(turn_sock); return; } #else on_connect_complete(turn_sock->active_sock, PJ_SUCCESS); #endif /* Done for now. Subsequent work will be done in * on_connect_complete() callback. */ } if (new_state >= PJ_TURN_STATE_DESTROYING && turn_sock->sess) { pj_time_val delay = {0, 0}; turn_sock->sess = NULL; pj_turn_session_set_user_data(sess, NULL); pj_timer_heap_cancel_if_active(turn_sock->cfg.timer_heap, &turn_sock->timer, 0); pj_timer_heap_schedule_w_grp_lock(turn_sock->cfg.timer_heap, &turn_sock->timer, &delay, TIMER_DESTROY, turn_sock->grp_lock); } }