/** * Create UDP stream transport. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_create2(pjmedia_endpt *endpt, 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(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &si.rtp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind RTP socket */ pj_sockaddr_in_init(&si.rtp_addr_name, addr, (pj_uint16_t)port); status = pj_sock_bind(si.rtp_sock, &si.rtp_addr_name, sizeof(si.rtp_addr_name)); if (status != PJ_SUCCESS) goto on_error; /* Create RTCP socket */ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &si.rtcp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind RTCP socket */ pj_sockaddr_in_init(&si.rtcp_addr_name, addr, (pj_uint16_t)(port+1)); status = pj_sock_bind(si.rtcp_sock, &si.rtcp_addr_name, sizeof(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; }
/* Called to get the transport info */ static pj_status_t transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) { PJ_ASSERT_RETURN(tp && info, PJ_EINVAL); info->sock_info.rtp_sock = 1; pj_sockaddr_in_init(&info->sock_info.rtp_addr_name.ipv4, 0, 0); info->sock_info.rtcp_sock = 2; pj_sockaddr_in_init(&info->sock_info.rtcp_addr_name.ipv4, 0, 0); return PJ_SUCCESS; }
/* Create socket */ static pj_status_t create_socket(int af, const pj_sockaddr_t *local_a, int addr_len, pj_sock_t *p_sock) { pj_sock_t sock; pj_sockaddr_in tmp_addr; pj_status_t status; status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) return status; if (local_a == NULL) { pj_sockaddr_in_init(&tmp_addr, NULL, 0); local_a = &tmp_addr; addr_len = sizeof(tmp_addr); } status = pj_sock_bind(sock, local_a, addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } *p_sock = sock; return PJ_SUCCESS; }
/* Create socket */ static pj_status_t create_socket(int af, const pj_sockaddr_t *local_a, int addr_len, pj_sock_t *p_sock) { pj_sock_t sock; pj_sockaddr_in tmp_addr; pj_sockaddr_in6 tmp_addr6; pj_status_t status; status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) return status; if (local_a == NULL) { if (af == pj_AF_INET6()) { pj_bzero(&tmp_addr6, sizeof(tmp_addr6)); tmp_addr6.sin6_family = (pj_uint16_t)af; local_a = &tmp_addr6; addr_len = sizeof(tmp_addr6); } else { pj_sockaddr_in_init(&tmp_addr, NULL, 0); local_a = &tmp_addr; addr_len = sizeof(tmp_addr); } } status = pj_sock_bind(sock, local_a, addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } *p_sock = sock; return PJ_SUCCESS; }
/* DNS resolver callback */ static void dns_srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { pj_stun_sock *stun_sock = (pj_stun_sock*) user_data; /* Clear query */ stun_sock->q = NULL; /* Handle error */ if (status != PJ_SUCCESS) { sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status); return; } pj_assert(rec->count); pj_assert(rec->entry[0].server.addr_count); PJ_TODO(SUPPORT_IPV6_IN_RESOLVER); pj_assert(stun_sock->af == pj_AF_INET()); /* Set the address */ pj_sockaddr_in_init(&stun_sock->srv_addr.ipv4, NULL, rec->entry[0].port); stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0]; /* Start sending Binding request */ get_mapped_addr(stun_sock); }
void vnat_stun_detect_nat_type(v_ice_trans_t *ice_tran, pj_str_t stun_srv) { pj_status_t status; printf("[DEBUG] %s, %d adress: %s \n", __func__, __LINE__, stun_srv.ptr); /* Command line option may contain port number */ char *pos; if ((pos=pj_strchr(&stun_srv, ':')) != NULL) { printf("[DEBUG] %s, %d \n", __func__, __LINE__); server.ptr = stun_srv.ptr; server.slen = (pos - stun_srv.ptr); port = (pj_uint16_t)atoi(pos+1) + 1; } else { printf("[DEBUG] %s, %d \n", __func__, __LINE__); server = stun_srv; port = PJ_STUN_PORT; } printf("[DEBUG] %s, %d \n", __func__, __LINE__); pj_sockaddr_in_init(&addr_in, &server, port); status = pj_stun_detect_nat_type(&addr_in, &ice_tran->ice_cfg.stun_cfg, NULL, &nat_detect_cb); }
/* * Init media stack. */ static pj_status_t init_media() { unsigned i; pj_uint16_t rtp_port; pj_status_t status; /* Initialize media endpoint so that at least error subsystem is properly * initialized. */ status = pjmedia_endpt_create(&app.cp.factory, pjsip_endpt_get_ioqueue(app.sip_endpt), 0, &app.med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Must register all codecs to be supported */ pjmedia_codec_register_audio_codecs(app.med_endpt, NULL); /* Init dummy socket addresses */ app.skinfo_cnt = 0; for (i=0, rtp_port=4000; i<PJ_ARRAY_SIZE(app.skinfo); ++i, rtp_port+=2) { pjmedia_sock_info *skinfo; skinfo = &app.skinfo[i]; pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr, (pj_uint16_t)rtp_port); pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr, (pj_uint16_t)(rtp_port+1)); app.skinfo_cnt++; } /* Generate dummy SDP */ dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr); status = pjmedia_sdp_parse(app.pool, dummy_sdp_str.ptr, dummy_sdp_str.slen, &app.dummy_sdp); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error parsing dummy SDP", status); return status; } /* Done */ return PJ_SUCCESS; }
/* * This callback is called when target is resolved with DNS A query. */ static void dns_a_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *pkt) { struct query *query = (struct query*) user_data; pjsip_server_addresses *srv = &query->server; /* Reset outstanding job */ query->object = NULL; if (status == PJ_SUCCESS) { pj_dns_addr_record rec; unsigned i; /* Parse the response */ rec.addr_count = 0; status = pj_dns_parse_addr_response(pkt, &rec); /* Build server addresses and call callback */ for (i = 0; i < rec.addr_count && srv->count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i) { /* Should not happen, just in case */ if (rec.addr[i].af != pj_AF_INET()) continue; srv->entry[srv->count].type = query->naptr[0].type; srv->entry[srv->count].priority = 0; srv->entry[srv->count].weight = 0; srv->entry[srv->count].addr_len = sizeof(pj_sockaddr_in); pj_sockaddr_in_init(&srv->entry[srv->count].addr.ipv4, 0, (pj_uint16_t)query->req.def_port); srv->entry[srv->count].addr.ipv4.sin_addr = rec.addr[i].ip.v4; ++srv->count; } } if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", errmsg)); query->last_error = status; } /* Call the callback if all DNS queries have been completed */ if (query->object == NULL && query->object6 == NULL) { if (srv->count > 0) (*query->cb)(PJ_SUCCESS, query->token, &query->server); else (*query->cb)(query->last_error, query->token, NULL); } }
int inv_offer_answer_test(void) { unsigned i; int rc = 0; /* Init UA layer */ if (pjsip_ua_instance()->id == -1) { pjsip_ua_init_param ua_param; pj_bzero(&ua_param, sizeof(ua_param)); ua_param.on_dlg_forked = &on_dlg_forked; pjsip_ua_init_module(endpt, &ua_param); } /* Init inv-usage */ if (pjsip_inv_usage_instance()->id == -1) { pjsip_inv_callback inv_cb; pj_bzero(&inv_cb, sizeof(inv_cb)); inv_cb.on_media_update = &on_media_update; inv_cb.on_rx_offer = &on_rx_offer; inv_cb.on_create_offer = &on_create_offer; inv_cb.on_state_changed = &on_state_changed; inv_cb.on_new_session = &on_new_session; pjsip_inv_usage_init(endpt, &inv_cb); } /* 100rel module */ pjsip_100rel_init_module(endpt); /* Our module */ pjsip_endpt_register_module(endpt, &mod_inv_oa_test); pjsip_endpt_register_module(endpt, &mod_msg_logger); /* Create SIP UDP transport */ { pj_sockaddr_in addr; pjsip_transport *tp; pj_status_t status; pj_sockaddr_in_init(&addr, NULL, PORT); status = pjsip_udp_transport_start(endpt, &addr, NULL, 1, &tp); pj_assert(status == PJ_SUCCESS); } /* Do tests */ for (i=0; i<PJ_ARRAY_SIZE(test_params); ++i) { rc = perform_test(&test_params[i]); if (rc != 0) goto on_return; } on_return: return rc; }
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; 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]!=A0 || p[1]!=A1 || p[2]!=A2 || p[3]!=A3) { 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 -30; /* 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; }
/* * This callback is called when target is resolved with DNS A query. */ static void dns_a_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *pkt) { struct query *query = (struct query*) user_data; pjsip_server_addresses srv; pj_dns_a_record rec; unsigned i; rec.addr_count = 0; /* Parse the response */ if (status == PJ_SUCCESS) { status = pj_dns_parse_a_response(pkt, &rec); } if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", errmsg)); /* Call the callback */ (*query->cb)(status, query->token, NULL); return; } /* Build server addresses and call callback */ srv.count = 0; for (i = 0; i < rec.addr_count && srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i) { srv.entry[srv.count].type = query->naptr[0].type; srv.entry[srv.count].priority = 0; srv.entry[srv.count].weight = 0; srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in); pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4, 0, (pj_uint16_t)query->req.def_port); srv.entry[srv.count].addr.ipv4.sin_addr.s_addr = rec.addr[i].s_addr; ++srv.count; } /* Call the callback */ (*query->cb)(PJ_SUCCESS, query->token, &srv); }
/* Callback to be called by DNS SRV resolution */ static void srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { struct query *query = (struct query*) user_data; pjsip_server_addresses srv; unsigned i; if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", errmsg)); /* Call the callback */ (*query->cb)(status, query->token, NULL); return; } /* Build server addresses and call callback */ srv.count = 0; for (i=0; i<rec->count; ++i) { unsigned j; for (j = 0; j < rec->entry[i].server.addr_count && srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++j) { srv.entry[srv.count].type = query->naptr[0].type; srv.entry[srv.count].priority = rec->entry[i].priority; srv.entry[srv.count].weight = rec->entry[i].weight; srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in); pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4, 0, (pj_uint16_t)rec->entry[i].port); srv.entry[srv.count].addr.ipv4.sin_addr.s_addr = rec->entry[i].server.addr[j].s_addr; ++srv.count; } } /* Call the callback */ (*query->cb)(PJ_SUCCESS, query->token, &srv); }
static pj_status_t udp_echo_srv_create(pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_bool_t enable_echo, struct udp_echo_srv **p_srv) { struct udp_echo_srv *srv; pj_sock_t sock_fd = PJ_INVALID_SOCKET; pj_sockaddr addr; int addr_len; pj_activesock_cb activesock_cb; pj_status_t status; srv = PJ_POOL_ZALLOC_T(pool, struct udp_echo_srv); srv->echo_enabled = enable_echo; pj_sockaddr_in_init(&addr.ipv4, NULL, 0); addr_len = sizeof(addr); pj_bzero(&activesock_cb, sizeof(activesock_cb)); activesock_cb.on_data_recvfrom = &udp_echo_srv_on_data_recvfrom; status = pj_activesock_create_udp(pool, &addr, NULL, ioqueue, &activesock_cb, srv, &srv->asock, &addr); if (status != PJ_SUCCESS) { pj_sock_close(sock_fd); udp_echo_err("pj_activesock_create()", status); return status; } srv->port = pj_ntohs(addr.ipv4.sin_port); pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key)); status = pj_activesock_start_recvfrom(srv->asock, pool, 32, 0); if (status != PJ_SUCCESS) { pj_activesock_close(srv->asock); udp_echo_err("pj_activesock_start_recvfrom()", status); return status; } *p_srv = srv; return PJ_SUCCESS; }
static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_sockaddr_in mapped_addr; pj_str_t s1; pj_status_t status; status = pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, pj_cstr(&s1, "test vector")); if (status != PJ_SUCCESS) goto on_error; status = pj_sockaddr_in_init(&mapped_addr, pj_cstr(&s1, "192.0.2.1"), 32853); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &mapped_addr, sizeof(pj_sockaddr_in)); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_msgint_attr(pool, msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); if (status != PJ_SUCCESS) goto on_error; return msg; on_error: app_perror(" error: create_msgint2()", status); return NULL; }
/* * Configure name servers for the DNS resolver. */ PJ_DEF(pj_status_t) pj_dns_resolver_set_ns( pj_dns_resolver *resolver, unsigned count, const pj_str_t servers[], const pj_uint16_t ports[]) { unsigned i; pj_time_val now; pj_status_t status; PJ_ASSERT_RETURN(resolver && count && servers, PJ_EINVAL); PJ_ASSERT_RETURN(count < PJ_DNS_RESOLVER_MAX_NS, PJ_EINVAL); pj_mutex_lock(resolver->mutex); if (count > PJ_DNS_RESOLVER_MAX_NS) count = PJ_DNS_RESOLVER_MAX_NS; resolver->ns_count = 0; pj_bzero(resolver->ns, sizeof(resolver->ns)); pj_gettimeofday(&now); for (i=0; i<count; ++i) { struct nameserver *ns = &resolver->ns[i]; status = pj_sockaddr_in_init(&ns->addr, &servers[i], (pj_uint16_t)(ports ? ports[i] : PORT)); if (status != PJ_SUCCESS) { pj_mutex_unlock(resolver->mutex); return PJLIB_UTIL_EDNSINNSADDR; } ns->state = STATE_ACTIVE; ns->state_expiry = now; ns->rt_delay.sec = 10; } resolver->ns_count = count; pj_mutex_unlock(resolver->mutex); return PJ_SUCCESS; }
static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_sockaddr_in mapped_addr; pj_str_t s1; pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, pj_cstr(&s1, "test vector")); pj_sockaddr_in_init(&mapped_addr, pj_cstr(&s1, "127.0.0.1"), 32853); pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &mapped_addr, sizeof(pj_sockaddr_in)); pj_stun_msg_add_msgint_attr(pool, msg); pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); return msg; }
static pj_status_t create_server(pj_pool_t *pool, pj_ioqueue_t *ioqueue, unsigned flag, struct stun_srv **p_srv) { struct stun_srv *srv; pj_activesock_cb activesock_cb; pj_status_t status; srv = PJ_POOL_ZALLOC_T(pool, struct stun_srv); srv->flag = flag; srv->ip_to_send = pj_str("1.1.1.1"); srv->port_to_send = 1000; status = pj_sockaddr_in_init(&srv->addr.ipv4, NULL, 0); if (status != PJ_SUCCESS) return status; pj_bzero(&activesock_cb, sizeof(activesock_cb)); activesock_cb.on_data_recvfrom = &srv_on_data_recvfrom; status = pj_activesock_create_udp(pool, &srv->addr, NULL, ioqueue, &activesock_cb, srv, &srv->asock, &srv->addr); if (status != PJ_SUCCESS) return status; pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key)); status = pj_activesock_start_recvfrom(srv->asock, pool, 512, 0); if (status != PJ_SUCCESS) { pj_activesock_close(srv->asock); return status; } *p_srv = srv; return PJ_SUCCESS; }
/* * Initialize IP socket address based on the address and port info. */ PJ_DEF(pj_status_t) pj_sockaddr_init(int af, pj_sockaddr *addr, const pj_str_t *cp, pj_uint16_t port) { pj_status_t status; if (af == PJ_AF_INET) { return pj_sockaddr_in_init(&addr->ipv4, cp, port); } /* IPv6 specific */ PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP); pj_bzero(addr, sizeof(pj_sockaddr_in6)); addr->addr.sa_family = PJ_AF_INET6; status = pj_sockaddr_set_str_addr(af, addr, cp); if (status != PJ_SUCCESS) return status; addr->ipv6.sin6_port = pj_htons(port); return PJ_SUCCESS; }
/* 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 is the public API to create, initialize, register, and start the * TLS listener. */ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pj_pool_t *pool; struct tls_listener *listener; pj_ssl_sock_param ssock_param; pj_sockaddr_in *listener_addr; pj_bool_t has_listener; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ if (a_name && a_name->host.slen) { pj_sockaddr_in tmp; status = pj_sockaddr_in_init(&tmp, &a_name->host, (pj_uint16_t)a_name->port); if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY || tmp.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; listener->factory.type = PJSIP_TRANSPORT_TLS; listener->factory.type_name = "tls"; listener->factory.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS); pj_ansi_strcpy(listener->factory.obj_name, "tlslis"); 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, "tlslis", &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.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.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; listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr; if (local) { pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, (const pj_sockaddr_t*)local); } else { pj_sockaddr_in_init(listener_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 (listener_addr->sin_addr.s_addr == 0) { pj_sockaddr hostip; status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) goto on_error; listener_addr->sin_addr.s_addr = hostip.ipv4.sin_addr.s_addr; } /* 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_ntohs(listener_addr->sin_port); } 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; }
/* * Benchmarking IOQueue */ static int bench_test(pj_bool_t allow_concur, int bufsize, int inactive_sock_count) { pj_sock_t ssock=-1, csock=-1; pj_sockaddr_in addr; pj_pool_t *pool = NULL; pj_sock_t *inactive_sock=NULL; pj_ioqueue_op_key_t *inactive_read_op; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; pj_ioqueue_key_t *skey, *ckey, *keys[SOCK_INACTIVE_MAX+2]; pj_timestamp t1, t2, t_elapsed; int rc=0, i; /* i must be signed */ pj_str_t temp; char errbuf[PJ_ERR_MSG_SIZE]; TRACE__((THIS_FILE, " bench test %d", inactive_sock_count)); // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Allocate buffers for send and receive. send_buf = (char*)pj_pool_alloc(pool, bufsize); recv_buf = (char*)pj_pool_alloc(pool, bufsize); // Allocate sockets for sending and receiving. rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock); if (rc == PJ_SUCCESS) { rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock); } else csock = PJ_INVALID_SOCKET; if (rc != PJ_SUCCESS) { app_perror("...error: pj_sock_socket()", rc); goto on_error; } // Bind server socket. pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); addr.sin_port = pj_htons(PORT); if (pj_sock_bind(ssock, &addr, sizeof(addr))) goto on_error; pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES); // Create I/O Queue. rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (rc != PJ_SUCCESS) { app_perror("...error: pj_ioqueue_create()", rc); goto on_error; } // Set concurrency rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { app_perror("...error: pj_ioqueue_set_default_concurrency()", rc); goto on_error; } // Allocate inactive sockets, and bind them to some arbitrary address. // Then register them to the I/O queue, and start a read operation. inactive_sock = (pj_sock_t*)pj_pool_alloc(pool, inactive_sock_count*sizeof(pj_sock_t)); inactive_read_op = (pj_ioqueue_op_key_t*)pj_pool_alloc(pool, inactive_sock_count*sizeof(pj_ioqueue_op_key_t)); pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); for (i=0; i<inactive_sock_count; ++i) { pj_ssize_t bytes; rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &inactive_sock[i]); if (rc != PJ_SUCCESS || inactive_sock[i] < 0) { app_perror("...error: pj_sock_socket()", rc); goto on_error; } if ((rc=pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) != 0) { pj_sock_close(inactive_sock[i]); inactive_sock[i] = PJ_INVALID_SOCKET; app_perror("...error: pj_sock_bind()", rc); goto on_error; } rc = pj_ioqueue_register_sock(pool, ioque, inactive_sock[i], NULL, &test_cb, &keys[i]); if (rc != PJ_SUCCESS) { pj_sock_close(inactive_sock[i]); inactive_sock[i] = PJ_INVALID_SOCKET; app_perror("...error(1): pj_ioqueue_register_sock()", rc); PJ_LOG(3,(THIS_FILE, "....i=%d", i)); goto on_error; } bytes = bufsize; rc = pj_ioqueue_recv(keys[i], &inactive_read_op[i], recv_buf, &bytes, 0); if (rc != PJ_EPENDING) { pj_sock_close(inactive_sock[i]); inactive_sock[i] = PJ_INVALID_SOCKET; app_perror("...error: pj_ioqueue_read()", rc); goto on_error; } } // Register server and client socket. // We put this after inactivity socket, hopefully this can represent the // worst waiting time. rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey); if (rc != PJ_SUCCESS) { app_perror("...error(2): pj_ioqueue_register_sock()", rc); goto on_error; } rc = pj_ioqueue_register_sock(pool, ioque, csock, NULL, &test_cb, &ckey); if (rc != PJ_SUCCESS) { app_perror("...error(3): pj_ioqueue_register_sock()", rc); goto on_error; } // Set destination address to send the packet. pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT); // Test loop. t_elapsed.u64 = 0; for (i=0; i<LOOP; ++i) { pj_ssize_t bytes; pj_ioqueue_op_key_t read_op, write_op; // Randomize send buffer. pj_create_random_string(send_buf, bufsize); // Start reading on the server side. bytes = bufsize; rc = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0); if (rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_read()", rc); break; } // Starts send on the client side. bytes = bufsize; rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &addr, sizeof(addr)); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_write()", rc); break; } if (rc == PJ_SUCCESS) { if (bytes < 0) { app_perror("...error: pj_ioqueue_sendto()",(pj_status_t)-bytes); break; } } // Begin time. pj_get_timestamp(&t1); // Poll the queue until we've got completion event in the server side. callback_read_key = NULL; callback_read_size = 0; TRACE__((THIS_FILE, " waiting for key = %p", skey)); do { pj_time_val timeout = { 1, 0 }; #ifdef PJ_SYMBIAN rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout)); #else rc = pj_ioqueue_poll(ioque, &timeout); #endif TRACE__((THIS_FILE, " poll rc=%d", rc)); } while (rc >= 0 && callback_read_key != skey); // End time. pj_get_timestamp(&t2); t_elapsed.u64 += (t2.u64 - t1.u64); if (rc < 0) { app_perror(" error: pj_ioqueue_poll", -rc); break; } // Compare recv buffer with send buffer. if (callback_read_size != bufsize || pj_memcmp(send_buf, recv_buf, bufsize)) { rc = -10; PJ_LOG(3,(THIS_FILE, " error: size/buffer mismatch")); break; } // Poll until all events are exhausted, before we start the next loop. do { pj_time_val timeout = { 0, 10 }; #ifdef PJ_SYMBIAN PJ_UNUSED_ARG(timeout); rc = pj_symbianos_poll(-1, 100); #else rc = pj_ioqueue_poll(ioque, &timeout); #endif } while (rc>0); rc = 0; } // Print results if (rc == 0) { pj_timestamp tzero; pj_uint32_t usec_delay; tzero.u32.hi = tzero.u32.lo = 0; usec_delay = pj_elapsed_usec( &tzero, &t_elapsed); PJ_LOG(3, (THIS_FILE, "...%10d %15d % 9d", bufsize, inactive_sock_count, usec_delay)); } else { PJ_LOG(2, (THIS_FILE, "...ERROR rc=%d (buf:%d, fds:%d)", rc, bufsize, inactive_sock_count+2)); } // Cleaning up. for (i=inactive_sock_count-1; i>=0; --i) { pj_ioqueue_unregister(keys[i]); } pj_ioqueue_unregister(skey); pj_ioqueue_unregister(ckey); pj_ioqueue_destroy(ioque); pj_pool_release( pool); return rc; on_error: PJ_LOG(1,(THIS_FILE, "...ERROR: %s", pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf)))); if (ssock) pj_sock_close(ssock); if (csock) pj_sock_close(csock); for (i=0; i<inactive_sock_count && inactive_sock && inactive_sock[i]!=PJ_INVALID_SOCKET; ++i) { pj_sock_close(inactive_sock[i]); } if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release( pool); return -1; }
pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_uint32_t flags, const char *domain, test_server **p_test_srv) { pj_pool_t *pool; test_server *test_srv; pj_sockaddr hostip; char strbuf[100]; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && domain && p_test_srv, PJ_EINVAL); status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) return status; pool = pj_pool_create(mem, THIS_FILE, 512, 512, NULL); test_srv = (test_server*) PJ_POOL_ZALLOC_T(pool, test_server); test_srv->pool = pool; test_srv->flags = flags; test_srv->stun_cfg = stun_cfg; pj_strdup2(pool, &test_srv->domain, domain); test_srv->username = pj_str(TURN_USERNAME); test_srv->passwd = pj_str(TURN_PASSWD); pj_ioqueue_op_key_init(&test_srv->send_key, sizeof(test_srv->send_key)); if (flags & CREATE_DNS_SERVER) { status = pj_dns_server_create(mem, test_srv->stun_cfg->ioqueue, pj_AF_INET(), DNS_SERVER_PORT, 0, &test_srv->dns_server); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } /* Add DNS A record for the domain, for fallback */ if (flags & CREATE_A_RECORD_FOR_DOMAIN) { pj_dns_parsed_rr rr; pj_str_t res_name; pj_in_addr ip_addr; pj_strdup2(pool, &res_name, domain); ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } if (flags & CREATE_STUN_SERVER) { pj_activesock_cb stun_sock_cb; pj_sockaddr bound_addr; pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); stun_sock_cb.on_data_recvfrom = &stun_on_data_recvfrom; pj_sockaddr_in_init(&bound_addr.ipv4, NULL, STUN_SERVER_PORT); status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &stun_sock_cb, test_srv, &test_srv->stun_sock, NULL); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } status = pj_activesock_start_recvfrom(test_srv->stun_sock, pool, MAX_STUN_PKT, 0); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } if (test_srv->dns_server && (flags & CREATE_STUN_SERVER_DNS_SRV)) { pj_str_t res_name, target; pj_dns_parsed_rr rr; pj_in_addr ip_addr; /* Add DNS entries: * _stun._udp.domain 60 IN SRV 0 0 PORT stun.domain. * stun.domain IN A 127.0.0.1 */ pj_ansi_snprintf(strbuf, sizeof(strbuf), "_stun._udp.%s", domain); pj_strdup2(pool, &res_name, strbuf); pj_ansi_snprintf(strbuf, sizeof(strbuf), "stun.%s", domain); pj_strdup2(pool, &target, strbuf); pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, STUN_SERVER_PORT, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } if (flags & CREATE_TURN_SERVER) { pj_activesock_cb turn_sock_cb; pj_sockaddr bound_addr; pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); turn_sock_cb.on_data_recvfrom = &turn_on_data_recvfrom; pj_sockaddr_in_init(&bound_addr.ipv4, NULL, TURN_SERVER_PORT); status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &turn_sock_cb, test_srv, &test_srv->turn_sock, NULL); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } status = pj_activesock_start_recvfrom(test_srv->turn_sock, pool, MAX_STUN_PKT, 0); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } if (test_srv->dns_server && (flags & CREATE_TURN_SERVER_DNS_SRV)) { pj_str_t res_name, target; pj_dns_parsed_rr rr; pj_in_addr ip_addr; /* Add DNS entries: * _turn._udp.domain 60 IN SRV 0 0 PORT turn.domain. * turn.domain IN A 127.0.0.1 */ pj_ansi_snprintf(strbuf, sizeof(strbuf), "_turn._udp.%s", domain); pj_strdup2(pool, &res_name, strbuf); pj_ansi_snprintf(strbuf, sizeof(strbuf), "turn.%s", domain); pj_strdup2(pool, &target, strbuf); pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, TURN_SERVER_PORT, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } *p_test_srv = test_srv; return PJ_SUCCESS; }
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; }
/* * 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; }
/* * Repeated connect/accept on the same listener socket. */ static int compliance_test_2(pj_bool_t allow_concur) { #if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 enum { MAX_PAIR = 1, TEST_LOOP = 2 }; #else enum { MAX_PAIR = 4, TEST_LOOP = 2 }; #endif struct listener { pj_sock_t sock; pj_ioqueue_key_t *key; pj_sockaddr_in addr; int addr_len; } listener; struct server { pj_sock_t sock; pj_ioqueue_key_t *key; pj_sockaddr_in local_addr; pj_sockaddr_in rem_addr; int rem_addr_len; pj_ioqueue_op_key_t accept_op; } server[MAX_PAIR]; struct client { pj_sock_t sock; pj_ioqueue_key_t *key; } client[MAX_PAIR]; pj_pool_t *pool = NULL; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; int i, bufsize = BUF_MIN_SIZE; pj_ssize_t status; int test_loop, pending_op = 0; pj_timestamp t_elapsed; pj_str_t s; pj_status_t rc; listener.sock = PJ_INVALID_SOCKET; listener.key = NULL; for (i=0; i<MAX_PAIR; ++i) { server[i].sock = PJ_INVALID_SOCKET; server[i].key = NULL; } for (i=0; i<MAX_PAIR; ++i) { client[i].sock = PJ_INVALID_SOCKET; client[i].key = NULL; } // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Create I/O Queue. rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_create()", rc); return -10; } // Concurrency rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_set_default_concurrency()", rc); return -11; } // Allocate buffers for send and receive. send_buf = (char*)pj_pool_alloc(pool, bufsize); recv_buf = (char*)pj_pool_alloc(pool, bufsize); // Create listener socket rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &listener.sock); if (rc != PJ_SUCCESS) { app_perror("...error creating socket", rc); status=-20; goto on_error; } // Bind listener socket. pj_sockaddr_in_init(&listener.addr, 0, 0); if ((rc=pj_sock_bind(listener.sock, &listener.addr, sizeof(listener.addr))) != 0 ) { app_perror("...bind error", rc); status=-30; goto on_error; } // Get listener address. listener.addr_len = sizeof(listener.addr); rc = pj_sock_getsockname(listener.sock, &listener.addr, &listener.addr_len); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_sock_getsockname()", rc); status=-40; goto on_error; } listener.addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); // Register listener socket. rc = pj_ioqueue_register_sock(pool, ioque, listener.sock, NULL, &test_cb, &listener.key); if (rc != PJ_SUCCESS) { app_perror("...ERROR", rc); status=-50; goto on_error; } // Listener socket listen(). if (pj_sock_listen(listener.sock, 5)) { app_perror("...ERROR in pj_sock_listen()", rc); status=-60; goto on_error; } for (test_loop=0; test_loop < TEST_LOOP; ++test_loop) { // Client connect and server accept. for (i=0; i<MAX_PAIR; ++i) { rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &client[i].sock); if (rc != PJ_SUCCESS) { app_perror("...error creating socket", rc); status=-70; goto on_error; } rc = pj_ioqueue_register_sock(pool, ioque, client[i].sock, NULL, &test_cb, &client[i].key); if (rc != PJ_SUCCESS) { app_perror("...error ", rc); status=-80; goto on_error; } // Server socket accept() pj_ioqueue_op_key_init(&server[i].accept_op, sizeof(server[i].accept_op)); server[i].rem_addr_len = sizeof(pj_sockaddr_in); status = pj_ioqueue_accept(listener.key, &server[i].accept_op, &server[i].sock, &server[i].local_addr, &server[i].rem_addr, &server[i].rem_addr_len); if (status!=PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...ERROR in pj_ioqueue_accept()", rc); status=-90; goto on_error; } if (status==PJ_EPENDING) { ++pending_op; } // Client socket connect() status = pj_ioqueue_connect(client[i].key, &listener.addr, sizeof(listener.addr)); if (status!=PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...ERROR in pj_ioqueue_connect()", rc); status=-100; goto on_error; } if (status==PJ_EPENDING) { ++pending_op; } } // Poll until all connected while (pending_op) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status > 0) { if (status > pending_op) { PJ_LOG(3,(THIS_FILE, "...error: pj_ioqueue_poll() returned %d " "(only expecting %d)", status, pending_op)); return -110; } pending_op -= status; if (pending_op == 0) { status = 0; } } } // There's no pending operation. // When we poll the ioqueue, there must not be events. if (pending_op == 0) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status != 0) { status=-120; goto on_error; } } for (i=0; i<MAX_PAIR; ++i) { // Check server socket. if (server[i].sock == PJ_INVALID_SOCKET) { status = -130; app_perror("...accept() error", pj_get_os_error()); goto on_error; } // Check addresses if (server[i].local_addr.sin_family != pj_AF_INET() || server[i].local_addr.sin_addr.s_addr == 0 || server[i].local_addr.sin_port == 0) { app_perror("...ERROR address not set", rc); status = -140; goto on_error; } if (server[i].rem_addr.sin_family != pj_AF_INET() || server[i].rem_addr.sin_addr.s_addr == 0 || server[i].rem_addr.sin_port == 0) { app_perror("...ERROR address not set", rc); status = -150; goto on_error; } // Register newly accepted socket. rc = pj_ioqueue_register_sock(pool, ioque, server[i].sock, NULL, &test_cb, &server[i].key); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_register_sock", rc); status = -160; goto on_error; } // Test send and receive. t_elapsed.u32.lo = 0; status = send_recv_test(ioque, server[i].key, client[i].key, send_buf, recv_buf, bufsize, &t_elapsed); if (status != 0) { goto on_error; } } // Success status = 0; for (i=0; i<MAX_PAIR; ++i) { if (server[i].key != NULL) { pj_ioqueue_unregister(server[i].key); server[i].key = NULL; server[i].sock = PJ_INVALID_SOCKET; } else if (server[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(server[i].sock); server[i].sock = PJ_INVALID_SOCKET; } if (client[i].key != NULL) { pj_ioqueue_unregister(client[i].key); client[i].key = NULL; client[i].sock = PJ_INVALID_SOCKET; } else if (client[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(client[i].sock); client[i].sock = PJ_INVALID_SOCKET; } } } status = 0; on_error: for (i=0; i<MAX_PAIR; ++i) { if (server[i].key != NULL) { pj_ioqueue_unregister(server[i].key); server[i].key = NULL; server[i].sock = PJ_INVALID_SOCKET; } else if (server[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(server[i].sock); server[i].sock = PJ_INVALID_SOCKET; } if (client[i].key != NULL) { pj_ioqueue_unregister(client[i].key); client[i].key = NULL; server[i].sock = PJ_INVALID_SOCKET; } else if (client[i].sock != PJ_INVALID_SOCKET) { pj_sock_close(client[i].sock); client[i].sock = PJ_INVALID_SOCKET; } } if (listener.key) { pj_ioqueue_unregister(listener.key); listener.key = NULL; } else if (listener.sock != PJ_INVALID_SOCKET) { pj_sock_close(listener.sock); listener.sock = PJ_INVALID_SOCKET; } if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release(pool); return status; }
/* * Compliance test for failed scenario. * In this case, the client connects to a non-existant service. */ static int compliance_test_1(pj_bool_t allow_concur) { pj_sock_t csock1=PJ_INVALID_SOCKET; pj_sockaddr_in addr; pj_pool_t *pool = NULL; pj_ioqueue_t *ioque = NULL; pj_ioqueue_key_t *ckey1 = NULL; pj_ssize_t status = -1; int pending_op = 0; pj_str_t s; pj_status_t rc; // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Create I/O Queue. rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (!ioque) { status=-20; goto on_error; } // Concurrency rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { status=-21; goto on_error; } // Create client socket rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &csock1); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_sock_socket()", rc); status=-1; goto on_error; } // Register client socket. rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb, &ckey1); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_register_sock()", rc); status=-23; goto on_error; } // Initialize remote address. pj_sockaddr_in_init(&addr, pj_cstr(&s, "127.0.0.1"), NON_EXISTANT_PORT); // Client socket connect() status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr)); if (status==PJ_SUCCESS) { // unexpectedly success! status = -30; goto on_error; } if (status != PJ_EPENDING) { // success } else { ++pending_op; } callback_connect_status = -2; callback_connect_key = NULL; // Poll until we've got result while (pending_op) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN callback_call_count = 0; pj_symbianos_poll(-1, 1000); status = callback_call_count; #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status > 0) { if (callback_connect_key==ckey1) { if (callback_connect_status == 0) { // unexpectedly connected! status = -50; goto on_error; } } if (status > pending_op) { PJ_LOG(3,(THIS_FILE, "...error: pj_ioqueue_poll() returned %d " "(only expecting %d)", status, pending_op)); return -552; } pending_op -= status; if (pending_op == 0) { status = 0; } } } // There's no pending operation. // When we poll the ioqueue, there must not be events. if (pending_op == 0) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status != 0) { status=-60; goto on_error; } } // Success status = 0; on_error: if (ckey1 != NULL) pj_ioqueue_unregister(ckey1); else if (csock1 != PJ_INVALID_SOCKET) pj_sock_close(csock1); if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release(pool); return status; }
/* Instantiate standard server */ static int create_std_server(pj_stun_auth_type auth_type, pj_bool_t responding) { pj_pool_t *pool; pj_stun_session_cb sess_cb; pj_stun_auth_cred cred; pj_status_t status; /* Create server */ pool = pj_pool_create(mem, "server", 1000, 1000, NULL); server = PJ_POOL_ZALLOC_T(pool, struct server); server->pool = pool; server->auth_type = auth_type; server->responding = responding; /* Create STUN session */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_rx_request = &server_on_rx_request; sess_cb.on_send_msg = &server_send_msg; status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, NULL, &server->sess); if (status != PJ_SUCCESS) { destroy_server(); return -10; } /* Configure credential */ pj_bzero(&cred, sizeof(cred)); cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; cred.data.dyn_cred.get_auth = &server_get_auth; cred.data.dyn_cred.get_password = &server_get_password; cred.data.dyn_cred.verify_nonce = &server_verify_nonce; status = pj_stun_session_set_credential(server->sess, auth_type, &cred); if (status != PJ_SUCCESS) { destroy_server(); return -20; } /* Create socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &server->sock); if (status != PJ_SUCCESS) { destroy_server(); return -30; } /* Bind */ pj_sockaddr_in_init(&server->addr.ipv4, NULL, 0); status = pj_sock_bind(server->sock, &server->addr, pj_sockaddr_get_len(&server->addr)); if (status != PJ_SUCCESS) { destroy_server(); return -40; } else { /* Get the bound IP address */ int namelen = sizeof(server->addr); pj_sockaddr addr; status = pj_sock_getsockname(server->sock, &server->addr, &namelen); if (status != PJ_SUCCESS) { destroy_server(); return -43; } status = pj_gethostip(pj_AF_INET(), &addr); if (status != PJ_SUCCESS) { destroy_server(); return -45; } pj_sockaddr_copy_addr(&server->addr, &addr); } /* Create worker thread */ status = pj_thread_create(pool, "server", &server_thread, 0, 0, 0, &server->thread); if (status != PJ_SUCCESS) { destroy_server(); return -30; } return 0; }
/* * compliance_test() * To test that the basic IOQueue functionality works. It will just exchange * data between two sockets. */ static int compliance_test(pj_bool_t allow_concur) { pj_sock_t ssock=-1, csock=-1; pj_sockaddr_in addr, dst_addr; int addrlen; pj_pool_t *pool = NULL; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; pj_ioqueue_key_t *skey = NULL, *ckey = NULL; pj_ioqueue_op_key_t read_op, write_op; int bufsize = BUF_MIN_SIZE; pj_ssize_t bytes; int status = -1; pj_str_t temp; pj_bool_t send_pending, recv_pending; pj_status_t rc; pj_set_os_error(PJ_SUCCESS); // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Allocate buffers for send and receive. send_buf = (char*)pj_pool_alloc(pool, bufsize); recv_buf = (char*)pj_pool_alloc(pool, bufsize); // Allocate sockets for sending and receiving. TRACE_("creating sockets..."); rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock); if (rc==PJ_SUCCESS) rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock); else csock = PJ_INVALID_SOCKET; if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_sock_socket()", rc); status=-1; goto on_error; } // Bind server socket. TRACE_("bind socket..."); pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); addr.sin_port = pj_htons(PORT); if (pj_sock_bind(ssock, &addr, sizeof(addr))) { status=-10; goto on_error; } // Create I/O Queue. TRACE_("create ioqueue..."); rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (rc != PJ_SUCCESS) { status=-20; goto on_error; } // Set concurrency TRACE_("set concurrency..."); rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { status=-21; goto on_error; } // Register server and client socket. // We put this after inactivity socket, hopefully this can represent the // worst waiting time. TRACE_("registering first sockets..."); rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey); if (rc != PJ_SUCCESS) { app_perror("...error(10): ioqueue_register error", rc); status=-25; goto on_error; } TRACE_("registering second sockets..."); rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL, &test_cb, &ckey); if (rc != PJ_SUCCESS) { app_perror("...error(11): ioqueue_register error", rc); status=-26; goto on_error; } // Randomize send_buf. pj_create_random_string(send_buf, bufsize); // Register reading from ioqueue. TRACE_("start recvfrom..."); pj_bzero(&addr, sizeof(addr)); addrlen = sizeof(addr); bytes = bufsize; rc = pj_ioqueue_recvfrom(skey, &read_op, recv_buf, &bytes, 0, &addr, &addrlen); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_recvfrom", rc); status=-28; goto on_error; } else if (rc == PJ_EPENDING) { recv_pending = 1; PJ_LOG(3, (THIS_FILE, "......ok: recvfrom returned pending")); } else { PJ_LOG(3, (THIS_FILE, "......error: recvfrom returned immediate ok!")); status=-29; goto on_error; } // Set destination address to send the packet. TRACE_("set destination address..."); temp = pj_str("127.0.0.1"); if ((rc=pj_sockaddr_in_init(&dst_addr, &temp, PORT)) != 0) { app_perror("...error: unable to resolve 127.0.0.1", rc); status=-290; goto on_error; } // Write must return the number of bytes. TRACE_("start sendto..."); bytes = bufsize; rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &dst_addr, sizeof(dst_addr)); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_sendto", rc); status=-30; goto on_error; } else if (rc == PJ_EPENDING) { send_pending = 1; PJ_LOG(3, (THIS_FILE, "......ok: sendto returned pending")); } else { send_pending = 0; PJ_LOG(3, (THIS_FILE, "......ok: sendto returned immediate success")); } // reset callback variables. callback_read_size = callback_write_size = 0; callback_accept_status = callback_connect_status = -2; callback_read_key = callback_write_key = callback_accept_key = callback_connect_key = NULL; callback_read_op = callback_write_op = NULL; // Poll if pending. while (send_pending || recv_pending) { int rc; pj_time_val timeout = { 5, 0 }; TRACE_("poll..."); #ifdef PJ_SYMBIAN rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout)); #else rc = pj_ioqueue_poll(ioque, &timeout); #endif if (rc == 0) { PJ_LOG(1,(THIS_FILE, "...ERROR: timed out...")); status=-45; goto on_error; } else if (rc < 0) { app_perror("...ERROR in ioqueue_poll()", -rc); status=-50; goto on_error; } if (callback_read_key != NULL) { if (callback_read_size != bufsize) { status=-61; goto on_error; } if (callback_read_key != skey) { status=-65; goto on_error; } if (callback_read_op != &read_op) { status=-66; goto on_error; } if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) { status=-67; goto on_error; } if (addrlen != sizeof(pj_sockaddr_in)) { status=-68; goto on_error; } if (addr.sin_family != pj_AF_INET()) { status=-69; goto on_error; } recv_pending = 0; } if (callback_write_key != NULL) { if (callback_write_size != bufsize) { status=-73; goto on_error; } if (callback_write_key != ckey) { status=-75; goto on_error; } if (callback_write_op != &write_op) { status=-76; goto on_error; } send_pending = 0; } } // Success status = 0; on_error: if (skey) pj_ioqueue_unregister(skey); else if (ssock != -1) pj_sock_close(ssock); if (ckey) pj_ioqueue_unregister(ckey); else if (csock != -1) pj_sock_close(csock); if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release(pool); return status; }
/* * Compliance test for success scenario. */ static int compliance_test_0(pj_bool_t allow_concur) { pj_sock_t ssock=-1, csock0=-1, csock1=-1; pj_sockaddr_in addr, client_addr, rmt_addr; int client_addr_len; pj_pool_t *pool = NULL; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; pj_ioqueue_key_t *skey=NULL, *ckey0=NULL, *ckey1=NULL; pj_ioqueue_op_key_t accept_op; int bufsize = BUF_MIN_SIZE; pj_ssize_t status = -1; int pending_op = 0; pj_timestamp t_elapsed; pj_str_t s; pj_status_t rc; // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Allocate buffers for send and receive. send_buf = (char*)pj_pool_alloc(pool, bufsize); recv_buf = (char*)pj_pool_alloc(pool, bufsize); // Create server socket and client socket for connecting rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &ssock); if (rc != PJ_SUCCESS) { app_perror("...error creating socket", rc); status=-1; goto on_error; } rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &csock1); if (rc != PJ_SUCCESS) { app_perror("...error creating socket", rc); status=-1; goto on_error; } // Bind server socket. pj_sockaddr_in_init(&addr, 0, 0); if ((rc=pj_sock_bind(ssock, &addr, sizeof(addr))) != 0 ) { app_perror("...bind error", rc); status=-10; goto on_error; } // Get server address. client_addr_len = sizeof(addr); rc = pj_sock_getsockname(ssock, &addr, &client_addr_len); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_sock_getsockname()", rc); status=-15; goto on_error; } addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); // Create I/O Queue. rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_create()", rc); status=-20; goto on_error; } // Concurrency rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_set_default_concurrency()", rc); status=-21; goto on_error; } // Register server socket and client socket. rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey); if (rc == PJ_SUCCESS) rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb, &ckey1); else ckey1 = NULL; if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_register_sock()", rc); status=-23; goto on_error; } // Server socket listen(). if (pj_sock_listen(ssock, 5)) { app_perror("...ERROR in pj_sock_listen()", rc); status=-25; goto on_error; } // Server socket accept() client_addr_len = sizeof(pj_sockaddr_in); status = pj_ioqueue_accept(skey, &accept_op, &csock0, &client_addr, &rmt_addr, &client_addr_len); if (status != PJ_EPENDING) { app_perror("...ERROR in pj_ioqueue_accept()", rc); status=-30; goto on_error; } if (status==PJ_EPENDING) { ++pending_op; } // Client socket connect() status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr)); if (status!=PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...ERROR in pj_ioqueue_connect()", rc); status=-40; goto on_error; } if (status==PJ_EPENDING) { ++pending_op; } // Poll until connected callback_read_size = callback_write_size = 0; callback_accept_status = callback_connect_status = -2; callback_call_count = 0; callback_read_key = callback_write_key = callback_accept_key = callback_connect_key = NULL; callback_accept_op = callback_read_op = callback_write_op = NULL; while (pending_op) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN callback_call_count = 0; pj_symbianos_poll(-1, 1000); status = callback_call_count; #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status > 0) { if (callback_accept_status != -2) { if (callback_accept_status != 0) { status=-41; goto on_error; } if (callback_accept_key != skey) { status=-42; goto on_error; } if (callback_accept_op != &accept_op) { status=-43; goto on_error; } callback_accept_status = -2; } if (callback_connect_status != -2) { if (callback_connect_status != 0) { status=-50; goto on_error; } if (callback_connect_key != ckey1) { status=-51; goto on_error; } callback_connect_status = -2; } if (status > pending_op) { PJ_LOG(3,(THIS_FILE, "...error: pj_ioqueue_poll() returned %d " "(only expecting %d)", status, pending_op)); return -52; } pending_op -= status; if (pending_op == 0) { status = 0; } } } // There's no pending operation. // When we poll the ioqueue, there must not be events. if (pending_op == 0) { pj_time_val timeout = {1, 0}; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status != 0) { status=-60; goto on_error; } } // Check accepted socket. if (csock0 == PJ_INVALID_SOCKET) { status = -69; app_perror("...accept() error", pj_get_os_error()); goto on_error; } // Register newly accepted socket. rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL, &test_cb, &ckey0); if (rc != PJ_SUCCESS) { app_perror("...ERROR in pj_ioqueue_register_sock", rc); status = -70; goto on_error; } // Test send and receive. t_elapsed.u32.lo = 0; status = send_recv_test(ioque, ckey0, ckey1, send_buf, recv_buf, bufsize, &t_elapsed); if (status != 0) { goto on_error; } // Success status = 0; on_error: if (skey != NULL) pj_ioqueue_unregister(skey); else if (ssock != PJ_INVALID_SOCKET) pj_sock_close(ssock); if (ckey1 != NULL) pj_ioqueue_unregister(ckey1); else if (csock1 != PJ_INVALID_SOCKET) pj_sock_close(csock1); if (ckey0 != NULL) pj_ioqueue_unregister(ckey0); else if (csock0 != PJ_INVALID_SOCKET) pj_sock_close(csock0); if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release(pool); return status; }
static pj_bool_t turn_on_data_recvfrom(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status) { test_server *test_srv; pj_pool_t *pool; turn_allocation *alloc; pj_stun_msg *req, *resp = NULL; pj_str_t auth_key = { NULL, 0 }; char client_info[PJ_INET6_ADDRSTRLEN+10]; unsigned i; pj_ssize_t len; if (status != PJ_SUCCESS) return PJ_TRUE; pj_sockaddr_print(src_addr, client_info, sizeof(client_info), 3); test_srv = (test_server*) pj_activesock_get_user_data(asock); pool = pj_pool_create(test_srv->stun_cfg->pf, NULL, 512, 512, NULL); /* Find the client */ for (i=0; i<test_srv->turn_alloc_cnt; i++) { if (pj_sockaddr_cmp(&test_srv->turn_alloc[i].client_addr, src_addr)==0) break; } if (pj_stun_msg_check((pj_uint8_t*)data, size, PJ_STUN_NO_FINGERPRINT_CHECK)!=PJ_SUCCESS) { /* Not STUN message, this probably is a ChannelData */ pj_turn_channel_data cd; const pj_turn_channel_data *pcd = (const pj_turn_channel_data*)data; pj_ssize_t sent; if (i==test_srv->turn_alloc_cnt) { /* Invalid data */ PJ_LOG(1,(THIS_FILE, "TURN Server received strayed data")); goto on_return; } alloc = &test_srv->turn_alloc[i]; cd.ch_number = pj_ntohs(pcd->ch_number); cd.length = pj_ntohs(pcd->length); /* For UDP check the packet length */ if (size < cd.length+sizeof(cd)) { PJ_LOG(1,(THIS_FILE, "TURN Server: ChannelData discarded: UDP size error")); goto on_return; } /* Lookup peer */ for (i=0; i<alloc->perm_cnt; ++i) { if (alloc->chnum[i] == cd.ch_number) break; } if (i==alloc->perm_cnt) { PJ_LOG(1,(THIS_FILE, "TURN Server: ChannelData discarded: invalid channel number")); goto on_return; } /* Relay the data to peer */ sent = cd.length; pj_activesock_sendto(alloc->sock, &alloc->send_key, pcd+1, &sent, 0, &alloc->perm[i], pj_sockaddr_get_len(&alloc->perm[i])); /* Done */ goto on_return; } status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK, &req, NULL, NULL); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,("", "STUN message decode error from client %s: %s", client_info, errmsg)); goto on_return; } if (i==test_srv->turn_alloc_cnt) { /* New client */ //pj_str_t ip_addr; pj_stun_username_attr *uname; pj_activesock_cb alloc_sock_cb; turn_allocation *alloc; /* Must be Allocate request */ if (req->hdr.type != PJ_STUN_ALLOCATE_REQUEST) { PJ_LOG(1,(THIS_FILE, "Invalid %s %s from client %s", pj_stun_get_method_name(req->hdr.type), pj_stun_get_class_name(req->hdr.type), client_info)); if (PJ_STUN_IS_REQUEST(req->hdr.type)) pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp); goto send_pkt; } test_srv->turn_stat.rx_allocate_cnt++; /* Skip if we're not responding to Allocate request */ if (!test_srv->turn_respond_allocate) return PJ_TRUE; /* Check if we have too many clients */ if (test_srv->turn_alloc_cnt == MAX_TURN_ALLOC) { pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp); goto send_pkt; } /* Get USERNAME attribute */ uname = (pj_stun_username_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0); /* Reject if it doesn't have MESSAGE-INTEGRITY or USERNAME attributes or * the user is incorrect */ if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0) == NULL || uname==NULL || pj_stricmp2(&uname->value, TURN_USERNAME) != 0) { pj_str_t tmp; pj_stun_msg_create_response(pool, req, PJ_STUN_SC_UNAUTHORIZED, NULL, &resp); pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_REALM, &test_srv->domain); pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE)); goto send_pkt; } pj_bzero(&alloc_sock_cb, sizeof(alloc_sock_cb)); alloc_sock_cb.on_data_recvfrom = &alloc_on_data_recvfrom; /* Create allocation */ alloc = &test_srv->turn_alloc[test_srv->turn_alloc_cnt]; alloc->perm_cnt = 0; alloc->test_srv = test_srv; pj_memcpy(&alloc->client_addr, src_addr, addr_len); pj_ioqueue_op_key_init(&alloc->send_key, sizeof(alloc->send_key)); alloc->pool = pj_pool_create(test_srv->stun_cfg->pf, "alloc", 512, 512, NULL); /* Create relay socket */ pj_sockaddr_in_init(&alloc->alloc_addr.ipv4, NULL, 0); pj_gethostip(pj_AF_INET(), &alloc->alloc_addr); status = pj_activesock_create_udp(alloc->pool, &alloc->alloc_addr, NULL, test_srv->stun_cfg->ioqueue, &alloc_sock_cb, alloc, &alloc->sock, &alloc->alloc_addr); if (status != PJ_SUCCESS) { pj_pool_release(alloc->pool); pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp); goto send_pkt; } //pj_sockaddr_set_str_addr(pj_AF_INET(), &alloc->alloc_addr, &ip_addr); pj_activesock_set_user_data(alloc->sock, alloc); status = pj_activesock_start_recvfrom(alloc->sock, alloc->pool, 1500, 0); if (status != PJ_SUCCESS) { pj_activesock_close(alloc->sock); pj_pool_release(alloc->pool); pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp); goto send_pkt; } /* Create Data indication */ status = pj_stun_msg_create(alloc->pool, PJ_STUN_DATA_INDICATION, PJ_STUN_MAGIC, NULL, &alloc->data_ind); if (status != PJ_SUCCESS) { pj_activesock_close(alloc->sock); pj_pool_release(alloc->pool); pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp); goto send_pkt; } pj_stun_msg_add_sockaddr_attr(alloc->pool, alloc->data_ind, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, &alloc->alloc_addr, pj_sockaddr_get_len(&alloc->alloc_addr)); pj_stun_msg_add_binary_attr(alloc->pool, alloc->data_ind, PJ_STUN_ATTR_DATA, (pj_uint8_t*)"", 1); /* Create response */ resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key); if (resp == NULL) { pj_activesock_close(alloc->sock); pj_pool_release(alloc->pool); pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp); goto send_pkt; } ++test_srv->turn_alloc_cnt; } else { alloc = &test_srv->turn_alloc[i]; if (req->hdr.type == PJ_STUN_ALLOCATE_REQUEST) { test_srv->turn_stat.rx_allocate_cnt++; /* Skip if we're not responding to Allocate request */ if (!test_srv->turn_respond_allocate) return PJ_TRUE; resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); } else if (req->hdr.type == PJ_STUN_REFRESH_REQUEST) { pj_stun_lifetime_attr *lf_attr; test_srv->turn_stat.rx_refresh_cnt++; /* Skip if we're not responding to Refresh request */ if (!test_srv->turn_respond_refresh) return PJ_TRUE; lf_attr = (pj_stun_lifetime_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0); if (lf_attr && lf_attr->value != 0) { resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key); pj_array_erase(test_srv->turn_alloc, sizeof(test_srv->turn_alloc[0]), test_srv->turn_alloc_cnt, i); --test_srv->turn_alloc_cnt; } else resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); } else if (req->hdr.type == PJ_STUN_CREATE_PERM_REQUEST) { for (i=0; i<req->attr_count; ++i) { if (req->attr[i]->type == PJ_STUN_ATTR_XOR_PEER_ADDR) { pj_stun_xor_peer_addr_attr *pa = (pj_stun_xor_peer_addr_attr*)req->attr[i]; unsigned j; for (j=0; j<alloc->perm_cnt; ++j) { if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0) break; } if (j==alloc->perm_cnt && alloc->perm_cnt < MAX_TURN_PERM) { char peer_info[PJ_INET6_ADDRSTRLEN]; pj_sockaddr_print(&pa->sockaddr, peer_info, sizeof(peer_info), 3); pj_sockaddr_cp(&alloc->perm[alloc->perm_cnt], &pa->sockaddr); ++alloc->perm_cnt; PJ_LOG(5,("", "Permission %s added to client %s, perm_cnt=%d", peer_info, client_info, alloc->perm_cnt)); } } } resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); } else if (req->hdr.type == PJ_STUN_SEND_INDICATION) { pj_stun_xor_peer_addr_attr *pa; pj_stun_data_attr *da; test_srv->turn_stat.rx_send_ind_cnt++; pa = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); da = (pj_stun_data_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_DATA, 0); if (pa && da) { unsigned j; char peer_info[PJ_INET6_ADDRSTRLEN]; pj_ssize_t sent; pj_sockaddr_print(&pa->sockaddr, peer_info, sizeof(peer_info), 3); for (j=0; j<alloc->perm_cnt; ++j) { if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0) break; } if (j==alloc->perm_cnt) { PJ_LOG(5,("", "SendIndication to %s is rejected (no permission)", peer_info, client_info, alloc->perm_cnt)); } else { PJ_LOG(5,(THIS_FILE, "Relaying %d bytes data from client %s to peer %s, " "perm_cnt=%d", da->length, client_info, peer_info, alloc->perm_cnt)); sent = da->length; pj_activesock_sendto(alloc->sock, &alloc->send_key, da->data, &sent, 0, &pa->sockaddr, pj_sockaddr_get_len(&pa->sockaddr)); } } else { PJ_LOG(1,(THIS_FILE, "Invalid Send Indication from %s", client_info)); } } else if (req->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) { pj_stun_xor_peer_addr_attr *pa; pj_stun_channel_number_attr *cna; unsigned j, cn; pa = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); cna = (pj_stun_channel_number_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_CHANNEL_NUMBER, 0); cn = PJ_STUN_GET_CH_NB(cna->value); resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); for (j=0; j<alloc->perm_cnt; ++j) { if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0) break; } if (i==alloc->perm_cnt) { if (alloc->perm_cnt==MAX_TURN_PERM) { pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp); goto send_pkt; } pj_sockaddr_cp(&alloc->perm[i], &pa->sockaddr); ++alloc->perm_cnt; } alloc->chnum[i] = cn; resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key); } else if (PJ_STUN_IS_REQUEST(req->hdr.type)) { pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp); } } send_pkt: if (resp) { status = pj_stun_msg_encode(resp, (pj_uint8_t*)data, MAX_STUN_PKT, 0, &auth_key, &size); if (status != PJ_SUCCESS) goto on_return; len = size; status = pj_activesock_sendto(asock, &test_srv->send_key, data, &len, 0, src_addr, addr_len); } on_return: pj_pool_release(pool); return PJ_TRUE; }