/* * 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 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; }
/* * Repeated connect/accept on the same listener socket. */ static int compliance_test_2(void) { enum { MAX_PAIR = 4, TEST_LOOP = 2 }; 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; // 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; } // 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}; status=pj_ioqueue_poll(ioque, &timeout); 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}; status = pj_ioqueue_poll(ioque, &timeout); 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; }
/* * Common function to create TCP transport, called when pending accept() and * pending connect() complete. */ static pj_status_t tcp_create( struct tcp_listener *listener, pj_pool_t *pool, pj_sock_t sock, pj_bool_t is_server, const pj_sockaddr *local, const pj_sockaddr *remote, struct tcp_transport **p_tcp) { struct tcp_transport *tcp; pj_ioqueue_t *ioqueue; pj_activesock_cfg asock_cfg; pj_activesock_cb tcp_callback; const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA; char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_status_t status; PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_EINVAL); if (pool == NULL) { pool = pjsip_endpt_create_pool(listener->endpt, "tcp", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); } /* * Create and initialize basic transport structure. */ tcp = PJ_POOL_ZALLOC_T(pool, struct tcp_transport); tcp->is_server = is_server; tcp->sock = sock; /*tcp->listener = listener;*/ pj_list_init(&tcp->delayed_list); tcp->base.pool = pool; pj_ansi_snprintf(tcp->base.obj_name, PJ_MAX_OBJ_NAME, (is_server ? "tcps%p" :"tcpc%p"), tcp); status = pj_atomic_create(pool, 0, &tcp->base.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, "tcp", &tcp->base.lock); if (status != PJ_SUCCESS) { goto on_error; } tcp->base.key.type = listener->factory.type; pj_sockaddr_cp(&tcp->base.key.rem_addr, remote); tcp->base.type_name = (char*)pjsip_transport_get_type_name( (pjsip_transport_type_e)tcp->base.key.type); tcp->base.flag = pjsip_transport_get_flag_from_type( (pjsip_transport_type_e)tcp->base.key.type); tcp->base.info = (char*) pj_pool_alloc(pool, 64); pj_ansi_snprintf(tcp->base.info, 64, "%s to %s", tcp->base.type_name, pj_sockaddr_print(remote, print_addr, sizeof(print_addr), 3)); tcp->base.addr_len = pj_sockaddr_get_len(remote); pj_sockaddr_cp(&tcp->base.local_addr, local); sockaddr_to_host_port(pool, &tcp->base.local_name, local); sockaddr_to_host_port(pool, &tcp->base.remote_name, remote); tcp->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; tcp->base.endpt = listener->endpt; tcp->base.tpmgr = listener->tpmgr; tcp->base.send_msg = &tcp_send_msg; tcp->base.do_shutdown = &tcp_shutdown; tcp->base.destroy = &tcp_destroy_transport; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.async_cnt = 1; pj_bzero(&tcp_callback, sizeof(tcp_callback)); tcp_callback.on_data_read = &on_data_read; tcp_callback.on_data_sent = &on_data_sent; tcp_callback.on_connect_complete = &on_connect_complete; ioqueue = pjsip_endpt_get_ioqueue(listener->endpt); status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, ioqueue, &tcp_callback, tcp, &tcp->asock); if (status != PJ_SUCCESS) { goto on_error; } /* Register transport to transport manager */ status = pjsip_transport_register(listener->tpmgr, &tcp->base); if (status != PJ_SUCCESS) { goto on_error; } tcp->is_registered = PJ_TRUE; /* Initialize keep-alive timer */ tcp->ka_timer.user_data = (void*)tcp; tcp->ka_timer.cb = &tcp_keep_alive_timer; pj_ioqueue_op_key_init(&tcp->ka_op_key.key, sizeof(pj_ioqueue_op_key_t)); pj_strdup(tcp->base.pool, &tcp->ka_pkt, &ka_pkt); /* Done setting up basic transport. */ *p_tcp = tcp; PJ_LOG(4,(tcp->base.obj_name, "TCP %s transport created", (tcp->is_server ? "server" : "client"))); return PJ_SUCCESS; on_error: tcp_destroy(&tcp->base, status); return status; }
/* * 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; pj_gettimeofday(&now); if (test_method == UNREGISTER_IN_APP && PJ_TIME_VAL_GTE(now, time_to_unregister) && sock_data.pool) { //Can't do this otherwise it'll deadlock //pj_mutex_lock(sock_data.mutex); sock_data.unregistered = 1; pj_ioqueue_unregister(sock_data.key); //pj_mutex_unlock(sock_data.mutex); pj_mutex_destroy(sock_data.mutex); pj_pool_release(sock_data.pool); sock_data.pool = NULL; } if (PJ_TIME_VAL_GT(now, end_time) && sock_data.unregistered) break; timeout.sec = 0; timeout.msec = 10; pj_ioqueue_poll(ioqueue, &timeout); //pj_thread_sleep(1); } thread_quitting = 1; for (i=0; i<WORKER_CNT; ++i) { pj_thread_join(thread[i]); pj_thread_destroy(thread[i]); } 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; }
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 the STUN transport using the specified configuration. */ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, const char *name, int af, const pj_stun_sock_cb *cb, const pj_stun_sock_cfg *cfg, void *user_data, pj_stun_sock **p_stun_sock) { pj_pool_t *pool; pj_stun_sock *stun_sock; pj_stun_sock_cfg default_cfg; unsigned i; pj_status_t status; long sobuf_size; PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); status = pj_stun_config_check_valid(stun_cfg); if (status != PJ_SUCCESS) return status; if (name == NULL) name = "stuntp%p"; if (cfg == NULL) { pj_stun_sock_cfg_default(&default_cfg); cfg = &default_cfg; } /* Create structure */ pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); stun_sock->pool = pool; stun_sock->obj_name = pool->obj_name; stun_sock->user_data = user_data; stun_sock->af = af; stun_sock->sock_fd = PJ_INVALID_SOCKET; pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); stun_sock->ka_interval = cfg->ka_interval; if (stun_sock->ka_interval == 0) stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; if (cfg->grp_lock) { stun_sock->grp_lock = cfg->grp_lock; } else { status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } pj_grp_lock_add_ref(stun_sock->grp_lock); pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, &stun_sock_destructor); /* Create socket and bind socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); if (status != PJ_SUCCESS) goto on_error; #if 1 // natnl set stun socket recv and send buffer size. sobuf_size = cfg->sock_recv_buf_size ? cfg->sock_recv_buf_size : PJ_STUN_SOCK_PKT_LEN; status = pj_sock_setsockopt(stun_sock->sock_fd, pj_SOL_SOCKET(), pj_SO_RCVBUF(), &sobuf_size, sizeof(sobuf_size)); if (status != PJ_SUCCESS) goto on_error; sobuf_size = cfg->sock_send_buf_size ? cfg->sock_send_buf_size : PJ_SOCKET_SND_BUFFER_SIZE; status = pj_sock_setsockopt(stun_sock->sock_fd, pj_SOL_SOCKET(), pj_SO_SNDBUF(), &sobuf_size, sizeof(sobuf_size)); if (status != PJ_SUCCESS) goto on_error; #endif /* Apply QoS, if specified */ status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, &cfg->qos_params, 2, stun_sock->obj_name, NULL); if (status != PJ_SUCCESS && !cfg->qos_ignore_error) goto on_error; /* Bind socket */ if (pj_sockaddr_has_addr(&cfg->bound_addr)) { status = pj_sock_bind(stun_sock->sock_fd, &cfg->bound_addr, pj_sockaddr_get_len(&cfg->bound_addr)); } else { pj_sockaddr bound_addr; pj_sockaddr_init(af, &bound_addr, NULL, 0); status = pj_sock_bind(stun_sock->sock_fd, &bound_addr, pj_sockaddr_get_len(&bound_addr)); } if (status != PJ_SUCCESS) goto on_error; /* Create more useful information string about this transport */ #if 0 { pj_sockaddr bound_addr; int addr_len = sizeof(bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); pj_sockaddr_print(&bound_addr, stun_sock->info, PJ_INET6_ADDRSTRLEN, 3); } #endif /* Init active socket configuration */ { pj_activesock_cfg activesock_cfg; pj_activesock_cb activesock_cb; pj_activesock_cfg_default(&activesock_cfg); activesock_cfg.grp_lock = stun_sock->grp_lock; activesock_cfg.async_cnt = cfg->async_cnt; activesock_cfg.concurrency = 0; /* Create the active socket */ pj_bzero(&activesock_cb, sizeof(activesock_cb)); activesock_cb.on_data_recvfrom = &on_data_recvfrom; activesock_cb.on_data_sent = &on_data_sent; status = pj_activesock_create(pool, stun_sock->sock_fd, pj_SOCK_DGRAM(), &activesock_cfg, stun_cfg->ioqueue, &activesock_cb, stun_sock, &stun_sock->active_sock); if (status != PJ_SUCCESS) goto on_error; /* Start asynchronous read operations */ status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, cfg->max_pkt_size, 0); if (status != PJ_SUCCESS) goto on_error; /* Init send keys */ pj_ioqueue_op_key_init(&stun_sock->send_key, sizeof(stun_sock->send_key)); pj_ioqueue_op_key_init(&stun_sock->int_send_key, sizeof(stun_sock->int_send_key)); } /* Create STUN session */ { pj_stun_session_cb sess_cb; pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &sess_on_request_complete; sess_cb.on_send_msg = &sess_on_send_msg; status = pj_stun_session_create(&stun_sock->stun_cfg, stun_sock->obj_name, &sess_cb, PJ_FALSE, stun_sock->grp_lock, &stun_sock->stun_sess); if (status != PJ_SUCCESS) goto on_error; } /* Associate us with the STUN session */ pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); /* Initialize random numbers to be used as STUN transaction ID for * outgoing Binding request. We use the 80bit number to distinguish * STUN messages we sent with STUN messages that the application sends. * The last 16bit value in the array is a counter. */ for (i=0; i<PJ_ARRAY_SIZE(stun_sock->tsx_id); ++i) { stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); } stun_sock->tsx_id[5] = 0; /* Init timer entry */ stun_sock->ka_timer.cb = &ka_timer_cb; stun_sock->ka_timer.user_data = stun_sock; /* Done */ *p_stun_sock = stun_sock; return PJ_SUCCESS; on_error: pj_stun_sock_destroy(stun_sock); return status; }
static int tcp_perf_test(void) { enum { COUNT=100000 }; pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_sock_t sock1=PJ_INVALID_SOCKET, sock2=PJ_INVALID_SOCKET; pj_activesock_t *asock1 = NULL, *asock2 = NULL; pj_activesock_cb cb; struct tcp_state *state1, *state2; unsigned i; pj_status_t status; pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL); status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1, &sock2); if (status != PJ_SUCCESS) { status = -100; goto on_return; } status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { status = -110; goto on_return; } pj_bzero(&cb, sizeof(cb)); cb.on_data_read = &tcp_on_data_read; cb.on_data_sent = &tcp_on_data_sent; state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state); status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue, &cb, state1, &asock1); if (status != PJ_SUCCESS) { status = -120; goto on_return; } state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state); status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue, &cb, state2, &asock2); if (status != PJ_SUCCESS) { status = -130; goto on_return; } status = pj_activesock_start_read(asock1, pool, 1000, 0); if (status != PJ_SUCCESS) { status = -140; goto on_return; } /* Send packet as quickly as possible */ for (i=0; i<COUNT && !state1->err && !state2->err; ++i) { struct tcp_pkt *pkt; struct send_key send_key[2], *op_key; pj_ssize_t len; pkt = (struct tcp_pkt*)state2->pkt; pkt->signature = SIGNATURE; pkt->seq = i; pj_memset(pkt->fill, 'a', sizeof(pkt->fill)); op_key = &send_key[i%2]; pj_ioqueue_op_key_init(&op_key->op_key, sizeof(*op_key)); state2->sent = PJ_FALSE; len = sizeof(*pkt); status = pj_activesock_send(asock2, &op_key->op_key, pkt, &len, 0); if (status == PJ_EPENDING) { do { #if PJ_SYMBIAN pj_symbianos_poll(-1, -1); #else pj_ioqueue_poll(ioqueue, NULL); #endif } while (!state2->sent); } else { #if PJ_SYMBIAN /* The Symbian socket always returns PJ_SUCCESS for TCP send, * eventhough the remote end hasn't received the data yet. * If we continue sending, eventually send() will block, * possibly because the send buffer is full. So we need to * poll the ioqueue periodically, to let receiver gets the * data. */ pj_symbianos_poll(-1, 0); #endif if (status != PJ_SUCCESS) { PJ_LOG(1,("", " err: send status=%d", status)); status = -180; break; } else if (status == PJ_SUCCESS) { if (len != sizeof(*pkt)) { PJ_LOG(1,("", " err: shouldn't report partial sent")); status = -190; break; } } } } /* Wait until everything has been sent/received */ if (state1->next_recv_seq < COUNT) { #ifdef PJ_SYMBIAN while (pj_symbianos_poll(-1, 1000) == PJ_TRUE) ; #else pj_time_val delay = {0, 100}; while (pj_ioqueue_poll(ioqueue, &delay) > 0) ; #endif } if (status == PJ_EPENDING) status = PJ_SUCCESS; if (status != 0) goto on_return; if (state1->err) { status = -183; goto on_return; } if (state2->err) { status = -186; goto on_return; } if (state1->next_recv_seq != COUNT) { PJ_LOG(3,("", " err: only %u packets received, expecting %u", state1->next_recv_seq, COUNT)); status = -195; goto on_return; } on_return: if (asock2) pj_activesock_close(asock2); if (asock1) pj_activesock_close(asock1); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }
/* * Create the resolver. */ PJ_DEF(pj_status_t) pj_dns_resolver_create( pj_pool_factory *pf, const char *name, unsigned options, pj_timer_heap_t *timer, pj_ioqueue_t *ioqueue, pj_dns_resolver **p_resolver) { pj_pool_t *pool; pj_dns_resolver *resv; pj_ioqueue_callback socket_cb; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(pf && p_resolver, PJ_EINVAL); if (name == NULL) name = THIS_FILE; /* Create and initialize resolver instance */ pool = pj_pool_create(pf, name, 4000, 4000, NULL); if (!pool) return PJ_ENOMEM; /* Create pool and name */ resv = PJ_POOL_ZALLOC_T(pool, struct pj_dns_resolver); resv->pool = pool; resv->udp_sock = PJ_INVALID_SOCKET; pj_strdup2_with_null(pool, &resv->name, name); /* Create the mutex */ status = pj_mutex_create_recursive(pool, name, &resv->mutex); if (status != PJ_SUCCESS) goto on_error; /* Timer, ioqueue, and settings */ resv->timer = timer; resv->ioqueue = ioqueue; resv->last_id = 1; pj_dns_settings_default(&resv->settings); resv->settings.options = options; /* Create the timer heap if one is not specified */ if (resv->timer == NULL) { status = pj_timer_heap_create(pool, TIMER_SIZE, &resv->timer); if (status != PJ_SUCCESS) goto on_error; } /* Create the ioqueue if one is not specified */ if (resv->ioqueue == NULL) { status = pj_ioqueue_create(pool, MAX_FD, &resv->ioqueue); if (status != PJ_SUCCESS) goto on_error; } /* Response cache hash table */ resv->hrescache = pj_hash_create(pool, RES_HASH_TABLE_SIZE); /* Query hash table and free list. */ resv->hquerybyid = pj_hash_create(pool, Q_HASH_TABLE_SIZE); resv->hquerybyres = pj_hash_create(pool, Q_HASH_TABLE_SIZE); pj_list_init(&resv->query_free_nodes); /* Create the UDP socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &resv->udp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind to any address/port */ status = pj_sock_bind_in(resv->udp_sock, 0, 0); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&socket_cb, sizeof(socket_cb)); socket_cb.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock(pool, resv->ioqueue, resv->udp_sock, resv, &socket_cb, &resv->udp_key); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&resv->udp_op_key, sizeof(resv->udp_op_key)); /* Start asynchronous read to the UDP socket */ resv->udp_len = sizeof(resv->udp_rx_pkt); resv->udp_addr_len = sizeof(resv->udp_src_addr); status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_key, resv->udp_rx_pkt, &resv->udp_len, PJ_IOQUEUE_ALWAYS_ASYNC, &resv->udp_src_addr, &resv->udp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Looks like everything is okay */ *p_resolver = resv; return PJ_SUCCESS; on_error: pj_dns_resolver_destroy(resv, PJ_FALSE); return status; }
SipIceTransport::SipIceTransport(pjsip_endpoint* endpt, pj_pool_t& /* pool */, long /* t_type */, const std::shared_ptr<IceTransport>& ice, int comp_id) : pool_(nullptr, pj_pool_release) , rxPool_(nullptr, pj_pool_release) , trData_() , rdata_() , ice_(ice) , comp_id_(comp_id) { trData_.self = this; if (not ice or not ice->isRunning()) throw std::logic_error("ice transport must exist and negotiation completed"); RING_DBG("SipIceTransport@%p {tr=%p}", this, &trData_.base); auto& base = trData_.base; pool_.reset(pjsip_endpt_create_pool(endpt, "SipIceTransport.pool", POOL_TP_INIT, POOL_TP_INC)); if (not pool_) throw std::bad_alloc(); auto pool = pool_.get(); pj_ansi_snprintf(base.obj_name, PJ_MAX_OBJ_NAME, "SipIceTransport"); base.endpt = endpt; base.tpmgr = pjsip_endpt_get_tpmgr(endpt); base.pool = pool; rdata_.tp_info.pool = pool; // FIXME: not destroyed in case of exception if (pj_atomic_create(pool, 0, &base.ref_cnt) != PJ_SUCCESS) throw std::runtime_error("Can't create PJSIP atomic."); // FIXME: not destroyed in case of exception if (pj_lock_create_recursive_mutex(pool, "SipIceTransport.mutex", &base.lock) != PJ_SUCCESS) throw std::runtime_error("Can't create PJSIP mutex."); auto remote = ice->getRemoteAddress(comp_id); RING_DBG("SipIceTransport: remote is %s", remote.toString(true).c_str()); pj_sockaddr_cp(&base.key.rem_addr, remote.pjPtr()); base.key.type = PJSIP_TRANSPORT_UDP;//t_type; base.type_name = (char*)pjsip_transport_get_type_name((pjsip_transport_type_e)base.key.type); base.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)base.key.type); base.info = (char*) pj_pool_alloc(pool, TRANSPORT_INFO_LENGTH); char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_ansi_snprintf(base.info, TRANSPORT_INFO_LENGTH, "%s to %s", base.type_name, pj_sockaddr_print(remote.pjPtr(), print_addr, sizeof(print_addr), 3)); base.addr_len = remote.getLength(); base.dir = PJSIP_TP_DIR_NONE;//is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; base.data = nullptr; /* Set initial local address */ auto local = ice->getDefaultLocalAddress(); pj_sockaddr_cp(&base.local_addr, local.pjPtr()); sockaddr_to_host_port(pool, &base.local_name, &base.local_addr); sockaddr_to_host_port(pool, &base.remote_name, remote.pjPtr()); base.send_msg = [](pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback) { auto& this_ = reinterpret_cast<TransportData*>(transport)->self; return this_->send(tdata, rem_addr, addr_len, token, callback); }; base.do_shutdown = [](pjsip_transport *transport) -> pj_status_t { auto& this_ = reinterpret_cast<TransportData*>(transport)->self; RING_WARN("SipIceTransport@%p: shutdown", this_); return PJ_SUCCESS; }; base.destroy = [](pjsip_transport *transport) -> pj_status_t { auto& this_ = reinterpret_cast<TransportData*>(transport)->self; RING_WARN("SipIceTransport@%p: destroy", this_); delete this_; return PJ_SUCCESS; }; /* Init rdata */ rxPool_.reset(pjsip_endpt_create_pool(base.endpt, "SipIceTransport.rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC)); if (not rxPool_) throw std::bad_alloc(); auto rx_pool = rxPool_.get(); rdata_.tp_info.pool = rx_pool; rdata_.tp_info.transport = &base; rdata_.tp_info.tp_data = this; rdata_.tp_info.op_key.rdata = &rdata_; pj_ioqueue_op_key_init(&rdata_.tp_info.op_key.op_key, sizeof(pj_ioqueue_op_key_t)); rdata_.pkt_info.src_addr = base.key.rem_addr; rdata_.pkt_info.src_addr_len = sizeof(rdata_.pkt_info.src_addr); auto rem_addr = &base.key.rem_addr; pj_sockaddr_print(rem_addr, rdata_.pkt_info.src_name, sizeof(rdata_.pkt_info.src_name), 0); rdata_.pkt_info.src_port = pj_sockaddr_get_port(rem_addr); rdata_.pkt_info.len = 0; rdata_.pkt_info.zero = 0; if (pjsip_transport_register(base.tpmgr, &base) != PJ_SUCCESS) throw std::runtime_error("Can't register PJSIP transport."); is_registered_ = true; Manager::instance().registerEventHandler((uintptr_t)this, [this]{ loop(); }); }
/** * 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; }
// // Constructor. // explicit Pj_Async_Op(Pj_Event_Handler *handler) : handler_(handler) { pj_ioqueue_op_key_init(this, sizeof(*this)); }
// // Construct with null handler. // App must call set_handler() before use. // Pj_Async_Op() : handler_(NULL) { pj_ioqueue_op_key_init(this, sizeof(*this)); }
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; }
/* * Create the STUN transport using the specified configuration. */ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, const char *name, int af, const pj_stun_sock_cb *cb, const pj_stun_sock_cfg *cfg, void *user_data, pj_stun_sock **p_stun_sock) { pj_pool_t *pool; pj_stun_sock *stun_sock; pj_stun_sock_cfg default_cfg; pj_sockaddr bound_addr; unsigned i; pj_uint16_t max_bind_retry; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); status = pj_stun_config_check_valid(stun_cfg); if (status != PJ_SUCCESS) return status; if (name == NULL) name = "stuntp%p"; if (cfg == NULL) { pj_stun_sock_cfg_default(&default_cfg); cfg = &default_cfg; } /* Create structure */ pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); stun_sock->pool = pool; stun_sock->obj_name = pool->obj_name; stun_sock->user_data = user_data; stun_sock->af = af; stun_sock->sock_fd = PJ_INVALID_SOCKET; pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); stun_sock->ka_interval = cfg->ka_interval; stun_sock->timerstat = cfg->timerstat; if (stun_sock->ka_interval == 0) stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; printf(": cfg->stunProtocol=%d\n", cfg->stunProtocol); printf("%s", cfg->timerstat ? "SBR keepalive enabled\n": "SBR keepalive disabled\n"); if (cfg->grp_lock) { stun_sock->grp_lock = cfg->grp_lock; } else { status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } pj_grp_lock_add_ref(stun_sock->grp_lock); pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, &stun_sock_destructor); /* Create socket and bind socket */ // status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); if(cfg->stunProtocol == 1) status = pj_sock_socket(af, PJ_SOCK_STREAM, 0, &stun_sock->sock_fd); else status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, &cfg->qos_params, 2, stun_sock->obj_name, NULL); if (status != PJ_SUCCESS && !cfg->qos_ignore_error) goto on_error; /* Apply socket buffer size */ if (cfg->so_rcvbuf_size > 0) { unsigned sobuf_size = cfg->so_rcvbuf_size; status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_RCVBUF"); } else { if (sobuf_size < cfg->so_rcvbuf_size) { PJ_LOG(4, (stun_sock->obj_name, "Warning! Cannot set SO_RCVBUF as configured, " "now=%d, configured=%d", sobuf_size, cfg->so_rcvbuf_size)); } else { PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); } } } if (cfg->so_sndbuf_size > 0) { unsigned sobuf_size = cfg->so_sndbuf_size; status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_SNDBUF"); } else { if (sobuf_size < cfg->so_sndbuf_size) { PJ_LOG(4, (stun_sock->obj_name, "Warning! Cannot set SO_SNDBUF as configured, " "now=%d, configured=%d", sobuf_size, cfg->so_sndbuf_size)); } else { PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); } } } #if 1 /* Bind socket */ max_bind_retry = MAX_BIND_RETRY; if (cfg->port_range && cfg->port_range < max_bind_retry) max_bind_retry = cfg->port_range; pj_sockaddr_init(af, &bound_addr, NULL, 0); if (cfg->bound_addr.addr.sa_family == pj_AF_INET() || cfg->bound_addr.addr.sa_family == pj_AF_INET6()) { pj_sockaddr_cp(&bound_addr, &cfg->bound_addr); } status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr, cfg->port_range, max_bind_retry); if (status != PJ_SUCCESS) goto on_error; #endif /* Create more useful information string about this transport */ #if 0 { pj_sockaddr bound_addr; int addr_len = sizeof(bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); pj_sockaddr_print(&bound_addr, stun_sock->info, PJ_INET6_ADDRSTRLEN, 3); } #endif /* Init active socket configuration */ { pj_activesock_cfg activesock_cfg; pj_activesock_cb activesock_cb; pj_activesock_cfg_default(&activesock_cfg); activesock_cfg.grp_lock = stun_sock->grp_lock; activesock_cfg.async_cnt = cfg->async_cnt; activesock_cfg.concurrency = 0; /* Create the active socket */ pj_bzero(&activesock_cb, sizeof(activesock_cb)); activesock_cb.on_data_recvfrom = &on_data_recvfrom; activesock_cb.on_data_sent = &on_data_sent; if(cfg->stunProtocol == 1) { status = pj_activesock_create(pool, stun_sock->sock_fd, PJ_SOCK_STREAM, &activesock_cfg, stun_cfg->ioqueue, &activesock_cb, stun_sock, &stun_sock->active_sock); } else { status = pj_activesock_create(pool, stun_sock->sock_fd, pj_SOCK_DGRAM(), //nish &activesock_cfg, stun_cfg->ioqueue, &activesock_cb, stun_sock, &stun_sock->active_sock); } if (status != PJ_SUCCESS) goto on_error; if(cfg->stunProtocol == 1) { stun_sock->srv_addr.addr.sa_family = AF_INET; stun_sock->srv_addr.ipv4.sin_port= pj_htons(cfg->sin_port); stun_sock->srv_addr.ipv4.sin_addr = (cfg->sin_addr); status=pj_activesock_start_connect(stun_sock->active_sock, pool, &(stun_sock->srv_addr), pj_sockaddr_get_len(&(stun_sock->srv_addr))); if (status == PJ_SUCCESS) { printf("actie sock connect succesful"); } } /* Start asynchronous read operations */ status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, cfg->max_pkt_size, 0); if (status != PJ_SUCCESS) goto on_error; /* Init send keys */ pj_ioqueue_op_key_init(&stun_sock->send_key, sizeof(stun_sock->send_key)); pj_ioqueue_op_key_init(&stun_sock->int_send_key, sizeof(stun_sock->int_send_key)); } /* Create STUN session */ { pj_stun_session_cb sess_cb; pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &sess_on_request_complete; sess_cb.on_send_msg = &sess_on_send_msg; // stun_sock->stunProtocol = 1; //FIXME: cfg->stunProtocol; // stun_sock->stunProtocol = 0; //FIXME: cfg->stunProtocol; printf(" stun_sock->stunProtocol=%d\n", stun_sock->stunProtocol); status = pj_stun_session_create(&stun_sock->stun_cfg, stun_sock->obj_name, &sess_cb, PJ_FALSE, stun_sock->grp_lock, &stun_sock->stun_sess); if (status != PJ_SUCCESS) goto on_error; } /* Associate us with the STUN session */ pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); /* Initialize random numbers to be used as STUN transaction ID for * outgoing Binding request. We use the 80bit number to distinguish * STUN messages we sent with STUN messages that the application sends. * The last 16bit value in the array is a counter. */ for (i=0; i<PJ_ARRAY_SIZE(stun_sock->tsx_id); ++i) { stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); } stun_sock->tsx_id[5] = 0; /* Init timer entry */ stun_sock->ka_timer.cb = &ka_timer_cb; stun_sock->ka_timer.user_data = stun_sock; /* Done */ *p_stun_sock = stun_sock; return PJ_SUCCESS; on_error: pj_stun_sock_destroy(stun_sock); return status; }
/* * Common function to create TLS transport, called when pending accept() and * pending connect() complete. */ static pj_status_t tls_create( struct tls_listener *listener, pj_pool_t *pool, pj_ssl_sock_t *ssock, pj_bool_t is_server, const pj_sockaddr *local, const pj_sockaddr *remote, const pj_str_t *remote_name, struct tls_transport **p_tls) { struct tls_transport *tls; const pj_str_t ka_pkt = PJSIP_TLS_KEEP_ALIVE_DATA; char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_status_t status; PJ_ASSERT_RETURN(listener && ssock && local && remote && p_tls, PJ_EINVAL); if (pool == NULL) { pool = pjsip_endpt_create_pool(listener->endpt, "tls", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); } /* * Create and initialize basic transport structure. */ tls = PJ_POOL_ZALLOC_T(pool, struct tls_transport); tls->is_server = is_server; tls->verify_server = listener->tls_setting.verify_server; pj_list_init(&tls->delayed_list); tls->base.pool = pool; pj_ansi_snprintf(tls->base.obj_name, PJ_MAX_OBJ_NAME, (is_server ? "tlss%p" :"tlsc%p"), tls); status = pj_atomic_create(pool, 0, &tls->base.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, "tls", &tls->base.lock); if (status != PJ_SUCCESS) { goto on_error; } if (remote_name) pj_strdup(pool, &tls->remote_name, remote_name); tls->base.key.type = listener->factory.type; pj_sockaddr_cp(&tls->base.key.rem_addr, remote); tls->base.type_name = (char*)pjsip_transport_get_type_name( (pjsip_transport_type_e)tls->base.key.type); tls->base.flag = pjsip_transport_get_flag_from_type( (pjsip_transport_type_e)tls->base.key.type); tls->base.info = (char*) pj_pool_alloc(pool, 64); pj_ansi_snprintf(tls->base.info, 64, "%s to %s", tls->base.type_name, pj_sockaddr_print(remote, print_addr, sizeof(print_addr), 3)); tls->base.addr_len = pj_sockaddr_get_len(remote); tls->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; /* Set initial local address */ if (!pj_sockaddr_has_addr(local)) { pj_sockaddr_cp(&tls->base.local_addr, &listener->factory.local_addr); } else { pj_sockaddr_cp(&tls->base.local_addr, local); } sockaddr_to_host_port(pool, &tls->base.local_name, &tls->base.local_addr); if (tls->remote_name.slen) { tls->base.remote_name.host = tls->remote_name; tls->base.remote_name.port = pj_sockaddr_get_port(remote); } else { sockaddr_to_host_port(pool, &tls->base.remote_name, remote); } tls->base.endpt = listener->endpt; tls->base.tpmgr = listener->tpmgr; tls->base.send_msg = &tls_send_msg; tls->base.do_shutdown = &tls_shutdown; tls->base.destroy = &tls_destroy_transport; tls->ssock = ssock; /* Register transport to transport manager */ status = pjsip_transport_register(listener->tpmgr, &tls->base); if (status != PJ_SUCCESS) { goto on_error; } tls->is_registered = PJ_TRUE; /* Initialize keep-alive timer */ tls->ka_timer.user_data = (void*)tls; tls->ka_timer.cb = &tls_keep_alive_timer; pj_ioqueue_op_key_init(&tls->ka_op_key.key, sizeof(pj_ioqueue_op_key_t)); pj_strdup(tls->base.pool, &tls->ka_pkt, &ka_pkt); /* Done setting up basic transport. */ *p_tls = tls; PJ_LOG(4,(tls->base.obj_name, "TLS %s transport created", (tls->is_server ? "server" : "client"))); return PJ_SUCCESS; on_error: tls_destroy(&tls->base, status); return status; }
/* * unregister_test() * Check if callback is still called after socket has been unregistered or * closed. */ static int unregister_test(pj_bool_t allow_concur) { enum { RPORT = 50000, SPORT = 50001 }; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_sock_t ssock; pj_sock_t rsock; int addrlen; pj_sockaddr_in addr; pj_ioqueue_key_t *key; pj_ioqueue_op_key_t opkey; pj_ioqueue_callback cb; unsigned packet_cnt; char sendbuf[10], recvbuf[10]; pj_ssize_t bytes; pj_time_val timeout; pj_status_t status; pool = pj_pool_create(mem, "test", 4000, 4000, NULL); if (!pool) { app_perror("Unable to create pool", PJ_ENOMEM); return -100; } status = pj_ioqueue_create(pool, 16, &ioqueue); if (status != PJ_SUCCESS) { app_perror("Error creating ioqueue", status); return -110; } // Set concurrency TRACE_("set concurrency..."); status = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur); if (status != PJ_SUCCESS) { return -112; } /* Create sender socket */ status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, SPORT, &ssock); if (status != PJ_SUCCESS) { app_perror("Error initializing socket", status); return -120; } /* Create receiver socket. */ status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, RPORT, &rsock); if (status != PJ_SUCCESS) { app_perror("Error initializing socket", status); return -130; } /* Register rsock to ioqueue. */ pj_bzero(&cb, sizeof(cb)); cb.on_read_complete = &on_read_complete; packet_cnt = 0; status = pj_ioqueue_register_sock(pool, ioqueue, rsock, &packet_cnt, &cb, &key); if (status != PJ_SUCCESS) { app_perror("Error registering to ioqueue", status); return -140; } /* Init operation key. */ pj_ioqueue_op_key_init(&opkey, sizeof(opkey)); /* Start reading. */ bytes = sizeof(recvbuf); status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0); if (status != PJ_EPENDING) { app_perror("Expecting PJ_EPENDING, but got this", status); return -150; } /* Init destination address. */ addrlen = sizeof(addr); status = pj_sock_getsockname(rsock, &addr, &addrlen); if (status != PJ_SUCCESS) { app_perror("getsockname error", status); return -160; } /* Override address with 127.0.0.1, since getsockname will return * zero in the address field. */ addr.sin_addr = pj_inet_addr2("127.0.0.1"); /* Init buffer to send */ pj_ansi_strcpy(sendbuf, "Hello0123"); /* Send one packet. */ bytes = sizeof(sendbuf); status = pj_sock_sendto(ssock, sendbuf, &bytes, 0, &addr, sizeof(addr)); if (status != PJ_SUCCESS) { app_perror("sendto error", status); return -170; } /* Check if packet is received. */ timeout.sec = 1; timeout.msec = 0; #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_ioqueue_poll(ioqueue, &timeout); #endif if (packet_cnt != 1) { return -180; } /* Just to make sure things are settled.. */ pj_thread_sleep(100); /* Start reading again. */ bytes = sizeof(recvbuf); status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0); if (status != PJ_EPENDING) { app_perror("Expecting PJ_EPENDING, but got this", status); return -190; } /* Reset packet counter */ packet_cnt = 0; /* Send one packet. */ bytes = sizeof(sendbuf); status = pj_sock_sendto(ssock, sendbuf, &bytes, 0, &addr, sizeof(addr)); if (status != PJ_SUCCESS) { app_perror("sendto error", status); return -200; } /* Now unregister and close socket. */ pj_ioqueue_unregister(key); /* Poll ioqueue. */ #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else timeout.sec = 1; timeout.msec = 0; pj_ioqueue_poll(ioqueue, &timeout); #endif /* Must NOT receive any packets after socket is closed! */ if (packet_cnt > 0) { PJ_LOG(3,(THIS_FILE, "....errror: not expecting to receive packet " "after socket has been closed")); return -210; } /* Success */ pj_sock_close(ssock); pj_ioqueue_destroy(ioqueue); pj_pool_release(pool); return 0; }