/* * Create new allocation. */ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, const pj_sockaddr_t *src_addr, unsigned src_addr_len, const pj_stun_rx_data *rdata, pj_stun_session *srv_sess, pj_turn_allocation **p_alloc) { pj_turn_srv *srv = transport->listener->server; const pj_stun_msg *msg = rdata->msg; pj_pool_t *pool; alloc_request req; pj_turn_allocation *alloc; pj_stun_session_cb sess_cb; char str_tmp[80]; pj_status_t status; /* Parse ALLOCATE request */ status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len); if (status != PJ_SUCCESS) return status; pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL); /* Init allocation structure */ alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation); alloc->pool = pool; alloc->obj_name = pool->obj_name; alloc->relay.tp.sock = PJ_INVALID_SOCKET; alloc->server = transport->listener->server; alloc->bandwidth = req.bandwidth; /* Set transport */ alloc->transport = transport; pj_turn_transport_add_ref(transport, alloc); alloc->hkey.tp_type = transport->listener->tp_type; pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len); status = pj_lock_create_recursive_mutex(pool, alloc->obj_name, &alloc->lock); if (status != PJ_SUCCESS) { goto on_error; } /* Create peer hash table */ alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Create channel hash table */ alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Print info */ pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(transport->listener->tp_type)); alloc->info[3] = ':'; pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3); /* Create STUN session to handle STUN communication with client */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_send_msg = &stun_on_send_msg; sess_cb.on_rx_request = &stun_on_rx_request; sess_cb.on_rx_indication = &stun_on_rx_indication; status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, &sess_cb, PJ_FALSE, &alloc->sess); if (status != PJ_SUCCESS) { goto on_error; } /* Attach to STUN session */ pj_stun_session_set_user_data(alloc->sess, alloc); /* Init authentication credential */ status = init_cred(alloc, msg); if (status != PJ_SUCCESS) { goto on_error; } /* Attach authentication credential to STUN session */ pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM, &alloc->cred); /* Create the relay resource */ status = create_relay(srv, alloc, msg, &req, &alloc->relay); if (status != PJ_SUCCESS) { goto on_error; } /* Register this allocation */ pj_turn_srv_register_allocation(srv, alloc); /* Respond to ALLOCATE request */ status = send_allocate_response(alloc, srv_sess, transport, rdata); if (status != PJ_SUCCESS) goto on_error; /* Done */ pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp, sizeof(str_tmp), 3); PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s", alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp)); /* Success */ *p_alloc = alloc; return PJ_SUCCESS; on_error: /* Send reply to the ALLOCATE request */ pj_strerror(status, str_tmp, sizeof(str_tmp)); pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp, transport, PJ_TRUE, src_addr, src_addr_len); /* Cleanup */ destroy_allocation(alloc); return status; }
/* * pj_thread_register(..) */ PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, pj_thread_desc desc, pj_thread_t **ptr_thread) { #if PJ_HAS_THREADS char stack_ptr; pj_status_t rc; pj_thread_t *thread = (pj_thread_t *)desc; pj_str_t thread_name = pj_str((char*)cstr_thread_name); /* Size sanity check. */ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { pj_assert(!"Not enough pj_thread_desc size!"); return PJ_EBUG; } /* Warn if this thread has been registered before */ if (pj_thread_local_get (thread_tls_id) != 0) { // 2006-02-26 bennylp: // This wouldn't work in all cases!. // If thread is created by external module (e.g. sound thread), // thread may be reused while the pool used for the thread descriptor // has been deleted by application. //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); //return PJ_SUCCESS; PJ_LOG(4,(THIS_FILE, "Info: possibly re-registering existing " "thread")); } /* On the other hand, also warn if the thread descriptor buffer seem to * have been used to register other threads. */ pj_assert(thread->signature1 != SIGNATURE1 || thread->signature2 != SIGNATURE2 || (thread->thread == pthread_self())); /* Initialize and set the thread entry. */ pj_bzero(desc, sizeof(struct pj_thread_t)); thread->thread = pthread_self(); thread->signature1 = SIGNATURE1; thread->signature2 = SIGNATURE2; if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), cstr_thread_name, thread->thread); else pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), "thr%p", (void*)thread->thread); rc = pj_thread_local_set(thread_tls_id, thread); if (rc != PJ_SUCCESS) { pj_bzero(desc, sizeof(struct pj_thread_t)); return rc; } #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 thread->stk_start = &stack_ptr; thread->stk_size = 0xFFFFFFFFUL; thread->stk_max_usage = 0; #else stack_ptr = '\0'; #endif *ptr_thread = thread; return PJ_SUCCESS; #else pj_thread_t *thread = (pj_thread_t*)desc; *ptr_thread = thread; return PJ_SUCCESS; #endif }
static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_srtp *srtp = (struct transport_srtp*) tp; unsigned member_tp_option; PJ_ASSERT_RETURN(tp, PJ_EINVAL); pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg)); pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg)); srtp->media_option = options; member_tp_option = options | PJMEDIA_TPMED_NO_TRANSPORT_CHECKING; srtp->offerer_side = sdp_remote == NULL; /* Validations */ if (srtp->offerer_side) { if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) goto BYPASS_SRTP; } else { pjmedia_sdp_media *m_rem; m_rem = sdp_remote->media[media_index]; /* Nothing to do on inactive media stream */ if (pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL)) goto BYPASS_SRTP; /* Validate remote media transport based on SRTP usage option. */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; goto BYPASS_SRTP; case PJMEDIA_SRTP_OPTIONAL: break; case PJMEDIA_SRTP_MANDATORY: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; break; } } goto PROPAGATE_MEDIA_CREATE; BYPASS_SRTP: srtp->bypass_srtp = PJ_TRUE; member_tp_option &= ~PJMEDIA_TPMED_NO_TRANSPORT_CHECKING; PROPAGATE_MEDIA_CREATE: return pjmedia_transport_media_create(srtp->member_tp, sdp_pool, member_tp_option, sdp_remote, media_index); }
PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, pj_size_t size ) { pj_bzero(op_key, size); }
PJ_DEF(void) pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm) { pj_bzero(prm, sizeof(*prm)); prm->active = PJ_TRUE; }
/* * Convert IPv4/IPv6 address to text. */ PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src, char *dst, int size) { PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL); *dst = '\0'; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); #if defined(PJ_SOCK_HAS_INET_NTOP) && PJ_SOCK_HAS_INET_NTOP != 0 /* * Implementation using inet_ntop() */ if (inet_ntop(af, src, dst, size) == NULL) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; #elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE) /* * Implementation on Windows, using WSAAddressToString(). * Should also work on Unicode systems. */ { PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) pj_sockaddr sock_addr; DWORD addr_len, addr_str_len; int rc; pj_bzero(&sock_addr, sizeof(sock_addr)); sock_addr.addr.sa_family = (pj_uint16_t)af; if (af == PJ_AF_INET) { if (size < PJ_INET_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&sock_addr.ipv4.sin_addr, src, 4); addr_len = sizeof(pj_sockaddr_in); addr_str_len = PJ_INET_ADDRSTRLEN; } else if (af == PJ_AF_INET6) { if (size < PJ_INET6_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&sock_addr.ipv6.sin6_addr, src, 16); addr_len = sizeof(pj_sockaddr_in6); addr_str_len = PJ_INET6_ADDRSTRLEN; } else { pj_assert(!"Unsupported address family"); return PJ_EAFNOTSUP; } #if PJ_NATIVE_STRING_IS_UNICODE rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, NULL, wtempaddr, &addr_str_len); if (rc == 0) { pj_unicode_to_ansi(wtempaddr, wcslen(wtempaddr), dst, size); } #else rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, NULL, dst, &addr_str_len); #endif if (rc != 0) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; } #elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 /* IPv6 support is disabled, just return error without raising assertion */ return PJ_EIPV6NOTSUP; #else pj_assert(!"Not supported"); return PJ_EIPV6NOTSUP; #endif }
PJ_DEF(pj_status_t) pj_stun_detect_nat_type(const pj_sockaddr_in *server, pj_stun_config *stun_cfg, void *user_data, pj_stun_nat_detect_cb *cb) { pj_pool_t *pool; nat_detect_session *sess; pj_stun_session_cb sess_cb; pj_ioqueue_callback ioqueue_cb; int addr_len; pj_status_t status; PJ_ASSERT_RETURN(server && stun_cfg, PJ_EINVAL); PJ_ASSERT_RETURN(stun_cfg->pf && stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL); /* * Init NAT detection session. */ pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK, PJNATH_POOL_INC_NATCK, NULL); if (!pool) return PJ_ENOMEM; sess = PJ_POOL_ZALLOC_T(pool, nat_detect_session); sess->pool = pool; sess->user_data = user_data; sess->cb = cb; status = pj_mutex_create_recursive(pool, pool->obj_name, &sess->mutex); if (status != PJ_SUCCESS) goto on_error; pj_memcpy(&sess->server, server, sizeof(pj_sockaddr_in)); /* * Init timer to self-destroy. */ sess->timer_heap = stun_cfg->timer_heap; sess->timer.cb = &on_sess_timer; sess->timer.user_data = sess; /* * Initialize socket. */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sess->sock); if (status != PJ_SUCCESS) goto on_error; /* * Bind to any. */ pj_bzero(&sess->local_addr, sizeof(pj_sockaddr_in)); sess->local_addr.sin_family = pj_AF_INET(); status = pj_sock_bind(sess->sock, &sess->local_addr, sizeof(pj_sockaddr_in)); if (status != PJ_SUCCESS) goto on_error; /* * Get local/bound address. */ addr_len = sizeof(sess->local_addr); status = pj_sock_getsockname(sess->sock, &sess->local_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; /* * Find out which interface is used to send to the server. */ status = get_local_interface(server, &sess->local_addr.sin_addr); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(5,(sess->pool->obj_name, "Local address is %s:%d", pj_inet_ntoa(sess->local_addr.sin_addr), pj_ntohs(sess->local_addr.sin_port))); PJ_LOG(5,(sess->pool->obj_name, "Server set to %s:%d", pj_inet_ntoa(server->sin_addr), pj_ntohs(server->sin_port))); /* * Register socket to ioqueue to receive asynchronous input * notification. */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock(sess->pool, stun_cfg->ioqueue, sess->sock, sess, &ioqueue_cb, &sess->key); if (status != PJ_SUCCESS) goto on_error; /* * Create STUN session. */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &on_request_complete; sess_cb.on_send_msg = &on_send_msg; status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb, PJ_FALSE, NULL, &sess->stun_sess); if (status != PJ_SUCCESS) goto on_error; pj_stun_session_set_user_data(sess->stun_sess, sess); /* * Kick-off ioqueue reading. */ pj_ioqueue_op_key_init(&sess->read_op, sizeof(sess->read_op)); pj_ioqueue_op_key_init(&sess->write_op, sizeof(sess->write_op)); on_read_complete(sess->key, &sess->read_op, 0); /* * Start TEST_1 */ sess->timer.id = TIMER_TEST; on_sess_timer(stun_cfg->timer_heap, &sess->timer); return PJ_SUCCESS; on_error: sess_destroy(sess); 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; }
/* 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; /* Unlock keys */ pj_ioqueue_unlock_key(udp->rtcp_key); pj_ioqueue_unlock_key(udp->rtp_key); return PJ_SUCCESS; }
/* * Create a new listener on the specified port. */ PJ_DEF(pj_status_t) pj_turn_listener_create_udp( pj_turn_srv *srv, int af, const pj_str_t *bound_addr, unsigned port, unsigned concurrency_cnt, unsigned flags, pj_turn_listener **p_listener) { pj_pool_t *pool; struct udp_listener *udp; pj_ioqueue_callback ioqueue_cb; unsigned i; pj_status_t status; /* Create structure */ pool = pj_pool_create(srv->core.pf, "udp%p", 1000, 1000, NULL); udp = PJ_POOL_ZALLOC_T(pool, struct udp_listener); udp->base.pool = pool; udp->base.obj_name = pool->obj_name; udp->base.server = srv; udp->base.tp_type = PJ_TURN_TP_UDP; udp->base.sock = PJ_INVALID_SOCKET; udp->base.destroy = &udp_destroy; udp->read_cnt = concurrency_cnt; udp->base.flags = flags; udp->tp.obj_name = udp->base.obj_name; udp->tp.info = udp->base.info; udp->tp.listener = &udp->base; udp->tp.sendto = &udp_sendto; udp->tp.add_ref = &udp_add_ref; udp->tp.dec_ref = &udp_dec_ref; /* Create socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &udp->base.sock); if (status != PJ_SUCCESS) goto on_error; /* Init bind address */ status = pj_sockaddr_init(af, &udp->base.addr, bound_addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; /* Create info */ pj_ansi_strcpy(udp->base.info, "UDP:"); pj_sockaddr_print(&udp->base.addr, udp->base.info+4, sizeof(udp->base.info)-4, 3); /* Bind socket */ status = pj_sock_bind(udp->base.sock, &udp->base.addr, pj_sockaddr_get_len(&udp->base.addr)); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = on_read_complete; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, udp->base.sock, udp, &ioqueue_cb, &udp->key); /* Create op keys */ udp->read_op = (struct read_op**)pj_pool_calloc(pool, concurrency_cnt, sizeof(struct read_op*)); /* Create each read_op and kick off read operation */ for (i=0; i<concurrency_cnt; ++i) { pj_pool_t *rpool = pj_pool_create(srv->core.pf, "rop%p", 1000, 1000, NULL); udp->read_op[i] = PJ_POOL_ZALLOC_T(pool, struct read_op); udp->read_op[i]->pkt.pool = rpool; on_read_complete(udp->key, &udp->read_op[i]->op_key, 0); } /* Done */ PJ_LOG(4,(udp->base.obj_name, "Listener %s created", udp->base.info)); *p_listener = &udp->base; return PJ_SUCCESS; on_error: udp_destroy(&udp->base); return status; }
/** * Create UDP stream transport. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_create3(pjmedia_endpt *endpt, int af, const char *name, const pj_str_t *addr, int port, unsigned options, pjmedia_transport **p_tp) { pjmedia_sock_info si; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && port && p_tp, PJ_EINVAL); pj_bzero(&si, sizeof(pjmedia_sock_info)); si.rtp_sock = si.rtcp_sock = PJ_INVALID_SOCKET; /* Create RTP socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &si.rtp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind RTP socket */ status = pj_sockaddr_init(af, &si.rtp_addr_name, addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; status = pj_sock_bind(si.rtp_sock, &si.rtp_addr_name, pj_sockaddr_get_len(&si.rtp_addr_name)); if (status != PJ_SUCCESS) goto on_error; /* Create RTCP socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &si.rtcp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind RTCP socket */ status = pj_sockaddr_init(af, &si.rtcp_addr_name, addr, (pj_uint16_t)(port+1)); if (status != PJ_SUCCESS) goto on_error; status = pj_sock_bind(si.rtcp_sock, &si.rtcp_addr_name, pj_sockaddr_get_len(&si.rtcp_addr_name)); if (status != PJ_SUCCESS) goto on_error; /* Create UDP transport by attaching socket info */ return pjmedia_transport_udp_attach( endpt, name, &si, options, p_tp); on_error: if (si.rtp_sock != PJ_INVALID_SOCKET) pj_sock_close(si.rtp_sock); if (si.rtcp_sock != PJ_INVALID_SOCKET) pj_sock_close(si.rtcp_sock); return status; }
/* Initialize with default values (zero) */ PJ_DEF(void) pjmedia_snd_port_param_default(pjmedia_snd_port_param *prm) { pj_bzero(prm, sizeof(*prm)); }
static int format_test(void) { pj_str_t s = pj_str(ADDRESS); unsigned char *p; pj_in_addr addr; char zero[64]; pj_sockaddr_in addr2; const pj_str_t *hostname; const unsigned char A[] = {127, 0, 0, 1}; PJ_LOG(3,("test", "...format_test()")); /* pj_inet_aton() */ if (pj_inet_aton(&s, &addr) != 1) return -10; /* Check the result. */ p = (unsigned char*)&addr; if (p[0]!=A[0] || p[1]!=A[1] || p[2]!=A[2] || p[3]!=A[3]) { PJ_LOG(3,("test", " error: mismatched address. p0=%d, p1=%d, " "p2=%d, p3=%d", p[0] & 0xFF, p[1] & 0xFF, p[2] & 0xFF, p[3] & 0xFF)); return -15; } /* pj_inet_ntoa() */ p = (unsigned char*) pj_inet_ntoa(addr); if (!p) return -20; if (pj_strcmp2(&s, (char*)p) != 0) return -22; #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0 /* pj_inet_pton() */ /* pj_inet_ntop() */ { const pj_str_t s_ipv4 = pj_str("127.0.0.1"); const pj_str_t s_ipv6 = pj_str("fe80::2ff:83ff:fe7c:8b42"); char buf_ipv4[PJ_INET_ADDRSTRLEN]; char buf_ipv6[PJ_INET6_ADDRSTRLEN]; pj_in_addr ipv4; pj_in6_addr ipv6; if (pj_inet_pton(pj_AF_INET(), &s_ipv4, &ipv4) != PJ_SUCCESS) return -24; p = (unsigned char*)&ipv4; if (p[0]!=A[0] || p[1]!=A[1] || p[2]!=A[2] || p[3]!=A[3]) { return -25; } if (pj_inet_pton(pj_AF_INET6(), &s_ipv6, &ipv6) != PJ_SUCCESS) return -26; p = (unsigned char*)&ipv6; if (p[0] != 0xfe || p[1] != 0x80 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 || p[7] != 0 || p[8] != 0x02 || p[9] != 0xff || p[10] != 0x83 || p[11] != 0xff || p[12]!=0xfe || p[13]!=0x7c || p[14] != 0x8b || p[15]!=0x42) { return -27; } if (pj_inet_ntop(pj_AF_INET(), &ipv4, buf_ipv4, sizeof(buf_ipv4)) != PJ_SUCCESS) return -28; if (pj_stricmp2(&s_ipv4, buf_ipv4) != 0) return -29; if (pj_inet_ntop(pj_AF_INET6(), &ipv6, buf_ipv6, sizeof(buf_ipv6)) != PJ_SUCCESS) return -30; if (pj_stricmp2(&s_ipv6, buf_ipv6) != 0) return -31; } #endif /* PJ_HAS_IPV6 */ /* Test that pj_sockaddr_in_init() initialize the whole structure, * including sin_zero. */ pj_sockaddr_in_init(&addr2, 0, 1000); pj_bzero(zero, sizeof(zero)); if (pj_memcmp(addr2.sin_zero, zero, sizeof(addr2.sin_zero)) != 0) return -35; /* pj_gethostname() */ hostname = pj_gethostname(); if (!hostname || !hostname->ptr || !hostname->slen) return -40; PJ_LOG(3,("test", "....hostname is %.*s", (int)hostname->slen, hostname->ptr)); /* pj_gethostaddr() */ return 0; }
/* * 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 int udp_test(void) { pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET; pj_sockaddr_in dstaddr, srcaddr; pj_str_t s; pj_status_t rc = 0, retval; PJ_LOG(3,("test", "...udp_test()")); rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ss); if (rc != 0) { app_perror("...error: unable to create socket", rc); return -100; } rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &cs); if (rc != 0) return -110; /* Bind server socket. */ pj_bzero(&dstaddr, sizeof(dstaddr)); dstaddr.sin_family = pj_AF_INET(); dstaddr.sin_port = pj_htons(UDP_PORT); dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS)); if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) { app_perror("...bind error udp:"ADDRESS, rc); rc = -120; goto on_error; } /* Bind client socket. */ pj_bzero(&srcaddr, sizeof(srcaddr)); srcaddr.sin_family = pj_AF_INET(); srcaddr.sin_port = pj_htons(UDP_PORT-1); srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS)); if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) { app_perror("...bind error", rc); rc = -121; goto on_error; } /* Test send/recv, with sendto */ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, &dstaddr, NULL, sizeof(dstaddr)); if (rc != 0) goto on_error; /* Test send/recv, with sendto and recvfrom */ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, &dstaddr, &srcaddr, sizeof(dstaddr)); if (rc != 0) goto on_error; /* Disable this test on Symbian since UDP connect()/send() failed * with S60 3rd edition (including MR2). * See http://www.pjsip.org/trac/ticket/264 */ #if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0 /* connect() the sockets. */ rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr)); if (rc != 0) { app_perror("...connect() error", rc); rc = -122; goto on_error; } /* Test send/recv with send() */ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, NULL, NULL, 0); if (rc != 0) goto on_error; /* Test send/recv with send() and recvfrom */ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, NULL, &srcaddr, sizeof(srcaddr)); if (rc != 0) goto on_error; #endif on_error: retval = rc; if (cs != PJ_INVALID_SOCKET) { rc = pj_sock_close(cs); if (rc != PJ_SUCCESS) { app_perror("...error in closing socket", rc); return -1000; } } if (ss != PJ_INVALID_SOCKET) { rc = pj_sock_close(ss); if (rc != PJ_SUCCESS) { app_perror("...error in closing socket", rc); return -1010; } } return retval; }
/* * GET request scenario 2: using on_complete() to get the * complete data. Server does not reply with content-length. * Request timed out, application sets a longer timeout, then * then restart the request. */ int http_client_test2() { pj_str_t url; pj_http_req_callback hcb; pj_http_req_param param; pj_time_val timeout; char urlbuf[80]; pj_bzero(&hcb, sizeof(hcb)); hcb.on_complete = &on_complete; hcb.on_response = &on_response; pj_http_req_param_default(¶m); /* Create pool, timer, and ioqueue */ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL); if (pj_timer_heap_create(pool, 16, &timer_heap)) return -41; if (pj_ioqueue_create(pool, 16, &ioqueue)) return -42; #ifdef USE_LOCAL_SERVER pj_cstr(&url, "http://127.0.0.1:380"); param.timeout.sec = 0; param.timeout.msec = 2000; thread_quit = PJ_FALSE; g_server.action = ACTION_IGNORE; g_server.send_content_length = PJ_FALSE; g_server.data_size = 4173; g_server.buf_size = 1024; sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &g_server.sock); if (sstatus != PJ_SUCCESS) return -41; pj_sockaddr_in_init(&addr, NULL, 0); sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr)); if (sstatus != PJ_SUCCESS) return -43; { pj_sockaddr_in addr; int addr_len = sizeof(addr); sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len); if (sstatus != PJ_SUCCESS) return -44; g_server.port = pj_sockaddr_in_get_port(&addr); pj_ansi_snprintf(urlbuf, sizeof(urlbuf), "http://127.0.0.1:%d", g_server.port); url = pj_str(urlbuf); } sstatus = pj_sock_listen(g_server.sock, 8); if (sstatus != PJ_SUCCESS) return -45; sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server, 0, 0, &g_server.thread); if (sstatus != PJ_SUCCESS) return -47; #else pj_cstr(&url, "http://www.google.com.sg"); param.timeout.sec = 0; param.timeout.msec = 50; #endif pj_http_headers_add_elmt2(¶m.headers, (char*)"Accept", (char*)"image/gif, image/x-xbitmap, image/jpeg, " "image/pjpeg, application/x-ms-application," " application/vnd.ms-xpsdocument, " "application/xaml+xml, " "application/x-ms-xbap, " "application/x-shockwave-flash, " "application/vnd.ms-excel, " "application/vnd.ms-powerpoint, " "application/msword, */*"); pj_http_headers_add_elmt2(¶m.headers, (char*)"Accept-Language", (char*)"en-sg"); pj_http_headers_add_elmt2(¶m.headers, (char*)"User-Agent", (char*)"Mozilla/4.0 (compatible; MSIE 7.0; " "Windows NT 6.0; SLCC1; " ".NET CLR 2.0.50727; " ".NET CLR 3.0.04506)"); if (pj_http_req_create(pool, &url, timer_heap, ioqueue, ¶m, &hcb, &http_req)) return -43; if (pj_http_req_start(http_req)) return -45; while (pj_http_req_is_running(http_req)) { pj_time_val delay = {0, 50}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer_heap, NULL); } #ifdef USE_LOCAL_SERVER g_server.action = ACTION_REPLY; #endif timeout.sec = 0; timeout.msec = 10000; pj_http_req_set_timeout(http_req, &timeout); if (pj_http_req_start(http_req)) return -47; while (pj_http_req_is_running(http_req)) { pj_time_val delay = {0, 50}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer_heap, NULL); } #ifdef USE_LOCAL_SERVER thread_quit = PJ_TRUE; pj_thread_join(g_server.thread); pj_sock_close(g_server.sock); #endif pj_http_req_destroy(http_req); pj_ioqueue_destroy(ioqueue); pj_timer_heap_destroy(timer_heap); pj_pool_release(pool); return PJ_SUCCESS; }
static int format_test(void) { pj_str_t s = pj_str(ADDRESS); unsigned char *p; pj_in_addr addr; char zero[64]; pj_sockaddr_in addr2; const pj_str_t *hostname; const unsigned char A[] = {127, 0, 0, 1}; PJ_LOG(3,("test", "...format_test()")); /* pj_inet_aton() */ if (pj_inet_aton(&s, &addr) != 1) return -10; /* Check the result. */ p = (unsigned char*)&addr; if (p[0]!=A[0] || p[1]!=A[1] || p[2]!=A[2] || p[3]!=A[3]) { PJ_LOG(3,("test", " error: mismatched address. p0=%d, p1=%d, " "p2=%d, p3=%d", p[0] & 0xFF, p[1] & 0xFF, p[2] & 0xFF, p[3] & 0xFF)); return -15; } /* pj_inet_ntoa() */ p = (unsigned char*) pj_inet_ntoa(addr); if (!p) return -20; if (pj_strcmp2(&s, (char*)p) != 0) return -22; #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0 /* pj_inet_pton() */ /* pj_inet_ntop() */ { const pj_str_t s_ipv4 = pj_str("127.0.0.1"); const pj_str_t s_ipv6 = pj_str("fe80::2ff:83ff:fe7c:8b42"); char buf_ipv4[PJ_INET_ADDRSTRLEN]; char buf_ipv6[PJ_INET6_ADDRSTRLEN]; pj_in_addr ipv4; pj_in6_addr ipv6; if (pj_inet_pton(pj_AF_INET(), &s_ipv4, &ipv4) != PJ_SUCCESS) return -24; p = (unsigned char*)&ipv4; if (p[0]!=A[0] || p[1]!=A[1] || p[2]!=A[2] || p[3]!=A[3]) { return -25; } if (pj_inet_pton(pj_AF_INET6(), &s_ipv6, &ipv6) != PJ_SUCCESS) return -26; p = (unsigned char*)&ipv6; if (p[0] != 0xfe || p[1] != 0x80 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 || p[7] != 0 || p[8] != 0x02 || p[9] != 0xff || p[10] != 0x83 || p[11] != 0xff || p[12]!=0xfe || p[13]!=0x7c || p[14] != 0x8b || p[15]!=0x42) { return -27; } if (pj_inet_ntop(pj_AF_INET(), &ipv4, buf_ipv4, sizeof(buf_ipv4)) != PJ_SUCCESS) return -28; if (pj_stricmp2(&s_ipv4, buf_ipv4) != 0) return -29; if (pj_inet_ntop(pj_AF_INET6(), &ipv6, buf_ipv6, sizeof(buf_ipv6)) != PJ_SUCCESS) return -30; if (pj_stricmp2(&s_ipv6, buf_ipv6) != 0) return -31; } #endif /* PJ_HAS_IPV6 */ /* Test that pj_sockaddr_in_init() initialize the whole structure, * including sin_zero. */ pj_sockaddr_in_init(&addr2, 0, 1000); pj_bzero(zero, sizeof(zero)); if (pj_memcmp(addr2.sin_zero, zero, sizeof(addr2.sin_zero)) != 0) return -35; /* pj_gethostname() */ hostname = pj_gethostname(); if (!hostname || !hostname->ptr || !hostname->slen) return -40; PJ_LOG(3,("test", "....hostname is %.*s", (int)hostname->slen, hostname->ptr)); /* pj_gethostaddr() */ /* Various constants */ #if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0 if (PJ_AF_INET==0xFFFF) return -5500; if (PJ_AF_INET6==0xFFFF) return -5501; /* 0xFFFF could be a valid SOL_SOCKET (e.g: on some Win or Mac) */ //if (PJ_SOL_SOCKET==0xFFFF) return -5503; if (PJ_SOL_IP==0xFFFF) return -5502; if (PJ_SOL_TCP==0xFFFF) return -5510; if (PJ_SOL_UDP==0xFFFF) return -5520; if (PJ_SOL_IPV6==0xFFFF) return -5530; if (PJ_SO_TYPE==0xFFFF) return -5540; if (PJ_SO_RCVBUF==0xFFFF) return -5550; if (PJ_SO_SNDBUF==0xFFFF) return -5560; if (PJ_TCP_NODELAY==0xFFFF) return -5570; if (PJ_SO_REUSEADDR==0xFFFF) return -5580; if (PJ_MSG_OOB==0xFFFF) return -5590; if (PJ_MSG_PEEK==0xFFFF) return -5600; #endif return 0; }
int http_client_test_delete() { pj_str_t url; pj_http_req_callback hcb; pj_http_req_param param; char urlbuf[80]; pj_bzero(&hcb, sizeof(hcb)); hcb.on_complete = &on_complete; hcb.on_response = &on_response; /* Create pool, timer, and ioqueue */ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL); if (pj_timer_heap_create(pool, 16, &timer_heap)) return -61; if (pj_ioqueue_create(pool, 16, &ioqueue)) return -62; #ifdef USE_LOCAL_SERVER thread_quit = PJ_FALSE; g_server.action = ACTION_REPLY; g_server.send_content_length = PJ_TRUE; g_server.data_size = 0; g_server.buf_size = 1024; sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &g_server.sock); if (sstatus != PJ_SUCCESS) return -41; pj_sockaddr_in_init(&addr, NULL, 0); sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr)); if (sstatus != PJ_SUCCESS) return -43; { pj_sockaddr_in addr; int addr_len = sizeof(addr); sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len); if (sstatus != PJ_SUCCESS) return -44; g_server.port = pj_sockaddr_in_get_port(&addr); pj_ansi_snprintf(urlbuf, sizeof(urlbuf), "http://127.0.0.1:%d/test/test2.txt", g_server.port); url = pj_str(urlbuf); } sstatus = pj_sock_listen(g_server.sock, 8); if (sstatus != PJ_SUCCESS) return -45; sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server, 0, 0, &g_server.thread); if (sstatus != PJ_SUCCESS) return -47; #else pj_cstr(&url, "http://127.0.0.1:280/test/test2.txt"); #endif pj_http_req_param_default(¶m); pj_strset2(¶m.method, (char*)"DELETE"); if (pj_http_req_create(pool, &url, timer_heap, ioqueue, ¶m, &hcb, &http_req)) return -63; if (pj_http_req_start(http_req)) return -65; while (pj_http_req_is_running(http_req)) { pj_time_val delay = {0, 50}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer_heap, NULL); } #ifdef USE_LOCAL_SERVER thread_quit = PJ_TRUE; pj_thread_join(g_server.thread); pj_sock_close(g_server.sock); #endif pj_http_req_destroy(http_req); pj_ioqueue_destroy(ioqueue); pj_timer_heap_destroy(timer_heap); pj_pool_release(pool); return PJ_SUCCESS; }
/* * And here's the main() */ int main(int argc, char *argv[]) { struct pj_getopt_option long_options[] = { { "comp-cnt", 1, 0, 'c'}, { "nameserver", 1, 0, 'n'}, { "max-host", 1, 0, 'H'}, { "help", 0, 0, 'h'}, { "stun-srv", 1, 0, 's'}, { "turn-srv", 1, 0, 't'}, #ifndef TSHOME { "turn-tcp", 0, 0, 'T'}, #else { "client-number", 1, 0, 'N'}, #endif { "turn-username", 1, 0, 'u'}, { "turn-password", 1, 0, 'p'}, { "turn-fingerprint", 0, 0, 'F'}, { "regular", 0, 0, 'R'}, { "log-file", 1, 0, 'L'}, }; int c, opt_id; pj_status_t status; unsigned comp_cnt = 1; unsigned max_host = -1; pj_bool_t turn_fingerprint = PJ_FALSE; pj_bool_t regular = PJ_FALSE; pj_bool_t name_server = PJ_FALSE; pj_bool_t stun_srv_flag = PJ_FALSE; pj_bool_t turn_srv_flag = PJ_FALSE; pj_bool_t turn_usr_flag = PJ_FALSE; pj_bool_t turn_pass_flag = PJ_FALSE; pj_str_t ns, stun_srv, turn_srv, turn_usr, turn_pass; char * log_file = NULL; int k = 0; pj_bool_t host_inner_mode = 0; while((c=pj_getopt_long(argc,argv, "c:n:N:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) { switch (c) { case 'c': comp_cnt = atoi(pj_optarg); if (comp_cnt < 1 || comp_cnt >= PJ_ICE_MAX_COMP) { puts("Invalid component count value"); return 1; } break; #ifdef TSHOME case 'N': clients = atoi(pj_optarg); break; #endif case 'n': name_server = PJ_TRUE; ns = pj_str(pj_optarg); break; case 'H': max_host = atoi(pj_optarg); break; case 'h': icedemo_usage(); return 0; case 's': stun_srv_flag = PJ_TRUE; stun_srv = pj_str(pj_optarg); break; case 't': turn_srv_flag = PJ_TRUE; turn_srv = pj_str(pj_optarg); break; #ifndef TSHOME case 'T': icedemo[k].opt.turn_tcp = PJ_TRUE; break; #else #endif case 'u': turn_usr_flag = PJ_TRUE; turn_usr = pj_str(pj_optarg); break; case 'p': turn_pass_flag = PJ_TRUE; turn_pass = pj_str(pj_optarg); break; case 'F': turn_fingerprint = PJ_TRUE; break; case 'R': regular = PJ_TRUE; break; case 'L': log_file = pj_optarg; break; default: printf("Argument \"%s\" is not valid. Use -h to see help", argv[pj_optind]); return 1; } } /* Start keep dog thread */ //keep_dog_thread(); /* Get MAX support users from DB */ status = db_get_max_users (&clients); if (status != 0) { printf ("Get Max support users from DB failed!\n"); return 1; } if(clients <= 0 || clients % 2 != 0) { printf ("client number must be even number\n"); return 1; } /* * Use the appt[0] to communicate with server, the others to do P2P communication. */ clients = 4; clients += 1; icedemo = (struct app_t*)malloc(sizeof(struct app_t) * clients); if(NULL == icedemo) { printf ("alloc memory failed!\n"); return 1; } pj_bzero (icedemo, sizeof(struct app_t) * clients); /* Init Outer-Mode configure for STUN or TURN server */ for (k=0; k<(clients/2+1); k++) { icedemo[k].opt.comp_cnt = comp_cnt; icedemo[k].opt.max_host = max_host; if (PJ_TRUE == name_server) icedemo[k].opt.ns = ns; if (PJ_TRUE == stun_srv_flag) icedemo[k].opt.stun_srv = stun_srv; if (PJ_TRUE == turn_srv_flag) icedemo[k].opt.turn_srv = turn_srv; if (PJ_TRUE == turn_usr_flag) { icedemo[k].opt.turn_username = turn_usr; icedemo[k].opt.turn_password = turn_usr; } //if (PJ_TRUE == turn_pass_flag) // icedemo[k].opt.turn_password = turn_pass; icedemo[k].opt.turn_fingerprint = turn_fingerprint; icedemo[k].opt.regular = regular; icedemo[k].opt.log_file = log_file; } /* Init Inner-Mode configure for STUN or TURN server */ for (k=(clients/2+1); k<clients; k++) { icedemo[k].opt.comp_cnt = comp_cnt; icedemo[k].opt.max_host = max_host; icedemo[k].opt.turn_fingerprint = turn_fingerprint; icedemo[k].opt.regular = regular; icedemo[k].opt.log_file = log_file; } status = icedemo_init(); if (status != PJ_SUCCESS) return 1; pjsip_stop_flag = 0; start(icedemo[0].pool); /* If host is in inner mode ,set 1; else set 0 */ if (PJ_FALSE == stun_srv_flag && PJ_FALSE == turn_srv_flag) host_inner_mode = 1; icedemo_console(host_inner_mode); end(); err_exit("Quitting..", PJ_SUCCESS); return 0; }
static int state_progression_test(pj_stun_config *stun_cfg, pj_bool_t use_ipv6) { struct test_session_cfg test_cfg = { { /* Client cfg */ PJ_TRUE, /* DNS SRV */ 0xFFFF /* Destroy on state */ }, { /* Server cfg */ 0xFFFFFFFF, /* flags */ PJ_TRUE, /* respond to allocate */ PJ_TRUE /* respond to refresh */ } }; struct test_session *sess; unsigned i; int rc = 0; PJ_LOG(3,("", " state progression tests - (%s)",use_ipv6?"IPv6":"IPv4")); set_server_flag(&test_cfg, use_ipv6); for (i=0; i<=1; ++i) { enum { TIMEOUT = 60 }; pjlib_state pjlib_state; pj_turn_session_info info; struct test_result result; pj_time_val tstart; PJ_LOG(3,("", " %s DNS SRV resolution", (i==0? "without" : "with"))); capture_pjlib_state(stun_cfg, &pjlib_state); test_cfg.client.enable_dns_srv = i; rc = create_test_session(stun_cfg, &test_cfg, &sess); if (rc != 0) return rc; pj_bzero(&info, sizeof(info)); /* Wait until state is READY */ pj_gettimeofday(&tstart); while (sess->turn_sock) { pj_time_val now; poll_events(stun_cfg, 10, PJ_FALSE); rc = pj_turn_sock_get_info(sess->turn_sock, &info); if (rc!=PJ_SUCCESS) break; if (info.state >= PJ_TURN_STATE_READY) break; pj_gettimeofday(&now); if (now.sec - tstart.sec > TIMEOUT) { PJ_LOG(3,("", " timed-out")); break; } } if (info.state != PJ_TURN_STATE_READY) { PJ_LOG(3,("", " error: state is not READY")); destroy_session(sess); return -130; } /* Deallocate */ pj_turn_sock_destroy(sess->turn_sock); /* Wait for couple of seconds. * We can't poll the session info since the session may have * been destroyed */ poll_events(stun_cfg, 2000, PJ_FALSE); sess->turn_sock = NULL; pj_memcpy(&result, &sess->result, sizeof(result)); destroy_session(sess); /* Check the result */ if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVING)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_RESOLVING is not called")); return -140; } if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVED)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_RESOLVED is not called")); return -150; } if ((result.state_called & (1<<PJ_TURN_STATE_ALLOCATING)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_ALLOCATING is not called")); return -155; } if ((result.state_called & (1<<PJ_TURN_STATE_READY)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_READY is not called")); return -160; } if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATING)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_DEALLOCATING is not called")); return -170; } if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATED)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_DEALLOCATED is not called")); return -180; } if ((result.state_called & (1<<PJ_TURN_STATE_DESTROYING)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_DESTROYING is not called")); return -190; } poll_events(stun_cfg, 500, PJ_FALSE); rc = check_pjlib_state(stun_cfg, &pjlib_state); if (rc != 0) { PJ_LOG(3,("", " error: memory/timer-heap leak detected")); return rc; } } if (use_ipv6) rc = state_progression_test(stun_cfg, 0); return rc; }
/* Esta función es invocada desde MAIN=>Plugin, al igual que en TCP Es invocada desde un thread. Básicamente lo que hace es bootear PJSIP y luego se queda loopeando eventos SIP (con un pseudo polling que tiene la LIB) Adicionalmente lanza el thread para controlar los eventos TUN (el tráfico es bidireccional) */ int sip_start(int argc,char **argv) { pj_status_t status; /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); pj_log_set_level(5); /* Then init PJLIB-UTIL: */ status = pjlib_util_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* Create global endpoint: */ { const pj_str_t *hostname; const char *endpt_name; /* Endpoint MUST be assigned a globally unique name. * The name will be used as the hostname in Warning header. */ /* For this implementation, we'll use hostname for simplicity */ hostname = pj_gethostname(); endpt_name = hostname->ptr; /* Create the endpoint: */ status = pjsip_endpt_create(&cp.factory, endpt_name, &g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* * Add UDP transport, with hard-coded port * Alternatively, application can use pjsip_udp_transport_attach() to * start UDP transport, if it already has an UDP socket (e.g. after it * resolves the address with STUN). */ { pj_sockaddr addr; pj_sockaddr_init(AF, &addr, NULL, (pj_uint16_t)SIP_PORT); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to start UDP transport", status); return 1; } } /* * Init transaction layer. * This will create/initialize transaction hash tables etc. */ status = pjsip_tsx_layer_init_module(g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Initialize UA layer module. * This will create/initialize dialog hash tables etc. */ status = pjsip_ua_init_module( g_endpt, NULL ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Esto seguramente no haga falta nada de esto de INVITE's ... */ /* * Init invite session module. * The invite session module initialization takes additional argument, * i.e. a structure containing callbacks to be called on specific * occurence of events. * * The on_state_changed and on_new_session callbacks are mandatory. * Application must supply the callback function. * * We use on_media_update() callback in this application to start * media transmission. */ { pjsip_inv_callback inv_cb; /* Init the callback for INVITE session: */ pj_bzero(&inv_cb, sizeof(inv_cb)); inv_cb.on_state_changed = &call_on_state_changed; inv_cb.on_new_session = &call_on_forked; // inv_cb.on_media_update = &call_on_media_update; /* Initialize invite session module: */ status = pjsip_inv_usage_init(g_endpt, &inv_cb); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* Initialize 100rel support */ status = pjsip_100rel_init_module(g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* * Register our module to receive incoming requests. */ status = pjsip_endpt_register_module( g_endpt, &mod_simpleua); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Register message logger module. */ status = pjsip_endpt_register_module( g_endpt, &msg_logger); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); debug(1,"SIP Plugin launching thread for looping events"); pthread_create( &sip_plugin_event_thread, NULL, sip_loop_sip_events, NULL); /* Aqui es donde estamos esperando a recibir datos del PIPE (que viene del TUN DRIVER) hacemos I/O BLOCK con select */ sip_loop_tun_events(); // Aqui llegamos cuando ha muerto todo ;) return 0; }
static int create_test_session(pj_stun_config *stun_cfg, const struct test_session_cfg *cfg, struct test_session **p_sess) { struct test_session *sess; pj_pool_t *pool; pj_turn_sock_cb turn_sock_cb; pj_turn_alloc_param alloc_param; pj_stun_auth_cred cred; pj_status_t status; pj_bool_t use_ipv6 = cfg->srv.flags & SERVER_IPV6; /* Create client */ pool = pj_pool_create(mem, "turnclient", 512, 512, NULL); sess = PJ_POOL_ZALLOC_T(pool, struct test_session); sess->pool = pool; sess->stun_cfg = stun_cfg; sess->destroy_on_state = cfg->client.destroy_on_state; pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); turn_sock_cb.on_rx_data = &turn_on_rx_data; turn_sock_cb.on_state = &turn_on_state; status = pj_turn_sock_create(sess->stun_cfg, GET_AF(use_ipv6), PJ_TURN_TP_UDP, &turn_sock_cb, 0, sess, &sess->turn_sock); if (status != PJ_SUCCESS) { destroy_session(sess); return -20; } /* Create test server */ status = create_test_server(sess->stun_cfg, cfg->srv.flags, SRV_DOMAIN, &sess->test_srv); if (status != PJ_SUCCESS) { destroy_session(sess); return -30; } sess->test_srv->turn_respond_allocate = cfg->srv.respond_allocate; sess->test_srv->turn_respond_refresh = cfg->srv.respond_refresh; /* Create client resolver */ status = pj_dns_resolver_create(mem, "resolver", 0, sess->stun_cfg->timer_heap, sess->stun_cfg->ioqueue, &sess->resolver); if (status != PJ_SUCCESS) { destroy_session(sess); return -40; } else { pj_str_t dns_srv = use_ipv6?pj_str("::1") : pj_str("127.0.0.1"); pj_uint16_t dns_srv_port = (pj_uint16_t) DNS_SERVER_PORT; status = pj_dns_resolver_set_ns(sess->resolver, 1, &dns_srv, &dns_srv_port); if (status != PJ_SUCCESS) { destroy_session(sess); return -50; } } /* Init TURN credential */ pj_bzero(&cred, sizeof(cred)); cred.type = PJ_STUN_AUTH_CRED_STATIC; cred.data.static_cred.realm = pj_str(SRV_DOMAIN); cred.data.static_cred.username = pj_str(TURN_USERNAME); cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; cred.data.static_cred.data = pj_str(TURN_PASSWD); /* Init TURN allocate parameter */ pj_turn_alloc_param_default(&alloc_param); alloc_param.ka_interval = KA_INTERVAL; /* Start the client */ if (cfg->client.enable_dns_srv) { /* Use DNS SRV to resolve server, may fallback to DNS A */ pj_str_t domain = pj_str(SRV_DOMAIN); status = pj_turn_sock_alloc(sess->turn_sock, &domain, TURN_SERVER_PORT, sess->resolver, &cred, &alloc_param); } else { /* Explicitly specify server address */ pj_str_t host = use_ipv6?pj_str("::1") : pj_str("127.0.0.1"); status = pj_turn_sock_alloc(sess->turn_sock, &host, TURN_SERVER_PORT, NULL, &cred, &alloc_param); } if (status != PJ_SUCCESS) { if (cfg->client.destroy_on_state >= PJ_TURN_STATE_READY) { destroy_session(sess); return -70; } } *p_sess = sess; return 0; }
/* * Perform unregistration test. * * This will create ioqueue and register a server socket. Depending * on the test method, either the callback or the main thread will * unregister and destroy the server socket after some period of time. */ static int perform_unreg_test(pj_ioqueue_t *ioqueue, pj_pool_t *test_pool, const char *title, pj_bool_t other_socket) { enum { WORKER_CNT = 1, MSEC = 500, QUIT_MSEC = 500 }; int i; pj_thread_t *thread[WORKER_CNT]; struct sock_data osd; pj_ioqueue_callback callback; pj_time_val end_time; pj_status_t status; /* Sometimes its important to have other sockets registered to * the ioqueue, because when no sockets are registered, the ioqueue * will return from the poll early. */ if (other_socket) { status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 56127, &osd.sock); if (status != PJ_SUCCESS) { app_perror("Error creating other socket", status); return -12; } pj_bzero(&callback, sizeof(callback)); status = pj_ioqueue_register_sock(test_pool, ioqueue, osd.sock, NULL, &callback, &osd.key); if (status != PJ_SUCCESS) { app_perror("Error registering other socket", status); return -13; } } else { osd.key = NULL; osd.sock = PJ_INVALID_SOCKET; } /* Init both time duration of testing */ thread_quitting = 0; pj_gettimeofday(&time_to_unregister); time_to_unregister.msec += MSEC; pj_time_val_normalize(&time_to_unregister); end_time = time_to_unregister; end_time.msec += QUIT_MSEC; pj_time_val_normalize(&end_time); /* Create polling thread */ for (i=0; i<WORKER_CNT; ++i) { status = pj_thread_create(test_pool, "unregtest", &worker_thread, ioqueue, 0, 0, &thread[i]); if (status != PJ_SUCCESS) { app_perror("Error creating thread", status); return -20; } } /* Create pair of client/server sockets */ status = app_socketpair(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock_data.sock, &sock_data.csock); if (status != PJ_SUCCESS) { app_perror("app_socketpair error", status); return -30; } /* Initialize test data */ sock_data.pool = pj_pool_create(mem, "sd", 1000, 1000, NULL); sock_data.buffer = (char*) pj_pool_alloc(sock_data.pool, 128); sock_data.bufsize = 128; sock_data.op_key = (pj_ioqueue_op_key_t*) pj_pool_alloc(sock_data.pool, sizeof(*sock_data.op_key)); sock_data.received = 0; sock_data.unregistered = 0; pj_ioqueue_op_key_init(sock_data.op_key, sizeof(*sock_data.op_key)); status = pj_mutex_create_simple(sock_data.pool, "sd", &sock_data.mutex); if (status != PJ_SUCCESS) { app_perror("create_mutex() error", status); return -35; } /* Register socket to ioqueue */ pj_bzero(&callback, sizeof(callback)); callback.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock(sock_data.pool, ioqueue, sock_data.sock, NULL, &callback, &sock_data.key); if (status != PJ_SUCCESS) { app_perror("pj_ioqueue_register error", status); return -40; } /* Bootstrap the first send/receive */ on_read_complete(sock_data.key, sock_data.op_key, 0); /* Loop until test time ends */ for (;;) { pj_time_val now, timeout; int n; pj_gettimeofday(&now); if (test_method == UNREGISTER_IN_APP && PJ_TIME_VAL_GTE(now, time_to_unregister) && !sock_data.unregistered) { sock_data.unregistered = 1; /* Wait (as much as possible) for callback to complete */ pj_mutex_lock(sock_data.mutex); pj_mutex_unlock(sock_data.mutex); pj_ioqueue_unregister(sock_data.key); } if (PJ_TIME_VAL_GT(now, end_time) && sock_data.unregistered) break; timeout.sec = 0; timeout.msec = 10; n = pj_ioqueue_poll(ioqueue, &timeout); if (n < 0) { app_perror("pj_ioqueue_poll error", -n); pj_thread_sleep(1); } } thread_quitting = 1; for (i=0; i<WORKER_CNT; ++i) { pj_thread_join(thread[i]); pj_thread_destroy(thread[i]); } /* Destroy data */ pj_mutex_destroy(sock_data.mutex); pj_pool_release(sock_data.pool); sock_data.pool = NULL; if (other_socket) { pj_ioqueue_unregister(osd.key); } pj_sock_close(sock_data.csock); PJ_LOG(3,(THIS_FILE, "....%s: done (%d KB/s)", title, sock_data.received * 1000 / MSEC / 1000)); return 0; }
/* Callback to be called to handle transaction state changed. */ static void tu_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) { struct uac_data *uac_data; pj_status_t status; if (tsx->role == PJSIP_ROLE_UAS) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { struct uas_data *uas_data; uas_data = (struct uas_data*) tsx->mod_data[mod_tu.id]; if (uas_data->uac_tsx) { uac_data = (struct uac_data*) uas_data->uac_tsx->mod_data[mod_tu.id]; uac_data->uas_tsx = NULL; } } return; } /* Get the data that we attached to the UAC transaction previously */ uac_data = (struct uac_data*) tsx->mod_data[mod_tu.id]; /* Handle incoming response */ if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pjsip_rx_data *rdata; pjsip_response_addr res_addr; pjsip_via_hdr *hvia; pjsip_tx_data *tdata; rdata = event->body.tsx_state.src.rdata; /* Do not forward 100 response for INVITE (we already responded * INVITE with 100) */ if (tsx->method.id == PJSIP_INVITE_METHOD && rdata->msg_info.msg->line.status.code == 100) { return; } /* Create response to be forwarded upstream * (Via will be stripped here) */ status = pjsip_endpt_create_response_fwd(global.endpt, rdata, 0, &tdata); if (status != PJ_SUCCESS) { app_perror("Error creating response", status); return; } /* Get topmost Via header of the new response */ hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (hvia == NULL) { /* Invalid response! Just drop it */ pjsip_tx_data_dec_ref(tdata); return; } /* Calculate the address to forward the response */ pj_bzero(&res_addr, sizeof(res_addr)); res_addr.dst_host.type = PJSIP_TRANSPORT_UDP; res_addr.dst_host.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP); /* Destination address is Via's received param */ res_addr.dst_host.addr.host = hvia->recvd_param; if (res_addr.dst_host.addr.host.slen == 0) { /* Someone has messed up our Via header! */ res_addr.dst_host.addr.host = hvia->sent_by.host; } /* Destination port is the rport */ if (hvia->rport_param != 0 && hvia->rport_param != -1) res_addr.dst_host.addr.port = hvia->rport_param; if (res_addr.dst_host.addr.port == 0) { /* Ugh, original sender didn't put rport! * At best, can only send the response to the port in Via. */ res_addr.dst_host.addr.port = hvia->sent_by.port; } /* Forward response with the UAS transaction */ pjsip_tsx_send_msg(uac_data->uas_tsx, tdata); } /* If UAC transaction is terminated, terminate the UAS as well. * This could happen because of: * - timeout on the UAC side * - receipt of 2xx response to INVITE */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED && uac_data && uac_data->uas_tsx) { pjsip_transaction *uas_tsx; struct uas_data *uas_data; uas_tsx = uac_data->uas_tsx; uas_data = (struct uas_data*) uas_tsx->mod_data[mod_tu.id]; uas_data->uac_tsx = NULL; if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) { /* Send 408/Timeout if this is an INVITE transaction, since * we must have sent provisional response before. For non * INVITE transaction, just destroy it. */ if (tsx->method.id == PJSIP_INVITE_METHOD) { pjsip_tx_data *tdata = uas_tsx->last_tx; tdata->msg->line.status.code = PJSIP_SC_REQUEST_TIMEOUT; tdata->msg->line.status.reason = pj_str("Request timed out"); tdata->msg->body = NULL; pjsip_tx_data_add_ref(tdata); pjsip_tx_data_invalidate_msg(tdata); pjsip_tsx_send_msg(uas_tsx, tdata); } else { /* For non-INVITE, just destroy the UAS transaction */ pjsip_tsx_terminate(uas_tsx, PJSIP_SC_REQUEST_TIMEOUT); } } else if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { if (uas_tsx->state < PJSIP_TSX_STATE_TERMINATED) { pjsip_msg *msg; int code; msg = event->body.tsx_state.src.rdata->msg_info.msg; code = msg->line.status.code; uac_data->uas_tsx = NULL; pjsip_tsx_terminate(uas_tsx, code); } } } }
PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool, const pjmedia_vid_port_param *prm, pjmedia_vid_port **p_vid_port) { pjmedia_vid_port *vp; const pjmedia_video_format_detail *vfd; char dev_name[64]; char fmt_name[5]; pjmedia_vid_dev_cb vid_cb; pj_bool_t need_frame_buf = PJ_FALSE; pj_status_t status; unsigned ptime_usec; pjmedia_vid_dev_param vparam; pjmedia_vid_dev_info di; unsigned i; PJ_ASSERT_RETURN(pool && prm && p_vid_port, PJ_EINVAL); PJ_ASSERT_RETURN(prm->vidparam.fmt.type == PJMEDIA_TYPE_VIDEO && prm->vidparam.dir != PJMEDIA_DIR_NONE && prm->vidparam.dir != PJMEDIA_DIR_CAPTURE_RENDER, PJ_EINVAL); /* Retrieve the video format detail */ vfd = pjmedia_format_get_video_format_detail(&prm->vidparam.fmt, PJ_TRUE); if (!vfd) return PJ_EINVAL; PJ_ASSERT_RETURN(vfd->fps.num, PJ_EINVAL); /* Allocate videoport */ vp = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_port); vp->pool = pj_pool_create(pool->factory, "video port", 500, 500, NULL); vp->role = prm->active ? ROLE_ACTIVE : ROLE_PASSIVE; vp->dir = prm->vidparam.dir; // vp->cap_size = vfd->size; vparam = prm->vidparam; dev_name[0] = '\0'; /* Get device info */ if (vp->dir & PJMEDIA_DIR_CAPTURE) status = pjmedia_vid_dev_get_info(prm->vidparam.cap_id, &di); else status = pjmedia_vid_dev_get_info(prm->vidparam.rend_id, &di); if (status != PJ_SUCCESS) return status; pj_ansi_snprintf(dev_name, sizeof(dev_name), "%s [%s]", di.name, di.driver); for (i = 0; i < di.fmt_cnt; ++i) { if (prm->vidparam.fmt.id == di.fmt[i].id) break; } if (i == di.fmt_cnt) { /* The device has no no matching format. Pick one from * the supported formats, and later use converter to * convert it to the required format. */ pj_assert(di.fmt_cnt != 0); vparam.fmt.id = di.fmt[0].id; } pj_strdup2_with_null(pool, &vp->dev_name, di.name); vp->stream_role = di.has_callback ? ROLE_ACTIVE : ROLE_PASSIVE; pjmedia_fourcc_name(vparam.fmt.id, fmt_name); PJ_LOG(4,(THIS_FILE, "Opening device %s for %s: format=%s, size=%dx%d @%d:%d fps", dev_name, vid_dir_name(prm->vidparam.dir), fmt_name, vfd->size.w, vfd->size.h, vfd->fps.num, vfd->fps.denum)); ptime_usec = PJMEDIA_PTIME(&vfd->fps); pjmedia_clock_src_init(&vp->clocksrc, PJMEDIA_TYPE_VIDEO, prm->vidparam.clock_rate, ptime_usec); vp->sync_clocksrc.max_sync_ticks = PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION * 1000 / vp->clocksrc.ptime_usec; /* Create the video stream */ pj_bzero(&vid_cb, sizeof(vid_cb)); vid_cb.capture_cb = &vidstream_cap_cb; vid_cb.render_cb = &vidstream_render_cb; status = pjmedia_vid_dev_stream_create(&vparam, &vid_cb, vp, &vp->strm); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(4,(THIS_FILE, "Device %s opened: format=%s, size=%dx%d @%d:%d fps", dev_name, fmt_name, vparam.fmt.det.vid.size.w, vparam.fmt.det.vid.size.h, vparam.fmt.det.vid.fps.num, vparam.fmt.det.vid.fps.denum)); /* Subscribe to device's events */ pjmedia_event_subscribe(NULL, &vidstream_event_cb, vp, vp->strm); if (vp->dir & PJMEDIA_DIR_CAPTURE) { pjmedia_format_copy(&vp->conv.conv_param.src, &vparam.fmt); pjmedia_format_copy(&vp->conv.conv_param.dst, &prm->vidparam.fmt); } else { pjmedia_format_copy(&vp->conv.conv_param.src, &prm->vidparam.fmt); pjmedia_format_copy(&vp->conv.conv_param.dst, &vparam.fmt); } status = create_converter(vp); if (status != PJ_SUCCESS) goto on_error; if (vp->role==ROLE_ACTIVE && ((vp->dir & PJMEDIA_DIR_ENCODING) || vp->stream_role==ROLE_PASSIVE)) { pjmedia_clock_param param; /* Active role is wanted, but our device is passive, so create * master clocks to run the media flow. For encoding direction, * we also want to create our own clock since the device's clock * may run at a different rate. */ need_frame_buf = PJ_TRUE; param.usec_interval = PJMEDIA_PTIME(&vfd->fps); param.clock_rate = prm->vidparam.clock_rate; status = pjmedia_clock_create2(pool, ¶m, PJMEDIA_CLOCK_NO_HIGHEST_PRIO, (vp->dir & PJMEDIA_DIR_ENCODING) ? &enc_clock_cb: &dec_clock_cb, vp, &vp->clock); if (status != PJ_SUCCESS) goto on_error; } else if (vp->role==ROLE_PASSIVE) { vid_pasv_port *pp; /* Always need to create media port for passive role */ vp->pasv_port = pp = PJ_POOL_ZALLOC_T(pool, vid_pasv_port); pp->vp = vp; pp->base.get_frame = &vid_pasv_port_get_frame; pp->base.put_frame = &vid_pasv_port_put_frame; pjmedia_port_info_init2(&pp->base.info, &vp->dev_name, PJMEDIA_SIG_VID_PORT, prm->vidparam.dir, &prm->vidparam.fmt); if (vp->stream_role == ROLE_ACTIVE) { need_frame_buf = PJ_TRUE; } } if (need_frame_buf) { const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; vfi = pjmedia_get_video_format_info(NULL, vparam.fmt.id); if (!vfi) { status = PJ_ENOTFOUND; goto on_error; } pj_bzero(&vafp, sizeof(vafp)); vafp.size = vparam.fmt.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) goto on_error; vp->frm_buf = PJ_POOL_ZALLOC_T(pool, pjmedia_frame); vp->frm_buf_size = vafp.framebytes; vp->frm_buf->buf = pj_pool_alloc(pool, vafp.framebytes); vp->frm_buf->size = vp->frm_buf_size; vp->frm_buf->type = PJMEDIA_FRAME_TYPE_NONE; status = pj_mutex_create_simple(pool, vp->dev_name.ptr, &vp->frm_mutex); if (status != PJ_SUCCESS) goto on_error; } *p_vid_port = vp; return PJ_SUCCESS; on_error: pjmedia_vid_port_destroy(vp); return status; }
PJ_DEF(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt) { pj_bzero(opt, sizeof(*opt)); opt->queue_request = PJSIP_PUBLISHC_QUEUE_REQUEST; }
/* Parse crypto attribute line */ static pj_status_t parse_attr_crypto(pj_pool_t *pool, const pjmedia_sdp_attr *attr, pjmedia_srtp_crypto *crypto, int *tag) { pj_str_t input; char *token; int token_len; pj_str_t tmp; pj_status_t status; int itmp; pj_bzero(crypto, sizeof(*crypto)); pj_strdup_with_null(pool, &input, &attr->value); /* Tag */ token = strtok(input.ptr, " "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag")); return PJMEDIA_SDP_EINATTR; } token_len = pj_ansi_strlen(token); /* Tag must not use leading zeroes. */ if (token_len > 1 && *token == '0') return PJMEDIA_SDP_EINATTR; /* Tag must be decimal, i.e: contains only digit '0'-'9'. */ for (itmp = 0; itmp < token_len; ++itmp) if (!pj_isdigit(token[itmp])) return PJMEDIA_SDP_EINATTR; /* Get tag value. */ *tag = atoi(token); /* Crypto-suite */ token = strtok(NULL, " "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite")); return PJMEDIA_SDP_EINATTR; } crypto->name = pj_str(token); /* Key method */ token = strtok(NULL, ":"); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method")); return PJMEDIA_SDP_EINATTR; } if (pj_ansi_stricmp(token, "inline")) { PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%s' not supported!", token)); return PJMEDIA_SDP_EINATTR; } /* Key */ token = strtok(NULL, "| "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key")); return PJMEDIA_SDP_EINATTR; } tmp = pj_str(token); if (PJ_BASE64_TO_BASE256_LEN(tmp.slen) > MAX_KEY_LEN) { PJ_LOG(4,(THIS_FILE, "Key too long")); return PJMEDIA_SRTP_EINKEYLEN; } /* Decode key */ crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN); itmp = MAX_KEY_LEN; status = pj_base64_decode(&tmp, (pj_uint8_t*)crypto->key.ptr, &itmp); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64")); return status; } crypto->key.slen = itmp; return PJ_SUCCESS; }
static int send_recv_test(int sock_type, pj_sock_t ss, pj_sock_t cs, pj_sockaddr_in *dstaddr, pj_sockaddr_in *srcaddr, int addrlen) { enum { DATA_LEN = 16 }; char senddata[DATA_LEN+4], recvdata[DATA_LEN+4]; pj_ssize_t sent, received, total_received; pj_status_t rc; TRACE_(("test", "....create_random_string()")); pj_create_random_string(senddata, DATA_LEN); senddata[DATA_LEN-1] = '\0'; /* * Test send/recv small data. */ TRACE_(("test", "....sendto()")); if (dstaddr) { sent = DATA_LEN; rc = pj_sock_sendto(cs, senddata, &sent, 0, dstaddr, addrlen); if (rc != PJ_SUCCESS || sent != DATA_LEN) { app_perror("...sendto error", rc); rc = -140; goto on_error; } } else { sent = DATA_LEN; rc = pj_sock_send(cs, senddata, &sent, 0); if (rc != PJ_SUCCESS || sent != DATA_LEN) { app_perror("...send error", rc); rc = -145; goto on_error; } } TRACE_(("test", "....recv()")); if (srcaddr) { pj_sockaddr_in addr; int srclen = sizeof(addr); pj_bzero(&addr, sizeof(addr)); received = DATA_LEN; rc = pj_sock_recvfrom(ss, recvdata, &received, 0, &addr, &srclen); if (rc != PJ_SUCCESS || received != DATA_LEN) { app_perror("...recvfrom error", rc); rc = -150; goto on_error; } if (srclen != addrlen) return -151; if (pj_sockaddr_cmp(&addr, srcaddr) != 0) { char srcaddr_str[32], addr_str[32]; strcpy(srcaddr_str, pj_inet_ntoa(srcaddr->sin_addr)); strcpy(addr_str, pj_inet_ntoa(addr.sin_addr)); PJ_LOG(3,("test", "...error: src address mismatch (original=%s, " "recvfrom addr=%s)", srcaddr_str, addr_str)); return -152; } } else { /* Repeat recv() until all data is received. * This applies only for non-UDP of course, since for UDP * we would expect all data to be received in one packet. */ total_received = 0; do { received = DATA_LEN-total_received; rc = pj_sock_recv(ss, recvdata+total_received, &received, 0); if (rc != PJ_SUCCESS) { app_perror("...recv error", rc); rc = -155; goto on_error; } if (received <= 0) { PJ_LOG(3,("", "...error: socket has closed! (received=%d)", received)); rc = -156; goto on_error; } if (received != DATA_LEN-total_received) { if (sock_type != pj_SOCK_STREAM()) { PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", DATA_LEN-total_received, received)); rc = -157; goto on_error; } } total_received += received; } while (total_received < DATA_LEN); } TRACE_(("test", "....memcmp()")); if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) { PJ_LOG(3,("","...error: received data mismatch " "(got:'%s' expecting:'%s'", recvdata, senddata)); rc = -160; goto on_error; } /* * Test send/recv big data. */ TRACE_(("test", "....sendto()")); if (dstaddr) { sent = BIG_DATA_LEN; rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen); if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { app_perror("...sendto error", rc); rc = -161; goto on_error; } } else { sent = BIG_DATA_LEN; rc = pj_sock_send(cs, bigdata, &sent, 0); if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { app_perror("...send error", rc); rc = -165; goto on_error; } } TRACE_(("test", "....recv()")); /* Repeat recv() until all data is received. * This applies only for non-UDP of course, since for UDP * we would expect all data to be received in one packet. */ total_received = 0; do { received = BIG_DATA_LEN-total_received; rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0); if (rc != PJ_SUCCESS) { app_perror("...recv error", rc); rc = -170; goto on_error; } if (received <= 0) { PJ_LOG(3,("", "...error: socket has closed! (received=%d)", received)); rc = -173; goto on_error; } if (received != BIG_DATA_LEN-total_received) { if (sock_type != pj_SOCK_STREAM()) { PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", BIG_DATA_LEN-total_received, received)); rc = -176; goto on_error; } } total_received += received; } while (total_received < BIG_DATA_LEN); TRACE_(("test", "....memcmp()")); if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) { PJ_LOG(3,("", "...error: received data has been altered!")); rc = -180; goto on_error; } rc = 0; on_error: return rc; }
static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pjmedia_sdp_media *m_rem, *m_loc; enum { MAXLEN = 512 }; char buffer[MAXLEN]; int buffer_len; pj_status_t status; pjmedia_sdp_attr *attr; pj_str_t attr_value; unsigned i, j; PJ_ASSERT_RETURN(tp && sdp_pool && sdp_local, PJ_EINVAL); pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg)); pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg)); srtp->offerer_side = sdp_remote == NULL; m_rem = sdp_remote ? sdp_remote->media[media_index] : NULL; m_loc = sdp_local->media[media_index]; /* Bypass if media transport is not RTP/AVP or RTP/SAVP */ if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP) != 0 && pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0) goto BYPASS_SRTP; /* If the media is inactive, do nothing. */ /* No, we still need to process SRTP offer/answer even if the media is * marked as inactive, because the transport is still alive in this * case (e.g. for keep-alive). See: * http://trac.pjsip.org/repos/ticket/1079 */ /* if (pjmedia_sdp_media_find_attr(m_loc, &ID_INACTIVE, NULL) || (m_rem && pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL))) goto BYPASS_SRTP; */ /* Check remote media transport & set local media transport * based on SRTP usage option. */ if (srtp->offerer_side) { /* Generate transport */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: goto BYPASS_SRTP; case PJMEDIA_SRTP_OPTIONAL: m_loc->desc.transport = (srtp->peer_use == PJMEDIA_SRTP_MANDATORY)? ID_RTP_SAVP : ID_RTP_AVP; break; case PJMEDIA_SRTP_MANDATORY: m_loc->desc.transport = ID_RTP_SAVP; break; } /* Generate crypto attribute if not yet */ if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) { for (i=0; i<srtp->setting.crypto_count; ++i) { /* Offer crypto-suites based on setting. */ buffer_len = MAXLEN; status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, &srtp->setting.crypto[i], i+1); if (status != PJ_SUCCESS) return status; /* If buffer_len==0, just skip the crypto attribute. */ if (buffer_len) { pj_strset(&attr_value, buffer, buffer_len); attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr, &attr_value); m_loc->attr[m_loc->attr_count++] = attr; } } } } else { /* Answerer side */ pj_assert(sdp_remote && m_rem); /* Generate transport */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; goto BYPASS_SRTP; case PJMEDIA_SRTP_OPTIONAL: m_loc->desc.transport = m_rem->desc.transport; break; case PJMEDIA_SRTP_MANDATORY: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; m_loc->desc.transport = ID_RTP_SAVP; break; } /* Generate crypto attribute if not yet */ if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) { pjmedia_srtp_crypto tmp_rx_crypto; pj_bool_t has_crypto_attr = PJ_FALSE; int matched_idx = -1; int chosen_tag = 0; int tags[64]; /* assume no more than 64 crypto attrs in a media */ unsigned cr_attr_count = 0; /* Find supported crypto-suite, get the tag, and assign policy_local */ for (i=0; i<m_rem->attr_count; ++i) { if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) continue; has_crypto_attr = PJ_TRUE; status = parse_attr_crypto(srtp->pool, m_rem->attr[i], &tmp_rx_crypto, &tags[cr_attr_count]); if (status != PJ_SUCCESS) return status; /* Check duplicated tag */ for (j=0; j<cr_attr_count; ++j) { if (tags[j] == tags[cr_attr_count]) { DEACTIVATE_MEDIA(sdp_pool, m_loc); return PJMEDIA_SRTP_ESDPDUPCRYPTOTAG; } } if (matched_idx == -1) { /* lets see if the crypto-suite offered is supported */ for (j=0; j<srtp->setting.crypto_count; ++j) if (pj_stricmp(&tmp_rx_crypto.name, &srtp->setting.crypto[j].name) == 0) { int cs_idx = get_crypto_idx(&tmp_rx_crypto.name); /* Force to use test key */ /* bad keys for snom: */ //char *hex_test_key = "58b29c5c8f42308120ce857e439f2d" // "7810a8b10ad0b1446be5470faea496"; //char *hex_test_key = "20a26aac7ba062d356ff52b61e3993" // "ccb78078f12c64db94b9c294927fd0"; //pj_str_t *test_key = &srtp->setting.crypto[j].key; //char *raw_test_key = pj_pool_zalloc(srtp->pool, 64); //hex_string_to_octet_string( // raw_test_key, // hex_test_key, // strlen(hex_test_key)); //pj_strset(test_key, raw_test_key, // crypto_suites[cs_idx].cipher_key_len); /* EO Force to use test key */ if (tmp_rx_crypto.key.slen != (int)crypto_suites[cs_idx].cipher_key_len) return PJMEDIA_SRTP_EINKEYLEN; srtp->rx_policy_neg = tmp_rx_crypto; chosen_tag = tags[cr_attr_count]; matched_idx = j; break; } } cr_attr_count++; } /* Check crypto negotiation result */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: pj_assert(!"Should never reach here"); break; case PJMEDIA_SRTP_OPTIONAL: /* bypass SRTP when no crypto-attr and remote uses RTP/AVP */ if (!has_crypto_attr && pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) goto BYPASS_SRTP; /* bypass SRTP when nothing match and remote uses RTP/AVP */ else if (matched_idx == -1 && pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) goto BYPASS_SRTP; break; case PJMEDIA_SRTP_MANDATORY: /* Do nothing, intentional */ break; } /* No crypto attr */ if (!has_crypto_attr) { DEACTIVATE_MEDIA(sdp_pool, m_loc); return PJMEDIA_SRTP_ESDPREQCRYPTO; } /* No crypto match */ if (matched_idx == -1) { DEACTIVATE_MEDIA(sdp_pool, m_loc); return PJMEDIA_SRTP_ENOTSUPCRYPTO; } /* we have to generate crypto answer, * with srtp->tx_policy_neg matched the offer * and rem_tag contains matched offer tag. */ buffer_len = MAXLEN; status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, &srtp->setting.crypto[matched_idx], chosen_tag); if (status != PJ_SUCCESS) return status; srtp->tx_policy_neg = srtp->setting.crypto[matched_idx]; /* If buffer_len==0, just skip the crypto attribute. */ if (buffer_len) { pj_strset(&attr_value, buffer, buffer_len); attr = pjmedia_sdp_attr_create(sdp_pool, ID_CRYPTO.ptr, &attr_value); m_loc->attr[m_loc->attr_count++] = attr; } /* At this point, we get valid rx_policy_neg & tx_policy_neg. */ } } goto PROPAGATE_MEDIA_CREATE; BYPASS_SRTP: /* Do not update this flag here as actually the media session hasn't been * updated. */ //srtp->bypass_srtp = PJ_TRUE; PROPAGATE_MEDIA_CREATE: return pjmedia_transport_encode_sdp(srtp->member_tp, sdp_pool, sdp_local, sdp_remote, media_index); }
/* Parse ALLOCATE request */ static pj_status_t parse_allocate_req(alloc_request *cfg, pj_stun_session *sess, const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { const pj_stun_msg *req = rdata->msg; pj_stun_bandwidth_attr *attr_bw; pj_stun_req_transport_attr *attr_req_tp; pj_stun_res_token_attr *attr_res_token; pj_stun_req_props_attr *attr_rpp; pj_stun_lifetime_attr *attr_lifetime; pj_bzero(cfg, sizeof(*cfg)); /* Get BANDWIDTH attribute, if any. */ attr_bw = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0); if (attr_bw) { cfg->bandwidth = attr_bw->value; } else { cfg->bandwidth = DEFA_CLIENT_BANDWIDTH; } /* Check if we can satisfy the bandwidth */ if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ALLOCATION_QUOTA_REACHED, "Invalid bandwidth", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED); } /* MUST have REQUESTED-TRANSPORT attribute */ attr_req_tp = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0); if (attr_req_tp == NULL) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "Missing REQUESTED-TRANSPORT attribute", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value); /* Can only support UDP for now */ if (cfg->tp_type != PJ_TURN_TP_UDP) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, NULL, NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO); } /* Get RESERVATION-TOKEN attribute, if any */ attr_res_token = (pj_stun_res_token_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN, 0); if (attr_res_token) { /* We don't support RESERVATION-TOKEN for now */ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "RESERVATION-TOKEN is not supported", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } /* Get REQUESTED-PROPS attribute, if any */ attr_rpp = (pj_stun_req_props_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0); if (attr_rpp) { /* We don't support REQUESTED-PROPS for now */ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "REQUESTED-PROPS is not supported", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } /* Get LIFETIME attribute */ attr_lifetime = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0); if (attr_lifetime) { cfg->lifetime = attr_lifetime->value; if (cfg->lifetime < MIN_LIFETIME) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "LIFETIME too short", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } if (cfg->lifetime > MAX_LIFETIME) cfg->lifetime = MAX_LIFETIME; } else { cfg->lifetime = DEF_LIFETIME; } return PJ_SUCCESS; }