static int load_module(void) { char hostname[MAXHOSTNAMELEN] = ""; pj_sockaddr addr; CHECK_PJSIP_MODULE_LOADED(); if (!gethostname(hostname, sizeof(hostname) - 1)) { ast_verb(2, "Performing DNS resolution of local hostname '%s' to get local IPv4 and IPv6 address\n", hostname); } if (!pj_gethostip(pj_AF_INET(), &addr)) { pj_sockaddr_print(&addr, host_ipv4, sizeof(host_ipv4), 2); ast_verb(3, "Local IPv4 address determined to be: %s\n", host_ipv4); } if (!pj_gethostip(pj_AF_INET6(), &addr)) { pj_sockaddr_print(&addr, host_ipv6, sizeof(host_ipv6), 2); ast_verb(3, "Local IPv6 address determined to be: %s\n", host_ipv6); } if (ast_sip_register_service(&multihomed_module)) { ast_log(LOG_ERROR, "Could not register multihomed module for incoming and outgoing requests\n"); return AST_MODULE_LOAD_FAILURE; } return AST_MODULE_LOAD_SUCCESS; }
IpAddr ip_utils::getLocalAddr(pj_uint16_t family) { if (family == pj_AF_UNSPEC()) { #if HAVE_IPV6 family = pj_AF_INET6(); #else family = pj_AF_INET(); #endif } IpAddr ip_addr = {}; pj_status_t status = pj_gethostip(family, ip_addr.pjPtr()); if (status == PJ_SUCCESS) { return ip_addr; } #if HAVE_IPV6 WARN("Could not get preferred address familly (%s)", (family == pj_AF_INET6()) ? "IPv6" : "IPv4"); family = (family == pj_AF_INET()) ? pj_AF_INET6() : pj_AF_INET(); status = pj_gethostip(family, ip_addr); if (status == PJ_SUCCESS) return ip_addr; #endif ERROR("Could not get local IP"); return ip_addr; }
/* * Enumerate the local IP interface currently active in the host. */ PJ_DEF(pj_status_t) pj_enum_ip_interface(unsigned *p_cnt, pj_in_addr ifs[]) { pj_status_t status; PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && ifs, PJ_EINVAL); pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt)); /* Just get one default route */ status = pj_gethostip(&ifs[0]); if (status != PJ_SUCCESS) return status; *p_cnt = 1; return PJ_SUCCESS; }
/* * Enumerate the IP routing table for this host. */ PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt, pj_ip_route_entry routes[]) { pj_status_t status; PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && routes, PJ_EINVAL); pj_bzero(routes, sizeof(routes[0]) * (*p_cnt)); /* Just get one default route */ status = pj_gethostip(&routes[0].ipv4.if_addr); if (status != PJ_SUCCESS) return status; routes[0].ipv4.dst_addr.s_addr = 0; routes[0].ipv4.mask.s_addr = 0; *p_cnt = 1; return PJ_SUCCESS; }
/* * main() * * If called with argument, treat argument as SIP URL to be called. * Otherwise wait for incoming calls. */ int main(int argc, char *argv[]) { pj_pool_t *pool = NULL; pj_status_t status; unsigned i; /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); pj_log_set_level(5); /* Then init PJLIB-UTIL: */ status = pjlib_util_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* Create global endpoint: */ { const pj_str_t *hostname; const char *endpt_name; /* Endpoint MUST be assigned a globally unique name. * The name will be used as the hostname in Warning header. */ /* For this implementation, we'll use hostname for simplicity */ hostname = pj_gethostname(); endpt_name = hostname->ptr; /* Create the endpoint: */ status = pjsip_endpt_create(&cp.factory, endpt_name, &g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* * Add UDP transport, with hard-coded port * Alternatively, application can use pjsip_udp_transport_attach() to * start UDP transport, if it already has an UDP socket (e.g. after it * resolves the address with STUN). */ { pj_sockaddr addr; pj_sockaddr_init(AF, &addr, NULL, (pj_uint16_t)SIP_PORT); if (AF == pj_AF_INET()) { status = pjsip_udp_transport_start( g_endpt, &addr.ipv4, NULL, 1, NULL); } else if (AF == pj_AF_INET6()) { status = pjsip_udp_transport_start6(g_endpt, &addr.ipv6, NULL, 1, NULL); } else { status = PJ_EAFNOTSUP; } if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to start UDP transport", status); return 1; } } /* * Init transaction layer. * This will create/initialize transaction hash tables etc. */ status = pjsip_tsx_layer_init_module(g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Initialize UA layer module. * This will create/initialize dialog hash tables etc. */ status = pjsip_ua_init_module( g_endpt, NULL ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Init invite session module. * The invite session module initialization takes additional argument, * i.e. a structure containing callbacks to be called on specific * occurence of events. * * The on_state_changed and on_new_session callbacks are mandatory. * Application must supply the callback function. * * We use on_media_update() callback in this application to start * media transmission. */ { pjsip_inv_callback inv_cb; /* Init the callback for INVITE session: */ pj_bzero(&inv_cb, sizeof(inv_cb)); inv_cb.on_state_changed = &call_on_state_changed; inv_cb.on_new_session = &call_on_forked; inv_cb.on_media_update = &call_on_media_update; /* Initialize invite session module: */ status = pjsip_inv_usage_init(g_endpt, &inv_cb); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* Initialize 100rel support */ status = pjsip_100rel_init_module(g_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* * Register our module to receive incoming requests. */ status = pjsip_endpt_register_module( g_endpt, &mod_simpleua); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Register message logger module. */ status = pjsip_endpt_register_module( g_endpt, &msg_logger); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ #if PJ_HAS_THREADS status = pjmedia_endpt_create(&cp.factory, NULL, 1, &g_med_endpt); #else status = pjmedia_endpt_create(&cp.factory, pjsip_endpt_get_ioqueue(g_endpt), 0, &g_med_endpt); #endif PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * Add PCMA/PCMU codec to the media endpoint. */ #if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0 status = pjmedia_codec_g711_init(g_med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); #endif #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) /* Init video subsystem */ pool = pjmedia_endpt_create_pool(g_med_endpt, "Video subsystem", 512, 512); status = pjmedia_video_format_mgr_create(pool, 64, 0, NULL); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_converter_mgr_create(pool, NULL); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_vid_codec_mgr_create(pool, NULL); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_vid_dev_subsys_init(&cp.factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); # if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0 /* Init ffmpeg video codecs */ status = pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); # endif /* PJMEDIA_HAS_FFMPEG_VID_CODEC */ #endif /* PJMEDIA_HAS_VIDEO */ /* * Create media transport used to send/receive RTP/RTCP socket. * One media transport is needed for each call. Application may * opt to re-use the same media transport for subsequent calls. */ for (i = 0; i < PJ_ARRAY_SIZE(g_med_transport); ++i) { status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL, RTP_PORT + i*2, 0, &g_med_transport[i]); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create media transport", status); return 1; } /* * Get socket info (address, port) of the media transport. We will * need this info to create SDP (i.e. the address and port info in * the SDP). */ pjmedia_transport_info_init(&g_med_tpinfo[i]); pjmedia_transport_get_info(g_med_transport[i], &g_med_tpinfo[i]); pj_memcpy(&g_sock_info[i], &g_med_tpinfo[i].sock_info, sizeof(pjmedia_sock_info)); } /* * If URL is specified, then make call immediately. */ if (argc > 1) { pj_sockaddr hostaddr; char hostip[PJ_INET6_ADDRSTRLEN+2]; char temp[80]; pj_str_t dst_uri = pj_str(argv[1]); pj_str_t local_uri; pjsip_dialog *dlg; pjmedia_sdp_session *local_sdp; pjsip_tx_data *tdata; if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to retrieve local host IP", status); return 1; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:simpleuac@%s:%d>", hostip, SIP_PORT); local_uri = pj_str(temp); /* Create UAC dialog */ status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, /* local URI */ &local_uri, /* local Contact */ &dst_uri, /* remote URI */ &dst_uri, /* remote target */ &dlg); /* dialog */ if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create UAC dialog", status); return 1; } /* If we expect the outgoing INVITE to be challenged, then we should * put the credentials in the dialog here, with something like this: * { pjsip_cred_info cred[1]; cred[0].realm = pj_str("sip.server.realm"); cred[0].scheme = pj_str("digest"); cred[0].username = pj_str("theuser"); cred[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; cred[0].data = pj_str("thepassword"); pjsip_auth_clt_set_credentials( &dlg->auth_sess, 1, cred); } * */ /* Get the SDP body to be put in the outgoing INVITE, by asking * media endpoint to create one for us. */ status = pjmedia_endpt_create_sdp( g_med_endpt, /* the media endpt */ dlg->pool, /* pool. */ MAX_MEDIA_CNT, /* # of streams */ g_sock_info, /* RTP sock info */ &local_sdp); /* the SDP result */ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create the INVITE session, and pass the SDP returned earlier * as the session's initial capability. */ status = pjsip_inv_create_uac( dlg, local_sdp, 0, &g_inv); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* If we want the initial INVITE to travel to specific SIP proxies, * then we should put the initial dialog's route set here. The final * route set will be updated once a dialog has been established. * To set the dialog's initial route set, we do it with something * like this: * { pjsip_route_hdr route_set; pjsip_route_hdr *route; const pj_str_t hname = { "Route", 5 }; char *uri = "sip:proxy.server;lr"; pj_list_init(&route_set); route = pjsip_parse_hdr( dlg->pool, &hname, uri, strlen(uri), NULL); PJ_ASSERT_RETURN(route != NULL, 1); pj_list_push_back(&route_set, route); pjsip_dlg_set_route_set(dlg, &route_set); } * * Note that Route URI SHOULD have an ";lr" parameter! */ /* Create initial INVITE request. * This INVITE request will contain a perfectly good request and * an SDP body as well. */ status = pjsip_inv_invite(g_inv, &tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Send initial INVITE request. * From now on, the invite session's state will be reported to us * via the invite session callbacks. */ status = pjsip_inv_send_msg(g_inv, tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } else { /* No URL to make call to */ PJ_LOG(3,(THIS_FILE, "Ready to accept incoming calls...")); } /* Loop until one call is completed */ for (;!g_complete;) { pj_time_val timeout = {0, 10}; pjsip_endpt_handle_events(g_endpt, &timeout); } /* On exit, dump current memory usage: */ dump_pool_usage(THIS_FILE, &cp); /* Destroy audio ports. Destroy the audio port first * before the stream since the audio port has threads * that get/put frames to the stream. */ if (g_snd_port) pjmedia_snd_port_destroy(g_snd_port); #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) /* Destroy video ports */ if (g_vid_capturer) pjmedia_vid_port_destroy(g_vid_capturer); if (g_vid_renderer) pjmedia_vid_port_destroy(g_vid_renderer); #endif /* Destroy streams */ if (g_med_stream) pjmedia_stream_destroy(g_med_stream); #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) if (g_med_vstream) pjmedia_vid_stream_destroy(g_med_vstream); /* Deinit ffmpeg codec */ # if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0 pjmedia_codec_ffmpeg_vid_deinit(); # endif #endif /* Destroy media transports */ for (i = 0; i < MAX_MEDIA_CNT; ++i) { if (g_med_transport[i]) pjmedia_transport_close(g_med_transport[i]); } /* Deinit pjmedia endpoint */ if (g_med_endpt) pjmedia_endpt_destroy(g_med_endpt); /* Deinit pjsip endpoint */ if (g_endpt) pjsip_endpt_destroy(g_endpt); /* Release pool */ if (pool) pj_pool_release(pool); return 0; }
pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_uint32_t flags, const char *domain, test_server **p_test_srv) { pj_pool_t *pool; test_server *test_srv; pj_sockaddr hostip; char strbuf[100]; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && domain && p_test_srv, PJ_EINVAL); status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) return status; pool = pj_pool_create(mem, THIS_FILE, 512, 512, NULL); test_srv = (test_server*) PJ_POOL_ZALLOC_T(pool, test_server); test_srv->pool = pool; test_srv->flags = flags; test_srv->stun_cfg = stun_cfg; pj_strdup2(pool, &test_srv->domain, domain); test_srv->username = pj_str(TURN_USERNAME); test_srv->passwd = pj_str(TURN_PASSWD); pj_ioqueue_op_key_init(&test_srv->send_key, sizeof(test_srv->send_key)); if (flags & CREATE_DNS_SERVER) { status = pj_dns_server_create(mem, test_srv->stun_cfg->ioqueue, pj_AF_INET(), DNS_SERVER_PORT, 0, &test_srv->dns_server); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } /* Add DNS A record for the domain, for fallback */ if (flags & CREATE_A_RECORD_FOR_DOMAIN) { pj_dns_parsed_rr rr; pj_str_t res_name; pj_in_addr ip_addr; pj_strdup2(pool, &res_name, domain); ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } if (flags & CREATE_STUN_SERVER) { pj_activesock_cb stun_sock_cb; pj_sockaddr bound_addr; pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); stun_sock_cb.on_data_recvfrom = &stun_on_data_recvfrom; pj_sockaddr_in_init(&bound_addr.ipv4, NULL, STUN_SERVER_PORT); status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &stun_sock_cb, test_srv, &test_srv->stun_sock, NULL); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } status = pj_activesock_start_recvfrom(test_srv->stun_sock, pool, MAX_STUN_PKT, 0); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } if (test_srv->dns_server && (flags & CREATE_STUN_SERVER_DNS_SRV)) { pj_str_t res_name, target; pj_dns_parsed_rr rr; pj_in_addr ip_addr; /* Add DNS entries: * _stun._udp.domain 60 IN SRV 0 0 PORT stun.domain. * stun.domain IN A 127.0.0.1 */ pj_ansi_snprintf(strbuf, sizeof(strbuf), "_stun._udp.%s", domain); pj_strdup2(pool, &res_name, strbuf); pj_ansi_snprintf(strbuf, sizeof(strbuf), "stun.%s", domain); pj_strdup2(pool, &target, strbuf); pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, STUN_SERVER_PORT, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } if (flags & CREATE_TURN_SERVER) { pj_activesock_cb turn_sock_cb; pj_sockaddr bound_addr; pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); turn_sock_cb.on_data_recvfrom = &turn_on_data_recvfrom; pj_sockaddr_in_init(&bound_addr.ipv4, NULL, TURN_SERVER_PORT); status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &turn_sock_cb, test_srv, &test_srv->turn_sock, NULL); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } status = pj_activesock_start_recvfrom(test_srv->turn_sock, pool, MAX_STUN_PKT, 0); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); return status; } if (test_srv->dns_server && (flags & CREATE_TURN_SERVER_DNS_SRV)) { pj_str_t res_name, target; pj_dns_parsed_rr rr; pj_in_addr ip_addr; /* Add DNS entries: * _turn._udp.domain 60 IN SRV 0 0 PORT turn.domain. * turn.domain IN A 127.0.0.1 */ pj_ansi_snprintf(strbuf, sizeof(strbuf), "_turn._udp.%s", domain); pj_strdup2(pool, &res_name, strbuf); pj_ansi_snprintf(strbuf, sizeof(strbuf), "turn.%s", domain); pj_strdup2(pool, &target, strbuf); pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, TURN_SERVER_PORT, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; ip_addr = hostip.ipv4.sin_addr; pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); } } *p_test_srv = test_srv; return PJ_SUCCESS; }
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; }
pj_status_t init_stack(const std::string& system_name, const std::string& sas_address, int trusted_port, int untrusted_port, const std::string& local_host, const std::string& home_domain, const std::string& sprout_cluster_domain, const std::string& alias_hosts, int num_pjsip_threads, int num_worker_threads) { pj_status_t status; pj_sockaddr pri_addr; pj_sockaddr addr_list[16]; unsigned addr_cnt = PJ_ARRAY_SIZE(addr_list); unsigned i; // Set up the vectors of threads. The threads don't get created until // start_stack is called. pjsip_threads.resize(num_pjsip_threads); worker_threads.resize(num_worker_threads); // Get ports and host names specified on options. If local host was not // specified, use the host name returned by pj_gethostname. memset(&stack_data, 0, sizeof(stack_data)); char* local_host_cstr = strdup(local_host.c_str()); char* home_domain_cstr = strdup(home_domain.c_str()); char* sprout_cluster_domain_cstr = strdup(sprout_cluster_domain.c_str()); stack_data.trusted_port = trusted_port; stack_data.untrusted_port = untrusted_port; stack_data.local_host = (local_host != "") ? pj_str(local_host_cstr) : *pj_gethostname(); stack_data.home_domain = (home_domain != "") ? pj_str(home_domain_cstr) : stack_data.local_host; stack_data.sprout_cluster_domain = (sprout_cluster_domain != "") ? pj_str(sprout_cluster_domain_cstr) : stack_data.local_host; // Initialize SAS logging. if (system_name != "") { SAS::init(system_name.length(), system_name.c_str(), sas_address); } else { SAS::init(stack_data.local_host.slen, stack_data.local_host.ptr, sas_address); } // Initialise PJSIP and all the associated resources. status = init_pjsip(); // Register the stack module. pjsip_endpt_register_module(stack_data.endpt, &mod_stack); stack_data.module_id = mod_stack.id; // Create listening transports for trusted and untrusted ports. if (stack_data.trusted_port != 0) { status = create_listener_transports(stack_data.trusted_port, &stack_data.tcp_factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); } if (stack_data.untrusted_port != 0) { status = create_listener_transports(stack_data.untrusted_port, NULL); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); } // List all names matching local endpoint. // Note that PJLIB version 0.6 and newer has a function to // enumerate local IP interface (pj_enum_ip_interface()), so // by using it would be possible to list all IP interfaces in // this host. // The first address is important since this would be the one // to be added in Record-Route. stack_data.name[stack_data.name_cnt] = stack_data.local_host; stack_data.name_cnt++; if (pj_gethostip(pj_AF_INET(), &pri_addr) == PJ_SUCCESS) { pj_strdup2(stack_data.pool, &stack_data.name[stack_data.name_cnt], pj_inet_ntoa(pri_addr.ipv4.sin_addr)); stack_data.name_cnt++; } // Get the rest of IP interfaces. if (pj_enum_ip_interface(pj_AF_INET(), &addr_cnt, addr_list) == PJ_SUCCESS) { for (i = 0; i < addr_cnt; ++i) { if (addr_list[i].ipv4.sin_addr.s_addr == pri_addr.ipv4.sin_addr.s_addr) { continue; } pj_strdup2(stack_data.pool, &stack_data.name[stack_data.name_cnt], pj_inet_ntoa(addr_list[i].ipv4.sin_addr)); stack_data.name_cnt++; } } // Add loopback address. #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF stack_data.name[stack_data.name_cnt] = pj_str("127.0.0.1"); stack_data.name_cnt++; #endif stack_data.name[stack_data.name_cnt] = pj_str("localhost"); stack_data.name_cnt++; // Parse the list of alias host names. if (alias_hosts != "") { std::list<std::string> hosts; Utils::split_string(alias_hosts, ',', hosts, 0, true); for (std::list<std::string>::iterator it = hosts.begin(); it != hosts.end(); ++it) { pj_strdup2(stack_data.pool, &stack_data.name[stack_data.name_cnt], it->c_str()); stack_data.name_cnt++; } } LOG_STATUS("Local host aliases:"); for (i = 0; i < stack_data.name_cnt; ++i) { LOG_STATUS(" %.*s", (int)stack_data.name[i].slen, stack_data.name[i].ptr); } stack_data.stats_aggregator = new LastValueCache(Statistic::known_stats_count(), Statistic::known_stats()); latency_accumulator = new StatisticAccumulator("latency_us"); return status; }
/* Instantiate standard server */ static int create_std_server(pj_stun_auth_type auth_type, pj_bool_t responding) { pj_pool_t *pool; pj_stun_session_cb sess_cb; pj_stun_auth_cred cred; pj_status_t status; /* Create server */ pool = pj_pool_create(mem, "server", 1000, 1000, NULL); server = PJ_POOL_ZALLOC_T(pool, struct server); server->pool = pool; server->auth_type = auth_type; server->responding = responding; /* Create STUN session */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_rx_request = &server_on_rx_request; sess_cb.on_send_msg = &server_send_msg; status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, NULL, &server->sess); if (status != PJ_SUCCESS) { destroy_server(); return -10; } /* Configure credential */ pj_bzero(&cred, sizeof(cred)); cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; cred.data.dyn_cred.get_auth = &server_get_auth; cred.data.dyn_cred.get_password = &server_get_password; cred.data.dyn_cred.verify_nonce = &server_verify_nonce; status = pj_stun_session_set_credential(server->sess, auth_type, &cred); if (status != PJ_SUCCESS) { destroy_server(); return -20; } /* Create socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &server->sock); if (status != PJ_SUCCESS) { destroy_server(); return -30; } /* Bind */ pj_sockaddr_in_init(&server->addr.ipv4, NULL, 0); status = pj_sock_bind(server->sock, &server->addr, pj_sockaddr_get_len(&server->addr)); if (status != PJ_SUCCESS) { destroy_server(); return -40; } else { /* Get the bound IP address */ int namelen = sizeof(server->addr); pj_sockaddr addr; status = pj_sock_getsockname(server->sock, &server->addr, &namelen); if (status != PJ_SUCCESS) { destroy_server(); return -43; } status = pj_gethostip(pj_AF_INET(), &addr); if (status != PJ_SUCCESS) { destroy_server(); return -45; } pj_sockaddr_copy_addr(&server->addr, &addr); } /* Create worker thread */ status = pj_thread_create(pool, "server", &server_thread, 0, 0, 0, &server->thread); if (status != PJ_SUCCESS) { destroy_server(); return -30; } return 0; }
/* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pj_pool_t *pool; pj_sock_t sock = PJ_INVALID_SOCKET; struct tcp_listener *listener; pj_activesock_cfg asock_cfg; pj_activesock_cb listener_cb; pj_sockaddr_in *listener_addr; int addr_len; 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, "tcplis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener); listener->factory.pool = pool; listener->factory.type = PJSIP_TRANSPORT_TCP; listener->factory.type_name = "tcp"; listener->factory.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP); pj_ansi_strcpy(listener->factory.obj_name, "tcplis"); status = pj_lock_create_recursive_mutex(pool, "tcplis", &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; /* Create and bind socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) goto on_error; listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr; if (local) { pj_memcpy(listener_addr, local, sizeof(pj_sockaddr_in)); } else { pj_sockaddr_in_init(listener_addr, NULL, 0); } status = pj_sock_bind(sock, listener_addr, sizeof(pj_sockaddr_in)); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the bound address */ addr_len = sizeof(pj_sockaddr_in); status = pj_sock_getsockname(sock, listener_addr, &addr_len); if (status != PJ_SUCCESS) 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), "tcplis:%d", listener->factory.addr_name.port); /* Start listening to the address */ status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG); if (status != PJ_SUCCESS) goto on_error; /* Create active socket */ if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT; pj_activesock_cfg_default(&asock_cfg); asock_cfg.async_cnt = async_cnt; pj_bzero(&listener_cb, sizeof(listener_cb)); listener_cb.on_accept_complete = &on_accept_complete; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, pjsip_endpt_get_ioqueue(endpt), &listener_cb, listener, &listener->asock); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport = 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; } /* Start pending accept() operations */ status = pj_activesock_start_accept(listener->asock, pool); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(4,(listener->factory.obj_name, "SIP TCP listener 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)); /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET) pj_sock_close(sock); lis_destroy(&listener->factory); return status; }
/* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( pjsip_endpoint *endpt, const pjsip_tcp_transport_cfg *cfg, pjsip_tpfactory **p_factory ) { pj_pool_t *pool; pj_sock_t sock = PJ_INVALID_SOCKET; struct tcp_listener *listener; pj_activesock_cfg asock_cfg; pj_activesock_cb listener_cb; pj_sockaddr *listener_addr; int addr_len; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ if (cfg->addr_name.host.slen) { pj_sockaddr tmp; status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, (pj_uint16_t)cfg->addr_name.port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || (cfg->af==pj_AF_INET() && tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener); listener->factory.pool = pool; listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP : PJSIP_TRANSPORT_TCP6; listener->factory.type_name = (char*) pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = pjsip_transport_get_flag_from_type(listener->factory.type); listener->qos_type = cfg->qos_type; pj_memcpy(&listener->qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); pj_ansi_strcpy(listener->factory.obj_name, "tcplis"); if (listener->factory.type==PJSIP_TRANSPORT_TCP6) pj_ansi_strcat(listener->factory.obj_name, "6"); status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; /* Create socket */ status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, 2, listener->factory.obj_name, "SIP TCP listener socket"); /* Bind address may be different than factory.local_addr because * factory.local_addr will be resolved below. */ pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr); /* Bind socket */ listener_addr = &listener->factory.local_addr; pj_sockaddr_cp(listener_addr, &cfg->bind_addr); status = pj_sock_bind(sock, listener_addr, pj_sockaddr_get_len(listener_addr)); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the bound address */ addr_len = pj_sockaddr_get_len(listener_addr); status = pj_sock_getsockname(sock, listener_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (cfg->addr_name.host.slen) { /* Copy the address */ listener->factory.addr_name = cfg->addr_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &cfg->addr_name.host); listener->factory.addr_name.port = cfg->addr_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 (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; status = pj_gethostip(listener->bound_addr.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_sockaddr_copy_addr(listener_addr, &hostip); } /* 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_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tcplis:%d", listener->factory.addr_name.port); /* Start listening to the address */ status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG); if (status != PJ_SUCCESS) goto on_error; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); if (cfg->async_cnt > MAX_ASYNC_CNT) asock_cfg.async_cnt = MAX_ASYNC_CNT; else asock_cfg.async_cnt = cfg->async_cnt; pj_bzero(&listener_cb, sizeof(listener_cb)); listener_cb.on_accept_complete = &on_accept_complete; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, pjsip_endpt_get_ioqueue(endpt), &listener_cb, listener, &listener->asock); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport = 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; } /* Start pending accept() operations */ status = pj_activesock_start_accept(listener->asock, pool); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(4,(listener->factory.obj_name, "SIP TCP listener 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)); /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET) pj_sock_close(sock); lis_destroy(&listener->factory); return status; }
static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) { pj_sockaddr hostaddr; char temp[80], hostip[PJ_INET6_ADDRSTRLEN]; pj_str_t local_uri; pjsip_dialog *dlg; pjsip_rdata_sdp_info *sdp_info; pjmedia_sdp_session *answer = NULL; pjsip_tx_data *tdata = NULL; call_t *call = NULL; unsigned i; pj_status_t status; PJ_LOG(3,(THIS_FILE, "RX %.*s from %s", (int)rdata->msg_info.msg->line.req.method.name.slen, rdata->msg_info.msg->line.req.method.name.ptr, rdata->pkt_info.src_name)); if (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) { /* Let me be a registrar! */ pjsip_hdr hdr_list, *h; pjsip_msg *msg; int expires = -1; pj_list_init(&hdr_list); msg = rdata->msg_info.msg; h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (h) { expires = ((pjsip_expires_hdr*)h)->ivalue; pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h)); PJ_LOG(3,(THIS_FILE, " Expires=%d", expires)); } if (expires != 0) { h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); if (h) pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h)); } pjsip_endpt_respond(app.sip_endpt, &mod_sipecho, rdata, 200, NULL, &hdr_list, NULL, NULL); return PJ_TRUE; } if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { pj_str_t reason = pj_str("Go away"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 400, &reason, NULL, NULL); } return PJ_TRUE; } sdp_info = pjsip_rdata_get_sdp_info(rdata); if (!sdp_info || !sdp_info->sdp) { pj_str_t reason = pj_str("Require valid offer"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 400, &reason, NULL, NULL); } for (i=0; i<MAX_CALLS; ++i) { if (app.call[i].inv == NULL) { call = &app.call[i]; break; } } if (i==MAX_CALLS) { pj_str_t reason = pj_str("We're full"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, PJSIP_SC_BUSY_HERE, &reason, NULL, NULL); return PJ_TRUE; } /* Generate Contact URI */ status = pj_gethostip(AF, &hostaddr); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to retrieve local host IP", status); return PJ_TRUE; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, SIP_PORT); local_uri = pj_str(temp); status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, &local_uri, &dlg); if (status == PJ_SUCCESS) answer = create_answer(call-app.call, dlg->pool, sdp_info->sdp); if (status == PJ_SUCCESS) status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &call->inv); if (status == PJ_SUCCESS) status = pjsip_inv_initial_answer(call->inv, rdata, 100, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status == PJ_SUCCESS) status = pjsip_inv_answer(call->inv, 180, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status == PJ_SUCCESS) status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 500, NULL, NULL, NULL); destroy_call(call); } else { call->inv->mod_data[mod_sipecho.id] = call; } return PJ_TRUE; }
/* Create ICE stream transport */ static int create_ice_strans(struct test_sess *test_sess, struct ice_ept *ept, pj_ice_strans **p_ice) { pj_ice_strans *ice; pj_ice_strans_cb ice_cb; pj_ice_strans_cfg ice_cfg; pj_sockaddr hostip; char serverip[PJ_INET6_ADDRSTRLEN]; pj_status_t status; status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) return -1030; pj_sockaddr_print(&hostip, serverip, sizeof(serverip), 0); /* Init callback structure */ pj_bzero(&ice_cb, sizeof(ice_cb)); ice_cb.on_rx_data = &ice_on_rx_data; ice_cb.on_ice_complete = &ice_on_ice_complete; /* Init ICE stream transport configuration structure */ pj_ice_strans_cfg_default(&ice_cfg); pj_memcpy(&ice_cfg.stun_cfg, test_sess->stun_cfg, sizeof(pj_stun_config)); if ((ept->cfg.enable_stun & SRV)==SRV || (ept->cfg.enable_turn & SRV)==SRV) ice_cfg.resolver = test_sess->resolver; if (ept->cfg.enable_stun & YES) { if ((ept->cfg.enable_stun & SRV) == SRV) { ice_cfg.stun.server = pj_str(SRV_DOMAIN); } else { ice_cfg.stun.server = pj_str(serverip); } ice_cfg.stun.port = STUN_SERVER_PORT; } if (ept->cfg.enable_host == 0) { ice_cfg.stun.max_host_cands = 0; } else { //ice_cfg.stun.no_host_cands = PJ_FALSE; ice_cfg.stun.loop_addr = PJ_TRUE; } if (ept->cfg.enable_turn & YES) { if ((ept->cfg.enable_turn & SRV) == SRV) { ice_cfg.turn.server = pj_str(SRV_DOMAIN); } else { ice_cfg.turn.server = pj_str(serverip); } ice_cfg.turn.port = TURN_SERVER_PORT; ice_cfg.turn.conn_type = PJ_TURN_TP_UDP; ice_cfg.turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC; ice_cfg.turn.auth_cred.data.static_cred.realm = pj_str(SRV_DOMAIN); if (ept->cfg.client_flag & WRONG_TURN) ice_cfg.turn.auth_cred.data.static_cred.username = pj_str("xxx"); else ice_cfg.turn.auth_cred.data.static_cred.username = pj_str(TURN_USERNAME); ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; ice_cfg.turn.auth_cred.data.static_cred.data = pj_str(TURN_PASSWD); } /* Create ICE stream transport */ status = pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt, (void*)ept, &ice_cb, &ice); if (status != PJ_SUCCESS) { app_perror(INDENT "err: pj_ice_strans_create()", status); return status; } pj_create_unique_string(test_sess->pool, &ept->ufrag); pj_create_unique_string(test_sess->pool, &ept->pass); /* Looks alright */ *p_ice = ice; return PJ_SUCCESS; }
/*! \brief Function which creates an outgoing stream */ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp) { pj_pool_t *pool = session->inv_session->pool_prov; static const pj_str_t STR_IN = { "IN", 2 }; static const pj_str_t STR_IP4 = { "IP4", 3}; static const pj_str_t STR_IP6 = { "IP6", 3}; static const pj_str_t STR_UDPTL = { "udptl", 5 }; static const pj_str_t STR_T38 = { "t38", 3 }; static const pj_str_t STR_TRANSFERREDTCF = { "transferredTCF", 14 }; static const pj_str_t STR_LOCALTCF = { "localTCF", 8 }; static const pj_str_t STR_T38UDPFEC = { "t38UDPFEC", 9 }; static const pj_str_t STR_T38UDPREDUNDANCY = { "t38UDPRedundancy", 16 }; struct t38_state *state; pjmedia_sdp_media *media; char hostip[PJ_INET6_ADDRSTRLEN+2]; struct ast_sockaddr addr; char tmp[512]; pj_str_t stmp; if (!session->endpoint->media.t38.enabled) { return 1; } else if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) && (session->t38state != T38_ENABLED)) { return 1; } else if (!(state = t38_state_get_or_alloc(session))) { return -1; } else if (t38_initialize_session(session, session_media)) { return -1; } if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { return -1; } media->desc.media = pj_str(session_media->stream_type); media->desc.transport = STR_UDPTL; if (ast_strlen_zero(session->endpoint->media.address)) { pj_sockaddr localaddr; if (pj_gethostip(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) { return -1; } pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2); } else { ast_copy_string(hostip, session->endpoint->media.address, sizeof(hostip)); } media->conn->net_type = STR_IN; media->conn->addr_type = session->endpoint->media.t38.ipv6 ? STR_IP6 : STR_IP4; pj_strdup2(pool, &media->conn->addr, hostip); ast_udptl_get_us(session_media->udptl, &addr); media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr); media->desc.port_count = 1; media->desc.fmt[media->desc.fmt_count++] = STR_T38; snprintf(tmp, sizeof(tmp), "%u", state->our_parms.version); media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxVersion", pj_cstr(&stmp, tmp)); snprintf(tmp, sizeof(tmp), "%u", t38_get_rate(state->our_parms.rate)); media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38MaxBitRate", pj_cstr(&stmp, tmp)); if (state->our_parms.fill_bit_removal) { media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxFillBitRemoval", NULL); } if (state->our_parms.transcoding_mmr) { media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxTranscodingMMR", NULL); } if (state->our_parms.transcoding_jbig) { media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxTranscodingJBIG", NULL); } switch (state->our_parms.rate_management) { case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF: media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxRateManagement", &STR_TRANSFERREDTCF); break; case AST_T38_RATE_MANAGEMENT_LOCAL_TCF: media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxRateManagement", &STR_LOCALTCF); break; } snprintf(tmp, sizeof(tmp), "%u", ast_udptl_get_local_max_datagram(session_media->udptl)); media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxMaxDatagram", pj_cstr(&stmp, tmp)); switch (ast_udptl_get_error_correction_scheme(session_media->udptl)) { case UDPTL_ERROR_CORRECTION_NONE: break; case UDPTL_ERROR_CORRECTION_FEC: media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxUdpEC", &STR_T38UDPFEC); break; case UDPTL_ERROR_CORRECTION_REDUNDANCY: media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxUdpEC", &STR_T38UDPREDUNDANCY); break; } sdp->media[sdp->media_count++] = media; return 1; }
/* 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; }
/* main() * * If called with argument, treat argument as SIP URL to be called. * Otherwise wait for incoming calls. */ int main(int argc, char *argv[]) { struct pj_getopt_option long_options[] = { { "local-port", 1, 0, 'p' }, { "tcp", 0, 0, 't' }, { "ipv6", 0, 0, '6' }, { "help", 0, 0, 'h' } }; int c, option_index; pj_log_set_level(5); pj_init(); sip_af = pj_AF_INET(); pj_optind = 0; while ((c = pj_getopt_long(argc, argv, "p:t6h", long_options, &option_index)) != -1) { switch (c) { case 'p': sip_port = atoi(pj_optarg); break; case 't': sip_tcp = PJ_TRUE; break; case 'h': usage(); return 0; case '6': sip_af = pj_AF_INET6(); break; default: PJ_LOG(1,(THIS_FILE, "Argument \"%s\" is not valid. Use --help to see help", argv[pj_optind-1])); return -1; } } if (init_stack()) goto on_error; /* If URL is specified, then make call immediately. */ if (pj_optind != argc) { pj_sockaddr hostaddr; char hostip[PJ_INET6_ADDRSTRLEN+2]; char temp[80]; call_t *call; pj_str_t dst_uri = pj_str(argv[pj_optind]); pj_str_t local_uri; pjsip_dialog *dlg; pj_status_t status; pjsip_tx_data *tdata; if (pj_gethostip(sip_af, &hostaddr) != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Unable to retrieve local host IP")); goto on_error; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, sip_port); local_uri = pj_str(temp); call = &app.call[0]; status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, /* local URI */ &local_uri, /* local Contact */ &dst_uri, /* remote URI */ &dst_uri, /* remote target */ &dlg); /* dialog */ if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create UAC dialog", status); return 1; } status = pjsip_inv_create_uac( dlg, NULL, 0, &call->inv); if (status != PJ_SUCCESS) goto on_error; call->inv->mod_data[mod_sipecho.id] = call; status = pjsip_inv_invite(call->inv, &tdata); if (status != PJ_SUCCESS) goto on_error; status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) goto on_error; puts("Press ENTER to quit..."); } else { puts("Ready for incoming calls. Press ENTER to quit..."); } for (;;) { char s[10]; printf("\nMenu:\n" " h Hangup all calls\n" " l %s message logging\n" " q Quit\n", (app.enable_msg_logging? "Disable" : "Enable")); if (fgets(s, sizeof(s), stdin) == NULL) continue; if (s[0]=='q') break; switch (s[0]) { case 'l': app.enable_msg_logging = !app.enable_msg_logging; break; case 'h': hangup_all(); break; } } destroy_stack(); puts("Bye bye.."); return 0; on_error: puts("An error has occurred. run a debugger.."); return 1; }
static pj_status_t init_proxy(void) { pj_sockaddr pri_addr; pj_sockaddr addr_list[16]; unsigned addr_cnt = PJ_ARRAY_SIZE(addr_list); unsigned i; /* List all names matching local endpoint. * Note that PJLIB version 0.6 and newer has a function to * enumerate local IP interface (pj_enum_ip_interface()), so * by using it would be possible to list all IP interfaces in * this host. */ /* The first address is important since this would be the one * to be added in Record-Route. */ if (pj_gethostip(pj_AF_INET(), &pri_addr)==PJ_SUCCESS) { pj_strdup2(global.pool, &global.name[global.name_cnt].host, pj_inet_ntoa(pri_addr.ipv4.sin_addr)); global.name[global.name_cnt].port = global.port; global.name_cnt++; } /* Get the rest of IP interfaces */ if (pj_enum_ip_interface(pj_AF_INET(), &addr_cnt, addr_list) == PJ_SUCCESS) { for (i=0; i<addr_cnt; ++i) { if (addr_list[i].ipv4.sin_addr.s_addr == pri_addr.ipv4.sin_addr.s_addr) continue; pj_strdup2(global.pool, &global.name[global.name_cnt].host, pj_inet_ntoa(addr_list[i].ipv4.sin_addr)); global.name[global.name_cnt].port = global.port; global.name_cnt++; } } /* Add loopback address. */ #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF global.name[global.name_cnt].host = pj_str("127.0.0.1"); global.name[global.name_cnt].port = global.port; global.name_cnt++; #endif global.name[global.name_cnt].host = *pj_gethostname(); global.name[global.name_cnt].port = global.port; global.name_cnt++; global.name[global.name_cnt].host = pj_str("localhost"); global.name[global.name_cnt].port = global.port; global.name_cnt++; PJ_LOG(3,(THIS_FILE, "Proxy started, listening on port %d", global.port)); PJ_LOG(3,(THIS_FILE, "Local host aliases:")); for (i=0; i<global.name_cnt; ++i) { PJ_LOG(3,(THIS_FILE, " %.*s:%d", (int)global.name[i].host.slen, global.name[i].host.ptr, global.name[i].port)); } if (global.record_route) { PJ_LOG(3,(THIS_FILE, "Using Record-Route mode")); } return PJ_SUCCESS; }
/* * Callback when incoming requests outside any transactions and any * dialogs are received. We're only interested to hande incoming INVITE * request, and we'll reject any other requests with 500 response. */ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) { pj_sockaddr hostaddr; char temp[80], hostip[PJ_INET6_ADDRSTRLEN]; pj_str_t local_uri; pjsip_dialog *dlg; pjmedia_sdp_session *local_sdp; pjsip_tx_data *tdata; unsigned options = 0; pj_status_t status; /* * Respond (statelessly) any non-INVITE requests with 500 */ if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { pj_str_t reason = pj_str("Simple UA unable to handle " "this request"); pjsip_endpt_respond_stateless( g_endpt, rdata, 500, &reason, NULL, NULL); } return PJ_TRUE; } /* * Reject INVITE if we already have an INVITE session in progress. */ if (g_inv) { pj_str_t reason = pj_str("Another call is in progress"); pjsip_endpt_respond_stateless( g_endpt, rdata, 500, &reason, NULL, NULL); return PJ_TRUE; } /* Verify that we can handle the request. */ status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, g_endpt, NULL); if (status != PJ_SUCCESS) { pj_str_t reason = pj_str("Sorry Simple UA can not handle this INVITE"); pjsip_endpt_respond_stateless( g_endpt, rdata, 500, &reason, NULL, NULL); return PJ_TRUE; } /* * Generate Contact URI */ if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to retrieve local host IP", status); return PJ_TRUE; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:simpleuas@%s:%d>", hostip, SIP_PORT); local_uri = pj_str(temp); /* * Create UAS dialog. */ status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, &local_uri, /* contact */ &dlg); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless(g_endpt, rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } /* * Get media capability from media endpoint: */ status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, MAX_MEDIA_CNT, g_sock_info, &local_sdp); PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); /* * Create invite session, and pass both the UAS dialog and the SDP * capability to the session. */ status = pjsip_inv_create_uas( dlg, rdata, local_sdp, 0, &g_inv); PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); /* * Initially send 180 response. * * The very first response to an INVITE must be created with * pjsip_inv_initial_answer(). Subsequent responses to the same * transaction MUST use pjsip_inv_answer(). */ status = pjsip_inv_initial_answer(g_inv, rdata, 180, NULL, NULL, &tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); /* Send the 180 response. */ status = pjsip_inv_send_msg(g_inv, tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); /* * Now create 200 response. */ status = pjsip_inv_answer( g_inv, 200, NULL, /* st_code and st_text */ NULL, /* SDP already specified */ &tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); /* * Send the 200 response. */ status = pjsip_inv_send_msg(g_inv, tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); /* Done. * When the call is disconnected, it will be reported via the callback. */ return PJ_TRUE; }
/* main() * * If called with argument, treat argument as SIP URL to be called. * Otherwise wait for incoming calls. */ int main(int argc, char *argv[]) { if (init_stack()) goto on_error; /* If URL is specified, then make call immediately. */ if (argc > 1) { pj_sockaddr hostaddr; char hostip[PJ_INET6_ADDRSTRLEN+2]; char temp[80]; call_t *call; pj_str_t dst_uri = pj_str(argv[1]); pj_str_t local_uri; pjsip_dialog *dlg; pj_status_t status; pjsip_tx_data *tdata; if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Unable to retrieve local host IP")); goto on_error; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, SIP_PORT); local_uri = pj_str(temp); call = &app.call[0]; status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, /* local URI */ &local_uri, /* local Contact */ &dst_uri, /* remote URI */ &dst_uri, /* remote target */ &dlg); /* dialog */ if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create UAC dialog", status); return 1; } status = pjsip_inv_create_uac( dlg, NULL, 0, &call->inv); if (status != PJ_SUCCESS) goto on_error; call->inv->mod_data[mod_sipecho.id] = call; status = pjsip_inv_invite(call->inv, &tdata); if (status != PJ_SUCCESS) goto on_error; status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) goto on_error; puts("Press ENTER to quit..."); } else { puts("Ready for incoming calls. Press ENTER to quit..."); } for (;;) { char s[10]; printf("\nMenu:\n" " h Hangup all calls\n" " l %s message logging\n" " q Quit\n", (app.enable_msg_logging? "Disable" : "Enable")); if (fgets(s, sizeof(s), stdin) == NULL) continue; if (s[0]=='q') break; switch (s[0]) { case 'l': app.enable_msg_logging = !app.enable_msg_logging; break; case 'h': hangup_all(); break; } } destroy_stack(); puts("Bye bye.."); return 0; on_error: puts("An error has occurred. run a debugger.."); return 1; }
/* * 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; }
/* Generate transport's published address */ static pj_status_t get_published_name(pj_sock_t sock, char hostbuf[], int hostbufsz, pjsip_host_port *bound_name) { pj_sockaddr tmp_addr; int addr_len; pj_status_t status; addr_len = sizeof(tmp_addr); status = pj_sock_getsockname(sock, &tmp_addr, &addr_len); if (status != PJ_SUCCESS) return status; bound_name->host.ptr = hostbuf; if (tmp_addr.addr.sa_family == pj_AF_INET()) { bound_name->port = pj_ntohs(tmp_addr.ipv4.sin_port); /* If bound address specifies "0.0.0.0", get the IP address * of local hostname. */ if (tmp_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) { pj_sockaddr hostip; status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) return status; pj_strcpy2(&bound_name->host, pj_inet_ntoa(hostip.ipv4.sin_addr)); } else { /* Otherwise use bound address. */ pj_strcpy2(&bound_name->host, pj_inet_ntoa(tmp_addr.ipv4.sin_addr)); status = PJ_SUCCESS; } } else { /* If bound address specifies "INADDR_ANY" (IPv6), get the * IP address of local hostname */ pj_uint32_t loop6[4] = { 0, 0, 0, 0}; bound_name->port = pj_ntohs(tmp_addr.ipv6.sin6_port); if (pj_memcmp(&tmp_addr.ipv6.sin6_addr, loop6, sizeof(loop6))==0) { status = pj_gethostip(tmp_addr.addr.sa_family, &tmp_addr); if (status != PJ_SUCCESS) return status; } status = pj_inet_ntop(tmp_addr.addr.sa_family, pj_sockaddr_get_addr(&tmp_addr), hostbuf, hostbufsz); if (status == PJ_SUCCESS) { bound_name->host.slen = pj_ansi_strlen(hostbuf); } } return status; }
/** * Create UDP stream transport from existing socket info. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, const char *name, const pjmedia_sock_info *si, unsigned options, pjmedia_transport **p_tp) { struct transport_udp *tp; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_ioqueue_callback rtp_cb, rtcp_cb; pj_ssize_t size; unsigned i; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && si && p_tp, PJ_EINVAL); /* Get ioqueue instance */ ioqueue = pjmedia_endpt_get_ioqueue(endpt); if (name==NULL) name = "udp%p"; /* Create transport structure */ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); if (!pool) return PJ_ENOMEM; tp = PJ_POOL_ZALLOC_T(pool, struct transport_udp); tp->pool = pool; tp->options = options; pj_memcpy(tp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); tp->base.op = &transport_udp_op; tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; /* Copy socket infos */ tp->rtp_sock = si->rtp_sock; tp->rtp_addr_name = si->rtp_addr_name; tp->rtcp_sock = si->rtcp_sock; tp->rtcp_addr_name = si->rtcp_addr_name; /* If address is 0.0.0.0, use host's IP address */ if (!pj_sockaddr_has_addr(&tp->rtp_addr_name)) { pj_sockaddr hostip; status = pj_gethostip(tp->rtp_addr_name.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_memcpy(pj_sockaddr_get_addr(&tp->rtp_addr_name), pj_sockaddr_get_addr(&hostip), pj_sockaddr_get_addr_len(&hostip)); } /* Same with RTCP */ if (!pj_sockaddr_has_addr(&tp->rtcp_addr_name)) { pj_memcpy(pj_sockaddr_get_addr(&tp->rtcp_addr_name), pj_sockaddr_get_addr(&tp->rtp_addr_name), pj_sockaddr_get_addr_len(&tp->rtp_addr_name)); } /* Setup RTP socket with the ioqueue */ pj_bzero(&rtp_cb, sizeof(rtp_cb)); rtp_cb.on_read_complete = &on_rx_rtp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtp_sock, tp, &rtp_cb, &tp->rtp_key); if (status != PJ_SUCCESS) goto on_error; /* Disallow concurrency so that detach() and destroy() are * synchronized with the callback. */ status = pj_ioqueue_set_concurrency(tp->rtp_key, PJ_FALSE); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtp_read_op, sizeof(tp->rtp_read_op)); for (i=0; i<PJ_ARRAY_SIZE(tp->rtp_pending_write); ++i) pj_ioqueue_op_key_init(&tp->rtp_pending_write[i].op_key, sizeof(tp->rtp_pending_write[i].op_key)); /* Kick of pending RTP read from the ioqueue */ tp->rtp_addrlen = sizeof(tp->rtp_src_addr); size = sizeof(tp->rtp_pkt); status = pj_ioqueue_recvfrom(tp->rtp_key, &tp->rtp_read_op, tp->rtp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtp_src_addr, &tp->rtp_addrlen); if (status != PJ_EPENDING) goto on_error; /* Setup RTCP socket with ioqueue */ pj_bzero(&rtcp_cb, sizeof(rtcp_cb)); rtcp_cb.on_read_complete = &on_rx_rtcp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtcp_sock, tp, &rtcp_cb, &tp->rtcp_key); if (status != PJ_SUCCESS) goto on_error; status = pj_ioqueue_set_concurrency(tp->rtcp_key, PJ_FALSE); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtcp_read_op, sizeof(tp->rtcp_read_op)); pj_ioqueue_op_key_init(&tp->rtcp_write_op, sizeof(tp->rtcp_write_op)); /* Kick of pending RTCP read from the ioqueue */ size = sizeof(tp->rtcp_pkt); tp->rtcp_addr_len = sizeof(tp->rtcp_src_addr); status = pj_ioqueue_recvfrom( tp->rtcp_key, &tp->rtcp_read_op, tp->rtcp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtcp_src_addr, &tp->rtcp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Done */ *p_tp = &tp->base; return PJ_SUCCESS; on_error: transport_destroy(&tp->base); return status; }
/** * Create UDP stream transport from existing socket info. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, const char *name, const pjmedia_sock_info *si, unsigned options, pjmedia_transport **p_tp) { struct transport_udp *tp; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_ioqueue_callback rtp_cb, rtcp_cb; pj_ssize_t size; unsigned i; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && si && p_tp, PJ_EINVAL); /* Check name */ if (!name) name = "udpmedia"; /* Get ioqueue instance */ ioqueue = pjmedia_endpt_get_ioqueue(endpt); /* Create transport structure */ pool = pjmedia_endpt_create_pool(endpt, name, 4000, 4000); if (!pool) return PJ_ENOMEM; tp = pj_pool_zalloc(pool, sizeof(struct transport_udp)); tp->pool = pool; tp->options = options; pj_ansi_strcpy(tp->base.name, name); tp->base.op = &transport_udp_op; /* Copy socket infos */ tp->rtp_sock = si->rtp_sock; tp->rtp_addr_name = si->rtp_addr_name; tp->rtcp_sock = si->rtcp_sock; tp->rtcp_addr_name = si->rtcp_addr_name; /* If address is 0.0.0.0, use host's IP address */ if (tp->rtp_addr_name.sin_addr.s_addr == 0) { pj_in_addr hostip; status = pj_gethostip(&hostip); if (status != PJ_SUCCESS) goto on_error; tp->rtp_addr_name.sin_addr = hostip; } /* Same with RTCP */ if (tp->rtcp_addr_name.sin_addr.s_addr == 0) { tp->rtcp_addr_name.sin_addr.s_addr = tp->rtp_addr_name.sin_addr.s_addr; } /* Setup RTP socket with the ioqueue */ pj_bzero(&rtp_cb, sizeof(rtp_cb)); rtp_cb.on_read_complete = &on_rx_rtp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtp_sock, tp, &rtp_cb, &tp->rtp_key); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtp_read_op, sizeof(tp->rtp_read_op)); for (i=0; i<PJ_ARRAY_SIZE(tp->rtp_pending_write); ++i) pj_ioqueue_op_key_init(&tp->rtp_pending_write[i].op_key, sizeof(tp->rtp_pending_write[i].op_key)); /* Kick of pending RTP read from the ioqueue */ tp->rtp_addrlen = sizeof(tp->rtp_src_addr); size = sizeof(tp->rtp_pkt); status = pj_ioqueue_recvfrom(tp->rtp_key, &tp->rtp_read_op, tp->rtp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtp_src_addr, &tp->rtp_addrlen); if (status != PJ_EPENDING) goto on_error; /* Setup RTCP socket with ioqueue */ pj_bzero(&rtcp_cb, sizeof(rtcp_cb)); rtcp_cb.on_read_complete = &on_rx_rtcp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtcp_sock, tp, &rtcp_cb, &tp->rtcp_key); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtcp_read_op, sizeof(tp->rtcp_read_op)); pj_ioqueue_op_key_init(&tp->rtcp_write_op, sizeof(tp->rtcp_write_op)); /* Kick of pending RTCP read from the ioqueue */ size = sizeof(tp->rtcp_pkt); tp->rtcp_addr_len = sizeof(tp->rtcp_src_addr); status = pj_ioqueue_recvfrom( tp->rtcp_key, &tp->rtcp_read_op, tp->rtcp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtcp_src_addr, &tp->rtcp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Done */ *p_tp = &tp->base; return PJ_SUCCESS; on_error: pjmedia_transport_udp_close(&tp->base); return status; }
static int purity_test(void) { PJ_LOG(3,("test", "...purity_test()")); #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 /* Check on "sin_len" member of sockaddr */ { const pj_str_t str_ip = {"1.1.1.1", 7}; pj_sockaddr addr[16]; pj_addrinfo ai[16]; unsigned cnt; pj_status_t rc; /* pj_enum_ip_interface() */ cnt = PJ_ARRAY_SIZE(addr); rc = pj_enum_ip_interface(pj_AF_UNSPEC(), &cnt, addr); if (rc == PJ_SUCCESS) { while (cnt--) CHECK_SA_ZERO_LEN(&addr[cnt], -10); } /* pj_gethostip() on IPv4 */ rc = pj_gethostip(pj_AF_INET(), &addr[0]); if (rc == PJ_SUCCESS) CHECK_SA_ZERO_LEN(&addr[0], -20); /* pj_gethostip() on IPv6 */ rc = pj_gethostip(pj_AF_INET6(), &addr[0]); if (rc == PJ_SUCCESS) CHECK_SA_ZERO_LEN(&addr[0], -30); /* pj_getdefaultipinterface() on IPv4 */ rc = pj_getdefaultipinterface(pj_AF_INET(), &addr[0]); if (rc == PJ_SUCCESS) CHECK_SA_ZERO_LEN(&addr[0], -40); /* pj_getdefaultipinterface() on IPv6 */ rc = pj_getdefaultipinterface(pj_AF_INET6(), &addr[0]); if (rc == PJ_SUCCESS) CHECK_SA_ZERO_LEN(&addr[0], -50); /* pj_getaddrinfo() on a host name */ cnt = PJ_ARRAY_SIZE(ai); rc = pj_getaddrinfo(pj_AF_UNSPEC(), pj_gethostname(), &cnt, ai); if (rc == PJ_SUCCESS) { while (cnt--) CHECK_SA_ZERO_LEN(&ai[cnt].ai_addr, -60); } /* pj_getaddrinfo() on an IP address */ cnt = PJ_ARRAY_SIZE(ai); rc = pj_getaddrinfo(pj_AF_UNSPEC(), &str_ip, &cnt, ai); if (rc == PJ_SUCCESS) { pj_assert(cnt == 1); CHECK_SA_ZERO_LEN(&ai[0].ai_addr, -70); } } #endif 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; }
pj_status_t init_stack(const std::string& system_name, const std::string& sas_address, int pcscf_trusted_port, int pcscf_untrusted_port, int scscf_port, int icscf_port, const std::string& local_host, const std::string& public_host, const std::string& home_domain, const std::string& additional_home_domains, const std::string& scscf_uri, const std::string& alias_hosts, SIPResolver* sipresolver, int num_pjsip_threads, int num_worker_threads, int record_routing_model, const int default_session_expires, QuiescingManager *quiescing_mgr_arg, LoadMonitor *load_monitor_arg, const std::string& cdf_domain) { pj_status_t status; pj_sockaddr pri_addr; pj_sockaddr addr_list[16]; unsigned addr_cnt = PJ_ARRAY_SIZE(addr_list); unsigned i; // Set up the vectors of threads. The threads don't get created until // start_stack is called. pjsip_threads.resize(num_pjsip_threads); worker_threads.resize(num_worker_threads); // Get ports and host names specified on options. If local host was not // specified, use the host name returned by pj_gethostname. char* local_host_cstr = strdup(local_host.c_str()); char* public_host_cstr = strdup(public_host.c_str()); char* home_domain_cstr = strdup(home_domain.c_str()); char* scscf_uri_cstr; if (scscf_uri.empty()) { // Create a default S-CSCF URI using the localhost and S-CSCF port. std::string tmp_scscf_uri = "sip:" + local_host + ":" + std::to_string(scscf_port) + ";transport=TCP"; scscf_uri_cstr = strdup(tmp_scscf_uri.c_str()); } else { // Use the specified URI. scscf_uri_cstr = strdup(scscf_uri.c_str()); } // This is only set on Bono nodes (it's the empty string otherwise) char* cdf_domain_cstr = strdup(cdf_domain.c_str()); // Copy port numbers to stack data. stack_data.pcscf_trusted_port = pcscf_trusted_port; stack_data.pcscf_untrusted_port = pcscf_untrusted_port; stack_data.scscf_port = scscf_port; stack_data.icscf_port = icscf_port; stack_data.sipresolver = sipresolver; // Copy other functional options to stack data. stack_data.default_session_expires = default_session_expires; // Work out local and public hostnames and cluster domain names. stack_data.local_host = (local_host != "") ? pj_str(local_host_cstr) : *pj_gethostname(); stack_data.public_host = (public_host != "") ? pj_str(public_host_cstr) : stack_data.local_host; stack_data.default_home_domain = (home_domain != "") ? pj_str(home_domain_cstr) : stack_data.local_host; stack_data.scscf_uri = pj_str(scscf_uri_cstr); stack_data.cdf_domain = pj_str(cdf_domain_cstr); // Build a set of home domains stack_data.home_domains = std::unordered_set<std::string>(); stack_data.home_domains.insert(PJUtils::pj_str_to_string(&stack_data.default_home_domain)); if (additional_home_domains != "") { std::list<std::string> domains; Utils::split_string(additional_home_domains, ',', domains, 0, true); stack_data.home_domains.insert(domains.begin(), domains.end()); } // Set up the default address family. This is IPv4 unless our local host is an IPv6 address. stack_data.addr_family = AF_INET; struct in6_addr dummy_addr; if (inet_pton(AF_INET6, local_host_cstr, &dummy_addr) == 1) { LOG_DEBUG("Local host is an IPv6 address - enabling IPv6 mode"); stack_data.addr_family = AF_INET6; } stack_data.record_route_on_every_hop = false; stack_data.record_route_on_initiation_of_originating = false; stack_data.record_route_on_initiation_of_terminating = false; stack_data.record_route_on_completion_of_originating = false; stack_data.record_route_on_completion_of_terminating = false; stack_data.record_route_on_diversion = false; if (scscf_port != 0) { switch (record_routing_model) { case 1: stack_data.record_route_on_initiation_of_originating = true; stack_data.record_route_on_completion_of_terminating = true; break; case 2: stack_data.record_route_on_initiation_of_originating = true; stack_data.record_route_on_initiation_of_terminating = true; stack_data.record_route_on_completion_of_originating = true; stack_data.record_route_on_completion_of_terminating = true; stack_data.record_route_on_diversion = true; break; case 3: stack_data.record_route_on_every_hop = true; stack_data.record_route_on_initiation_of_originating = true; stack_data.record_route_on_initiation_of_terminating = true; stack_data.record_route_on_completion_of_originating = true; stack_data.record_route_on_completion_of_terminating = true; stack_data.record_route_on_diversion = true; break; default: LOG_ERROR("Record-Route setting should be 1, 2, or 3, is %d. Defaulting to Record-Route on every hop.", record_routing_model); stack_data.record_route_on_every_hop = true; } } std::string system_name_sas = system_name; std::string system_type_sas = (pcscf_trusted_port != 0) ? "bono" : "sprout"; // Initialize SAS logging. if (system_name_sas == "") { system_name_sas = std::string(stack_data.local_host.ptr, stack_data.local_host.slen); } SAS::init(system_name, system_type_sas, SASEvent::CURRENT_RESOURCE_BUNDLE, sas_address, sas_write); // Initialise PJSIP and all the associated resources. status = init_pjsip(); // Register the stack module. pjsip_endpt_register_module(stack_data.endpt, &mod_stack); stack_data.module_id = mod_stack.id; // Initialize the PJUtils module. PJUtils::init(); // Create listening transports for the ports whichtrusted and untrusted ports. stack_data.pcscf_trusted_tcp_factory = NULL; if (stack_data.pcscf_trusted_port != 0) { status = start_transports(stack_data.pcscf_trusted_port, stack_data.local_host, &stack_data.pcscf_trusted_tcp_factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); } stack_data.pcscf_untrusted_tcp_factory = NULL; if (stack_data.pcscf_untrusted_port != 0) { status = start_transports(stack_data.pcscf_untrusted_port, stack_data.public_host, &stack_data.pcscf_untrusted_tcp_factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); } stack_data.scscf_tcp_factory = NULL; if (stack_data.scscf_port != 0) { status = start_transports(stack_data.scscf_port, stack_data.public_host, &stack_data.scscf_tcp_factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); } stack_data.icscf_tcp_factory = NULL; if (stack_data.icscf_port != 0) { status = start_transports(stack_data.icscf_port, stack_data.public_host, &stack_data.icscf_tcp_factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); } // List all names matching local endpoint. // Note that PJLIB version 0.6 and newer has a function to // enumerate local IP interface (pj_enum_ip_interface()), so // by using it would be possible to list all IP interfaces in // this host. // The first address is important since this would be the one // to be added in Record-Route. stack_data.name[stack_data.name_cnt] = stack_data.local_host; stack_data.name_cnt++; if (strcmp(local_host_cstr, public_host_cstr)) { stack_data.name[stack_data.name_cnt] = stack_data.public_host; stack_data.name_cnt++; } if ((scscf_port != 0) && (!scscf_uri.empty())) { // S-CSCF enabled with a specified URI, so add host name from the URI to hostnames. pjsip_sip_uri* uri = (pjsip_sip_uri*)PJUtils::uri_from_string(scscf_uri, stack_data.pool); if (uri != NULL) { stack_data.name[stack_data.name_cnt] = uri->host; stack_data.name_cnt++; } } if (pj_gethostip(pj_AF_INET(), &pri_addr) == PJ_SUCCESS) { pj_strdup2(stack_data.pool, &stack_data.name[stack_data.name_cnt], pj_inet_ntoa(pri_addr.ipv4.sin_addr)); stack_data.name_cnt++; } // Get the rest of IP interfaces. if (pj_enum_ip_interface(pj_AF_INET(), &addr_cnt, addr_list) == PJ_SUCCESS) { for (i = 0; i < addr_cnt; ++i) { if (addr_list[i].ipv4.sin_addr.s_addr == pri_addr.ipv4.sin_addr.s_addr) { continue; } pj_strdup2(stack_data.pool, &stack_data.name[stack_data.name_cnt], pj_inet_ntoa(addr_list[i].ipv4.sin_addr)); stack_data.name_cnt++; } } // Note that we no longer consider 127.0.0.1 and localhost as aliases. // Parse the list of alias host names. stack_data.aliases = std::unordered_set<std::string>(); if (alias_hosts != "") { std::list<std::string> aliases; Utils::split_string(alias_hosts, ',', aliases, 0, true); stack_data.aliases.insert(aliases.begin(), aliases.end()); for (std::unordered_set<std::string>::iterator it = stack_data.aliases.begin(); it != stack_data.aliases.end(); ++it) { pj_strdup2(stack_data.pool, &stack_data.name[stack_data.name_cnt], it->c_str()); stack_data.name_cnt++; } } LOG_STATUS("Local host aliases:"); for (i = 0; i < stack_data.name_cnt; ++i) { LOG_STATUS(" %.*s", (int)stack_data.name[i].slen, stack_data.name[i].ptr); } // Set up the Last Value Cache, accumulators and counters. std::string zmq_port = SPROUT_ZMQ_PORT; if ((stack_data.pcscf_trusted_port != 0) && (stack_data.pcscf_untrusted_port != 0)) { zmq_port = BONO_ZMQ_PORT; } stack_data.stats_aggregator = new LastValueCache(num_known_stats, known_statnames, zmq_port); latency_accumulator = new StatisticAccumulator("latency_us", stack_data.stats_aggregator); queue_size_accumulator = new StatisticAccumulator("queue_size", stack_data.stats_aggregator); requests_counter = new StatisticCounter("incoming_requests", stack_data.stats_aggregator); overload_counter = new StatisticCounter("rejected_overload", stack_data.stats_aggregator); if (load_monitor_arg != NULL) { load_monitor = load_monitor_arg; } if (quiescing_mgr_arg != NULL) { quiescing_mgr = quiescing_mgr_arg; // Create an instance of the stack quiesce handler. This acts as a glue // class between the stack modulem connections tracker, and the quiescing // manager. stack_quiesce_handler = new StackQuiesceHandler(); // Create a new connection tracker, and register the quiesce handler with // it. connection_tracker = new ConnectionTracker(stack_quiesce_handler); // Register the quiesce handler with the quiescing manager (the former // implements the connection handling interface). quiescing_mgr->register_conns_handler(stack_quiesce_handler); } return status; }