/* Notification from ioqueue about incoming RTCP packet */ static void on_rx_rtcp(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); do { void (*cb)(void*,void*,pj_ssize_t); void *user_data; cb = udp->rtcp_cb; user_data = udp->user_data; if (udp->attached && cb) (*cb)(user_data, udp->rtcp_pkt, bytes_read); /* Check if RTCP source address is the same as the configured * remote address, and switch the address when they are * different. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0) { if (pj_sockaddr_cmp(&udp->rem_rtcp_addr, &udp->rtcp_src_addr) == 0) { /* Still receiving from rem_rtcp_addr, don't switch */ udp->rtcp_src_cnt = 0; } else { ++udp->rtcp_src_cnt; if (udp->rtcp_src_cnt >= PJMEDIA_RTCP_NAT_PROBATION_CNT ) { char addr_text[80]; udp->rtcp_src_cnt = 0; pj_memcpy(&udp->rem_rtcp_addr, &udp->rtcp_src_addr, sizeof(pj_sockaddr)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&udp->rtcp_src_addr, addr_text, sizeof(addr_text), 3))); } } } bytes_read = sizeof(udp->rtcp_pkt); udp->rtcp_addr_len = sizeof(udp->rtcp_src_addr); status = pj_ioqueue_recvfrom(udp->rtcp_key, &udp->rtcp_read_op, udp->rtcp_pkt, &bytes_read, 0, &udp->rtcp_src_addr, &udp->rtcp_addr_len); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); }
/* Check that the pair in both agents are matched */ static int check_pair(const struct ice_ept *ept1, const struct ice_ept *ept2, int start_err) { unsigned i, min_cnt, max_cnt; if (ept1->cfg.comp_cnt < ept2->cfg.comp_cnt) { min_cnt = ept1->cfg.comp_cnt; max_cnt = ept2->cfg.comp_cnt; } else { min_cnt = ept2->cfg.comp_cnt; max_cnt = ept1->cfg.comp_cnt; } /* Must have valid pair for common components */ for (i=0; i<min_cnt; ++i) { const pj_ice_sess_check *c1; const pj_ice_sess_check *c2; c1 = pj_ice_strans_get_valid_pair(ept1->ice, i+1); if (c1 == NULL) { PJ_LOG(3,("", INDENT "err: unable to get valid pair for ice1 " "component %d", i+1)); return start_err - 2; } c2 = pj_ice_strans_get_valid_pair(ept2->ice, i+1); if (c2 == NULL) { PJ_LOG(3,("", INDENT "err: unable to get valid pair for ice2 " "component %d", i+1)); return start_err - 4; } if (pj_sockaddr_cmp(&c1->rcand->addr, &c2->lcand->addr) != 0) { PJ_LOG(3,("", INDENT "err: candidate pair does not match " "for component %d", i+1)); return start_err - 6; } } /* Extra components must not have valid pair */ for (; i<max_cnt; ++i) { if (ept1->cfg.comp_cnt>i && pj_ice_strans_get_valid_pair(ept1->ice, i+1) != NULL) { PJ_LOG(3,("", INDENT "err: ice1 shouldn't have valid pair " "for component %d", i+1)); return start_err - 8; } if (ept2->cfg.comp_cnt>i && pj_ice_strans_get_valid_pair(ept2->ice, i+1) != NULL) { PJ_LOG(3,("", INDENT "err: ice2 shouldn't have valid pair " "for component %d", i+1)); return start_err - 9; } } return 0; }
static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_transport_state *b) { if (a->type != b->type) { return -1; } if (pj_sockaddr_cmp(&a->host, &b->host)) { return -1; } if ((a->localnet || b->localnet) && ((!a->localnet != !b->localnet) || ast_sockaddr_cmp(&a->localnet->addr, &b->localnet->addr) || ast_sockaddr_cmp(&a->localnet->netmask, &b->localnet->netmask))) { return -1; } if (ast_sockaddr_cmp(&a->external_address, &b->external_address)) { return -1; } if (a->tls.method != b->tls.method || a->tls.ciphers_num != b->tls.ciphers_num #ifdef HAVE_PJSIP_TLS_TRANSPORT_PROTO || a->tls.proto != b->tls.proto #endif || a->tls.verify_client != b->tls.verify_client || a->tls.verify_server != b->tls.verify_server || a->tls.require_client_cert != b->tls.require_client_cert) { return -1; } if (memcmp(a->ciphers, b->ciphers, sizeof(pj_ssl_cipher) * fmax(a->tls.ciphers_num, b->tls.ciphers_num))) { return -1; } return 0; }
static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status) { struct peer *peer = (struct peer*) pj_stun_sock_get_user_data(stun_sock); if (status == PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "peer%d: %s success", peer-g.peer, pj_stun_sock_op_name(op))); } else { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, "peer%d: %s error: %s", peer-g.peer, pj_stun_sock_op_name(op), errmsg)); return PJ_FALSE; } if (op==PJ_STUN_SOCK_BINDING_OP || op==PJ_STUN_SOCK_KEEP_ALIVE_OP) { pj_stun_sock_info info; int cmp; pj_stun_sock_get_info(stun_sock, &info); cmp = pj_sockaddr_cmp(&info.mapped_addr, &peer->mapped_addr); if (cmp) { char straddr[PJ_INET6_ADDRSTRLEN+10]; pj_sockaddr_cp(&peer->mapped_addr, &info.mapped_addr); pj_sockaddr_print(&peer->mapped_addr, straddr, sizeof(straddr), 3); PJ_LOG(3,(THIS_FILE, "peer%d: STUN mapped address is %s", peer-g.peer, straddr)); } } return PJ_TRUE; }
std::vector<IpAddr> ip_utils::getAddrList(const std::string &name, pj_uint16_t family) { std::vector<IpAddr> ipList; if (name.empty()) return ipList; if (IpAddr::isValid(name, family)) { ipList.push_back(name); return ipList; } static const unsigned MAX_ADDR_NUM = 128; pj_addrinfo res[MAX_ADDR_NUM]; unsigned addr_num = MAX_ADDR_NUM; pj_str_t pjname; pj_cstr(&pjname, name.c_str()); auto status = pj_getaddrinfo(family, &pjname, &addr_num, res); if (status != PJ_SUCCESS) { ERROR("Error resolving %s :", name.c_str()); sip_utils::sip_strerror(status); return ipList; } for (unsigned i=0; i<addr_num; i++) { bool found = false; for (const auto& ip : ipList) if (!pj_sockaddr_cmp(&ip, &res[i].ai_addr)) { found = true; break; } if (!found) ipList.push_back(res[i].ai_addr); } return ipList; }
bool PJUtils::compare_pj_sockaddr(const pj_sockaddr& lhs, const pj_sockaddr& rhs) { return (pj_sockaddr_cmp(&lhs, &rhs) < 0); }
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; }
static int send_recv_test(int sock_type, pj_sock_t ss, pj_sock_t cs, pj_sockaddr_in *dstaddr, pj_sockaddr_in *srcaddr, int addrlen) { enum { DATA_LEN = 16 }; char senddata[DATA_LEN+4], recvdata[DATA_LEN+4]; pj_ssize_t sent, received, total_received; pj_status_t rc; TRACE_(("test", "....create_random_string()")); pj_create_random_string(senddata, DATA_LEN); senddata[DATA_LEN-1] = '\0'; /* * Test send/recv small data. */ TRACE_(("test", "....sendto()")); if (dstaddr) { sent = DATA_LEN; rc = pj_sock_sendto(cs, senddata, &sent, 0, dstaddr, addrlen); if (rc != PJ_SUCCESS || sent != DATA_LEN) { app_perror("...sendto error", rc); rc = -140; goto on_error; } } else { sent = DATA_LEN; rc = pj_sock_send(cs, senddata, &sent, 0); if (rc != PJ_SUCCESS || sent != DATA_LEN) { app_perror("...send error", rc); rc = -145; goto on_error; } } TRACE_(("test", "....recv()")); if (srcaddr) { pj_sockaddr_in addr; int srclen = sizeof(addr); pj_bzero(&addr, sizeof(addr)); received = DATA_LEN; rc = pj_sock_recvfrom(ss, recvdata, &received, 0, &addr, &srclen); if (rc != PJ_SUCCESS || received != DATA_LEN) { app_perror("...recvfrom error", rc); rc = -150; goto on_error; } if (srclen != addrlen) return -151; if (pj_sockaddr_cmp(&addr, srcaddr) != 0) { char srcaddr_str[32], addr_str[32]; strcpy(srcaddr_str, pj_inet_ntoa(srcaddr->sin_addr)); strcpy(addr_str, pj_inet_ntoa(addr.sin_addr)); PJ_LOG(3,("test", "...error: src address mismatch (original=%s, " "recvfrom addr=%s)", srcaddr_str, addr_str)); return -152; } } else { /* Repeat recv() until all data is received. * This applies only for non-UDP of course, since for UDP * we would expect all data to be received in one packet. */ total_received = 0; do { received = DATA_LEN-total_received; rc = pj_sock_recv(ss, recvdata+total_received, &received, 0); if (rc != PJ_SUCCESS) { app_perror("...recv error", rc); rc = -155; goto on_error; } if (received <= 0) { PJ_LOG(3,("", "...error: socket has closed! (received=%d)", received)); rc = -156; goto on_error; } if (received != DATA_LEN-total_received) { if (sock_type != pj_SOCK_STREAM()) { PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", DATA_LEN-total_received, received)); rc = -157; goto on_error; } } total_received += received; } while (total_received < DATA_LEN); } TRACE_(("test", "....memcmp()")); if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) { PJ_LOG(3,("","...error: received data mismatch " "(got:'%s' expecting:'%s'", recvdata, senddata)); rc = -160; goto on_error; } /* * Test send/recv big data. */ TRACE_(("test", "....sendto()")); if (dstaddr) { sent = BIG_DATA_LEN; rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen); if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { app_perror("...sendto error", rc); rc = -161; goto on_error; } } else { sent = BIG_DATA_LEN; rc = pj_sock_send(cs, bigdata, &sent, 0); if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { app_perror("...send error", rc); rc = -165; goto on_error; } } TRACE_(("test", "....recv()")); /* Repeat recv() until all data is received. * This applies only for non-UDP of course, since for UDP * we would expect all data to be received in one packet. */ total_received = 0; do { received = BIG_DATA_LEN-total_received; rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0); if (rc != PJ_SUCCESS) { app_perror("...recv error", rc); rc = -170; goto on_error; } if (received <= 0) { PJ_LOG(3,("", "...error: socket has closed! (received=%d)", received)); rc = -173; goto on_error; } if (received != BIG_DATA_LEN-total_received) { if (sock_type != pj_SOCK_STREAM()) { PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", BIG_DATA_LEN-total_received, received)); rc = -176; goto on_error; } } total_received += received; } while (total_received < BIG_DATA_LEN); TRACE_(("test", "....memcmp()")); if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) { PJ_LOG(3,("", "...error: received data has been altered!")); rc = -180; goto on_error; } rc = 0; on_error: return rc; }
static int parse_test(void) { #define IPv4 1 #define IPv6 2 struct test_t { const char *input; int result_af; const char *result_ip; pj_uint16_t result_port; }; struct test_t valid_tests[] = { /* IPv4 */ { "10.0.0.1:80", IPv4, "10.0.0.1", 80}, { "10.0.0.1", IPv4, "10.0.0.1", 0}, { "10.0.0.1:", IPv4, "10.0.0.1", 0}, { "10.0.0.1:0", IPv4, "10.0.0.1", 0}, { ":80", IPv4, "0.0.0.0", 80}, { ":", IPv4, "0.0.0.0", 0}, #if !PJ_SYMBIAN { "localhost", IPv4, "127.0.0.1", 0}, { "localhost:", IPv4, "127.0.0.1", 0}, { "localhost:80", IPv4, "127.0.0.1", 80}, #endif #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 { "fe::01:80", IPv6, "fe::01:80", 0}, { "[fe::01]:80", IPv6, "fe::01", 80}, { "fe::01", IPv6, "fe::01", 0}, { "[fe::01]", IPv6, "fe::01", 0}, { "fe::01:", IPv6, "fe::01", 0}, { "[fe::01]:", IPv6, "fe::01", 0}, { "::", IPv6, "::0", 0}, { "[::]", IPv6, "::", 0}, { ":::", IPv6, "::", 0}, { "[::]:", IPv6, "::", 0}, { ":::80", IPv6, "::", 80}, { "[::]:80", IPv6, "::", 80}, #endif }; struct test_t invalid_tests[] = { /* IPv4 */ { "10.0.0.1:abcd", IPv4}, /* port not numeric */ { "10.0.0.1:-1", IPv4}, /* port contains illegal character */ { "10.0.0.1:123456", IPv4}, /* port too big */ { "1.2.3.4.5:80", IPv4}, /* invalid IP */ { "10:0:80", IPv4}, /* hostname has colon */ #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 { "[fe::01]:abcd", IPv6}, /* port not numeric */ { "[fe::01]:-1", IPv6}, /* port contains illegal character */ { "[fe::01]:123456", IPv6}, /* port too big */ { "fe::01:02::03:04:80", IPv6}, /* invalid IP */ { "[fe::01:02::03:04]:80", IPv6}, /* invalid IP */ { "[fe:01", IPv6}, /* Unterminated bracket */ #endif }; unsigned i; PJ_LOG(3,("test", "...IP address parsing")); for (i=0; i<PJ_ARRAY_SIZE(valid_tests); ++i) { pj_status_t status; pj_str_t input; pj_sockaddr addr, result; switch (valid_tests[i].result_af) { case IPv4: valid_tests[i].result_af = PJ_AF_INET; break; case IPv6: valid_tests[i].result_af = PJ_AF_INET6; break; default: pj_assert(!"Invalid AF!"); continue; } /* Try parsing with PJ_AF_UNSPEC */ status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, pj_cstr(&input, valid_tests[i].input), &addr); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... failed when parsing %s (i=%d)", valid_tests[i].input, i)); return -10; } /* Check "sin_len" member of parse result */ CHECK_SA_ZERO_LEN(&addr, -20); /* Build the correct result */ status = pj_sockaddr_init(valid_tests[i].result_af, &result, pj_cstr(&input, valid_tests[i].result_ip), valid_tests[i].result_port); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... error building IP address %s", valid_tests[i].input)); return -30; } /* Compare the result */ if (pj_sockaddr_cmp(&addr, &result) != 0) { PJ_LOG(1,("test", ".... parsed result mismatched for %s", valid_tests[i].input)); return -40; } /* Parse again with the specified af */ status = pj_sockaddr_parse(valid_tests[i].result_af, 0, pj_cstr(&input, valid_tests[i].input), &addr); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... failed when parsing %s", valid_tests[i].input)); return -50; } /* Check "sin_len" member of parse result */ CHECK_SA_ZERO_LEN(&addr, -55); /* Compare the result again */ if (pj_sockaddr_cmp(&addr, &result) != 0) { PJ_LOG(1,("test", ".... parsed result mismatched for %s", valid_tests[i].input)); return -60; } } for (i=0; i<PJ_ARRAY_SIZE(invalid_tests); ++i) { pj_status_t status; pj_str_t input; pj_sockaddr addr; switch (invalid_tests[i].result_af) { case IPv4: invalid_tests[i].result_af = PJ_AF_INET; break; case IPv6: invalid_tests[i].result_af = PJ_AF_INET6; break; default: pj_assert(!"Invalid AF!"); continue; } /* Try parsing with PJ_AF_UNSPEC */ status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, pj_cstr(&input, invalid_tests[i].input), &addr); if (status == PJ_SUCCESS) { PJ_LOG(1,("test", ".... expecting failure when parsing %s", invalid_tests[i].input)); return -100; } } return 0; }
/* Notification from ioqueue about incoming RTP packet */ static void on_rx_rtp( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); do { void (*cb)(void*,void*,pj_ssize_t); void *user_data; cb = udp->rtp_cb; user_data = udp->user_data; /* Simulate packet lost on RX direction */ if (udp->rx_drop_pct) { if ((pj_rand() % 100) <= (int)udp->rx_drop_pct) { PJ_LOG(5,(udp->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); goto read_next_packet; } } if (udp->attached && cb) (*cb)(user_data, udp->rtp_pkt, bytes_read); /* See if source address of RTP packet is different than the * configured address, and switch RTP remote address to * source packet address after several consecutive packets * have been received. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0) { if (pj_sockaddr_cmp(&udp->rem_rtp_addr, &udp->rtp_src_addr) != 0) { udp->rtp_src_cnt++; if (udp->rtp_src_cnt >= PJMEDIA_RTP_NAT_PROBATION_CNT) { char addr_text[80]; /* Set remote RTP address to source address */ pj_memcpy(&udp->rem_rtp_addr, &udp->rtp_src_addr, sizeof(pj_sockaddr)); /* Reset counter */ udp->rtp_src_cnt = 0; PJ_LOG(4,(udp->base.name, "Remote RTP address switched to %s", pj_sockaddr_print(&udp->rtp_src_addr, addr_text, sizeof(addr_text), 3))); /* Also update remote RTCP address if actual RTCP source * address is not heard yet. */ if (!pj_sockaddr_has_addr(&udp->rtcp_src_addr)) { pj_uint16_t port; pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, sizeof(pj_sockaddr)); pj_sockaddr_copy_addr(&udp->rem_rtcp_addr, &udp->rem_rtp_addr); port = (pj_uint16_t) (pj_sockaddr_get_port(&udp->rem_rtp_addr)+1); pj_sockaddr_set_port(&udp->rem_rtcp_addr, port); pj_memcpy(&udp->rtcp_src_addr, &udp->rem_rtcp_addr, sizeof(pj_sockaddr)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&udp->rtcp_src_addr, addr_text, sizeof(addr_text), 3))); } } } } read_next_packet: bytes_read = sizeof(udp->rtp_pkt); udp->rtp_addrlen = sizeof(udp->rtp_src_addr); status = pj_ioqueue_recvfrom(udp->rtp_key, &udp->rtp_read_op, udp->rtp_pkt, &bytes_read, 0, &udp->rtp_src_addr, &udp->rtp_addrlen); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); }
/* Verify incoming offer */ static pj_status_t verify_ice_sdp(struct transport_ice *tp_ice, pj_pool_t *tmp_pool, const pjmedia_sdp_session *rem_sdp, unsigned media_index, pj_ice_sess_role current_ice_role, struct sdp_state *sdp_state) { const pjmedia_sdp_media *rem_m; const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; const pjmedia_sdp_conn *rem_conn; pj_bool_t comp1_found=PJ_FALSE, comp2_found=PJ_FALSE, has_rtcp=PJ_FALSE; pj_sockaddr rem_conn_addr, rtcp_addr; unsigned i; pj_status_t status; rem_m = rem_sdp->media[media_index]; /* Get the "ice-ufrag" and "ice-pwd" attributes */ get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); /* If "ice-ufrag" or "ice-pwd" are not found, disable ICE */ if (ufrag_attr==NULL || pwd_attr==NULL) { sdp_state->match_comp_cnt = 0; return PJ_SUCCESS; } /* Verify that default target for each component matches one of the * candidate for the component. Otherwise stop ICE with ICE ice_mismatch * error. */ /* Component 1 is the c= line */ rem_conn = rem_m->conn; if (rem_conn == NULL) rem_conn = rem_sdp->conn; if (!rem_conn) return PJMEDIA_SDP_EMISSINGCONN; /* Verify address family matches */ if ((tp_ice->af==pj_AF_INET() && pj_strcmp(&rem_conn->addr_type, &STR_IP4)!=0) || (tp_ice->af==pj_AF_INET6() && pj_strcmp(&rem_conn->addr_type, &STR_IP6)!=0)) { return PJMEDIA_SDP_ETPORTNOTEQUAL; } /* Assign remote connection address */ status = pj_sockaddr_init(tp_ice->af, &rem_conn_addr, &rem_conn->addr, (pj_uint16_t)rem_m->desc.port); if (status != PJ_SUCCESS) return status; if (tp_ice->comp_cnt > 1) { const pjmedia_sdp_attr *attr; /* Get default RTCP candidate from a=rtcp line, if present, otherwise * calculate default RTCP candidate from default RTP target. */ attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, &STR_RTCP, NULL); has_rtcp = (attr != NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp_attr; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp_attr); if (status != PJ_SUCCESS) { /* Error parsing a=rtcp attribute */ return status; } if (rtcp_attr.addr.slen) { /* Verify address family matches */ if ((tp_ice->af==pj_AF_INET() && pj_strcmp(&rtcp_attr.addr_type, &STR_IP4)!=0) || (tp_ice->af==pj_AF_INET6() && pj_strcmp(&rtcp_attr.addr_type, &STR_IP6)!=0)) { return PJMEDIA_SDP_ETPORTNOTEQUAL; } /* Assign RTCP address */ status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, &rtcp_attr.addr, (pj_uint16_t)rtcp_attr.port); if (status != PJ_SUCCESS) { return PJMEDIA_SDP_EINRTCP; } } else { /* Assign RTCP address */ status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, NULL, (pj_uint16_t)rtcp_attr.port); if (status != PJ_SUCCESS) { return PJMEDIA_SDP_EINRTCP; } pj_sockaddr_copy_addr(&rtcp_addr, &rem_conn_addr); } } else { unsigned rtcp_port; rtcp_port = pj_sockaddr_get_port(&rem_conn_addr) + 1; pj_sockaddr_cp(&rtcp_addr, &rem_conn_addr); pj_sockaddr_set_port(&rtcp_addr, (pj_uint16_t)rtcp_port); } } /* Find the default addresses in a=candidate attributes. */ for (i=0; i<rem_m->attr_count; ++i) { pj_ice_sess_cand cand; if (pj_strcmp(&rem_m->attr[i]->name, &STR_CANDIDATE)!=0) continue; status = parse_cand(tp_ice->base.name, tmp_pool, &rem_m->attr[i]->value, &cand); if (status != PJ_SUCCESS) { PJ_LOG(4,(tp_ice->base.name, "Error in parsing SDP candidate attribute '%.*s', " "candidate is ignored", (int)rem_m->attr[i]->value.slen, rem_m->attr[i]->value.ptr)); continue; } if (!comp1_found && cand.comp_id==COMP_RTP && pj_sockaddr_cmp(&rem_conn_addr, &cand.addr)==0) { comp1_found = PJ_TRUE; } else if (!comp2_found && cand.comp_id==COMP_RTCP && pj_sockaddr_cmp(&rtcp_addr, &cand.addr)==0) { comp2_found = PJ_TRUE; } if (cand.comp_id == COMP_RTCP) has_rtcp = PJ_TRUE; if (comp1_found && (comp2_found || tp_ice->comp_cnt==1)) break; } /* Check matched component count and ice_mismatch */ if (comp1_found && (tp_ice->comp_cnt==1 || !has_rtcp)) { sdp_state->match_comp_cnt = 1; sdp_state->ice_mismatch = PJ_FALSE; } else if (comp1_found && comp2_found) { sdp_state->match_comp_cnt = 2; sdp_state->ice_mismatch = PJ_FALSE; } else { sdp_state->match_comp_cnt = (tp_ice->comp_cnt > 1 && has_rtcp)? 2 : 1; sdp_state->ice_mismatch = PJ_TRUE; } /* Detect remote restarting session */ if (pj_ice_strans_has_sess(tp_ice->ice_st) && (pj_ice_strans_sess_is_running(tp_ice->ice_st) || pj_ice_strans_sess_is_complete(tp_ice->ice_st))) { pj_str_t rem_run_ufrag, rem_run_pwd; pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, NULL, NULL, &rem_run_ufrag, &rem_run_pwd); if (pj_strcmp(&ufrag_attr->value, &rem_run_ufrag) || pj_strcmp(&pwd_attr->value, &rem_run_pwd)) { /* Remote offers to restart ICE */ sdp_state->ice_restart = PJ_TRUE; } else { sdp_state->ice_restart = PJ_FALSE; } } else { sdp_state->ice_restart = PJ_FALSE; } /* Detect our role */ if (current_ice_role==PJ_ICE_SESS_ROLE_CONTROLLING) { sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; } else { if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, &STR_ICE_LITE, NULL) != NULL) { /* Remote is ICE Lite */ sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; } else { sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLED; } } PJ_LOG(4,(tp_ice->base.name, "Processing SDP: support ICE=%u, common comp_cnt=%u, " "ice_mismatch=%u, ice_restart=%u, local_role=%s", (sdp_state->match_comp_cnt != 0), sdp_state->match_comp_cnt, sdp_state->ice_mismatch, sdp_state->ice_restart, pj_ice_sess_role_name(sdp_state->local_role))); return PJ_SUCCESS; }
/* Resolve the IP address of local machine */ PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr) { unsigned i, count, cand_cnt; enum { CAND_CNT = 8, /* Weighting to be applied to found addresses */ WEIGHT_HOSTNAME = 1, /* hostname IP is not always valid! */ WEIGHT_DEF_ROUTE = 2, WEIGHT_INTERFACE = 1, WEIGHT_LOOPBACK = -5, WEIGHT_LINK_LOCAL = -4, WEIGHT_DISABLED = -50, MIN_WEIGHT = WEIGHT_DISABLED+1 /* minimum weight to use */ }; /* candidates: */ pj_sockaddr cand_addr[CAND_CNT]; int cand_weight[CAND_CNT]; int selected_cand; char strip[PJ_INET6_ADDRSTRLEN+10]; /* Special IPv4 addresses. */ struct spec_ipv4_t { pj_uint32_t addr; pj_uint32_t mask; int weight; } spec_ipv4[] = { /* 127.0.0.0/8, loopback addr will be used if there is no other * addresses. */ { 0x7f000000, 0xFF000000, WEIGHT_LOOPBACK }, /* 0.0.0.0/8, special IP that doesn't seem to be practically useful */ { 0x00000000, 0xFF000000, WEIGHT_DISABLED }, /* 169.254.0.0/16, a zeroconf/link-local address, which has higher * priority than loopback and will be used if there is no other * valid addresses. */ { 0xa9fe0000, 0xFFFF0000, WEIGHT_LINK_LOCAL } }; /* Special IPv6 addresses */ struct spec_ipv6_t { pj_uint8_t addr[16]; pj_uint8_t mask[16]; int weight; } spec_ipv6[] = { /* Loopback address, ::1/128 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, WEIGHT_LOOPBACK }, /* Link local, fe80::/10 */ { {0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0xff,0xc0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, WEIGHT_LINK_LOCAL }, /* Disabled, ::/128 */ { {0x0,0x0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, WEIGHT_DISABLED } }; pj_addrinfo ai; pj_status_t status; /* May not be used if TRACE_ is disabled */ PJ_UNUSED_ARG(strip); #ifdef _MSC_VER /* Get rid of "uninitialized he variable" with MS compilers */ pj_bzero(&ai, sizeof(ai)); #endif cand_cnt = 0; pj_bzero(cand_addr, sizeof(cand_addr)); pj_bzero(cand_weight, sizeof(cand_weight)); for (i=0; i<PJ_ARRAY_SIZE(cand_addr); ++i) { cand_addr[i].addr.sa_family = (pj_uint16_t)af; PJ_SOCKADDR_RESET_LEN(&cand_addr[i]); } addr->addr.sa_family = (pj_uint16_t)af; PJ_SOCKADDR_RESET_LEN(addr); #if !defined(PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION) || \ PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION == 0 TRACE_((THIS_FILE, "pj_gethostip() pj_getaddrinfo1")); /* Get hostname's IP address */ count = 1; status = pj_getaddrinfo(af, pj_gethostname(), &count, &ai); if (status == PJ_SUCCESS) { pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af); pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr); pj_sockaddr_set_port(&cand_addr[cand_cnt], 0); cand_weight[cand_cnt] += WEIGHT_HOSTNAME; ++cand_cnt; TRACE_((THIS_FILE, "hostname IP is %s", pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0))); } TRACE_((THIS_FILE, "pj_gethostip() pj_getaddrinfo2")); #else PJ_UNUSED_ARG(ai); PJ_UNUSED_ARG(count); #endif /* Get default interface (interface for default route) */ if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { status = pj_getdefaultipinterface(af, addr); if (status == PJ_SUCCESS) { TRACE_((THIS_FILE, "default IP is %s", pj_sockaddr_print(addr, strip, sizeof(strip), 0))); pj_sockaddr_set_port(addr, 0); for (i=0; i<cand_cnt; ++i) { if (pj_sockaddr_cmp(&cand_addr[i], addr)==0) break; } cand_weight[i] += WEIGHT_DEF_ROUTE; if (i >= cand_cnt) { pj_sockaddr_copy_addr(&cand_addr[i], addr); ++cand_cnt; } } } /* Enumerate IP interfaces */ if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { unsigned start_if = cand_cnt; unsigned count = PJ_ARRAY_SIZE(cand_addr) - start_if; status = pj_enum_ip_interface(af, &count, &cand_addr[start_if]); if (status == PJ_SUCCESS && count) { /* Clear the port number */ for (i=0; i<count; ++i) pj_sockaddr_set_port(&cand_addr[start_if+i], 0); /* For each candidate that we found so far (that is the hostname * address and default interface address, check if they're found * in the interface list. If found, add the weight, and if not, * decrease the weight. */ for (i=0; i<cand_cnt; ++i) { unsigned j; for (j=0; j<count; ++j) { if (pj_sockaddr_cmp(&cand_addr[i], &cand_addr[start_if+j])==0) break; } if (j == count) { /* Not found */ cand_weight[i] -= WEIGHT_INTERFACE; } else { cand_weight[i] += WEIGHT_INTERFACE; } } /* Add remaining interface to candidate list. */ for (i=0; i<count; ++i) { unsigned j; for (j=0; j<cand_cnt; ++j) { if (pj_sockaddr_cmp(&cand_addr[start_if+i], &cand_addr[j])==0) break; } if (j == cand_cnt) { pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &cand_addr[start_if+i]); cand_weight[cand_cnt] += WEIGHT_INTERFACE; ++cand_cnt; } } } } /* Apply weight adjustment for special IPv4/IPv6 addresses * See http://trac.pjsip.org/repos/ticket/1046 */ if (af == PJ_AF_INET) { for (i=0; i<cand_cnt; ++i) { unsigned j; for (j=0; j<PJ_ARRAY_SIZE(spec_ipv4); ++j) { pj_uint32_t a = pj_ntohl(cand_addr[i].ipv4.sin_addr.s_addr); pj_uint32_t pa = spec_ipv4[j].addr; pj_uint32_t pm = spec_ipv4[j].mask; if ((a & pm) == pa) { cand_weight[i] += spec_ipv4[j].weight; break; } } } } else if (af == PJ_AF_INET6) { for (i=0; i<PJ_ARRAY_SIZE(spec_ipv6); ++i) { unsigned j; for (j=0; j<cand_cnt; ++j) { pj_uint8_t *a = cand_addr[j].ipv6.sin6_addr.s6_addr; pj_uint8_t am[16]; pj_uint8_t *pa = spec_ipv6[i].addr; pj_uint8_t *pm = spec_ipv6[i].mask; unsigned k; for (k=0; k<16; ++k) { am[k] = (pj_uint8_t)((a[k] & pm[k]) & 0xFF); } if (pj_memcmp(am, pa, 16)==0) { cand_weight[j] += spec_ipv6[i].weight; } } } } else { return PJ_EAFNOTSUP; } /* Enumerate candidates to get the best IP address to choose */ selected_cand = -1; for (i=0; i<cand_cnt; ++i) { TRACE_((THIS_FILE, "Checking candidate IP %s, weight=%d", pj_sockaddr_print(&cand_addr[i], strip, sizeof(strip), 0), cand_weight[i])); if (cand_weight[i] < MIN_WEIGHT) { continue; } if (selected_cand == -1) selected_cand = i; else if (cand_weight[i] > cand_weight[selected_cand]) selected_cand = i; } /* If else fails, returns loopback interface as the last resort */ if (selected_cand == -1) { if (af==PJ_AF_INET) { addr->ipv4.sin_addr.s_addr = pj_htonl (0x7f000001); } else { pj_in6_addr *s6_addr; s6_addr = (pj_in6_addr*) pj_sockaddr_get_addr(addr); pj_bzero(s6_addr, sizeof(pj_in6_addr)); s6_addr->s6_addr[15] = 1; } TRACE_((THIS_FILE, "Loopback IP %s returned", pj_sockaddr_print(addr, strip, sizeof(strip), 0))); } else { pj_sockaddr_copy_addr(addr, &cand_addr[selected_cand]); TRACE_((THIS_FILE, "Candidate %s selected", pj_sockaddr_print(addr, strip, sizeof(strip), 0))); } return PJ_SUCCESS; }
/* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_sock *stun_sock; const pj_stun_sockaddr_attr *mapped_attr; pj_stun_sock_op op; pj_bool_t mapped_changed; pj_bool_t resched = PJ_TRUE; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); /* Check if this is a keep-alive or the first Binding request */ if (pj_sockaddr_has_addr(&stun_sock->mapped_addr)) op = PJ_STUN_SOCK_KEEP_ALIVE_OP; else op = PJ_STUN_SOCK_BINDING_OP; /* Handle failure */ if (status != PJ_SUCCESS) { resched = sess_fail(stun_sock, op, status); goto on_return; } /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS * doesn't exist. */ mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mapped_attr==NULL) { mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mapped_attr == NULL) { resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR); goto on_return; } /* Determine if mapped address has changed, and save the new mapped * address and call callback if so */ mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || pj_sockaddr_cmp(&stun_sock->mapped_addr, &mapped_attr->sockaddr) != 0; if (mapped_changed) { /* Print mapped adress */ { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; PJ_LOG(4,(stun_sock->obj_name, "STUN mapped address found/changed: %s", pj_sockaddr_print(&mapped_attr->sockaddr, addrinfo, sizeof(addrinfo), 3))); } pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr); if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP) op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE; } /* Notify user */ resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS); on_return: /* Start/restart keep-alive timer */ if (resched) start_ka_timer(stun_sock); }
/* Get info */ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, pj_stun_sock_info *info) { int addr_len; pj_status_t status; PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL); /* Copy STUN server address and mapped address */ pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, sizeof(pj_sockaddr)); pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, sizeof(pj_sockaddr)); /* Retrieve bound address */ addr_len = sizeof(info->bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr, &addr_len); if (status != PJ_SUCCESS) return status; /* If socket is bound to a specific interface, then only put that * interface in the alias list. Otherwise query all the interfaces * in the host. */ if (pj_sockaddr_has_addr(&info->bound_addr)) { info->alias_cnt = 1; pj_sockaddr_cp(&info->aliases[0], &info->bound_addr); } else { pj_sockaddr def_addr; pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); unsigned i; /* Get the default address */ status = pj_gethostip(stun_sock->af, &def_addr); if (status != PJ_SUCCESS) return status; pj_sockaddr_set_port(&def_addr, port); /* Enum all IP interfaces in the host */ info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, info->aliases); if (status != PJ_SUCCESS) return status; /* Set the port number for each address. */ for (i=0; i<info->alias_cnt; ++i) { pj_sockaddr_set_port(&info->aliases[i], port); } /* Put the default IP in the first slot */ for (i=0; i<info->alias_cnt; ++i) { if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) { if (i!=0) { pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]); pj_sockaddr_cp(&info->aliases[0], &def_addr); } break; } } } return PJ_SUCCESS; }
/* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_sock *stun_sock; const pj_stun_sockaddr_attr *mapped_attr; pj_stun_sock_op op; pj_bool_t mapped_changed; pj_bool_t resched = PJ_TRUE; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); if (!stun_sock) return; PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); /* Check if this is a keep-alive or the first Binding request */ if (pj_sockaddr_has_addr(&stun_sock->mapped_addr)) op = PJ_STUN_SOCK_KEEP_ALIVE_OP; else op = PJ_STUN_SOCK_BINDING_OP; /* Handle failure */ if (status != PJ_SUCCESS) { resched = sess_fail(stun_sock, op, status); goto on_return; } /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS * doesn't exist. */ mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mapped_attr==NULL) { mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mapped_attr == NULL) { resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR); goto on_return; } /* Determine if mapped address has changed, and save the new mapped * address and call callback if so */ mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || pj_sockaddr_cmp(&stun_sock->mapped_addr, &mapped_attr->sockaddr) != 0; if (mapped_changed) { /* Print mapped adress */ { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; PJ_LOG(2,(stun_sock->obj_name, "STUN mapped address found/changed: %s", pj_sockaddr_print(&mapped_attr->sockaddr, addrinfo, sizeof(addrinfo), 3))); } pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr); if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP) { op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE; PJ_LOG(2, (THIS_FILE, "sess_on_rquest_complete() Operation is PJ_STUN_SOCK_MAPPED_ADDR_CHANGE.")); } } // 2013-10-16 DEAN // 2013-10-21 DEAN { int addr_len = sizeof(stun_sock->current_local_addr); char addrinfo1[PJ_INET6_ADDRSTRLEN+10]; char addrinfo2[PJ_INET6_ADDRSTRLEN+10]; pj_sock_getsockname(stun_sock->sock_fd, &stun_sock->current_local_addr, &addr_len); PJ_LOG(6,(stun_sock->obj_name, "Current Local address: %s", pj_sockaddr_print(&stun_sock->current_local_addr, addrinfo1, sizeof(addrinfo1), 3))); /* * Find out which interface is used to send to the server. */ status = get_local_interface(&stun_sock->srv_addr, &((pj_sockaddr_in *)(&stun_sock->current_local_addr))->sin_addr); PJ_LOG(6,(stun_sock->obj_name, "Current Local address: %s", pj_sockaddr_print(&stun_sock->current_local_addr, addrinfo2, sizeof(addrinfo2), 3))); } /* Notify user */ resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS); PJ_LOG(5, (THIS_FILE, "sess_on_request_complete() resched=%d.", resched)); on_return: /* Start/restart keep-alive timer */ if (resched) start_ka_timer(stun_sock); }
/* On received data from peer */ static pj_bool_t alloc_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) { turn_allocation *alloc; pj_stun_xor_peer_addr_attr *pa; pj_stun_data_attr *da; char peer_info[PJ_INET6_ADDRSTRLEN+10]; char client_info[PJ_INET6_ADDRSTRLEN+10]; pj_uint8_t buffer[1500]; pj_ssize_t sent; unsigned i; PJ_UNUSED_ARG(addr_len); if (status != PJ_SUCCESS) return PJ_TRUE; alloc = (turn_allocation*) pj_activesock_get_user_data(asock); pj_sockaddr_print(&alloc->client_addr, client_info, sizeof(client_info), 3); pj_sockaddr_print(src_addr, peer_info, sizeof(peer_info), 3); /* Check that this peer has a permission */ for (i=0; i<alloc->perm_cnt; ++i) { if (pj_sockaddr_cmp(&alloc->perm[i], src_addr) == 0) { break; } } if (i==alloc->perm_cnt) { PJ_LOG(5,("", "Client %s received %d bytes unauthorized data from peer %s", client_info, size, peer_info)); if (alloc->perm_cnt == 0) PJ_LOG(5,("", "Client %s has no permission", client_info)); return PJ_TRUE; } /* Format a Data indication */ pa = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(alloc->data_ind, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); da = (pj_stun_data_attr*) pj_stun_msg_find_attr(alloc->data_ind, PJ_STUN_ATTR_DATA, 0); pj_assert(pa && da); pj_sockaddr_cp(&pa->sockaddr, src_addr); da->data = (pj_uint8_t*)data; da->length = (unsigned)size; /* Encode Data indication */ status = pj_stun_msg_encode(alloc->data_ind, buffer, sizeof(buffer), 0, NULL, &size); if (status != PJ_SUCCESS) return PJ_TRUE; /* Send */ sent = size; PJ_LOG(5,("", "Forwarding %d bytes data from peer %s to client %s", sent, peer_info, client_info)); pj_activesock_sendto(alloc->test_srv->turn_sock, &alloc->send_key, buffer, &sent, 0, &alloc->client_addr, pj_sockaddr_get_len(&alloc->client_addr)); return PJ_TRUE; }
/* * Callback upon request completion. */ static void on_request_complete(pj_stun_session *stun_sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { nat_detect_session *sess; pj_stun_sockaddr_attr *mattr = NULL; pj_stun_changed_addr_attr *ca = NULL; pj_uint32_t *tsx_id; int cmp; unsigned test_id; PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess); pj_grp_lock_acquire(sess->grp_lock); /* Find errors in the response */ if (status == PJ_SUCCESS) { /* Check error message */ if (PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) { pj_stun_errcode_attr *eattr; int err_code; eattr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (eattr != NULL) err_code = eattr->err_code; else err_code = PJ_STUN_SC_SERVER_ERROR; status = PJ_STATUS_FROM_STUN_CODE(err_code); } else { /* Get MAPPED-ADDRESS or XOR-MAPPED-ADDRESS */ mattr = (pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mattr == NULL) { mattr = (pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mattr == NULL) { status = PJNATH_ESTUNNOMAPPEDADDR; } /* Get CHANGED-ADDRESS attribute */ ca = (pj_stun_changed_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0); if (ca == NULL) { status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR); } } } /* Save the result */ tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id; test_id = tsx_id[2]; if (test_id >= ST_MAX) { PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response", test_id)); end_session(sess, PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR), PJ_STUN_NAT_TYPE_ERR_UNKNOWN); goto on_return; } PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d", test_names[test_id], status)); sess->result[test_id].complete = PJ_TRUE; sess->result[test_id].status = status; if (status == PJ_SUCCESS) { pj_sockaddr_cp(&sess->result[test_id].ma, &mattr->sockaddr); pj_sockaddr_cp(&sess->result[test_id].ca, &ca->sockaddr); } /* Send Test 1B only when Test 2 completes. Must not send Test 1B * before Test 2 completes to avoid creating mapping on the NAT. */ if (!sess->result[ST_TEST_1B].executed && sess->result[ST_TEST_2].complete && sess->result[ST_TEST_2].status != PJ_SUCCESS && sess->result[ST_TEST_1].complete && sess->result[ST_TEST_1].status == PJ_SUCCESS) { cmp = pj_sockaddr_cmp(&sess->local_addr, &sess->result[ST_TEST_1].ma); if (cmp != 0) send_test(sess, ST_TEST_1B, &sess->result[ST_TEST_1].ca, 0); } if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess)) goto on_return; /* Handle the test result according to RFC 3489 page 22: +--------+ | Test | | 1 | +--------+ | | V /\ /\ N / \ Y / \ Y +--------+ UDP <-------/Resp\--------->/ IP \------------->| Test | Blocked \ ? / \Same/ | 2 | \ / \? / +--------+ \/ \/ | | N | | V V /\ +--------+ Sym. N / \ | Test | UDP <---/Resp\ | 2 | Firewall \ ? / +--------+ \ / | \/ V |Y /\ /\ | Symmetric N / \ +--------+ N / \ V NAT <--- / IP \<-----| Test |<--- /Resp\ Open \Same/ | 1B | \ ? / Internet \? / +--------+ \ / \/ \/ | |Y | | | V | Full | Cone V /\ +--------+ / \ Y | Test |------>/Resp\---->Restricted | 3 | \ ? / +--------+ \ / \/ |N | Port +------>Restricted Figure 2: Flow for type discovery process */ switch (sess->result[ST_TEST_1].status) { case PJNATH_ESTUNTIMEDOUT: /* * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); break; case PJ_SUCCESS: /* * Test 1 is successful. Further tests are needed to detect * NAT type. Compare the MAPPED-ADDRESS with the local address. */ cmp = pj_sockaddr_cmp(&sess->local_addr, &sess->result[ST_TEST_1].ma); if (cmp==0) { /* * MAPPED-ADDRESS and local address is equal. Need one more * test to determine NAT type. */ switch (sess->result[ST_TEST_2].status) { case PJ_SUCCESS: /* * Test 2 is also successful. We're in the open. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 2 has timed out. We're behind somekind of UDP * firewall. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP); break; default: /* * We've got other error with Test 2. */ end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } else { /* * MAPPED-ADDRESS is different than local address. * We're behind NAT. */ switch (sess->result[ST_TEST_2].status) { case PJ_SUCCESS: /* * Test 2 is successful. We're behind a full-cone NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 2 has timed-out Check result of test 1B.. */ switch (sess->result[ST_TEST_1B].status) { case PJ_SUCCESS: /* * Compare the MAPPED-ADDRESS of test 1B with the * MAPPED-ADDRESS returned in test 1.. */ cmp = pj_sockaddr_cmp(&sess->result[ST_TEST_1].ma, &sess->result[ST_TEST_1B].ma); if (cmp != 0) { /* * MAPPED-ADDRESS is different, we're behind a * symmetric NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC); } else { /* * MAPPED-ADDRESS is equal. We're behind a restricted * or port-restricted NAT, depending on the result of * test 3. */ switch (sess->result[ST_TEST_3].status) { case PJ_SUCCESS: /* * Test 3 is successful, we're behind a restricted * NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 3 failed, we're behind a port restricted * NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_PORT_RESTRICTED); break; default: /* * Got other error with test 3. */ end_session(sess, sess->result[ST_TEST_3].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } break; case PJNATH_ESTUNTIMEDOUT: /* * Strangely test 1B has failed. Maybe connectivity was * lost? Or perhaps port 3489 (the usual port number in * CHANGED-ADDRESS) is blocked? */ switch (sess->result[ST_TEST_3].status) { case PJ_SUCCESS: /* Although test 1B failed, test 3 was successful. * It could be that port 3489 is blocked, while the * NAT itself looks to be a Restricted one. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; default: /* Can't distinguish between Symmetric and Port * Restricted, so set the type to Unknown */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } break; default: /* * Got other error with test 1B. */ end_session(sess, sess->result[ST_TEST_1B].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } break; default: /* * We've got other error with Test 2. */ end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } break; default: /* * We've got other error with Test 1. */ end_session(sess, sess->result[ST_TEST_1].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } on_return: pj_grp_lock_release(sess->grp_lock); }
/* This callback is called by transport manager for the TCP factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **p_transport) { struct tcp_listener *listener; struct tcp_transport *tcp; pj_sock_t sock; pj_sockaddr local_addr; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in or sockaddr_in6*/ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in)) || (rem_addr->addr.sa_family == pj_AF_INET6() && addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tcp_listener*)factory; /* Create socket */ status = pj_sock_socket(rem_addr->addr.sa_family, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) return status; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "outgoing SIP TCP socket"); /* Bind to listener's address and any port */ pj_bzero(&local_addr, sizeof(local_addr)); pj_sockaddr_cp(&local_addr, &listener->bound_addr); pj_sockaddr_set_port(&local_addr, 0); status = pj_sock_bind(sock, &local_addr, pj_sockaddr_get_len(&local_addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Get the local port */ addr_len = sizeof(local_addr); status = pj_sock_getsockname(sock, &local_addr, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Initially set the address from the listener's address */ if (!pj_sockaddr_has_addr(&local_addr)) { pj_sockaddr_copy_addr(&local_addr, &listener->factory.local_addr); } /* Create the transport descriptor */ status = tcp_create(listener, NULL, sock, PJ_FALSE, &local_addr, rem_addr, &tcp); if (status != PJ_SUCCESS) return status; /* Start asynchronous connect() operation */ tcp->has_pending_connect = PJ_TRUE; status = pj_activesock_start_connect(tcp->asock, tcp->base.pool, rem_addr, addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tcp->asock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { tcp_destroy(&tcp->base, status); return status; } if (tcp->has_pending_connect) { /* Update (again) local address, just in case local address currently * set is different now that asynchronous connect() is started. */ addr_len = sizeof(local_addr); if (pj_sock_getsockname(sock, &local_addr, &addr_len)==PJ_SUCCESS) { pj_sockaddr *tp_addr = &tcp->base.local_addr; /* Some systems (like old Win32 perhaps) may not set local address * properly before socket is fully connected. */ if (pj_sockaddr_cmp(tp_addr, &local_addr) && pj_sockaddr_get_port(&local_addr) != 0) { pj_sockaddr_cp(tp_addr, &local_addr); sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, &local_addr); } } PJ_LOG(4,(tcp->base.obj_name, "TCP transport %.*s:%d is connecting to %.*s:%d...", (int)tcp->base.local_name.host.slen, tcp->base.local_name.host.ptr, tcp->base.local_name.port, (int)tcp->base.remote_name.host.slen, tcp->base.remote_name.host.ptr, tcp->base.remote_name.port)); } /* Done */ *p_transport = &tcp->base; return PJ_SUCCESS; }
/* * Keep-alive test. */ static int keep_alive_test(pj_stun_config *cfg) { struct stun_srv *srv; struct stun_client *client; pj_sockaddr_in mapped_addr; pj_stun_sock_info info; pj_str_t srv_addr; pj_time_val timeout, t; int i, ret = 0; pj_status_t status; PJ_LOG(3,(THIS_FILE, " normal operation")); status = create_client(cfg, &client, PJ_TRUE); if (status != PJ_SUCCESS) return -310; status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv); if (status != PJ_SUCCESS) { destroy_client(client); return -320; } /* * Part 1: initial Binding resolution. */ PJ_LOG(3,(THIS_FILE, " initial Binding request")); srv_addr = pj_str("127.0.0.1"); status = pj_stun_sock_start(client->sock, &srv_addr, pj_ntohs(srv->addr.ipv4.sin_port), NULL); if (status != PJ_SUCCESS) { destroy_server(srv); destroy_client(client); return -330; } /* Wait until on_status() callback is called with success status */ pj_gettimeofday(&timeout); timeout.sec += 60; do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that callback with correct operation is called */ if (client->last_op != PJ_STUN_SOCK_BINDING_OP) { PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status")); ret = -340; goto on_return; } if (client->last_status != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " error: expecting PJ_SUCCESS status")); ret = -350; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -370; goto on_return; } /* Get info */ pj_bzero(&info, sizeof(info)); pj_stun_sock_get_info(client->sock, &info); /* Check that we have server address */ if (!pj_sockaddr_has_addr(&info.srv_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing server address")); ret = -380; goto on_return; } /* .. and bound address port must not be zero */ if (pj_sockaddr_get_port(&info.bound_addr)==0) { PJ_LOG(3,(THIS_FILE, " error: bound address is zero")); ret = -381; goto on_return; } /* .. and mapped address */ if (!pj_sockaddr_has_addr(&info.mapped_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing mapped address")); ret = -382; goto on_return; } /* verify the mapped address */ pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send); if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); ret = -383; goto on_return; } /* .. and at least one alias */ if (info.alias_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: must have at least one alias")); ret = -384; goto on_return; } if (!pj_sockaddr_has_addr(&info.aliases[0])) { PJ_LOG(3,(THIS_FILE, " error: missing alias")); ret = -386; goto on_return; } /* * Part 2: sending and receiving data */ PJ_LOG(3,(THIS_FILE, " sending/receiving data")); /* Change server operation mode to echo back data */ srv->flag = ECHO; /* Reset server */ srv->rx_cnt = 0; /* Client sending data to echo server */ { char txt[100]; PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3))); } status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret), 0, &info.srv_addr, pj_sockaddr_get_len(&info.srv_addr)); if (status != PJ_SUCCESS && status != PJ_EPENDING) { app_perror(" error: server sending data", status); ret = -390; goto on_return; } /* Wait for a short period until client receives data. We can't wait for * too long otherwise the keep-alive will kick in. */ pj_gettimeofday(&timeout); timeout.sec += 1; do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that data is received in server */ if (srv->rx_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: server didn't receive data")); ret = -395; goto on_return; } /* Check that status is still OK */ if (client->last_status != PJ_SUCCESS) { app_perror(" error: client has failed", client->last_status); ret = -400; goto on_return; } /* Check that data has been received */ if (client->on_rx_data_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: client doesn't receive data")); ret = -410; goto on_return; } /* * Part 3: Successful keep-alive, */ PJ_LOG(3,(THIS_FILE, " successful keep-alive scenario")); /* Change server operation mode to normal mode */ srv->flag = RESPOND_STUN | WITH_XOR_MAPPED; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait for keep-alive duration to see if client actually sends the * keep-alive. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (PJ_TIME_VAL_LT(t, timeout)); /* Check that server receives some packets */ if (srv->rx_cnt == 0) { PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received")); ret = -420; goto on_return; } /* Check that client status is still okay and on_status() callback is NOT * called */ /* No longer valid due to this ticket: * http://trac.pjsip.org/repos/ticket/742 if (client->on_status_cnt != 0) { PJ_LOG(3, (THIS_FILE, " error: on_status() must not be called on successful" "keep-alive when mapped-address does not change")); ret = -430; goto on_return; } */ /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -440; goto on_return; } /* * Part 4: Successful keep-alive with IP address change */ PJ_LOG(3,(THIS_FILE, " mapped IP address change")); /* Change server operation mode to normal mode */ srv->flag = RESPOND_STUN | WITH_XOR_MAPPED; /* Change mapped address in the response */ srv->ip_to_send = pj_str("2.2.2.2"); srv->port_to_send++; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait for keep-alive duration to see if client actually sends the * keep-alive. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (PJ_TIME_VAL_LT(t, timeout)); /* Check that server receives some packets */ if (srv->rx_cnt == 0) { PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received")); ret = -450; goto on_return; } /* Check that on_status() callback is called (because mapped address * has changed) */ if (client->on_status_cnt != 1) { PJ_LOG(3, (THIS_FILE, " error: on_status() was not called")); ret = -460; goto on_return; } /* Check that callback was called with correct operation */ if (client->last_op != PJ_STUN_SOCK_MAPPED_ADDR_CHANGE) { PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status")); ret = -470; goto on_return; } /* Check that last status is still success */ if (client->last_status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, " error: expecting successful status")); ret = -480; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -490; goto on_return; } /* Get info */ pj_bzero(&info, sizeof(info)); pj_stun_sock_get_info(client->sock, &info); /* Check that we have server address */ if (!pj_sockaddr_has_addr(&info.srv_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing server address")); ret = -500; goto on_return; } /* .. and mapped address */ if (!pj_sockaddr_has_addr(&info.mapped_addr)) { PJ_LOG(3,(THIS_FILE, " error: missing mapped address")); ret = -510; goto on_return; } /* verify the mapped address */ pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send); if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); ret = -520; goto on_return; } /* .. and at least one alias */ if (info.alias_cnt == 0) { PJ_LOG(3,(THIS_FILE, " error: must have at least one alias")); ret = -530; goto on_return; } if (!pj_sockaddr_has_addr(&info.aliases[0])) { PJ_LOG(3,(THIS_FILE, " error: missing alias")); ret = -540; goto on_return; } /* * Part 5: Failed keep-alive */ PJ_LOG(3,(THIS_FILE, " failed keep-alive scenario")); /* Change server operation mode to respond without attribute */ srv->flag = RESPOND_STUN; /* Reset server */ srv->rx_cnt = 0; /* Reset client */ client->on_status_cnt = 0; client->last_status = PJ_SUCCESS; client->on_rx_data_cnt = 0; /* Wait until on_status() is called with failure. */ pj_gettimeofday(&timeout); timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5); do { handle_events(cfg, 100); pj_gettimeofday(&t); } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout)); /* Check that callback with correct operation is called */ if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) { PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status")); ret = -600; goto on_return; } if (client->last_status == PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " error: expecting failed keep-alive")); ret = -610; goto on_return; } /* Check that client doesn't receive anything */ if (client->on_rx_data_cnt != 0) { PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything")); ret = -620; goto on_return; } on_return: destroy_server(srv); destroy_client(client); for (i=0; i<7; ++i) handle_events(cfg, 50); return ret; }
/* * 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; }
static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { struct transport_ice *tp_ice; tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st); if (comp_id==1 && tp_ice->rtp_cb) { /* Simulate packet lost on RX direction */ if (tp_ice->rx_drop_pct) { if ((pj_rand() % 100) <= (int)tp_ice->rx_drop_pct) { PJ_LOG(5,(tp_ice->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); return; } } (*tp_ice->rtp_cb)(tp_ice->stream, pkt, size); /* See if source address of RTP packet is different than the * configured address, and switch RTP remote address to * source packet address after several consecutive packets * have been received. */ if (!tp_ice->use_ice) { /* Increment counter and avoid zero */ if (++tp_ice->rtp_src_cnt == 0) tp_ice->rtp_src_cnt = 1; if (pj_sockaddr_cmp(&tp_ice->remote_rtp, src_addr) != 0) { /* Check if the source address is recognized. */ if (pj_sockaddr_cmp(src_addr, &tp_ice->rtp_src_addr) != 0) { /* Remember the new source address. */ pj_sockaddr_cp(&tp_ice->rtp_src_addr, src_addr); /* Reset counter */ tp_ice->rtp_src_cnt = 0; } if ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0 && tp_ice->rtp_src_cnt >= PJMEDIA_RTP_NAT_PROBATION_CNT) { char addr_text[80]; /* Set remote RTP address to source address */ pj_sockaddr_cp(&tp_ice->remote_rtp, &tp_ice->rtp_src_addr); tp_ice->addr_len = pj_sockaddr_get_len(&tp_ice->remote_rtp); /* Reset counter */ tp_ice->rtp_src_cnt = 0; PJ_LOG(4,(tp_ice->base.name, "Remote RTP address switched to %s", pj_sockaddr_print(&tp_ice->remote_rtp, addr_text, sizeof(addr_text), 3))); /* Also update remote RTCP address if actual RTCP source * address is not heard yet. */ if (!pj_sockaddr_has_addr(&tp_ice->rtcp_src_addr)) { pj_uint16_t port; pj_sockaddr_cp(&tp_ice->remote_rtcp, &tp_ice->remote_rtp); port = (pj_uint16_t) (pj_sockaddr_get_port(&tp_ice->remote_rtp)+1); pj_sockaddr_set_port(&tp_ice->remote_rtcp, port); PJ_LOG(4,(tp_ice->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&tp_ice->remote_rtcp, addr_text, sizeof(addr_text), 3))); } } } } } else if (comp_id==2 && tp_ice->rtcp_cb) { (*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size); /* Check if RTCP source address is the same as the configured * remote address, and switch the address when they are * different. */ if (!tp_ice->use_ice && pj_sockaddr_cmp(&tp_ice->remote_rtcp, src_addr) != 0) { pj_sockaddr_cp(&tp_ice->rtcp_src_addr, src_addr); if ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0) { char addr_text[80]; pj_sockaddr_cp(&tp_ice->remote_rtcp, src_addr); pj_assert(tp_ice->addr_len == pj_sockaddr_get_len(src_addr)); PJ_LOG(4,(tp_ice->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&tp_ice->remote_rtcp, addr_text, sizeof(addr_text), 3))); } } } PJ_UNUSED_ARG(src_addr_len); }