/* * Send application data to remote agent. */ void natclient_send_data(struct ice_trans_s* icetrans, unsigned comp_id, const char *data) { pj_status_t status; if (icetrans->icest == NULL) { PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); return; } if (!pj_ice_strans_has_sess(icetrans->icest)) { PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first")); return; } /* if (!pj_ice_strans_sess_is_complete(icetrans->icest)) { PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress")); return; } */ if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icetrans->icest)) { PJ_LOG(1,(THIS_FILE, "Error: invalid component ID")); return; } status = pj_ice_strans_sendto(icetrans->icest, comp_id, data, strlen(data), &icetrans->rem.def_addr[comp_id-1], pj_sockaddr_get_len(&icetrans->rem.def_addr[comp_id-1])); if (status != PJ_SUCCESS) natclient_perror("Error sending data", status); else PJ_LOG(3,(THIS_FILE, "Data sent")); }
/* * Bind socket at random port. */ PJ_DEF(pj_status_t) pj_sock_bind_random( pj_sock_t sockfd, const pj_sockaddr_t *addr, pj_uint16_t port_range, pj_uint16_t max_try) { pj_sockaddr bind_addr; int addr_len; pj_uint16_t base_port; pj_status_t status = PJ_SUCCESS; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(addr, PJ_EINVAL); pj_sockaddr_cp(&bind_addr, addr); addr_len = pj_sockaddr_get_len(addr); base_port = pj_sockaddr_get_port(addr); if (base_port == 0 || port_range == 0) { return pj_sock_bind(sockfd, &bind_addr, addr_len); } for (; max_try; --max_try) { pj_uint16_t port; port = (pj_uint16_t)(base_port + pj_rand() % (port_range + 1)); pj_sockaddr_set_port(&bind_addr, port); status = pj_sock_bind(sockfd, &bind_addr, addr_len); if (status == PJ_SUCCESS) break; } return status; }
/* Start sending STUN Binding request */ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock) { pj_stun_tx_data *tdata; pj_status_t status; /* Increment request counter and create STUN Binding request */ ++stun_sock->tsx_id[5]; status = pj_stun_session_create_req(stun_sock->stun_sess, PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, (const pj_uint8_t*)stun_sock->tsx_id, &tdata); if (status != PJ_SUCCESS) goto on_error; /* Send request */ status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN, PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr, pj_sockaddr_get_len(&stun_sock->srv_addr), tdata); if (status != PJ_SUCCESS) goto on_error; return PJ_SUCCESS; on_error: sess_fail(stun_sock, PJ_STUN_SOCK_BINDING_OP, status); return status; }
/* Get the default IP interface */ PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr) { pj_sock_t fd; pj_str_t cp; pj_sockaddr a; int len; pj_uint8_t zero[64]; pj_status_t status; addr->addr.sa_family = (pj_uint16_t)af; status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd); if (status != PJ_SUCCESS) { return status; } PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() pj_sock_socket.")); if (af == PJ_AF_INET) { cp = pj_str("1.1.1.1"); } else { cp = pj_str("1::1"); } status = pj_sockaddr_init(af, &a, &cp, 53); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() pj_sockaddr_init.")); status = pj_sock_connect(fd, &a, pj_sockaddr_get_len(&a)); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() pj_sock_connect.")); len = sizeof(a); status = pj_sock_getsockname(fd, &a, &len); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() pj_sock_getsockname.")); pj_sock_close(fd); /* Check that the address returned is not zero */ pj_bzero(zero, sizeof(zero)); if (pj_memcmp(pj_sockaddr_get_addr(&a), zero, pj_sockaddr_get_addr_len(&a))==0) { PJ_LOG(4, ("sock_common.c", "pj_getdefaultipinterface() interface not found.")); return PJ_ENOTFOUND; } pj_sockaddr_copy_addr(addr, &a); /* Success */ return PJ_SUCCESS; }
/* Respond to ALLOCATE request */ static pj_status_t send_allocate_response(pj_turn_allocation *alloc, pj_stun_session *srv_sess, pj_turn_transport *transport, const pj_stun_rx_data *rdata) { pj_stun_tx_data *tdata; pj_status_t status; /* Respond the original ALLOCATE request */ status = pj_stun_session_create_res(srv_sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add XOR-RELAYED-ADDRESS attribute */ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE, &alloc->relay.hkey.addr, pj_sockaddr_get_len(&alloc->relay.hkey.addr)); /* Add LIFETIME. */ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, (unsigned)alloc->relay.lifetime); /* Add BANDWIDTH */ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_BANDWIDTH, alloc->bandwidth); /* Add RESERVATION-TOKEN */ PJ_TODO(ADD_RESERVATION_TOKEN); /* Add XOR-MAPPED-ADDRESS */ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr)); /* Send the response */ return pj_stun_session_send_msg(srv_sess, transport, PJ_TRUE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); }
/* Callback to be called by DNS SRV resolution */ static void srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { struct query *query = (struct query*) user_data; pjsip_server_addresses srv; unsigned i; if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query->objname, "DNS A/AAAA record resolution failed: %s", errmsg)); /* Call the callback */ (*query->cb)(status, query->token, NULL); return; } /* Build server addresses and call callback */ srv.count = 0; for (i=0; i<rec->count; ++i) { const pj_dns_addr_record *s = &rec->entry[i].server; unsigned j; for (j = 0; j < s->addr_count && srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++j) { srv.entry[srv.count].type = query->naptr[0].type; srv.entry[srv.count].priority = rec->entry[i].priority; srv.entry[srv.count].weight = rec->entry[i].weight; pj_sockaddr_init(s->addr[j].af, &srv.entry[srv.count].addr, 0, (pj_uint16_t)rec->entry[i].port); if (s->addr[j].af == pj_AF_INET6()) srv.entry[srv.count].addr.ipv6.sin6_addr = s->addr[j].ip.v6; else srv.entry[srv.count].addr.ipv4.sin_addr = s->addr[j].ip.v4; srv.entry[srv.count].addr_len = pj_sockaddr_get_len(&srv.entry[srv.count].addr); /* Update transport type if this is IPv6 */ if (s->addr[j].af == pj_AF_INET6()) srv.entry[srv.count].type |= PJSIP_TRANSPORT_IPV6; ++srv.count; } } /* Call the callback */ (*query->cb)(PJ_SUCCESS, query->token, &srv); }
/* Set local peer address */ PJ_DEF(void) pj_tcp_session_set_local_addr( pj_tcp_session *sess, const pj_sockaddr_t *addr) { char buf[PJ_INET6_ADDRSTRLEN+20]; if (!sess) return; if (!sess->local_addr) { sess->local_addr = (pj_sockaddr_t *) pj_pool_calloc(sess->pool, 1, pj_sockaddr_get_len(addr)); } sess->local_addr_len = pj_sockaddr_get_len(addr); pj_sockaddr_cp(sess->local_addr, addr); //pj_sockaddr_set_str_addr(pj_AF_INET(), (pj_sockaddr *)sess->peer_addr, &str_addr); PJ_LOG(4, (THIS_FILE, "pj_tcp_session_set_remote_peer_addr() %s", pj_sockaddr_print(sess->local_addr, buf, sizeof(buf), 3))); }
static pj_stun_msg* create_success_response(test_server *test_srv, turn_allocation *alloc, pj_stun_msg *req, pj_pool_t *pool, unsigned lifetime, pj_str_t *auth_key) { pj_stun_msg *resp; pj_str_t tmp; pj_status_t status; /* Create response */ status = pj_stun_msg_create_response(pool, req, 0, NULL, &resp); if (status != PJ_SUCCESS) { return NULL; } /* Add TURN_NONCE */ pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE)); /* Add LIFETIME */ pj_stun_msg_add_uint_attr(pool, resp, PJ_STUN_ATTR_LIFETIME, lifetime); if (lifetime != 0) { /* Add XOR-RELAYED-ADDRESS */ pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE, &alloc->alloc_addr, pj_sockaddr_get_len(&alloc->alloc_addr)); /* Add XOR-MAPPED-ADDRESS */ pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->client_addr, pj_sockaddr_get_len(&alloc->client_addr)); } /* Add blank MESSAGE-INTEGRITY */ pj_stun_msg_add_msgint_attr(pool, resp); /* Set auth key */ pj_stun_create_key(pool, auth_key, &test_srv->domain, &test_srv->username, PJ_STUN_PASSWD_PLAIN, &test_srv->passwd); return resp; }
/* Create and send error response */ static void send_reply_err(pj_turn_allocation *alloc, const pj_stun_rx_data *rdata, pj_bool_t cache, int code, const char *errmsg) { pj_status_t status; status = pj_stun_session_respond(alloc->sess, rdata, code, errmsg, NULL, cache, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr)); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error sending STUN error response", status); return; } }
PJ_DEF(pj_status_t) pj_activesock_create_udp( pj_pool_t *pool, const pj_sockaddr *addr, const pj_activesock_cfg *opt, pj_ioqueue_t *ioqueue, const pj_activesock_cb *cb, void *user_data, pj_activesock_t **p_asock, pj_sockaddr *bound_addr) { pj_sock_t sock_fd; pj_sockaddr default_addr; pj_status_t status; if (addr == NULL) { pj_sockaddr_init(pj_AF_INET(), &default_addr, NULL, 0); addr = &default_addr; } status = pj_sock_socket(addr->addr.sa_family, pj_SOCK_DGRAM(), 0, &sock_fd); if (status != PJ_SUCCESS) { return status; } status = pj_sock_bind(sock_fd, addr, pj_sockaddr_get_len(addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock_fd); return status; } status = pj_activesock_create(pool, sock_fd, pj_SOCK_DGRAM(), opt, ioqueue, cb, user_data, p_asock); if (status != PJ_SUCCESS) { pj_sock_close(sock_fd); return status; } if (bound_addr) { int addr_len = sizeof(*bound_addr); status = pj_sock_getsockname(sock_fd, bound_addr, &addr_len); if (status != PJ_SUCCESS) { pj_activesock_close(*p_asock); return status; } } return PJ_SUCCESS; }
static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size) { struct transport_ice *tp_ice = (struct transport_ice*)tp; if (tp_ice->comp_cnt > 1) { if (addr == NULL) { addr = &tp_ice->remote_rtcp; addr_len = pj_sockaddr_get_len(addr); } return pj_ice_strans_sendto(tp_ice->ice_st, 2, pkt, size, addr, addr_len); } else { return PJ_SUCCESS; } }
/* Create and send successful response */ static void send_reply_ok(pj_turn_allocation *alloc, const pj_stun_rx_data *rdata) { pj_status_t status; unsigned interval; pj_stun_tx_data *tdata; status = pj_stun_session_create_res(alloc->sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error creating STUN success response", status); return; } /* Calculate time to expiration */ if (alloc->relay.lifetime != 0) { pj_time_val now; pj_gettimeofday(&now); interval = alloc->relay.expiry.sec - now.sec; } else { interval = 0; } /* Add LIFETIME if this is not ChannelBind. */ if (PJ_STUN_GET_METHOD(tdata->msg->hdr.type)!=PJ_STUN_CHANNEL_BIND_METHOD){ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, interval); /* Add BANDWIDTH if lifetime is not zero */ if (interval != 0) { pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_BANDWIDTH, alloc->bandwidth); } } status = pj_stun_session_send_msg(alloc->sess, NULL, PJ_TRUE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error sending STUN success response", status); return; } }
/* * Send REFRESH */ static void send_refresh(pj_tcp_session *sess, int lifetime) { pj_stun_tx_data *tdata; pj_status_t status; PJ_ASSERT_ON_FAIL(sess->state==PJ_TCP_STATE_READY, return); /* Create a bare REFRESH request */ status = pj_stun_session_create_req(sess->stun, PJ_STUN_REFRESH_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) goto on_error; /* Add LIFETIME */ if (lifetime >= 0) { pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, lifetime); } #if TCP_TODO /* Send request */ if (lifetime == 0) { pj_tcp_session_set_state(sess, PJ_TCP_STATE_DISCONNECTING); } status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, (sess->conn_type==PJ_TCP_TP_UDP), sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) goto on_error; #endif return; on_error: if (lifetime == 0) { pj_tcp_session_set_state(sess, PJ_TCP_STATE_DISCONNECTED); sess_shutdown(sess, status); } }
static pj_status_t get_local_interface(const pj_sockaddr *server, pj_sockaddr *local_addr) { pj_sock_t sock; pj_sockaddr tmp, local; int addr_len; pj_status_t status; status = pj_sock_socket(server->addr.sa_family, pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) return status; addr_len = pj_sockaddr_get_len(server); pj_sockaddr_init(server->addr.sa_family, &local, NULL, 0); status = pj_sock_bind(sock, &local, addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } status = pj_sock_connect(sock, server, addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } status = pj_sock_getsockname(sock, &tmp, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } pj_sockaddr_cp(local_addr, &tmp); pj_sock_close(sock); return PJ_SUCCESS; }
/* * Callback notification from STUN session when it receives STUN * indications. This callback was trigger by STUN incoming message * processing in pj_turn_allocation_on_rx_client_pkt(). */ static pj_status_t stun_on_rx_indication(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_xor_peer_addr_attr *peer_attr; pj_stun_data_attr *data_attr; pj_turn_allocation *alloc; pj_turn_permission *perm; pj_ssize_t len; PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); /* Only expect Send Indication */ if (msg->hdr.type != PJ_STUN_SEND_INDICATION) { /* Ignore */ return PJ_SUCCESS; } /* Get XOR-PEER-ADDRESS attribute */ peer_attr = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); /* MUST have XOR-PEER-ADDRESS attribute */ if (!peer_attr) return PJ_SUCCESS; /* Get DATA attribute */ data_attr = (pj_stun_data_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0); /* Create/update/refresh the permission */ perm = lookup_permission_by_addr(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (perm == NULL) { perm = create_permission(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); } refresh_permission(perm); /* Return if we don't have data */ if (data_attr == NULL) return PJ_SUCCESS; /* Relay the data to peer */ len = data_attr->length; pj_sock_sendto(alloc->relay.tp.sock, data_attr->data, &len, 0, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); return PJ_SUCCESS; }
/* * Callback notification from STUN session when it receives STUN * requests. This callback was trigger by STUN incoming message * processing in pj_turn_allocation_on_rx_client_pkt(). */ static pj_status_t stun_on_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { const pj_stun_msg *msg = rdata->msg; pj_turn_allocation *alloc; PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); /* Refuse to serve any request if we've been shutdown */ if (alloc->relay.lifetime == 0) { /* Reject with 437 if we're shutting down */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); return PJ_SUCCESS; } if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) { /* * Handle REFRESH request */ pj_stun_lifetime_attr *lifetime; pj_stun_bandwidth_attr *bandwidth; /* Get LIFETIME attribute */ lifetime = (pj_stun_lifetime_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0); /* Get BANDWIDTH attribute */ bandwidth = (pj_stun_bandwidth_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0); /* TODO: process bandwidth */ PJ_UNUSED_ARG(bandwidth); if (lifetime && lifetime->value==0) { /* * This is deallocation request. */ alloc->relay.lifetime = 0; /* Respond first */ send_reply_ok(alloc, rdata); /* Shutdown allocation */ PJ_LOG(4,(alloc->obj_name, "Client %s request to dealloc, shutting down", alloc->info)); alloc_shutdown(alloc); } else { /* * This is a refresh request. */ /* Update lifetime */ if (lifetime) { alloc->relay.lifetime = lifetime->value; } /* Update bandwidth */ // TODO: /* Update expiration timer */ resched_timeout(alloc); /* Send reply */ send_reply_ok(alloc, rdata); } } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) { /* * ChannelBind request. */ pj_stun_channel_number_attr *ch_attr; pj_stun_xor_peer_addr_attr *peer_attr; pj_turn_permission *p1, *p2; ch_attr = (pj_stun_channel_number_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0); peer_attr = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); if (!ch_attr || !peer_attr) { send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); return PJ_SUCCESS; } /* Find permission with the channel number */ p1 = lookup_permission_by_chnum(alloc, PJ_STUN_GET_CH_NB(ch_attr->value)); /* If permission is found, this is supposed to be a channel bind * refresh. Make sure it's for the same peer. */ if (p1) { if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) { /* Address mismatch. Send 400 */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address mismatch"); return PJ_SUCCESS; } /* Refresh permission */ refresh_permission(p1); /* Send response */ send_reply_ok(alloc, rdata); /* Done */ return PJ_SUCCESS; } /* If permission is not found, create a new one. Make sure the peer * has not alreadyy assigned with a channel number. */ p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) { send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address already assigned a channel number"); return PJ_SUCCESS; } /* Create permission if it doesn't exist */ if (!p2) { p2 = create_permission(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (!p2) return PJ_SUCCESS; } /* Assign channel number to permission */ p2->channel = PJ_STUN_GET_CH_NB(ch_attr->value); /* Register to hash table */ pj_assert(sizeof(p2->channel)==2); pj_hash_set(alloc->pool, alloc->ch_table, &p2->channel, sizeof(p2->channel), 0, p2); /* Update */ refresh_permission(p2); /* Reply */ send_reply_ok(alloc, rdata); return PJ_SUCCESS; } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) { /* Respond with 437 (section 6.3 turn-07) */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); } else { /* Respond with Bad Request? */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); } return PJ_SUCCESS; }
/* * Handle incoming packet from peer. This function is called by * on_rx_from_peer(). */ static void handle_peer_pkt(pj_turn_allocation *alloc, pj_turn_relay_res *rel, char *pkt, pj_size_t len, const pj_sockaddr *src_addr) { pj_turn_permission *perm; /* Lookup permission */ perm = lookup_permission_by_addr(alloc, src_addr, pj_sockaddr_get_len(src_addr)); if (perm == NULL) { /* No permission, discard data */ return; } /* Send Data Indication or ChannelData, depends on whether * this permission is attached to a channel number. */ if (perm->channel != PJ_TURN_INVALID_CHANNEL) { /* Send ChannelData */ pj_turn_channel_data *cd = (pj_turn_channel_data*)rel->tp.tx_pkt; if (len > PJ_TURN_MAX_PKT_LEN) { char peer_addr[80]; pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3); PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s " "because it's too long (%d bytes)", alloc->info, peer_addr, len)); return; } /* Init header */ cd->ch_number = pj_htons(perm->channel); cd->length = pj_htons((pj_uint16_t)len); /* Copy data */ pj_memcpy(rel->tp.tx_pkt+sizeof(pj_turn_channel_data), pkt, len); /* Send to client */ alloc->transport->sendto(alloc->transport, rel->tp.tx_pkt, len+sizeof(pj_turn_channel_data), 0, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr)); } else { /* Send Data Indication */ pj_stun_tx_data *tdata; pj_status_t status; status = pj_stun_session_create_ind(alloc->sess, PJ_STUN_DATA_INDICATION, &tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error creating Data indication", status); return; } pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, src_addr, pj_sockaddr_get_len(src_addr)); pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_DATA, (const pj_uint8_t*)pkt, len); pj_stun_session_send_msg(alloc->sess, NULL, PJ_FALSE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); } }
static int run_client_test(const char *title, pj_bool_t server_responding, pj_stun_auth_type server_auth_type, pj_stun_auth_type client_auth_type, const char *realm, const char *username, const char *nonce, const char *password, pj_bool_t dummy_mi, pj_bool_t expected_error, pj_status_t expected_code, const char *expected_realm, const char *expected_nonce, int (*more_check)(void)) { pj_pool_t *pool; pj_stun_session_cb sess_cb; pj_stun_auth_cred cred; pj_stun_tx_data *tdata; pj_status_t status; int rc = 0; PJ_LOG(3,(THIS_FILE, " %s test", title)); /* Create client */ pool = pj_pool_create(mem, "client", 1000, 1000, NULL); client = PJ_POOL_ZALLOC_T(pool, struct client); client->pool = pool; client->responding = PJ_TRUE; /* Create STUN session */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &client_on_request_complete; sess_cb.on_send_msg = &client_send_msg; status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, NULL, &client->sess); if (status != PJ_SUCCESS) { destroy_client_server(); return -200; } /* Create semaphore */ status = pj_sem_create(pool, "client", 0, 1, &client->test_complete); if (status != PJ_SUCCESS) { destroy_client_server(); return -205; } /* Create client socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &client->sock); if (status != PJ_SUCCESS) { destroy_client_server(); return -210; } /* Bind client socket */ status = pj_sock_bind_in(client->sock, 0, 0); if (status != PJ_SUCCESS) { destroy_client_server(); return -220; } /* Create client thread */ status = pj_thread_create(pool, "client", &client_thread, NULL, 0, 0, &client->thread); if (status != PJ_SUCCESS) { destroy_client_server(); return -230; } /* Initialize credential */ pj_bzero(&cred, sizeof(cred)); cred.type = PJ_STUN_AUTH_CRED_STATIC; if (realm) cred.data.static_cred.realm = pj_str((char*)realm); if (username) cred.data.static_cred.username = pj_str((char*)username); if (nonce) cred.data.static_cred.nonce = pj_str((char*)nonce); if (password) cred.data.static_cred.data = pj_str((char*)password); cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; status = pj_stun_session_set_credential(client->sess, client_auth_type, &cred); if (status != PJ_SUCCESS) { destroy_client_server(); return -240; } /* Create the server */ status = create_std_server(server_auth_type, server_responding); if (status != 0) { destroy_client_server(); return status; } /* Create request */ status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) { destroy_client_server(); return -250; } /* Add our own attributes if client authentication is set to none */ if (client_auth_type == PJ_STUN_AUTH_NONE) { pj_str_t tmp; if (realm) pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_REALM, pj_cstr(&tmp, realm)); if (username) pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&tmp, username)); if (nonce) pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, nonce)); if (password) { // ignored } if (dummy_mi) { pj_stun_msgint_attr *mi; pj_stun_msgint_attr_create(tdata->pool, &mi); pj_stun_msg_add_attr(tdata->msg, &mi->hdr); } } /* Send the request */ status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr, pj_sockaddr_get_len(&server->addr), tdata); if (status != PJ_SUCCESS) { destroy_client_server(); return -270; } /* Wait until test complete */ pj_sem_wait(client->test_complete); /* Verify response */ if (expected_error) { if (expected_code != client->response_status) { char e1[PJ_ERR_MSG_SIZE], e2[PJ_ERR_MSG_SIZE]; pj_strerror(expected_code, e1, sizeof(e1)); pj_strerror(client->response_status, e2, sizeof(e2)); PJ_LOG(3,(THIS_FILE, " err: expecting %d (%s) but got %d (%s) response", expected_code, e1, client->response_status, e2)); rc = -500; } } else { int res_code = 0; pj_stun_realm_attr *arealm; pj_stun_nonce_attr *anonce; if (client->response_status != 0) { PJ_LOG(3,(THIS_FILE, " err: expecting successful operation but got error %d", client->response_status)); rc = -600; goto done; } if (PJ_STUN_IS_ERROR_RESPONSE(client->response->hdr.type)) { pj_stun_errcode_attr *aerr = NULL; aerr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_ERROR_CODE, 0); if (aerr == NULL) { PJ_LOG(3,(THIS_FILE, " err: received error response without ERROR-CODE")); rc = -610; goto done; } res_code = aerr->err_code; } else { res_code = 0; } /* Check that code matches */ if (expected_code != res_code) { PJ_LOG(3,(THIS_FILE, " err: expecting response code %d but got %d", expected_code, res_code)); rc = -620; goto done; } /* Find REALM and NONCE attributes */ arealm = (pj_stun_realm_attr*) pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0); anonce = (pj_stun_nonce_attr*) pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0); if (expected_realm) { if (arealm == NULL) { PJ_LOG(3,(THIS_FILE, " err: expecting REALM in esponse")); rc = -630; goto done; } if (pj_strcmp2(&arealm->value, expected_realm)!=0) { PJ_LOG(3,(THIS_FILE, " err: REALM mismatch in response")); rc = -640; goto done; } } else { if (arealm != NULL) { PJ_LOG(3,(THIS_FILE, " err: non expecting REALM in response")); rc = -650; goto done; } } if (expected_nonce) { if (anonce == NULL) { PJ_LOG(3,(THIS_FILE, " err: expecting NONCE in esponse")); rc = -660; goto done; } if (pj_strcmp2(&anonce->value, expected_nonce)!=0) { PJ_LOG(3,(THIS_FILE, " err: NONCE mismatch in response")); rc = -670; goto done; } } else { if (anonce != NULL) { PJ_LOG(3,(THIS_FILE, " err: non expecting NONCE in response")); rc = -680; goto done; } } } /* Our tests are okay so far. Let caller do some more tests if * it wants to. */ if (rc==0 && more_check) { rc = (*more_check)(); } done: destroy_client_server(); return rc; }
/* 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; }
static int stun_destroy_test(void) { enum { LOOP = 500 }; struct stun_test_session test_sess; pj_sockaddr bind_addr; int addr_len; pj_caching_pool cp; pj_pool_t *pool; unsigned i; pj_status_t status; int rc = 0; PJ_LOG(3,(THIS_FILE, " STUN destroy concurrency test")); pj_bzero(&test_sess, sizeof(test_sess)); pj_caching_pool_init(&cp, NULL, 0); pool = pj_pool_create(&cp.factory, "testsess", 512, 512, NULL); pj_stun_config_init(&test_sess.stun_cfg, &cp.factory, 0, NULL, NULL); status = pj_timer_heap_create(pool, 1023, &test_sess.stun_cfg.timer_heap); pj_assert(status == PJ_SUCCESS); status = pj_lock_create_recursive_mutex(pool, NULL, &test_sess.lock); pj_assert(status == PJ_SUCCESS); pj_timer_heap_set_lock(test_sess.stun_cfg.timer_heap, test_sess.lock, PJ_TRUE); pj_assert(status == PJ_SUCCESS); status = pj_ioqueue_create(pool, 512, &test_sess.stun_cfg.ioqueue); pj_assert(status == PJ_SUCCESS); pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &test_sess.server_sock); pj_sockaddr_init(pj_AF_INET(), &bind_addr, NULL, 0); status = pj_sock_bind(test_sess.server_sock, &bind_addr, pj_sockaddr_get_len(&bind_addr)); pj_assert(status == PJ_SUCCESS); addr_len = sizeof(bind_addr); status = pj_sock_getsockname(test_sess.server_sock, &bind_addr, &addr_len); pj_assert(status == PJ_SUCCESS); test_sess.server_port = pj_sockaddr_get_port(&bind_addr); status = pj_event_create(pool, NULL, PJ_TRUE, PJ_FALSE, &test_sess.server_event); pj_assert(status == PJ_SUCCESS); for (i=0; i<SERVER_THREAD_CNT; ++i) { status = pj_thread_create(pool, NULL, &server_thread_proc, &test_sess, 0, 0, &test_sess.server_threads[i]); pj_assert(status == PJ_SUCCESS); } for (i=0; i<WORKER_THREAD_CNT; ++i) { status = pj_thread_create(pool, NULL, &worker_thread_proc, &test_sess, 0, 0, &test_sess.worker_threads[i]); pj_assert(status == PJ_SUCCESS); } /* Test 1: Main thread calls destroy while callback is processing response */ PJ_LOG(3,(THIS_FILE, " Destroy in main thread while callback is running")); for (i=0; i<LOOP; ++i) { int sleep = pj_rand() % 5; PJ_LOG(3,(THIS_FILE, " Try %-3d of %d", i+1, LOOP)); /* Test 1: destroy at the same time when receiving response */ pj_bzero(&test_sess.param, sizeof(test_sess.param)); test_sess.param.client_sleep_after_start = 20; test_sess.param.client_sleep_before_destroy = sleep; test_sess.param.server_wait_for_event = PJ_TRUE; stun_destroy_test_session(&test_sess); PJ_LOG(3,(THIS_FILE, " stun test a: sleep delay:%d: clients with response: %d", sleep, test_sess.param.client_got_response)); /* Test 2: destroy at the same time with STUN retransmit timer */ test_sess.param.server_drop_request = PJ_TRUE; test_sess.param.client_sleep_after_start = 0; test_sess.param.client_sleep_before_destroy = PJ_STUN_RTO_VALUE; test_sess.param.server_wait_for_event = PJ_FALSE; stun_destroy_test_session(&test_sess); PJ_LOG(3,(THIS_FILE, " stun test b: retransmit concurrency")); /* Test 3: destroy at the same time with receiving response * AND STUN retransmit timer */ test_sess.param.client_got_response = 0; test_sess.param.server_drop_request = PJ_FALSE; test_sess.param.client_sleep_after_start = PJ_STUN_RTO_VALUE; test_sess.param.client_sleep_before_destroy = 0; test_sess.param.server_wait_for_event = PJ_TRUE; stun_destroy_test_session(&test_sess); PJ_LOG(3,(THIS_FILE, " stun test c: clients with response: %d", test_sess.param.client_got_response)); pj_thread_sleep(10); ice_one_conc_test(&test_sess.stun_cfg, PJ_FALSE); pj_thread_sleep(10); } /* Avoid compiler warning */ goto on_return; on_return: test_sess.thread_quit_flag = PJ_TRUE; for (i=0; i<SERVER_THREAD_CNT; ++i) { pj_thread_join(test_sess.server_threads[i]); } for (i=0; i<WORKER_THREAD_CNT; ++i) { pj_thread_join(test_sess.worker_threads[i]); } pj_event_destroy(test_sess.server_event); pj_sock_close(test_sess.server_sock); pj_ioqueue_destroy(test_sess.stun_cfg.ioqueue); pj_timer_heap_destroy(test_sess.stun_cfg.timer_heap); pj_pool_release(pool); pj_caching_pool_destroy(&cp); PJ_LOG(3,(THIS_FILE, " Done. rc=%d", rc)); return rc; }
/* SSL socket try to connect to raw TCP socket server, once * connection established, SSL socket will try to perform SSL * handshake. SSL client socket should be able to close the * connection after specified timeout period (set ms_timeout to * 0 to disable timer). */ static int server_non_ssl(unsigned ms_timeout) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_timer_heap_t *timer = NULL; pj_activesock_t *asock_serv = NULL; pj_ssl_sock_t *ssock_cli = NULL; pj_activesock_cb asock_cb = { 0 }; pj_sock_t sock = PJ_INVALID_SOCKET; pj_ssl_sock_param param; struct test_state state_serv = { 0 }; struct test_state state_cli = { 0 }; pj_sockaddr addr, listen_addr; pj_status_t status; pool = pj_pool_create(mem, "ssl_connect_raw_tcp", 256, 256, NULL); status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } status = pj_timer_heap_create(pool, 4, &timer); if (status != PJ_SUCCESS) { goto on_return; } /* SERVER */ state_serv.pool = pool; state_serv.ioqueue = ioqueue; status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) { goto on_return; } /* Init bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } status = pj_sock_bind(sock, (pj_sockaddr_t*)&listen_addr, pj_sockaddr_get_len((pj_sockaddr_t*)&listen_addr)); if (status != PJ_SUCCESS) { goto on_return; } status = pj_sock_listen(sock, PJ_SOMAXCONN); if (status != PJ_SUCCESS) { goto on_return; } asock_cb.on_accept_complete = &asock_on_accept_complete; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL, ioqueue, &asock_cb, &state_serv, &asock_serv); if (status != PJ_SUCCESS) { goto on_return; } status = pj_activesock_start_accept(asock_serv, pool); if (status != PJ_SUCCESS) goto on_return; /* Update listener address */ { int addr_len; addr_len = sizeof(listen_addr); pj_sock_getsockname(sock, (pj_sockaddr_t*)&listen_addr, &addr_len); } /* CLIENT */ pj_ssl_sock_param_default(¶m); param.cb.on_connect_complete = &ssl_on_connect_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.timer_heap = timer; param.timeout.sec = 0; param.timeout.msec = ms_timeout; pj_time_val_normalize(¶m.timeout); param.user_data = &state_cli; state_cli.pool = pool; state_cli.is_server = PJ_FALSE; state_cli.is_verbose = PJ_TRUE; status = pj_ssl_sock_create(pool, ¶m, &ssock_cli); if (status != PJ_SUCCESS) { goto on_return; } /* Init default bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } status = pj_ssl_sock_start_connect(ssock_cli, pool, (pj_sockaddr_t*)&addr, (pj_sockaddr_t*)&listen_addr, pj_sockaddr_get_len(&listen_addr)); if (status != PJ_EPENDING) { goto on_return; } /* Wait until everything has been sent/received or error */ while ((!state_serv.err && !state_serv.done) || (!state_cli.err && !state_cli.done)) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer, &delay); #endif } if (state_serv.err || state_cli.err) { if (state_cli.err != PJ_SUCCESS) status = state_cli.err; else status = state_serv.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); on_return: if (asock_serv) pj_activesock_close(asock_serv); if (ssock_cli && !state_cli.err && !state_cli.done) pj_ssl_sock_close(ssock_cli); if (timer) pj_timer_heap_destroy(timer); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }
static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher, pj_bool_t req_client_cert, pj_bool_t client_provide_cert) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_ssl_sock_t *ssock_serv = NULL; pj_ssl_sock_t *ssock_cli = NULL; pj_ssl_sock_param param; struct test_state state_serv = { 0 }; struct test_state state_cli = { 0 }; pj_sockaddr addr, listen_addr; pj_ssl_cipher ciphers[1]; pj_ssl_cert_t *cert = NULL; pj_status_t status; pool = pj_pool_create(mem, "ssl_echo", 256, 256, NULL); status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } pj_ssl_sock_param_default(¶m); param.cb.on_accept_complete = &ssl_on_accept_complete; param.cb.on_connect_complete = &ssl_on_connect_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.ciphers = ciphers; /* Init default bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } /* === SERVER === */ param.proto = srv_proto; param.user_data = &state_serv; param.ciphers_num = (srv_cipher == -1)? 0 : 1; param.require_client_cert = req_client_cert; ciphers[0] = srv_cipher; state_serv.pool = pool; state_serv.echo = PJ_TRUE; state_serv.is_server = PJ_TRUE; state_serv.is_verbose = PJ_TRUE; status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); if (status != PJ_SUCCESS) { goto on_return; } /* Set server cert */ { pj_str_t tmp1, tmp2, tmp3, tmp4; status = pj_ssl_cert_load_from_files(pool, pj_strset2(&tmp1, (char*)CERT_CA_FILE), pj_strset2(&tmp2, (char*)CERT_FILE), pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), &cert); if (status != PJ_SUCCESS) { goto on_return; } status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } } status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr)); if (status != PJ_SUCCESS) { goto on_return; } /* Get listener address */ { pj_ssl_sock_info info; pj_ssl_sock_get_info(ssock_serv, &info); pj_sockaddr_cp(&listen_addr, &info.local_addr); } /* === CLIENT === */ param.proto = cli_proto; param.user_data = &state_cli; param.ciphers_num = (cli_cipher == -1)? 0 : 1; ciphers[0] = cli_cipher; state_cli.pool = pool; state_cli.check_echo = PJ_TRUE; state_cli.is_verbose = PJ_TRUE; { pj_time_val now; pj_gettimeofday(&now); pj_srand((unsigned)now.sec); state_cli.send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024; } state_cli.send_str = pj_pool_alloc(pool, state_cli.send_str_len); { unsigned i; for (i = 0; i < state_cli.send_str_len; ++i) state_cli.send_str[i] = (char)(pj_rand() % 256); } status = pj_ssl_sock_create(pool, ¶m, &ssock_cli); if (status != PJ_SUCCESS) { goto on_return; } /* Set cert for client */ { if (!client_provide_cert) { pj_str_t tmp1, tmp2; pj_strset2(&tmp1, (char*)CERT_CA_FILE); pj_strset2(&tmp2, NULL); status = pj_ssl_cert_load_from_files(pool, &tmp1, &tmp2, &tmp2, &tmp2, &cert); if (status != PJ_SUCCESS) { goto on_return; } } status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } } status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr)); if (status == PJ_SUCCESS) { ssl_on_connect_complete(ssock_cli, PJ_SUCCESS); } else if (status == PJ_EPENDING) { status = PJ_SUCCESS; } else { goto on_return; } /* Wait until everything has been sent/received or error */ while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); #endif } /* Clean up sockets */ { pj_time_val delay = {0, 100}; while (pj_ioqueue_poll(ioqueue, &delay) > 0); } if (state_serv.err || state_cli.err) { if (state_serv.err != PJ_SUCCESS) status = state_serv.err; else status = state_cli.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state_cli.sent, state_cli.recv)); on_return: if (ssock_serv) pj_ssl_sock_close(ssock_serv); if (ssock_cli && !state_cli.err && !state_cli.done) pj_ssl_sock_close(ssock_cli); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }
/* * Copy socket address. */ PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src) { pj_memcpy(dst, src, pj_sockaddr_get_len(src)); }
static int server_thread_proc(void *p) { struct stun_test_session *test_sess = (struct stun_test_session*)p; pj_pool_t *pool; pj_status_t status; PJ_LOG(4,(THIS_FILE, "Server thread running")); pool = pj_pool_create(test_sess->stun_cfg.pf, "server", 512, 512, NULL); while (!test_sess->thread_quit_flag) { pj_time_val timeout = {0, 10}; pj_fd_set_t rdset; int n; /* Serve client */ PJ_FD_ZERO(&rdset); PJ_FD_SET(test_sess->server_sock, &rdset); n = pj_sock_select(test_sess->server_sock+1, &rdset, NULL, NULL, &timeout); if (n==1 && PJ_FD_ISSET(test_sess->server_sock, &rdset)) { pj_uint8_t pkt[512]; pj_ssize_t pkt_len; pj_size_t res_len; pj_sockaddr client_addr; int addr_len; pj_stun_msg *stun_req, *stun_res; pj_pool_reset(pool); /* Got query */ pkt_len = sizeof(pkt); addr_len = sizeof(client_addr); status = pj_sock_recvfrom(test_sess->server_sock, pkt, &pkt_len, 0, &client_addr, &addr_len); if (status != PJ_SUCCESS) { continue; } status = pj_stun_msg_decode(pool, pkt, pkt_len, PJ_STUN_IS_DATAGRAM, &stun_req, NULL, NULL); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "STUN request decode error")); continue; } status = pj_stun_msg_create_response(pool, stun_req, PJ_STUN_SC_BAD_REQUEST, NULL, &stun_res); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "STUN create response error")); continue; } status = pj_stun_msg_encode(stun_res, pkt, sizeof(pkt), 0, NULL, &res_len); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "STUN encode error")); continue; } /* Ignore request */ if (test_sess->param.server_drop_request) continue; /* Wait for signal to continue */ if (test_sess->param.server_wait_for_event) pj_event_wait(test_sess->server_event); pkt_len = res_len; pj_sock_sendto(test_sess->server_sock, pkt, &pkt_len, 0, &client_addr, pj_sockaddr_get_len(&client_addr)); } } pj_pool_release(pool); PJ_LOG(4,(THIS_FILE, "Server thread quitting")); return 0; }
/* * Create relay. */ static pj_status_t create_relay(pj_turn_srv *srv, pj_turn_allocation *alloc, const pj_stun_msg *msg, const alloc_request *req, pj_turn_relay_res *relay) { enum { RETRY = 40 }; pj_pool_t *pool = alloc->pool; int retry, retry_max, sock_type; pj_ioqueue_callback icb; int af, namelen; pj_stun_string_attr *sa; pj_status_t status; pj_bzero(relay, sizeof(*relay)); relay->allocation = alloc; relay->tp.sock = PJ_INVALID_SOCKET; /* TODO: get the requested address family from somewhere */ af = alloc->transport->listener->addr.addr.sa_family; /* Save realm */ sa = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP); pj_strdup(pool, &relay->realm, &sa->value); /* Save username */ sa = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP); pj_strdup(pool, &relay->user, &sa->value); /* Lifetime and timeout */ relay->lifetime = req->lifetime; pj_timer_entry_init(&relay->timer, TIMER_ID_NONE, relay, &relay_timeout_cb); resched_timeout(alloc); /* Transport type */ relay->hkey.tp_type = req->tp_type; /* Create the socket */ if (req->tp_type == PJ_TURN_TP_UDP) { sock_type = pj_SOCK_DGRAM(); } else if (req->tp_type == PJ_TURN_TP_TCP) { sock_type = pj_SOCK_STREAM(); } else { pj_assert(!"Unknown transport"); return PJ_EINVALIDOP; } status = pj_sock_socket(af, sock_type, 0, &relay->tp.sock); if (status != PJ_SUCCESS) { pj_bzero(relay, sizeof(*relay)); return status; } /* Find suitable port for this allocation */ if (req->rpp_port) { retry_max = 1; } else { retry_max = RETRY; } for (retry=0; retry<retry_max; ++retry) { pj_uint16_t port; pj_sockaddr bound_addr; pj_lock_acquire(srv->core.lock); if (req->rpp_port) { port = (pj_uint16_t) req->rpp_port; } else if (req->tp_type == PJ_TURN_TP_UDP) { port = (pj_uint16_t) srv->ports.next_udp++; if (srv->ports.next_udp > srv->ports.max_udp) srv->ports.next_udp = srv->ports.min_udp; } else if (req->tp_type == PJ_TURN_TP_TCP) { port = (pj_uint16_t) srv->ports.next_tcp++; if (srv->ports.next_tcp > srv->ports.max_tcp) srv->ports.next_tcp = srv->ports.min_tcp; } else { pj_assert(!"Invalid transport"); port = 0; } pj_lock_release(srv->core.lock); pj_sockaddr_init(af, &bound_addr, NULL, port); status = pj_sock_bind(relay->tp.sock, &bound_addr, pj_sockaddr_get_len(&bound_addr)); if (status == PJ_SUCCESS) break; } if (status != PJ_SUCCESS) { /* Unable to allocate port */ PJ_LOG(4,(THIS_FILE, "Unable to allocate relay, giving up: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } /* Init relay key */ namelen = sizeof(relay->hkey.addr); status = pj_sock_getsockname(relay->tp.sock, &relay->hkey.addr, &namelen); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "pj_sock_getsockname() failed: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } if (!pj_sockaddr_has_addr(&relay->hkey.addr)) { pj_sockaddr_copy_addr(&relay->hkey.addr, &alloc->transport->listener->addr); } if (!pj_sockaddr_has_addr(&relay->hkey.addr)) { pj_sockaddr tmp_addr; pj_gethostip(af, &tmp_addr); pj_sockaddr_copy_addr(&relay->hkey.addr, &tmp_addr); } /* Init ioqueue */ pj_bzero(&icb, sizeof(icb)); icb.on_read_complete = &on_rx_from_peer; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, relay->tp.sock, relay, &icb, &relay->tp.key); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "pj_ioqueue_register_sock() failed: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } /* Kick off pending read operation */ pj_ioqueue_op_key_init(&relay->tp.read_key, sizeof(relay->tp.read_key)); on_rx_from_peer(relay->tp.key, &relay->tp.read_key, 0); /* Done */ return PJ_SUCCESS; }
/* * 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; }
/* Raw TCP socket try to connect to SSL socket server, once * connection established, it will just do nothing, SSL socket * server should be able to close the connection after specified * timeout period (set ms_timeout to 0 to disable timer). */ static int client_non_ssl(unsigned ms_timeout) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_timer_heap_t *timer = NULL; pj_ssl_sock_t *ssock_serv = NULL; pj_activesock_t *asock_cli = NULL; pj_activesock_cb asock_cb = { 0 }; pj_sock_t sock = PJ_INVALID_SOCKET; pj_ssl_sock_param param; struct test_state state_serv = { 0 }; struct test_state state_cli = { 0 }; pj_sockaddr listen_addr; pj_ssl_cert_t *cert = NULL; pj_status_t status; pool = pj_pool_create(mem, "ssl_accept_raw_tcp", 256, 256, NULL); status = pj_ioqueue_create(pool, 4, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } status = pj_timer_heap_create(pool, 4, &timer); if (status != PJ_SUCCESS) { goto on_return; } /* Set cert */ { pj_str_t tmp1, tmp2, tmp3, tmp4; status = pj_ssl_cert_load_from_files(pool, pj_strset2(&tmp1, (char*)CERT_CA_FILE), pj_strset2(&tmp2, (char*)CERT_FILE), pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), &cert); if (status != PJ_SUCCESS) { goto on_return; } } pj_ssl_sock_param_default(¶m); param.cb.on_accept_complete = &ssl_on_accept_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.timer_heap = timer; param.timeout.sec = 0; param.timeout.msec = ms_timeout; pj_time_val_normalize(¶m.timeout); /* SERVER */ param.user_data = &state_serv; state_serv.pool = pool; state_serv.is_server = PJ_TRUE; state_serv.is_verbose = PJ_TRUE; status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); if (status != PJ_SUCCESS) { goto on_return; } status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } /* Init bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } status = pj_ssl_sock_start_accept(ssock_serv, pool, &listen_addr, pj_sockaddr_get_len(&listen_addr)); if (status != PJ_SUCCESS) { goto on_return; } /* Update listener address */ { pj_ssl_sock_info info; pj_ssl_sock_get_info(ssock_serv, &info); pj_sockaddr_cp(&listen_addr, &info.local_addr); } /* CLIENT */ state_cli.pool = pool; status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) { goto on_return; } asock_cb.on_connect_complete = &asock_on_connect_complete; asock_cb.on_data_read = &asock_on_data_read; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL, ioqueue, &asock_cb, &state_cli, &asock_cli); if (status != PJ_SUCCESS) { goto on_return; } status = pj_activesock_start_connect(asock_cli, pool, (pj_sockaddr_t*)&listen_addr, pj_sockaddr_get_len(&listen_addr)); if (status == PJ_SUCCESS) { asock_on_connect_complete(asock_cli, PJ_SUCCESS); } else if (status == PJ_EPENDING) { status = PJ_SUCCESS; } else { goto on_return; } /* Wait until everything has been sent/received or error */ while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer, &delay); #endif } if (state_serv.err || state_cli.err) { if (state_serv.err != PJ_SUCCESS) status = state_serv.err; else status = state_cli.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); on_return: if (ssock_serv) pj_ssl_sock_close(ssock_serv); if (asock_cli && !state_cli.err && !state_cli.done) pj_activesock_close(asock_cli); if (timer) pj_timer_heap_destroy(timer); if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }
/* This callback is called by transport manager for the TCP factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **p_transport) { struct tcp_listener *listener; struct tcp_transport *tcp; pj_sock_t sock; pj_sockaddr local_addr; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in or sockaddr_in6*/ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in)) || (rem_addr->addr.sa_family == pj_AF_INET6() && addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tcp_listener*)factory; /* Create socket */ status = pj_sock_socket(rem_addr->addr.sa_family, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) return status; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "outgoing SIP TCP socket"); /* Bind to listener's address and any port */ pj_bzero(&local_addr, sizeof(local_addr)); pj_sockaddr_cp(&local_addr, &listener->bound_addr); pj_sockaddr_set_port(&local_addr, 0); status = pj_sock_bind(sock, &local_addr, pj_sockaddr_get_len(&local_addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Get the local port */ addr_len = sizeof(local_addr); status = pj_sock_getsockname(sock, &local_addr, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Initially set the address from the listener's address */ if (!pj_sockaddr_has_addr(&local_addr)) { pj_sockaddr_copy_addr(&local_addr, &listener->factory.local_addr); } /* Create the transport descriptor */ status = tcp_create(listener, NULL, sock, PJ_FALSE, &local_addr, rem_addr, &tcp); if (status != PJ_SUCCESS) return status; /* Start asynchronous connect() operation */ tcp->has_pending_connect = PJ_TRUE; status = pj_activesock_start_connect(tcp->asock, tcp->base.pool, rem_addr, addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tcp->asock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { tcp_destroy(&tcp->base, status); return status; } if (tcp->has_pending_connect) { /* Update (again) local address, just in case local address currently * set is different now that asynchronous connect() is started. */ addr_len = sizeof(local_addr); if (pj_sock_getsockname(sock, &local_addr, &addr_len)==PJ_SUCCESS) { pj_sockaddr *tp_addr = &tcp->base.local_addr; /* Some systems (like old Win32 perhaps) may not set local address * properly before socket is fully connected. */ if (pj_sockaddr_cmp(tp_addr, &local_addr) && pj_sockaddr_get_port(&local_addr) != 0) { pj_sockaddr_cp(tp_addr, &local_addr); sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, &local_addr); } } PJ_LOG(4,(tcp->base.obj_name, "TCP transport %.*s:%d is connecting to %.*s:%d...", (int)tcp->base.local_name.host.slen, tcp->base.local_name.host.ptr, tcp->base.local_name.port, (int)tcp->base.remote_name.host.slen, tcp->base.remote_name.host.ptr, tcp->base.remote_name.port)); } /* Done */ *p_transport = &tcp->base; return PJ_SUCCESS; }
/* * Handle incoming packet from client. This would have been called by * server upon receiving packet from a listener. */ PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc, pj_turn_pkt *pkt) { pj_bool_t is_stun; pj_status_t status; /* Lock this allocation */ pj_lock_acquire(alloc->lock); /* Quickly check if this is STUN message */ is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0); if (is_stun) { /* * This could be an incoming STUN requests or indications. * Pass this through to the STUN session, which will call * our stun_on_rx_request() or stun_on_rx_indication() * callbacks. * * Note: currently it is necessary to specify the * PJ_STUN_NO_FINGERPRINT_CHECK otherwise the FINGERPRINT * attribute inside STUN Send Indication message will mess up * with fingerprint checking. */ unsigned options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; pj_size_t parsed_len = 0; if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) options |= PJ_STUN_IS_DATAGRAM; status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len, options, NULL, &parsed_len, &pkt->src.clt_addr, pkt->src_addr_len); if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) { pkt->len = 0; } else if (parsed_len > 0) { if (parsed_len == pkt->len) { pkt->len = 0; } else { pj_memmove(pkt->pkt, pkt->pkt+parsed_len, pkt->len - parsed_len); pkt->len -= parsed_len; } } if (status != PJ_SUCCESS) { alloc_err(alloc, "Error handling STUN packet", status); goto on_return; } } else { /* * This is not a STUN packet, must be ChannelData packet. */ pj_turn_channel_data *cd = (pj_turn_channel_data*)pkt->pkt; pj_turn_permission *perm; pj_ssize_t len; pj_assert(sizeof(*cd)==4); /* For UDP check the packet length */ if (alloc->transport->listener->tp_type == PJ_TURN_TP_UDP) { if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) { PJ_LOG(4,(alloc->obj_name, "ChannelData from %s discarded: UDP size error", alloc->info)); goto on_return; } } else { pj_assert(!"Unsupported transport"); goto on_return; } perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number)); if (!perm) { /* Discard */ PJ_LOG(4,(alloc->obj_name, "ChannelData from %s discarded: ch#0x%x not found", alloc->info, pj_ntohs(cd->ch_number))); goto on_return; } /* Relay the data */ len = pj_ntohs(cd->length); pj_sock_sendto(alloc->relay.tp.sock, cd+1, &len, 0, &perm->hkey.peer_addr, pj_sockaddr_get_len(&perm->hkey.peer_addr)); /* Refresh permission */ refresh_permission(perm); } on_return: /* Release lock */ pj_lock_release(alloc->lock); }
/* Test will perform multiple clients trying to connect to single server. * Once SSL connection established, echo test will be performed. */ static int perf_test(unsigned clients, unsigned ms_handshake_timeout) { pj_pool_t *pool = NULL; pj_ioqueue_t *ioqueue = NULL; pj_timer_heap_t *timer = NULL; pj_ssl_sock_t *ssock_serv = NULL; pj_ssl_sock_t **ssock_cli = NULL; pj_ssl_sock_param param; struct test_state state_serv = { 0 }; struct test_state *state_cli = NULL; pj_sockaddr addr, listen_addr; pj_ssl_cert_t *cert = NULL; pj_status_t status; unsigned i, cli_err = 0, tot_sent = 0, tot_recv = 0; pj_time_val start; pool = pj_pool_create(mem, "ssl_perf", 256, 256, NULL); status = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioqueue); if (status != PJ_SUCCESS) { goto on_return; } status = pj_timer_heap_create(pool, PJ_IOQUEUE_MAX_HANDLES, &timer); if (status != PJ_SUCCESS) { goto on_return; } /* Set cert */ { pj_str_t tmp1, tmp2, tmp3, tmp4; status = pj_ssl_cert_load_from_files(pool, pj_strset2(&tmp1, (char*)CERT_CA_FILE), pj_strset2(&tmp2, (char*)CERT_FILE), pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), &cert); if (status != PJ_SUCCESS) { goto on_return; } } pj_ssl_sock_param_default(¶m); param.cb.on_accept_complete = &ssl_on_accept_complete; param.cb.on_connect_complete = &ssl_on_connect_complete; param.cb.on_data_read = &ssl_on_data_read; param.cb.on_data_sent = &ssl_on_data_sent; param.ioqueue = ioqueue; param.timer_heap = timer; param.timeout.sec = 0; param.timeout.msec = ms_handshake_timeout; pj_time_val_normalize(¶m.timeout); /* Init default bind address */ { pj_str_t tmp_st; pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0); } /* SERVER */ param.user_data = &state_serv; state_serv.pool = pool; state_serv.echo = PJ_TRUE; state_serv.is_server = PJ_TRUE; status = pj_ssl_sock_create(pool, ¶m, &ssock_serv); if (status != PJ_SUCCESS) { goto on_return; } status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { goto on_return; } status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr)); if (status != PJ_SUCCESS) { goto on_return; } /* Get listening address for clients to connect to */ { pj_ssl_sock_info info; char buf[64]; pj_ssl_sock_get_info(ssock_serv, &info); pj_sockaddr_cp(&listen_addr, &info.local_addr); pj_sockaddr_print((pj_sockaddr_t*)&listen_addr, buf, sizeof(buf), 1); PJ_LOG(3, ("", "...Listener ready at %s", buf)); } /* CLIENTS */ clients_num = clients; param.timeout.sec = 0; param.timeout.msec = 0; /* Init random seed */ { pj_time_val now; pj_gettimeofday(&now); pj_srand((unsigned)now.sec); } /* Allocate SSL socket pointers and test state */ ssock_cli = pj_pool_calloc(pool, clients, sizeof(pj_ssl_sock_t*)); state_cli = pj_pool_calloc(pool, clients, sizeof(struct test_state)); /* Get start timestamp */ pj_gettimeofday(&start); /* Setup clients */ for (i = 0; i < clients; ++i) { param.user_data = &state_cli[i]; state_cli[i].pool = pool; state_cli[i].check_echo = PJ_TRUE; state_cli[i].send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024; state_cli[i].send_str = pj_pool_alloc(pool, state_cli[i].send_str_len); { unsigned j; for (j = 0; j < state_cli[i].send_str_len; ++j) state_cli[i].send_str[j] = (char)(pj_rand() % 256); } status = pj_ssl_sock_create(pool, ¶m, &ssock_cli[i]); if (status != PJ_SUCCESS) { app_perror("...ERROR pj_ssl_sock_create()", status); cli_err++; clients_num--; continue; } status = pj_ssl_sock_start_connect(ssock_cli[i], pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr)); if (status == PJ_SUCCESS) { ssl_on_connect_complete(ssock_cli[i], PJ_SUCCESS); } else if (status == PJ_EPENDING) { status = PJ_SUCCESS; } else { app_perror("...ERROR pj_ssl_sock_create()", status); pj_ssl_sock_close(ssock_cli[i]); ssock_cli[i] = NULL; clients_num--; cli_err++; continue; } /* Give chance to server to accept this client */ { unsigned n = 5; #ifdef PJ_SYMBIAN while(n && pj_symbianos_poll(-1, 1000)) n--; #else pj_time_val delay = {0, 100}; while(n && pj_ioqueue_poll(ioqueue, &delay) > 0) n--; #endif } } /* Wait until everything has been sent/received or error */ while (clients_num) { #ifdef PJ_SYMBIAN pj_symbianos_poll(-1, 1000); #else pj_time_val delay = {0, 100}; pj_ioqueue_poll(ioqueue, &delay); pj_timer_heap_poll(timer, &delay); #endif } /* Clean up sockets */ { pj_time_val delay = {0, 500}; while (pj_ioqueue_poll(ioqueue, &delay) > 0); } if (state_serv.err != PJ_SUCCESS) { status = state_serv.err; goto on_return; } PJ_LOG(3, ("", "...Done!")); /* SSL setup and data transfer duration */ { pj_time_val stop; pj_gettimeofday(&stop); PJ_TIME_VAL_SUB(stop, start); PJ_LOG(3, ("", ".....Setup & data transfer duration: %d.%03ds", stop.sec, stop.msec)); } /* Check clients status */ for (i = 0; i < clients; ++i) { if (state_cli[i].err != PJ_SUCCESS) cli_err++; tot_sent += state_cli[1].sent; tot_recv += state_cli[1].recv; } PJ_LOG(3, ("", ".....Clients: %d (%d errors)", clients, cli_err)); PJ_LOG(3, ("", ".....Total sent/recv: %d/%d bytes", tot_sent, tot_recv)); on_return: if (ssock_serv) pj_ssl_sock_close(ssock_serv); for (i = 0; i < clients; ++i) { if (ssock_cli[i] && !state_cli[i].err && !state_cli[i].done) pj_ssl_sock_close(ssock_cli[i]); } if (ioqueue) pj_ioqueue_destroy(ioqueue); if (pool) pj_pool_release(pool); return status; }