/* Called by application to initialize the transport */ static pj_status_t transport_attach( pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)) { struct transport_udp *udp = (struct transport_udp*) tp; const pj_sockaddr *rtcp_addr; /* Validate arguments */ PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL); /* Must not be "attached" to existing application */ PJ_ASSERT_RETURN(!udp->attached, PJ_EINVALIDOP); /* "Attach" the application: */ /* Copy remote RTP address */ pj_memcpy(&udp->rem_rtp_addr, rem_addr, addr_len); /* Copy remote RTP address, if one is specified. */ rtcp_addr = (const pj_sockaddr*) rem_rtcp; if (rtcp_addr && pj_sockaddr_has_addr(rtcp_addr)) { pj_memcpy(&udp->rem_rtcp_addr, rem_rtcp, addr_len); } else { unsigned rtcp_port; /* Otherwise guess the RTCP address from the RTP address */ pj_memcpy(&udp->rem_rtcp_addr, rem_addr, addr_len); rtcp_port = pj_sockaddr_get_port(&udp->rem_rtp_addr) + 1; pj_sockaddr_set_port(&udp->rem_rtcp_addr, (pj_uint16_t)rtcp_port); } /* Save the callbacks */ udp->rtp_cb = rtp_cb; udp->rtcp_cb = rtcp_cb; udp->user_data = user_data; /* Save address length */ udp->addr_len = addr_len; /* Last, mark transport as attached */ udp->attached = PJ_TRUE; /* Reset source RTP & RTCP addresses and counter */ pj_bzero(&udp->rtp_src_addr, sizeof(udp->rtp_src_addr)); pj_bzero(&udp->rtcp_src_addr, sizeof(udp->rtcp_src_addr)); udp->rtp_src_cnt = 0; return PJ_SUCCESS; }
/* * 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; }
/* * 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; }
/* Notification from ioqueue about incoming RTP packet */ static void on_rx_rtp( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); do { void (*cb)(void*,void*,pj_ssize_t); void *user_data; cb = udp->rtp_cb; user_data = udp->user_data; /* Simulate packet lost on RX direction */ if (udp->rx_drop_pct) { if ((pj_rand() % 100) <= (int)udp->rx_drop_pct) { PJ_LOG(5,(udp->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); goto read_next_packet; } } if (udp->attached && cb) (*cb)(user_data, udp->rtp_pkt, bytes_read); /* See if source address of RTP packet is different than the * configured address, and switch RTP remote address to * source packet address after several consecutive packets * have been received. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0) { if (pj_sockaddr_cmp(&udp->rem_rtp_addr, &udp->rtp_src_addr) != 0) { udp->rtp_src_cnt++; if (udp->rtp_src_cnt >= PJMEDIA_RTP_NAT_PROBATION_CNT) { char addr_text[80]; /* Set remote RTP address to source address */ pj_memcpy(&udp->rem_rtp_addr, &udp->rtp_src_addr, sizeof(pj_sockaddr)); /* Reset counter */ udp->rtp_src_cnt = 0; PJ_LOG(4,(udp->base.name, "Remote RTP address switched to %s", pj_sockaddr_print(&udp->rtp_src_addr, addr_text, sizeof(addr_text), 3))); /* Also update remote RTCP address if actual RTCP source * address is not heard yet. */ if (!pj_sockaddr_has_addr(&udp->rtcp_src_addr)) { pj_uint16_t port; pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, sizeof(pj_sockaddr)); pj_sockaddr_copy_addr(&udp->rem_rtcp_addr, &udp->rem_rtp_addr); port = (pj_uint16_t) (pj_sockaddr_get_port(&udp->rem_rtp_addr)+1); pj_sockaddr_set_port(&udp->rem_rtcp_addr, port); pj_memcpy(&udp->rtcp_src_addr, &udp->rem_rtcp_addr, sizeof(pj_sockaddr)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&udp->rtcp_src_addr, addr_text, sizeof(addr_text), 3))); } } } } read_next_packet: bytes_read = sizeof(udp->rtp_pkt); udp->rtp_addrlen = sizeof(udp->rtp_src_addr); status = pj_ioqueue_recvfrom(udp->rtp_key, &udp->rtp_read_op, udp->rtp_pkt, &bytes_read, 0, &udp->rtp_src_addr, &udp->rtp_addrlen); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); }
/* This callback is called by transport manager for the TLS factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_tx_data *tdata, pjsip_transport **p_transport) { struct tls_listener *listener; struct tls_transport *tls; pj_pool_t *pool; pj_ssl_sock_t *ssock; pj_ssl_sock_param ssock_param; pj_sockaddr_in local_addr; pj_str_t remote_name; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in */ PJ_ASSERT_RETURN(rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in), PJ_EINVAL); listener = (struct tls_listener*)factory; pool = pjsip_endpt_create_pool(listener->endpt, "tls", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); /* Get remote host name from tdata */ if (tdata) remote_name = tdata->dest_info.name; else pj_bzero(&remote_name, sizeof(remote_name)); /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); ssock_param.cb.on_connect_complete = &on_connect_complete; ssock_param.cb.on_data_read = &on_data_read; ssock_param.cb.on_data_sent = &on_data_sent; ssock_param.async_cnt = 1; ssock_param.ioqueue = pjsip_endpt_get_ioqueue(listener->endpt); ssock_param.server_name = remote_name; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = NULL; /* pending, must be set later */ ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket * due to verification error */ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; ssock_param.ciphers_num = listener->tls_setting.ciphers_num; ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, sizeof(ssock_param.qos_params)); switch(listener->tls_setting.method) { case PJSIP_TLSV1_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1; break; case PJSIP_SSLV2_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL2; break; case PJSIP_SSLV3_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL3; break; case PJSIP_SSLV23_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL23; break; default: ssock_param.proto = PJ_SSL_SOCK_PROTO_DEFAULT; break; } status = pj_ssl_sock_create(pool, &ssock_param, &ssock); if (status != PJ_SUCCESS) return status; /* Apply SSL certificate */ if (listener->cert) { status = pj_ssl_sock_set_certificate(ssock, pool, listener->cert); if (status != PJ_SUCCESS) return status; } /* Initially set bind address to PJ_INADDR_ANY port 0 */ pj_sockaddr_in_init(&local_addr, NULL, 0); /* Create the transport descriptor */ status = tls_create(listener, pool, ssock, PJ_FALSE, &local_addr, (pj_sockaddr_in*)rem_addr, &remote_name, &tls); if (status != PJ_SUCCESS) return status; /* Set the "pending" SSL socket user data */ pj_ssl_sock_set_user_data(tls->ssock, tls); /* Start asynchronous connect() operation */ tls->has_pending_connect = PJ_TRUE; status = pj_ssl_sock_start_connect(tls->ssock, tls->base.pool, (pj_sockaddr_t*)&local_addr, (pj_sockaddr_t*)rem_addr, addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tls->ssock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { tls_destroy(&tls->base, status); return status; } if (tls->has_pending_connect) { pj_ssl_sock_info info; /* Update local address, just in case local address currently set is * different now that asynchronous connect() is started. */ /* Retrieve the bound address */ status = pj_ssl_sock_get_info(tls->ssock, &info); if (status == PJ_SUCCESS) { pj_uint16_t new_port; new_port = pj_sockaddr_get_port((pj_sockaddr_t*)&info.local_addr); if (pj_sockaddr_has_addr((pj_sockaddr_t*)&info.local_addr)) { /* Update sockaddr */ pj_sockaddr_cp((pj_sockaddr_t*)&tls->base.local_addr, (pj_sockaddr_t*)&info.local_addr); } else if (new_port && new_port != pj_sockaddr_get_port( (pj_sockaddr_t*)&tls->base.local_addr)) { /* Update port only */ pj_sockaddr_set_port(&tls->base.local_addr, new_port); } sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, (pj_sockaddr_in*)&tls->base.local_addr); } PJ_LOG(4,(tls->base.obj_name, "TLS transport %.*s:%d is connecting to %.*s:%d...", (int)tls->base.local_name.host.slen, tls->base.local_name.host.ptr, tls->base.local_name.port, (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port)); } /* Done */ *p_transport = &tls->base; return PJ_SUCCESS; }
/* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_sock *stun_sock; const pj_stun_sockaddr_attr *mapped_attr; pj_stun_sock_op op; pj_bool_t mapped_changed; pj_bool_t resched = PJ_TRUE; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); if (!stun_sock) return; PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); /* Check if this is a keep-alive or the first Binding request */ if (pj_sockaddr_has_addr(&stun_sock->mapped_addr)) op = PJ_STUN_SOCK_KEEP_ALIVE_OP; else op = PJ_STUN_SOCK_BINDING_OP; /* Handle failure */ if (status != PJ_SUCCESS) { resched = sess_fail(stun_sock, op, status); goto on_return; } /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS * doesn't exist. */ mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mapped_attr==NULL) { mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mapped_attr == NULL) { resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR); goto on_return; } /* Determine if mapped address has changed, and save the new mapped * address and call callback if so */ mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || pj_sockaddr_cmp(&stun_sock->mapped_addr, &mapped_attr->sockaddr) != 0; if (mapped_changed) { /* Print mapped adress */ { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; PJ_LOG(2,(stun_sock->obj_name, "STUN mapped address found/changed: %s", pj_sockaddr_print(&mapped_attr->sockaddr, addrinfo, sizeof(addrinfo), 3))); } pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr); if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP) { op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE; PJ_LOG(2, (THIS_FILE, "sess_on_rquest_complete() Operation is PJ_STUN_SOCK_MAPPED_ADDR_CHANGE.")); } } // 2013-10-16 DEAN // 2013-10-21 DEAN { int addr_len = sizeof(stun_sock->current_local_addr); char addrinfo1[PJ_INET6_ADDRSTRLEN+10]; char addrinfo2[PJ_INET6_ADDRSTRLEN+10]; pj_sock_getsockname(stun_sock->sock_fd, &stun_sock->current_local_addr, &addr_len); PJ_LOG(6,(stun_sock->obj_name, "Current Local address: %s", pj_sockaddr_print(&stun_sock->current_local_addr, addrinfo1, sizeof(addrinfo1), 3))); /* * Find out which interface is used to send to the server. */ status = get_local_interface(&stun_sock->srv_addr, &((pj_sockaddr_in *)(&stun_sock->current_local_addr))->sin_addr); PJ_LOG(6,(stun_sock->obj_name, "Current Local address: %s", pj_sockaddr_print(&stun_sock->current_local_addr, addrinfo2, sizeof(addrinfo2), 3))); } /* Notify user */ resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS); PJ_LOG(5, (THIS_FILE, "sess_on_request_complete() resched=%d.", resched)); on_return: /* Start/restart keep-alive timer */ if (resched) start_ka_timer(stun_sock); }
/* * 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; }
/* Dump media session */ static void dump_media_session(const char *indent, char *buf, unsigned maxlen, pjsua_call *call) { unsigned i; char *p = buf, *end = buf+maxlen; int len; for (i=0; i<call->med_cnt; ++i) { pjsua_call_media *call_med = &call->media[i]; pjmedia_rtcp_stat stat; pj_bool_t has_stat; pjmedia_transport_info tp_info; char rem_addr_buf[80]; char codec_info[32] = {'0'}; char rx_info[80] = {'\0'}; char tx_info[80] = {'\0'}; const char *rem_addr; const char *dir_str; const char *media_type_str; switch (call_med->type) { case PJMEDIA_TYPE_AUDIO: media_type_str = "audio"; break; case PJMEDIA_TYPE_VIDEO: media_type_str = "video"; break; case PJMEDIA_TYPE_APPLICATION: media_type_str = "application"; break; default: media_type_str = "unknown"; break; } /* Check if the stream is deactivated */ if (call_med->tp == NULL || (!call_med->strm.a.stream && !call_med->strm.v.stream)) { len = pj_ansi_snprintf(p, end-p, "%s #%d %s deactivated\n", indent, i, media_type_str); if (len < 1 || len > end-p) { *p = '\0'; return; } p += len; continue; } pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call_med->tp, &tp_info); // rem_addr will contain actual address of RTP originator, instead of // remote RTP address specified by stream which is fetched from the SDP. // Please note that we are assuming only one stream per call. //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr, // rem_addr_buf, sizeof(rem_addr_buf), 3); if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) { rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf, sizeof(rem_addr_buf), 3); } else { pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-"); rem_addr = rem_addr_buf; } if (call_med->dir == PJMEDIA_DIR_NONE) { /* To handle when the stream that is currently being paused * (http://trac.pjsip.org/repos/ticket/1079) */ dir_str = "inactive"; } else if (call_med->dir == PJMEDIA_DIR_ENCODING) dir_str = "sendonly"; else if (call_med->dir == PJMEDIA_DIR_DECODING) dir_str = "recvonly"; else if (call_med->dir == PJMEDIA_DIR_ENCODING_DECODING) dir_str = "sendrecv"; else dir_str = "inactive"; if (call_med->type == PJMEDIA_TYPE_AUDIO) { pjmedia_stream *stream = call_med->strm.a.stream; pjmedia_stream_info info; pjmedia_stream_get_stat(stream, &stat); has_stat = PJ_TRUE; pjmedia_stream_get_info(stream, &info); pj_ansi_snprintf(codec_info, sizeof(codec_info), " %.*s @%dkHz", (int)info.fmt.encoding_name.slen, info.fmt.encoding_name.ptr, info.fmt.clock_rate / 1000); pj_ansi_snprintf(rx_info, sizeof(rx_info), "pt=%d,", info.rx_pt); pj_ansi_snprintf(tx_info, sizeof(tx_info), "pt=%d, ptime=%d,", info.tx_pt, info.param->setting.frm_per_pkt* info.param->info.frm_ptime); #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) } else if (call_med->type == PJMEDIA_TYPE_VIDEO) { pjmedia_vid_stream *stream = call_med->strm.v.stream; pjmedia_vid_stream_info info; pjmedia_vid_stream_get_stat(stream, &stat); has_stat = PJ_TRUE; pjmedia_vid_stream_get_info(stream, &info); pj_ansi_snprintf(codec_info, sizeof(codec_info), " %.*s", (int)info.codec_info.encoding_name.slen, info.codec_info.encoding_name.ptr); if (call_med->dir & PJMEDIA_DIR_DECODING) { pjmedia_video_format_detail *vfd; vfd = pjmedia_format_get_video_format_detail( &info.codec_param->dec_fmt, PJ_TRUE); pj_ansi_snprintf(rx_info, sizeof(rx_info), "pt=%d, size=%dx%d, fps=%.2f,", info.rx_pt, vfd->size.w, vfd->size.h, vfd->fps.num*1.0/vfd->fps.denum); } if (call_med->dir & PJMEDIA_DIR_ENCODING) { pjmedia_video_format_detail *vfd; vfd = pjmedia_format_get_video_format_detail( &info.codec_param->enc_fmt, PJ_TRUE); pj_ansi_snprintf(tx_info, sizeof(tx_info), "pt=%d, size=%dx%d, fps=%.2f,", info.tx_pt, vfd->size.w, vfd->size.h, vfd->fps.num*1.0/vfd->fps.denum); } #endif /* PJMEDIA_HAS_VIDEO */ } else { has_stat = PJ_FALSE; } len = pj_ansi_snprintf(p, end-p, "%s #%d %s%s, %s, peer=%s\n", indent, call_med->idx, media_type_str, codec_info, dir_str, rem_addr); if (len < 1 || len > end-p) { *p = '\0'; return; } p += len; /* Get and ICE SRTP status */ if (call_med->tp) { pjmedia_transport_info tp_info; pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call_med->tp, &tp_info); if (tp_info.specific_info_cnt > 0) { unsigned j; for (j = 0; j < tp_info.specific_info_cnt; ++j) { if (tp_info.spc_info[j].type == PJMEDIA_TRANSPORT_TYPE_SRTP) { pjmedia_srtp_info *srtp_info = (pjmedia_srtp_info*) tp_info.spc_info[j].buffer; len = pj_ansi_snprintf(p, end-p, " %s SRTP status: %s Crypto-suite: %s", indent, (srtp_info->active?"Active":"Not active"), srtp_info->tx_policy.name.ptr); if (len > 0 && len < end-p) { p += len; *p++ = '\n'; *p = '\0'; } } else if (tp_info.spc_info[j].type==PJMEDIA_TRANSPORT_TYPE_ICE) { const pjmedia_ice_transport_info *ii; unsigned jj; ii = (const pjmedia_ice_transport_info*) tp_info.spc_info[j].buffer; len = pj_ansi_snprintf(p, end-p, " %s ICE role: %s, state: %s, comp_cnt: %u", indent, pj_ice_sess_role_name(ii->role), pj_ice_strans_state_name(ii->sess_state), ii->comp_cnt); if (len > 0 && len < end-p) { p += len; *p++ = '\n'; *p = '\0'; } for (jj=0; ii->sess_state==PJ_ICE_STRANS_STATE_RUNNING && jj<2; ++jj) { const char *type1 = pj_ice_get_cand_type_name(ii->comp[jj].lcand_type); const char *type2 = pj_ice_get_cand_type_name(ii->comp[jj].rcand_type); char addr1[PJ_INET6_ADDRSTRLEN+10]; char addr2[PJ_INET6_ADDRSTRLEN+10]; if (pj_sockaddr_has_addr(&ii->comp[jj].lcand_addr)) pj_sockaddr_print(&ii->comp[jj].lcand_addr, addr1, sizeof(addr1), 3); else strcpy(addr1, "0.0.0.0:0"); if (pj_sockaddr_has_addr(&ii->comp[jj].rcand_addr)) pj_sockaddr_print(&ii->comp[jj].rcand_addr, addr2, sizeof(addr2), 3); else strcpy(addr2, "0.0.0.0:0"); len = pj_ansi_snprintf(p, end-p, " %s [%d]: L:%s (%c) --> R:%s (%c)\n", indent, jj, addr1, type1[0], addr2, type2[0]); if (len > 0 && len < end-p) { p += len; *p = '\0'; } } } } } } if (has_stat) { len = dump_media_stat(indent, p, end-p, &stat, rx_info, tx_info); p += len; } #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) # define SAMPLES_TO_USEC(usec, samples, clock_rate) \ do { \ if (samples <= 4294) \ usec = samples * 1000000 / clock_rate; \ else { \ usec = samples * 1000 / clock_rate; \ usec *= 1000; \ } \ } while(0) # define PRINT_VOIP_MTC_VAL(s, v) \ if (v == 127) \ sprintf(s, "(na)"); \ else \ sprintf(s, "%d", v) # define VALIDATE_PRINT_BUF() \ if (len < 1 || len > end-p) { *p = '\0'; return; } \ p += len; *p++ = '\n'; *p = '\0' if (call_med->type == PJMEDIA_TYPE_AUDIO) { pjmedia_stream_info info; char last_update[64]; char loss[16], dup[16]; char jitter[80]; char toh[80]; char plc[16], jba[16], jbr[16]; char signal_lvl[16], noise_lvl[16], rerl[16]; char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16]; pjmedia_rtcp_xr_stat xr_stat; unsigned clock_rate; pj_time_val now; if (pjmedia_stream_get_stat_xr(call_med->strm.a.stream, &xr_stat) != PJ_SUCCESS) { continue; } if (pjmedia_stream_get_info(call_med->strm.a.stream, &info) != PJ_SUCCESS) { continue; } clock_rate = info.fmt.clock_rate; pj_gettimeofday(&now); len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent); VALIDATE_PRINT_BUF(); /* Statistics Summary */ len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent); VALIDATE_PRINT_BUF(); if (xr_stat.rx.stat_sum.l) sprintf(loss, "%d", xr_stat.rx.stat_sum.lost); else sprintf(loss, "(na)"); if (xr_stat.rx.stat_sum.d) sprintf(dup, "%d", xr_stat.rx.stat_sum.dup); else sprintf(dup, "(na)"); if (xr_stat.rx.stat_sum.j) { unsigned jmin, jmax, jmean, jdev; SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min, clock_rate); SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max, clock_rate); SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean, clock_rate); SAMPLES_TO_USEC(jdev, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter), clock_rate); sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f", jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0); } else sprintf(jitter, "(report not available)"); if (xr_stat.rx.stat_sum.t) { sprintf(toh, "%11d %11d %11d %11d", xr_stat.rx.stat_sum.toh.min, xr_stat.rx.stat_sum.toh.mean, xr_stat.rx.stat_sum.toh.max, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh)); } else sprintf(toh, "(report not available)"); if (xr_stat.rx.stat_sum.update.sec == 0) strcpy(last_update, "never"); else { pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update); sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } len = pj_ansi_snprintf(p, end-p, "%s RX last update: %s\n" "%s begin seq=%d, end seq=%d\n" "%s pkt loss=%s, dup=%s\n" "%s (msec) min avg max dev\n" "%s jitter : %s\n" "%s toh : %s", indent, last_update, indent, xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq, indent, loss, dup, indent, indent, jitter, indent, toh ); VALIDATE_PRINT_BUF(); if (xr_stat.tx.stat_sum.l) sprintf(loss, "%d", xr_stat.tx.stat_sum.lost); else sprintf(loss, "(na)"); if (xr_stat.tx.stat_sum.d) sprintf(dup, "%d", xr_stat.tx.stat_sum.dup); else sprintf(dup, "(na)"); if (xr_stat.tx.stat_sum.j) { unsigned jmin, jmax, jmean, jdev; SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min, clock_rate); SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max, clock_rate); SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean, clock_rate); SAMPLES_TO_USEC(jdev, pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter), clock_rate); sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f", jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0); } else sprintf(jitter, "(report not available)"); if (xr_stat.tx.stat_sum.t) { sprintf(toh, "%11d %11d %11d %11d", xr_stat.tx.stat_sum.toh.min, xr_stat.tx.stat_sum.toh.mean, xr_stat.tx.stat_sum.toh.max, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh)); } else sprintf(toh, "(report not available)"); if (xr_stat.tx.stat_sum.update.sec == 0) strcpy(last_update, "never"); else { pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update); sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } len = pj_ansi_snprintf(p, end-p, "%s TX last update: %s\n" "%s begin seq=%d, end seq=%d\n" "%s pkt loss=%s, dup=%s\n" "%s (msec) min avg max dev\n" "%s jitter : %s\n" "%s toh : %s", indent, last_update, indent, xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq, indent, loss, dup, indent, indent, jitter, indent, toh ); VALIDATE_PRINT_BUF(); /* VoIP Metrics */ len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent); VALIDATE_PRINT_BUF(); PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl); PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl); PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl); PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor); PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor); PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq); PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq); switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) { case PJMEDIA_RTCP_XR_PLC_DIS: sprintf(plc, "DISABLED"); break; case PJMEDIA_RTCP_XR_PLC_ENH: sprintf(plc, "ENHANCED"); break; case PJMEDIA_RTCP_XR_PLC_STD: sprintf(plc, "STANDARD"); break; case PJMEDIA_RTCP_XR_PLC_UNK: default: sprintf(plc, "UNKNOWN"); break; } switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) { case PJMEDIA_RTCP_XR_JB_FIXED: sprintf(jba, "FIXED"); break; case PJMEDIA_RTCP_XR_JB_ADAPTIVE: sprintf(jba, "ADAPTIVE"); break; default: sprintf(jba, "UNKNOWN"); break; } sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F); if (xr_stat.rx.voip_mtc.update.sec == 0) strcpy(last_update, "never"); else { pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update); sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } len = pj_ansi_snprintf(p, end-p, "%s RX last update: %s\n" "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n" "%s burst : density=%d (%.2f%%), duration=%d%s\n" "%s gap : density=%d (%.2f%%), duration=%d%s\n" "%s delay : round trip=%d%s, end system=%d%s\n" "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n" "%s quality : R factor=%s, ext R factor=%s\n" "%s MOS LQ=%s, MOS CQ=%s\n" "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n" "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s", indent, last_update, /* packets */ indent, xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256, xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256, /* burst */ indent, xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256, xr_stat.rx.voip_mtc.burst_dur, "ms", /* gap */ indent, xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256, xr_stat.rx.voip_mtc.gap_dur, "ms", /* delay */ indent, xr_stat.rx.voip_mtc.rnd_trip_delay, "ms", xr_stat.rx.voip_mtc.end_sys_delay, "ms", /* level */ indent, signal_lvl, "dB", noise_lvl, "dB", rerl, "", /* quality */ indent, r_factor, ext_r_factor, indent, mos_lq, mos_cq, /* config */ indent, plc, jba, jbr, xr_stat.rx.voip_mtc.gmin, /* JB delay */ indent, xr_stat.rx.voip_mtc.jb_nom, "ms", xr_stat.rx.voip_mtc.jb_max, "ms", xr_stat.rx.voip_mtc.jb_abs_max, "ms" ); VALIDATE_PRINT_BUF(); PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl); PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl); PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl); PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor); PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor); PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq); PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq); switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) { case PJMEDIA_RTCP_XR_PLC_DIS: sprintf(plc, "DISABLED"); break; case PJMEDIA_RTCP_XR_PLC_ENH: sprintf(plc, "ENHANCED"); break; case PJMEDIA_RTCP_XR_PLC_STD: sprintf(plc, "STANDARD"); break; case PJMEDIA_RTCP_XR_PLC_UNK: default: sprintf(plc, "unknown"); break; } switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) { case PJMEDIA_RTCP_XR_JB_FIXED: sprintf(jba, "FIXED"); break; case PJMEDIA_RTCP_XR_JB_ADAPTIVE: sprintf(jba, "ADAPTIVE"); break; default: sprintf(jba, "unknown"); break; } sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F); if (xr_stat.tx.voip_mtc.update.sec == 0) strcpy(last_update, "never"); else { pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update); sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } len = pj_ansi_snprintf(p, end-p, "%s TX last update: %s\n" "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n" "%s burst : density=%d (%.2f%%), duration=%d%s\n" "%s gap : density=%d (%.2f%%), duration=%d%s\n" "%s delay : round trip=%d%s, end system=%d%s\n" "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n" "%s quality : R factor=%s, ext R factor=%s\n" "%s MOS LQ=%s, MOS CQ=%s\n" "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n" "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s", indent, last_update, /* pakcets */ indent, xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256, xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256, /* burst */ indent, xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256, xr_stat.tx.voip_mtc.burst_dur, "ms", /* gap */ indent, xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256, xr_stat.tx.voip_mtc.gap_dur, "ms", /* delay */ indent, xr_stat.tx.voip_mtc.rnd_trip_delay, "ms", xr_stat.tx.voip_mtc.end_sys_delay, "ms", /* level */ indent, signal_lvl, "dB", noise_lvl, "dB", rerl, "", /* quality */ indent, r_factor, ext_r_factor, indent, mos_lq, mos_cq, /* config */ indent, plc, jba, jbr, xr_stat.tx.voip_mtc.gmin, /* JB delay */ indent, xr_stat.tx.voip_mtc.jb_nom, "ms", xr_stat.tx.voip_mtc.jb_max, "ms", xr_stat.tx.voip_mtc.jb_abs_max, "ms" ); VALIDATE_PRINT_BUF(); /* RTT delay (by receiver side) */ len = pj_ansi_snprintf(p, end-p, "%s RTT (from recv) min avg max last dev", indent); VALIDATE_PRINT_BUF(); len = pj_ansi_snprintf(p, end-p, "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f", indent, xr_stat.rtt.min / 1000.0, xr_stat.rtt.mean / 1000.0, xr_stat.rtt.max / 1000.0, xr_stat.rtt.last / 1000.0, pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0 ); VALIDATE_PRINT_BUF(); } /* if audio */; #endif }
/* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_sock *stun_sock; const pj_stun_sockaddr_attr *mapped_attr; pj_stun_sock_op op; pj_bool_t mapped_changed; pj_bool_t resched = PJ_TRUE; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); /* Check if this is a keep-alive or the first Binding request */ if (pj_sockaddr_has_addr(&stun_sock->mapped_addr)) op = PJ_STUN_SOCK_KEEP_ALIVE_OP; else op = PJ_STUN_SOCK_BINDING_OP; /* Handle failure */ if (status != PJ_SUCCESS) { resched = sess_fail(stun_sock, op, status); goto on_return; } /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS * doesn't exist. */ mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mapped_attr==NULL) { mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mapped_attr == NULL) { resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR); goto on_return; } /* Determine if mapped address has changed, and save the new mapped * address and call callback if so */ mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || pj_sockaddr_cmp(&stun_sock->mapped_addr, &mapped_attr->sockaddr) != 0; if (mapped_changed) { /* Print mapped adress */ { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; PJ_LOG(4,(stun_sock->obj_name, "STUN mapped address found/changed: %s", pj_sockaddr_print(&mapped_attr->sockaddr, addrinfo, sizeof(addrinfo), 3))); } pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr); if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP) op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE; } /* Notify user */ resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS); on_return: /* Start/restart keep-alive timer */ if (resched) start_ka_timer(stun_sock); }
/* Called by application to initialize the transport */ static pj_status_t transport_attach( pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)) { struct transport_udp *udp = (struct transport_udp*) tp; const pj_sockaddr *rtcp_addr; /* Validate arguments */ PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL); /* Must not be "attached" to existing application */ PJ_ASSERT_RETURN(!udp->attached, PJ_EINVALIDOP); /* Lock the ioqueue keys to make sure that callbacks are * not executed. See ticket #844 for details. */ pj_ioqueue_lock_key(udp->rtp_key); pj_ioqueue_lock_key(udp->rtcp_key); /* "Attach" the application: */ /* Copy remote RTP address */ pj_memcpy(&udp->rem_rtp_addr, rem_addr, addr_len); /* Copy remote RTP address, if one is specified. */ rtcp_addr = (const pj_sockaddr*) rem_rtcp; if (rtcp_addr && pj_sockaddr_has_addr(rtcp_addr)) { pj_memcpy(&udp->rem_rtcp_addr, rem_rtcp, addr_len); } else { unsigned rtcp_port; /* Otherwise guess the RTCP address from the RTP address */ pj_memcpy(&udp->rem_rtcp_addr, rem_addr, addr_len); rtcp_port = pj_sockaddr_get_port(&udp->rem_rtp_addr) + 1; pj_sockaddr_set_port(&udp->rem_rtcp_addr, (pj_uint16_t)rtcp_port); } /* Save the callbacks */ udp->rtp_cb = rtp_cb; udp->rtcp_cb = rtcp_cb; udp->user_data = user_data; /* Save address length */ udp->addr_len = addr_len; /* Last, mark transport as attached */ udp->attached = PJ_TRUE; /* Reset source RTP & RTCP addresses and counter */ pj_bzero(&udp->rtp_src_addr, sizeof(udp->rtp_src_addr)); pj_bzero(&udp->rtcp_src_addr, sizeof(udp->rtcp_src_addr)); udp->rtp_src_cnt = 0; udp->rtcp_src_cnt = 0; /* Set buffer size for RTP socket */ #if PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE { unsigned sobuf_size = PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE; pj_status_t status; status = pj_sock_setsockopt_sobuf(udp->rtp_sock, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, tp->name, status, "Failed setting SO_RCVBUF"); } else { if (sobuf_size < PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE) { PJ_LOG(4, (tp->name, "Warning! Cannot set SO_RCVBUF as configured, " "now=%d, configured=%d", sobuf_size, PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE)); } else { PJ_LOG(5, (tp->name, "SO_RCVBUF set to %d", sobuf_size)); } } } #endif #if PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE { unsigned sobuf_size = PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE; pj_status_t status; status = pj_sock_setsockopt_sobuf(udp->rtp_sock, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, tp->name, status, "Failed setting SO_SNDBUF"); } else { if (sobuf_size < PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE) { PJ_LOG(4, (tp->name, "Warning! Cannot set SO_SNDBUF as configured, " "now=%d, configured=%d", sobuf_size, PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE)); } else { PJ_LOG(5, (tp->name, "SO_SNDBUF set to %d", sobuf_size)); } } } #endif /* Unlock keys */ pj_ioqueue_unlock_key(udp->rtcp_key); pj_ioqueue_unlock_key(udp->rtp_key); return PJ_SUCCESS; }
/* Get info */ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, pj_stun_sock_info *info) { int addr_len; pj_status_t status; PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL); /* Copy STUN server address and mapped address */ pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, sizeof(pj_sockaddr)); pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, sizeof(pj_sockaddr)); /* Retrieve bound address */ addr_len = sizeof(info->bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr, &addr_len); if (status != PJ_SUCCESS) return status; /* If socket is bound to a specific interface, then only put that * interface in the alias list. Otherwise query all the interfaces * in the host. */ if (pj_sockaddr_has_addr(&info->bound_addr)) { info->alias_cnt = 1; pj_sockaddr_cp(&info->aliases[0], &info->bound_addr); } else { pj_sockaddr def_addr; pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); unsigned i; /* Get the default address */ status = pj_gethostip(stun_sock->af, &def_addr); if (status != PJ_SUCCESS) return status; pj_sockaddr_set_port(&def_addr, port); /* Enum all IP interfaces in the host */ info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, info->aliases); if (status != PJ_SUCCESS) return status; /* Set the port number for each address. */ for (i=0; i<info->alias_cnt; ++i) { pj_sockaddr_set_port(&info->aliases[i], port); } /* Put the default IP in the first slot */ for (i=0; i<info->alias_cnt; ++i) { if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) { if (i!=0) { pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]); pj_sockaddr_cp(&info->aliases[0], &def_addr); } break; } } } return PJ_SUCCESS; }
/* * Create the STUN transport using the specified configuration. */ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, const char *name, int af, const pj_stun_sock_cb *cb, const pj_stun_sock_cfg *cfg, void *user_data, pj_stun_sock **p_stun_sock) { pj_pool_t *pool; pj_stun_sock *stun_sock; pj_stun_sock_cfg default_cfg; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); status = pj_stun_config_check_valid(stun_cfg); if (status != PJ_SUCCESS) return status; if (name == NULL) name = "stuntp%p"; if (cfg == NULL) { pj_stun_sock_cfg_default(&default_cfg); cfg = &default_cfg; } /* Create structure */ pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); stun_sock->pool = pool; stun_sock->obj_name = pool->obj_name; stun_sock->user_data = user_data; stun_sock->af = af; stun_sock->sock_fd = PJ_INVALID_SOCKET; pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); stun_sock->ka_interval = cfg->ka_interval; if (stun_sock->ka_interval == 0) stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; /* Create socket and bind socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, &cfg->qos_params, 2, stun_sock->obj_name, NULL); if (status != PJ_SUCCESS && !cfg->qos_ignore_error) goto on_error; /* Bind socket */ if (pj_sockaddr_has_addr(&cfg->bound_addr)) { status = pj_sock_bind(stun_sock->sock_fd, &cfg->bound_addr, pj_sockaddr_get_len(&cfg->bound_addr)); } else { pj_sockaddr bound_addr; pj_sockaddr_init(af, &bound_addr, NULL, 0); status = pj_sock_bind(stun_sock->sock_fd, &bound_addr, pj_sockaddr_get_len(&bound_addr)); } if (status != PJ_SUCCESS) goto on_error; /* Create more useful information string about this transport */ #if 0 { pj_sockaddr bound_addr; int addr_len = sizeof(bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); pj_sockaddr_print(&bound_addr, stun_sock->info, PJ_INET6_ADDRSTRLEN, 3); } #endif /* Init active socket configuration */ { pj_activesock_cfg activesock_cfg; pj_activesock_cb activesock_cb; pj_activesock_cfg_default(&activesock_cfg); activesock_cfg.async_cnt = cfg->async_cnt; activesock_cfg.concurrency = 0; /* Create the active socket */ pj_bzero(&activesock_cb, sizeof(activesock_cb)); activesock_cb.on_data_recvfrom = &on_data_recvfrom; activesock_cb.on_data_sent = &on_data_sent; status = pj_activesock_create(pool, stun_sock->sock_fd, pj_SOCK_DGRAM(), &activesock_cfg, stun_cfg->ioqueue, &activesock_cb, stun_sock, &stun_sock->active_sock); if (status != PJ_SUCCESS) goto on_error; /* Start asynchronous read operations */ status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, cfg->max_pkt_size, 0); if (status != PJ_SUCCESS) goto on_error; /* Init send keys */ pj_ioqueue_op_key_init(&stun_sock->send_key, sizeof(stun_sock->send_key)); pj_ioqueue_op_key_init(&stun_sock->int_send_key, sizeof(stun_sock->int_send_key)); } /* Create STUN session */ { pj_stun_session_cb sess_cb; pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &sess_on_request_complete; sess_cb.on_send_msg = &sess_on_send_msg; status = pj_stun_session_create(&stun_sock->stun_cfg, stun_sock->obj_name, &sess_cb, PJ_FALSE, &stun_sock->stun_sess); if (status != PJ_SUCCESS) goto on_error; } /* Associate us with the STUN session */ pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); /* Initialize random numbers to be used as STUN transaction ID for * outgoing Binding request. We use the 80bit number to distinguish * STUN messages we sent with STUN messages that the application sends. * The last 16bit value in the array is a counter. */ for (i=0; i<PJ_ARRAY_SIZE(stun_sock->tsx_id); ++i) { stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); } stun_sock->tsx_id[5] = 0; /* Init timer entry */ stun_sock->ka_timer.cb = &ka_timer_cb; stun_sock->ka_timer.user_data = stun_sock; /* Done */ *p_stun_sock = stun_sock; return PJ_SUCCESS; on_error: pj_stun_sock_destroy(stun_sock); return status; }
PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pj_pool_t *pool; pj_bool_t is_ipv6; int af; struct tls_listener *listener; pj_ssl_sock_param ssock_param; pj_sockaddr *listener_addr; pj_bool_t has_listener; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); is_ipv6 = (local && local->addr.sa_family == pj_AF_INET6()); af = is_ipv6 ? pj_AF_INET6() : pj_AF_INET(); /* Verify that address given in a_name (if any) is valid */ if (a_name && a_name->host.slen) { pj_sockaddr tmp; status = pj_sockaddr_init(af, &tmp, &a_name->host, (pj_uint16_t)a_name->port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || (!is_ipv6 && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tlslis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener); listener->factory.pool = pool; if (is_ipv6) listener->factory.type = PJSIP_TRANSPORT_TLS6; else listener->factory.type = PJSIP_TRANSPORT_TLS; 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); pj_ansi_strcpy(listener->factory.obj_name, "tlslis"); if (is_ipv6) pj_ansi_strcat(listener->factory.obj_name, "6"); if (opt) pjsip_tls_setting_copy(pool, &listener->tls_setting, opt); else pjsip_tls_setting_default(&listener->tls_setting); status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT; /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); ssock_param.sock_af = af; ssock_param.cb.on_accept_complete = &on_accept_complete; ssock_param.cb.on_data_read = &on_data_read; ssock_param.cb.on_data_sent = &on_data_sent; ssock_param.async_cnt = async_cnt; ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt); ssock_param.require_client_cert = listener->tls_setting.require_client_cert; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = listener; ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket * due to verification error */ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; ssock_param.ciphers_num = listener->tls_setting.ciphers_num; ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.reuse_addr = listener->tls_setting.reuse_addr; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, sizeof(ssock_param.qos_params)); has_listener = PJ_FALSE; switch(listener->tls_setting.method) { case PJSIP_TLSV1_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_TLS1; break; case PJSIP_SSLV2_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL2; break; case PJSIP_SSLV3_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL3; break; case PJSIP_SSLV23_METHOD: ssock_param.proto = PJ_SSL_SOCK_PROTO_SSL23; break; default: ssock_param.proto = PJ_SSL_SOCK_PROTO_DEFAULT; break; } /* Create SSL socket */ status = pj_ssl_sock_create(pool, &ssock_param, &listener->ssock); if (status != PJ_SUCCESS) goto on_error; /* Bind address may be different than factory.local_addr because * factory.local_addr will be resolved below. */ listener_addr = &listener->factory.local_addr; if (local) { pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, (const pj_sockaddr_t*)local); pj_sockaddr_cp(&listener->bound_addr, local); } else { pj_sockaddr_init(af, listener_addr, NULL, 0); pj_sockaddr_init(af, &listener->bound_addr, NULL, 0); } /* Check if certificate/CA list for SSL socket is set */ if (listener->tls_setting.cert_file.slen || listener->tls_setting.ca_list_file.slen) { status = pj_ssl_cert_load_from_files(pool, &listener->tls_setting.ca_list_file, &listener->tls_setting.cert_file, &listener->tls_setting.privkey_file, &listener->tls_setting.password, &listener->cert); if (status != PJ_SUCCESS) goto on_error; status = pj_ssl_sock_set_certificate(listener->ssock, pool, listener->cert); if (status != PJ_SUCCESS) goto on_error; } /* Start accepting incoming connections. Note that some TLS/SSL backends * may not support for SSL socket server. */ has_listener = PJ_FALSE; status = pj_ssl_sock_start_accept(listener->ssock, pool, (pj_sockaddr_t*)listener_addr, pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr)); if (status == PJ_SUCCESS || status == PJ_EPENDING) { pj_ssl_sock_info info; has_listener = PJ_TRUE; /* Retrieve the bound address */ status = pj_ssl_sock_get_info(listener->ssock, &info); if (status == PJ_SUCCESS) pj_sockaddr_cp(listener_addr, (pj_sockaddr_t*)&info.local_addr); } else if (status != PJ_ENOTSUP) { goto on_error; } /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (a_name && a_name->host.slen) { /* Copy the address */ listener->factory.addr_name = *a_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &a_name->host); listener->factory.addr_name.port = a_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(af, &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), "tlslis:%d", listener->factory.addr_name.port); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport2 = 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; } if (has_listener) { PJ_LOG(4,(listener->factory.obj_name, "SIP TLS listener is 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)); } else { PJ_LOG(4,(listener->factory.obj_name, "SIP TLS is ready " "(client only)")); } /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: lis_destroy(&listener->factory); return status; }
/* This callback is called by transport manager for the TCP factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **p_transport) { struct tcp_listener *listener; struct tcp_transport *tcp; pj_sock_t sock; pj_sockaddr local_addr; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in or sockaddr_in6*/ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in)) || (rem_addr->addr.sa_family == pj_AF_INET6() && addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tcp_listener*)factory; /* Create socket */ status = pj_sock_socket(rem_addr->addr.sa_family, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) return status; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "outgoing SIP TCP socket"); /* Bind to listener's address and any port */ pj_bzero(&local_addr, sizeof(local_addr)); pj_sockaddr_cp(&local_addr, &listener->bound_addr); pj_sockaddr_set_port(&local_addr, 0); status = pj_sock_bind(sock, &local_addr, pj_sockaddr_get_len(&local_addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Get the local port */ addr_len = sizeof(local_addr); status = pj_sock_getsockname(sock, &local_addr, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Initially set the address from the listener's address */ if (!pj_sockaddr_has_addr(&local_addr)) { pj_sockaddr_copy_addr(&local_addr, &listener->factory.local_addr); } /* Create the transport descriptor */ status = tcp_create(listener, NULL, sock, PJ_FALSE, &local_addr, rem_addr, &tcp); if (status != PJ_SUCCESS) return status; /* Start asynchronous connect() operation */ tcp->has_pending_connect = PJ_TRUE; status = pj_activesock_start_connect(tcp->asock, tcp->base.pool, rem_addr, addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tcp->asock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { tcp_destroy(&tcp->base, status); return status; } if (tcp->has_pending_connect) { /* Update (again) local address, just in case local address currently * set is different now that asynchronous connect() is started. */ addr_len = sizeof(local_addr); if (pj_sock_getsockname(sock, &local_addr, &addr_len)==PJ_SUCCESS) { pj_sockaddr *tp_addr = &tcp->base.local_addr; /* Some systems (like old Win32 perhaps) may not set local address * properly before socket is fully connected. */ if (pj_sockaddr_cmp(tp_addr, &local_addr) && pj_sockaddr_get_port(&local_addr) != 0) { pj_sockaddr_cp(tp_addr, &local_addr); sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, &local_addr); } } PJ_LOG(4,(tcp->base.obj_name, "TCP transport %.*s:%d is connecting to %.*s:%d...", (int)tcp->base.local_name.host.slen, tcp->base.local_name.host.ptr, tcp->base.local_name.port, (int)tcp->base.remote_name.host.slen, tcp->base.remote_name.host.ptr, tcp->base.remote_name.port)); } /* Done */ *p_transport = &tcp->base; return PJ_SUCCESS; }
/* * Keep-alive test. */ static int keep_alive_test(pj_stun_config *cfg) { struct stun_srv *srv; struct stun_client *client; pj_sockaddr_in mapped_addr; pj_stun_sock_info info; pj_str_t srv_addr; pj_time_val timeout, t; int i, ret = 0; pj_status_t status; PJ_LOG(3,(THIS_FILE, " normal operation")); status = create_client(cfg, &client, PJ_TRUE); if (status != PJ_SUCCESS) return -310; status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv); if (status != PJ_SUCCESS) { destroy_client(client); return -320; } /* * Part 1: initial Binding resolution. */ PJ_LOG(3,(THIS_FILE, " initial Binding request")); srv_addr = pj_str("127.0.0.1"); status = pj_stun_sock_start(client->sock, &srv_addr, pj_ntohs(srv->addr.ipv4.sin_port), NULL); if (status != PJ_SUCCESS) { destroy_server(srv); destroy_client(client); return -330; } /* Wait until on_status() callback is called with success status */ pj_gettimeofday(&timeout); timeout.sec += 60; do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that callback with correct operation is called */ if (client->last_op != PJ_STUN_SOCK_BINDING_OP) { PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status")); ret = -340; goto on_return; } if (client->last_status != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " error: expecting PJ_SUCCESS status")); ret = -350; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -370; goto on_return; } /* Get info */ pj_bzero(&info, sizeof(info)); pj_stun_sock_get_info(client->sock, &info); /* Check that we have server address */ if (!pj_sockaddr_has_addr(&info.srv_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing server address")); ret = -380; goto on_return; } /* .. and bound address port must not be zero */ if (pj_sockaddr_get_port(&info.bound_addr)==0) { PJ_LOG(3,(THIS_FILE, " error: bound address is zero")); ret = -381; goto on_return; } /* .. and mapped address */ if (!pj_sockaddr_has_addr(&info.mapped_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing mapped address")); ret = -382; goto on_return; } /* verify the mapped address */ pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send); if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); ret = -383; goto on_return; } /* .. and at least one alias */ if (info.alias_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: must have at least one alias")); ret = -384; goto on_return; } if (!pj_sockaddr_has_addr(&info.aliases[0])) { PJ_LOG(3,(THIS_FILE, " error: missing alias")); ret = -386; goto on_return; } /* * Part 2: sending and receiving data */ PJ_LOG(3,(THIS_FILE, " sending/receiving data")); /* Change server operation mode to echo back data */ srv->flag = ECHO; /* Reset server */ srv->rx_cnt = 0; /* Client sending data to echo server */ { char txt[100]; PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3))); } status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret), 0, &info.srv_addr, pj_sockaddr_get_len(&info.srv_addr)); if (status != PJ_SUCCESS && status != PJ_EPENDING) { app_perror(" error: server sending data", status); ret = -390; goto on_return; } /* Wait for a short period until client receives data. We can't wait for * too long otherwise the keep-alive will kick in. */ pj_gettimeofday(&timeout); timeout.sec += 1; do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that data is received in server */ if (srv->rx_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: server didn't receive data")); ret = -395; goto on_return; } /* Check that status is still OK */ if (client->last_status != PJ_SUCCESS) { app_perror(" error: client has failed", client->last_status); ret = -400; goto on_return; } /* Check that data has been received */ if (client->on_rx_data_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: client doesn't receive data")); ret = -410; goto on_return; } /* * Part 3: Successful keep-alive, */ PJ_LOG(3,(THIS_FILE, " successful keep-alive scenario")); /* Change server operation mode to normal mode */ srv->flag = RESPOND_STUN | WITH_XOR_MAPPED; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait for keep-alive duration to see if client actually sends the * keep-alive. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (PJ_TIME_VAL_LT(t, timeout)); /* Check that server receives some packets */ if (srv->rx_cnt == 0) { PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received")); ret = -420; goto on_return; } /* Check that client status is still okay and on_status() callback is NOT * called */ /* No longer valid due to this ticket: * http://trac.pjsip.org/repos/ticket/742 if (client->on_status_cnt != 0) { PJ_LOG(3, (THIS_FILE, " error: on_status() must not be called on successful" "keep-alive when mapped-address does not change")); ret = -430; goto on_return; } */ /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -440; goto on_return; } /* * Part 4: Successful keep-alive with IP address change */ PJ_LOG(3,(THIS_FILE, " mapped IP address change")); /* Change server operation mode to normal mode */ srv->flag = RESPOND_STUN | WITH_XOR_MAPPED; /* Change mapped address in the response */ srv->ip_to_send = pj_str("2.2.2.2"); srv->port_to_send++; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait for keep-alive duration to see if client actually sends the * keep-alive. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (PJ_TIME_VAL_LT(t, timeout)); /* Check that server receives some packets */ if (srv->rx_cnt == 0) { PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received")); ret = -450; goto on_return; } /* Check that on_status() callback is called (because mapped address * has changed) */ if (client->on_status_cnt != 1) { PJ_LOG(3, (THIS_FILE, " error: on_status() was not called")); ret = -460; goto on_return; } /* Check that callback was called with correct operation */ if (client->last_op != PJ_STUN_SOCK_MAPPED_ADDR_CHANGE) { PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status")); ret = -470; goto on_return; } /* Check that last status is still success */ if (client->last_status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, " error: expecting successful status")); ret = -480; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -490; goto on_return; } /* Get info */ pj_bzero(&info, sizeof(info)); pj_stun_sock_get_info(client->sock, &info); /* Check that we have server address */ if (!pj_sockaddr_has_addr(&info.srv_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing server address")); ret = -500; goto on_return; } /* .. and mapped address */ if (!pj_sockaddr_has_addr(&info.mapped_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing mapped address")); ret = -510; goto on_return; } /* verify the mapped address */ pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send); if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); ret = -520; goto on_return; } /* .. and at least one alias */ if (info.alias_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: must have at least one alias")); ret = -530; goto on_return; } if (!pj_sockaddr_has_addr(&info.aliases[0])) { PJ_LOG(3,(THIS_FILE, " error: missing alias")); ret = -540; goto on_return; } /* * Part 5: Failed keep-alive */ PJ_LOG(3,(THIS_FILE, " failed keep-alive scenario")); /* Change server operation mode to respond without attribute */ srv->flag = RESPOND_STUN; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait until on_status() is called with failure. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that callback with correct operation is called */ if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) { PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status")); ret = -600; goto on_return; } if (client->last_status == PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " error: expecting failed keep-alive")); ret = -610; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -620; goto on_return; } on_return: destroy_server(srv); destroy_client(client); for (i=0; i<7; ++i) handle_events(cfg, 50); return ret; }
/* * Common function to create TLS transport, called when pending accept() and * pending connect() complete. */ static pj_status_t tls_create( struct tls_listener *listener, pj_pool_t *pool, pj_ssl_sock_t *ssock, pj_bool_t is_server, const pj_sockaddr_in *local, const pj_sockaddr_in *remote, const pj_str_t *remote_name, struct tls_transport **p_tls) { struct tls_transport *tls; const pj_str_t ka_pkt = PJSIP_TLS_KEEP_ALIVE_DATA; pj_status_t status; PJ_ASSERT_RETURN(listener && ssock && local && remote && p_tls, PJ_EINVAL); if (pool == NULL) { pool = pjsip_endpt_create_pool(listener->endpt, "tls", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); } /* * Create and initialize basic transport structure. */ tls = PJ_POOL_ZALLOC_T(pool, struct tls_transport); tls->is_server = is_server; tls->verify_server = listener->tls_setting.verify_server; pj_list_init(&tls->delayed_list); tls->base.pool = pool; pj_ansi_snprintf(tls->base.obj_name, PJ_MAX_OBJ_NAME, (is_server ? "tlss%p" :"tlsc%p"), tls); status = pj_atomic_create(pool, 0, &tls->base.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, "tls", &tls->base.lock); if (status != PJ_SUCCESS) { goto on_error; } if (remote_name) pj_strdup(pool, &tls->remote_name, remote_name); tls->base.key.type = PJSIP_TRANSPORT_TLS; pj_memcpy(&tls->base.key.rem_addr, remote, sizeof(pj_sockaddr_in)); tls->base.type_name = "tls"; tls->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS); tls->base.info = (char*) pj_pool_alloc(pool, 64); pj_ansi_snprintf(tls->base.info, 64, "TLS to %s:%d", pj_inet_ntoa(remote->sin_addr), (int)pj_ntohs(remote->sin_port)); tls->base.addr_len = sizeof(pj_sockaddr_in); tls->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; /* Set initial local address */ if (!pj_sockaddr_has_addr(local)) { pj_sockaddr_cp(&tls->base.local_addr, &listener->factory.local_addr); } else { pj_sockaddr_cp(&tls->base.local_addr, local); } sockaddr_to_host_port(pool, &tls->base.local_name, (pj_sockaddr_in*)&tls->base.local_addr); if (tls->remote_name.slen) { tls->base.remote_name.host = tls->remote_name; tls->base.remote_name.port = pj_sockaddr_in_get_port(remote); } else { sockaddr_to_host_port(pool, &tls->base.remote_name, remote); } tls->base.endpt = listener->endpt; tls->base.tpmgr = listener->tpmgr; tls->base.send_msg = &tls_send_msg; tls->base.do_shutdown = &tls_shutdown; tls->base.destroy = &tls_destroy_transport; tls->ssock = ssock; /* Register transport to transport manager */ status = pjsip_transport_register(listener->tpmgr, &tls->base); if (status != PJ_SUCCESS) { goto on_error; } tls->is_registered = PJ_TRUE; /* Initialize keep-alive timer */ tls->ka_timer.user_data = (void*)tls; tls->ka_timer.cb = &tls_keep_alive_timer; pj_ioqueue_op_key_init(&tls->ka_op_key.key, sizeof(pj_ioqueue_op_key_t)); pj_strdup(tls->base.pool, &tls->ka_pkt, &ka_pkt); /* Done setting up basic transport. */ *p_tls = tls; PJ_LOG(4,(tls->base.obj_name, "TLS %s transport created", (tls->is_server ? "server" : "client"))); return PJ_SUCCESS; on_error: tls_destroy(&tls->base, status); return status; }
/** * Create UDP stream transport from existing socket info. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, const char *name, const pjmedia_sock_info *si, unsigned options, pjmedia_transport **p_tp) { struct transport_udp *tp; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_ioqueue_callback rtp_cb, rtcp_cb; pj_ssize_t size; unsigned i; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && si && p_tp, PJ_EINVAL); /* Get ioqueue instance */ ioqueue = pjmedia_endpt_get_ioqueue(endpt); if (name==NULL) name = "udp%p"; /* Create transport structure */ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); if (!pool) return PJ_ENOMEM; tp = PJ_POOL_ZALLOC_T(pool, struct transport_udp); tp->pool = pool; tp->options = options; pj_memcpy(tp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); tp->base.op = &transport_udp_op; tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; /* Copy socket infos */ tp->rtp_sock = si->rtp_sock; tp->rtp_addr_name = si->rtp_addr_name; tp->rtcp_sock = si->rtcp_sock; tp->rtcp_addr_name = si->rtcp_addr_name; /* If address is 0.0.0.0, use host's IP address */ if (!pj_sockaddr_has_addr(&tp->rtp_addr_name)) { pj_sockaddr hostip; status = pj_gethostip(tp->rtp_addr_name.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_memcpy(pj_sockaddr_get_addr(&tp->rtp_addr_name), pj_sockaddr_get_addr(&hostip), pj_sockaddr_get_addr_len(&hostip)); } /* Same with RTCP */ if (!pj_sockaddr_has_addr(&tp->rtcp_addr_name)) { pj_memcpy(pj_sockaddr_get_addr(&tp->rtcp_addr_name), pj_sockaddr_get_addr(&tp->rtp_addr_name), pj_sockaddr_get_addr_len(&tp->rtp_addr_name)); } /* Setup RTP socket with the ioqueue */ pj_bzero(&rtp_cb, sizeof(rtp_cb)); rtp_cb.on_read_complete = &on_rx_rtp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtp_sock, tp, &rtp_cb, &tp->rtp_key); if (status != PJ_SUCCESS) goto on_error; /* Disallow concurrency so that detach() and destroy() are * synchronized with the callback. */ status = pj_ioqueue_set_concurrency(tp->rtp_key, PJ_FALSE); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtp_read_op, sizeof(tp->rtp_read_op)); for (i=0; i<PJ_ARRAY_SIZE(tp->rtp_pending_write); ++i) pj_ioqueue_op_key_init(&tp->rtp_pending_write[i].op_key, sizeof(tp->rtp_pending_write[i].op_key)); /* Kick of pending RTP read from the ioqueue */ tp->rtp_addrlen = sizeof(tp->rtp_src_addr); size = sizeof(tp->rtp_pkt); status = pj_ioqueue_recvfrom(tp->rtp_key, &tp->rtp_read_op, tp->rtp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtp_src_addr, &tp->rtp_addrlen); if (status != PJ_EPENDING) goto on_error; /* Setup RTCP socket with ioqueue */ pj_bzero(&rtcp_cb, sizeof(rtcp_cb)); rtcp_cb.on_read_complete = &on_rx_rtcp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtcp_sock, tp, &rtcp_cb, &tp->rtcp_key); if (status != PJ_SUCCESS) goto on_error; status = pj_ioqueue_set_concurrency(tp->rtcp_key, PJ_FALSE); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtcp_read_op, sizeof(tp->rtcp_read_op)); pj_ioqueue_op_key_init(&tp->rtcp_write_op, sizeof(tp->rtcp_write_op)); /* Kick of pending RTCP read from the ioqueue */ size = sizeof(tp->rtcp_pkt); tp->rtcp_addr_len = sizeof(tp->rtcp_src_addr); status = pj_ioqueue_recvfrom( tp->rtcp_key, &tp->rtcp_read_op, tp->rtcp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtcp_src_addr, &tp->rtcp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Done */ *p_tp = &tp->base; return PJ_SUCCESS; on_error: transport_destroy(&tp->base); return status; }
/* * 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; }
static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { struct transport_ice *tp_ice; tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st); if (comp_id==1 && tp_ice->rtp_cb) { /* Simulate packet lost on RX direction */ if (tp_ice->rx_drop_pct) { if ((pj_rand() % 100) <= (int)tp_ice->rx_drop_pct) { PJ_LOG(5,(tp_ice->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); return; } } (*tp_ice->rtp_cb)(tp_ice->stream, pkt, size); /* See if source address of RTP packet is different than the * configured address, and switch RTP remote address to * source packet address after several consecutive packets * have been received. */ if (!tp_ice->use_ice) { /* Increment counter and avoid zero */ if (++tp_ice->rtp_src_cnt == 0) tp_ice->rtp_src_cnt = 1; if (pj_sockaddr_cmp(&tp_ice->remote_rtp, src_addr) != 0) { /* Check if the source address is recognized. */ if (pj_sockaddr_cmp(src_addr, &tp_ice->rtp_src_addr) != 0) { /* Remember the new source address. */ pj_sockaddr_cp(&tp_ice->rtp_src_addr, src_addr); /* Reset counter */ tp_ice->rtp_src_cnt = 0; } if ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0 && tp_ice->rtp_src_cnt >= PJMEDIA_RTP_NAT_PROBATION_CNT) { char addr_text[80]; /* Set remote RTP address to source address */ pj_sockaddr_cp(&tp_ice->remote_rtp, &tp_ice->rtp_src_addr); tp_ice->addr_len = pj_sockaddr_get_len(&tp_ice->remote_rtp); /* Reset counter */ tp_ice->rtp_src_cnt = 0; PJ_LOG(4,(tp_ice->base.name, "Remote RTP address switched to %s", pj_sockaddr_print(&tp_ice->remote_rtp, addr_text, sizeof(addr_text), 3))); /* Also update remote RTCP address if actual RTCP source * address is not heard yet. */ if (!pj_sockaddr_has_addr(&tp_ice->rtcp_src_addr)) { pj_uint16_t port; pj_sockaddr_cp(&tp_ice->remote_rtcp, &tp_ice->remote_rtp); port = (pj_uint16_t) (pj_sockaddr_get_port(&tp_ice->remote_rtp)+1); pj_sockaddr_set_port(&tp_ice->remote_rtcp, port); PJ_LOG(4,(tp_ice->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&tp_ice->remote_rtcp, addr_text, sizeof(addr_text), 3))); } } } } } else if (comp_id==2 && tp_ice->rtcp_cb) { (*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size); /* Check if RTCP source address is the same as the configured * remote address, and switch the address when they are * different. */ if (!tp_ice->use_ice && pj_sockaddr_cmp(&tp_ice->remote_rtcp, src_addr) != 0) { pj_sockaddr_cp(&tp_ice->rtcp_src_addr, src_addr); if ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0) { char addr_text[80]; pj_sockaddr_cp(&tp_ice->remote_rtcp, src_addr); pj_assert(tp_ice->addr_len == pj_sockaddr_get_len(src_addr)); PJ_LOG(4,(tp_ice->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&tp_ice->remote_rtcp, addr_text, sizeof(addr_text), 3))); } } } PJ_UNUSED_ARG(src_addr_len); }