int proxy_thread_callback(void* p) { pj_time_val delay = {0, 10}; while (!global.quit_flag) { pjsip_endpt_handle_events(global.endpt, &delay); } return 0; }
/* Worker thread */ static int worker_thread(void *arg) { PJ_UNUSED_ARG(arg); while (!quit_flag) { pj_time_val timeout = {0, 500}; pjsip_endpt_handle_events(sip_endpt, &timeout); } return 0; }
static int rt_worker_thread(void *arg) { int i; pj_time_val poll_delay = { 0, 10 }; PJ_UNUSED_ARG(arg); /* Sleep to allow main threads to run. */ pj_thread_sleep(10); while (!rt_stop) { pjsip_endpt_handle_events(endpt, &poll_delay); } /* Exhaust responses. */ for (i=0; i<100; ++i) pjsip_endpt_handle_events(endpt, &poll_delay); return 0; }
static int worker_proc(void *arg) { PJ_UNUSED_ARG(arg); while (!app.quit) { pj_time_val interval = { 0, 20 }; pjsip_endpt_handle_events(app.sip_endpt, &interval); } return 0; }
/// PJSIP threads are donated to PJSIP to handle receiving at transport level /// and timers. static int pjsip_thread(void *p) { pj_time_val delay = {0, 10}; PJ_UNUSED_ARG(p); LOG_DEBUG("PJSIP thread started"); while (!quit_flag) { pjsip_endpt_handle_events(stack_data.endpt, &delay); } LOG_DEBUG("PJSIP thread ended"); return 0; }
void flush_events(unsigned duration) { pj_time_val stop_time; pj_gettimeofday(&stop_time); stop_time.msec += duration; pj_time_val_normalize(&stop_time); /* Process all events for the specified duration. */ for (;;) { pj_time_val timeout = {0, 1}, now; pjsip_endpt_handle_events(endpt, &timeout); pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, stop_time)) break; } }
/* Cualquier REQUEST SIP pasa por aquí Aquí es dónde hacemos la magia negra de coger la header, desencapsular y tal */ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) { if ( (rdata->msg_info.msg->line.req.method.id != PJSIP_OPTIONS_METHOD)) { debug(2,"Received no OPTIONS Request, reply 500 sent"); pj_str_t reason = pj_str("Go home"); pjsip_endpt_respond_stateless( g_endpt, rdata,500, &reason,NULL, NULL); return PJ_TRUE; } debug(1,"Received INFO/OPTIONS Request, lets parse it"); pjsip_cid_hdr *headerMultivpn; pj_str_t hdrname = { .ptr = "Multivpn", .slen = 8 }; headerMultivpn = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg ,&hdrname,NULL); if (!headerMultivpn) { debug(2,"No multivpn header found! Nothing to parse"); pj_str_t reason = pj_str("Go home and sleep"); pjsip_endpt_respond_stateless( g_endpt, rdata,500, &reason,NULL, NULL); return PJ_TRUE; } else debug(1,"Header multivpn found ! Lets play with it"); char payload[2048]; memset(payload, 0, sizeof(payload)); strncpy(payload, headerMultivpn->id.ptr, headerMultivpn->id.slen); debug(1,"Received payload is: %s with len %d",payload,strlen(payload)); unsigned char *decodedpayload; size_t lenDecoded; decodedpayload = base64_decode(payload,strlen(payload),&lenDecoded); debug(1,"Decoded %d bytes",lenDecoded); debug(1,"Decoded payload with len %d and value %.3s",lenDecoded, decodedpayload); debug(1,"Now sending to PIPE for TUNDRIVER !"); int nBytes = lenDecoded; nBytes=write(global_v.pipe_from_plugin[1],decodedpayload,nBytes); if (nBytes<=0) debug(3,"SIP Plugin: Failed Writing to Pipe"); else debug(3,"SIP Plugin: Write %d bytes to pipe",nBytes); // Tras el parsing, contestamos: pj_str_t reason = pj_str("Traffic Accepted"); pjsip_endpt_respond_stateless( g_endpt, rdata,666, &reason,NULL, NULL); debug(1,"End process of request"); return PJ_TRUE; } int sip_envia_datos(unsigned char *datos,int longitud) { debug(1,"Starting function to send %d bytes", longitud); debug(1,"Now base64 encoding %d bytes",longitud); int longitudEncodeado; char * payload; payload = base64_encode(datos,longitud,(size_t *)&longitudEncodeado); pj_status_t status; pjsip_tx_data *tdata; pj_str_t target = pj_str(global_v.sip_remoteuri); pj_str_t from = pj_str(global_v.sip_fromuri); status = pjsip_endpt_create_request( g_endpt, &pjsip_options_method, &target, &from, &target, NULL, NULL, -1, NULL, &tdata ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); pjsip_transaction *tsx; status = pjsip_tsx_create_uac(NULL, tdata, &tsx); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); // Create Custom header pj_str_t hdr_name = { .ptr = "Multivpn", .slen = 8 }; pj_str_t hdr_value = { .ptr = payload, .slen = longitudEncodeado }; pjsip_generic_string_hdr *multivpn_hdr = pjsip_generic_string_hdr_create( tdata->pool, &hdr_name, &hdr_value ); // Add to message pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) multivpn_hdr); status = pjsip_tsx_send_msg(tsx, NULL); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); return 0; } void* sip_loop_sip_events(void *none) { for (;!g_complete;) { //pj_time_val timeout = {0, 10}; //pjsip_endpt_handle_events(g_endpt, &timeout); pjsip_endpt_handle_events(g_endpt, NULL); debug(1,"Looping SIP"); } return NULL; } void sip_loop_tun_events() { unsigned char buffer[BLOCK_SIZE]; int nBytes; fd_set setReading; int maxfd; debug(2,"SIP Plugin: Starting LOOPING for events from TUN"); maxfd = global_v.pipe_to_plugin[0]+1; while (1) { FD_SET(global_v.pipe_to_plugin[0],&setReading); debug(3,"SIP Plugin: Blocking now, until data from tundriver is received"); select(maxfd,&setReading,NULL,NULL,NULL); debug(1,"SIP PLUGIN: Select unlocked !"); if (FD_ISSET(global_v.pipe_to_plugin[0],&setReading)) { // Message is from pipe nBytes=read(global_v.pipe_to_plugin[0],buffer, BLOCK_SIZE-1); if (nBytes<=0) debug(3,"SIP Plugin: failed reading From Pipe"); else { debug(3,"SIP Plugin: Read %d bytes from pipe",nBytes); sip_envia_datos(buffer,nBytes); } } } }
/* Test that we receive loopback message. */ int transport_send_recv_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, char *target_url, int *p_usec_rtt) { pj_bool_t msg_log_enabled; pj_status_t status; pj_str_t target, from, to, contact, call_id, body; pjsip_method method; pjsip_tx_data *tdata; pj_time_val timeout; PJ_UNUSED_ARG(tp_type); PJ_UNUSED_ARG(ref_tp); PJ_LOG(3,(THIS_FILE, " single message round-trip test...")); /* Register out test module to receive the message (if necessary). */ if (my_module.id == -1) { status = pjsip_endpt_register_module( endpt, &my_module ); if (status != PJ_SUCCESS) { app_perror(" error: unable to register module", status); return -500; } } /* Disable message logging. */ msg_log_enabled = msg_logger_set_enabled(0); /* Create a request message. */ target = pj_str(target_url); from = pj_str(FROM_HDR); to = pj_str(target_url); contact = pj_str(CONTACT_HDR); call_id = pj_str(CALL_ID_HDR); body = pj_str(BODY); pjsip_method_set(&method, PJSIP_OPTIONS_METHOD); status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to, &contact, &call_id, CSEQ_VALUE, &body, &tdata ); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return -510; } /* Reset statuses */ send_status = recv_status = NO_STATUS; /* Start time. */ pj_get_timestamp(&my_send_time); /* Send the message (statelessly). */ PJ_LOG(5,(THIS_FILE, "Sending request to %.*s", (int)target.slen, target.ptr)); status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, &send_msg_callback); if (status != PJ_SUCCESS) { /* Immediate error! */ pjsip_tx_data_dec_ref(tdata); send_status = status; } /* Set the timeout (2 seconds from now) */ pj_gettimeofday(&timeout); timeout.sec += 2; /* Loop handling events until we get status */ do { pj_time_val now; pj_time_val poll_interval = { 0, 10 }; pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, timeout)) { PJ_LOG(3,(THIS_FILE, " error: timeout in send/recv test")); status = -540; goto on_return; } if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) { app_perror(" error sending message", send_status); status = -550; goto on_return; } if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) { app_perror(" error receiving message", recv_status); status = -560; goto on_return; } if (send_status!=NO_STATUS && recv_status!=NO_STATUS) { /* Success! */ break; } pjsip_endpt_handle_events(endpt, &poll_interval); } while (1); if (status == PJ_SUCCESS) { unsigned usec_rt; usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time); PJ_LOG(3,(THIS_FILE, " round-trip = %d usec", usec_rt)); *p_usec_rtt = usec_rt; } /* Restore message logging. */ msg_logger_set_enabled(msg_log_enabled); status = PJ_SUCCESS; on_return: return status; }
static int perform_test(inv_test_param_t *param) { pj_str_t uri; pjsip_dialog *dlg; pjmedia_sdp_session *sdp; pjsip_tx_data *tdata; pj_status_t status; PJ_LOG(3,(THIS_FILE, " %s", param->title)); pj_bzero(&inv_test, sizeof(inv_test)); pj_memcpy(&inv_test.param, param, sizeof(*param)); job_cnt = 0; uri = pj_str(CONTACT); /* * Create UAC */ status = pjsip_dlg_create_uac(pjsip_ua_instance(), &uri, &uri, &uri, &uri, &dlg); PJ_ASSERT_RETURN(status==PJ_SUCCESS, -10); if (inv_test.param.oa[0] == OFFERER_UAC) sdp = create_sdp(dlg->pool, oa_sdp[0].offer); else sdp = NULL; status = pjsip_inv_create_uac(dlg, sdp, inv_test.param.inv_option, &inv_test.uac); PJ_ASSERT_RETURN(status==PJ_SUCCESS, -20); TRACE_((THIS_FILE, " Sending INVITE %s offer", (sdp ? "with" : "without"))); /* * Make call! */ status = pjsip_inv_invite(inv_test.uac, &tdata); PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30); status = pjsip_inv_send_msg(inv_test.uac, tdata); PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30); /* * Wait until test completes */ while (!inv_test.complete) { pj_time_val delay = {0, 20}; pjsip_endpt_handle_events(endpt, &delay); while (job_cnt) { job_t j; j = jobs[0]; pj_array_erase(jobs, sizeof(jobs[0]), job_cnt, 0); --job_cnt; run_job(&j); } } flush_events(100); /* * Hangup */ TRACE_((THIS_FILE, " Disconnecting call")); status = pjsip_inv_end_session(inv_test.uas, PJSIP_SC_DECLINE, 0, &tdata); pj_assert(status == PJ_SUCCESS); status = pjsip_inv_send_msg(inv_test.uas, tdata); pj_assert(status == PJ_SUCCESS); flush_events(500); return 0; }
/* * 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; }
/* * main() */ int main(int argc, char *argv[]) { pj_status_t status; global.port = 5060; pj_log_set_level(4); status = init_options(argc, argv); if (status != PJ_SUCCESS) return 1; status = init_stack(); if (status != PJ_SUCCESS) { app_perror("Error initializing stack", status); return 1; } status = init_proxy(); if (status != PJ_SUCCESS) { app_perror("Error initializing proxy", status); return 1; } status = init_stateless_proxy(); if (status != PJ_SUCCESS) { app_perror("Error initializing stateless proxy", status); return 1; } #if PJ_HAS_THREADS status = pj_thread_create(global.pool, "sproxy", &worker_thread, NULL, 0, 0, &global.thread); if (status != PJ_SUCCESS) { app_perror("Error creating thread", status); return 1; } while (!global.quit_flag) { char line[10]; puts("\n" "Menu:\n" " q quit\n" " d dump status\n" " dd dump detailed status\n" ""); if (fgets(line, sizeof(line), stdin) == NULL) { puts("EOF while reading stdin, will quit now.."); global.quit_flag = PJ_TRUE; break; } if (line[0] == 'q') { global.quit_flag = PJ_TRUE; } else if (line[0] == 'd') { pj_bool_t detail = (line[1] == 'd'); pjsip_endpt_dump(global.endpt, detail); #if STATEFUL pjsip_tsx_layer_dump(detail); #endif } } pj_thread_join(global.thread); #else puts("\nPress Ctrl-C to quit\n"); for (;;) { pj_time_val delay = {0, 0}; pjsip_endpt_handle_events(global.endpt, &delay); } #endif destroy_stack(); return 0; }
/* * Perform server resolution where the results are expected to * come in strict order. */ static int test_resolve(const char *title, pj_pool_t *pool, pjsip_transport_type_e type, char *name, int port, pjsip_server_addresses *ref) { pjsip_host_info dest; struct result result; PJ_LOG(3,(THIS_FILE, " test_resolve(): %s", title)); dest.type = type; dest.flag = pjsip_transport_get_flag_from_type(type); dest.addr.host = pj_str(name); dest.addr.port = port; result.status = 0x12345678; pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb); while (result.status == 0x12345678) { int i = 0; pj_time_val timeout = { 1, 0 }; pjsip_endpt_handle_events(endpt, &timeout); if (i == 1) pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE); } if (result.status != PJ_SUCCESS) { app_perror(" pjsip_endpt_resolve() error", result.status); return result.status; } if (ref) { unsigned i; if (ref->count != result.servers.count) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 10: result count mismatch")); return 10; } for (i=0; i<ref->count; ++i) { pj_sockaddr_in *ra = (pj_sockaddr_in *)&ref->entry[i].addr; pj_sockaddr_in *rb = (pj_sockaddr_in *)&result.servers.entry[i].addr; if (ra->sin_addr.s_addr != rb->sin_addr.s_addr) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 20: IP address mismatch")); return 20; } if (ra->sin_port != rb->sin_port) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 30: port mismatch")); return 30; } if (ref->entry[i].addr_len != result.servers.entry[i].addr_len) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 40: addr_len mismatch")); return 40; } if (ref->entry[i].type != result.servers.entry[i].type) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 50: transport type mismatch")); return 50; } } } return PJ_SUCCESS; }
/* * The generic test framework, used by most of the tests. */ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, char *branch_param, int test_time, const pjsip_method *method) { pjsip_tx_data *tdata; pjsip_transaction *tsx; pj_str_t target, from, tsx_key; pjsip_via_hdr *via; pj_time_val timeout; pj_status_t status; PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", test_time)); /* Reset test. */ recv_count = 0; test_complete = 0; /* Init headers. */ target = pj_str(target_uri); from = pj_str(from_uri); /* Create request. */ status = pjsip_endpt_create_request( endpt, method, &target, &from, &target, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create request", status); return -100; } /* Set the branch param for test 1. */ via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param = pj_str(branch_param); /* Add additional reference to tdata to prevent transaction from * deleting it. */ pjsip_tx_data_add_ref(tdata); /* Create transaction. */ status = pjsip_tsx_create_uac( &tsx_user, tdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create UAC transaction", status); pjsip_tx_data_dec_ref(tdata); return -110; } /* Get transaction key. */ pj_strdup(tdata->pool, &tsx_key, &tsx->transaction_key); /* Send the message. */ status = pjsip_tsx_send_msg(tsx, NULL); // Ignore send result. Some tests do deliberately triggers error // when sending message. if (status != PJ_SUCCESS) { // app_perror(" Error: unable to send request", status); pjsip_tx_data_dec_ref(tdata); // return -120; } /* Set test completion time. */ pj_gettimeofday(&timeout); timeout.sec += test_time; /* Wait until test complete. */ while (!test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); pj_gettimeofday(&now); if (now.sec > timeout.sec) { PJ_LOG(3,(THIS_FILE, " Error: test has timed out")); pjsip_tx_data_dec_ref(tdata); return -130; } } if (test_complete < 0) { tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_mutex_unlock(tsx->mutex); flush_events(1000); } pjsip_tx_data_dec_ref(tdata); return test_complete; } else { pj_time_val now; /* Allow transaction to destroy itself */ flush_events(500); /* Wait until test completes */ pj_gettimeofday(&now); if (PJ_TIME_VAL_LT(now, timeout)) { pj_time_val interval; interval = timeout; PJ_TIME_VAL_SUB(interval, now); flush_events(PJ_TIME_VAL_MSEC(interval)); } } /* Make sure transaction has been destroyed. */ if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) { PJ_LOG(3,(THIS_FILE, " Error: transaction has not been destroyed")); pjsip_tx_data_dec_ref(tdata); return -140; } /* Check tdata reference counter. */ if (pj_atomic_get(tdata->ref_cnt) != 1) { PJ_LOG(3,(THIS_FILE, " Error: tdata reference counter is %d", pj_atomic_get(tdata->ref_cnt))); pjsip_tx_data_dec_ref(tdata); return -150; } /* Destroy txdata */ pjsip_tx_data_dec_ref(tdata); return PJ_SUCCESS; }
/* * main() * */ int main(int argc, char *argv[]) { pj_caching_pool cp; pj_pool_t *pool = NULL; pjsip_module mod_app = { NULL, NULL, /* prev, next. */ { "mod-app", 7 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ &on_rx_request, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */ }; int c; pj_status_t status; /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* 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: */ { /* Endpoint MUST be assigned a globally unique name. * Ideally we should put hostname or public IP address, but * we'll just use an arbitrary name here. */ /* Create the endpoint: */ status = pjsip_endpt_create(&cp.factory, "sipstateless", &sip_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* Parse arguments */ pj_optind = 0; pj_list_init(&hdr_list); while ((c=pj_getopt(argc, argv , "H:")) != -1) { switch (c) { case 'H': if (pool == NULL) { pool = pj_pool_create(&cp.factory, "sipstateless", 1000, 1000, NULL); } if (pool) { char *name; name = strtok(pj_optarg, ":"); if (name == NULL) { puts("Error: invalid header format"); return 1; } else { char *val = strtok(NULL, "\r\n"); pjsip_generic_string_hdr *h; pj_str_t hname, hvalue; hname = pj_str(name); hvalue = pj_str(val); h = pjsip_generic_string_hdr_create(pool, &hname, &hvalue); pj_list_push_back(&hdr_list, h); PJ_LOG(4,(THIS_FILE, "Header %s: %s added", name, val)); } } break; default: puts("Error: invalid argument"); usage(); return 1; } } if (pj_optind != argc) { code = atoi(argv[pj_optind]); if (code < 200 || code > 699) { puts("Error: invalid status code"); usage(); return 1; } } PJ_LOG(4,(THIS_FILE, "Returning %d to incoming requests", code)); /* * Add UDP transport, with hard-coded port */ #ifdef HAS_UDP_TRANSPORT { pj_sockaddr_in addr; addr.sin_family = pj_AF_INET(); addr.sin_addr.s_addr = 0; addr.sin_port = pj_htons(5060); status = pjsip_udp_transport_start( sip_endpt, &addr, NULL, 1, NULL); if (status != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, "Error starting UDP transport (port in use?)")); return 1; } } #endif #if HAS_TCP_TRANSPORT /* * Add UDP transport, with hard-coded port */ { pj_sockaddr_in addr; addr.sin_family = pj_AF_INET(); addr.sin_addr.s_addr = 0; addr.sin_port = pj_htons(5060); status = pjsip_tcp_transport_start(sip_endpt, &addr, 1, NULL); if (status != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, "Error starting TCP transport (port in use?)")); return 1; } } #endif /* * Register our module to receive incoming requests. */ status = pjsip_endpt_register_module( sip_endpt, &mod_app); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Done. Loop forever to handle incoming events. */ PJ_LOG(3,(THIS_FILE, "Press Ctrl-C to quit..")); for (;;) { pjsip_endpt_handle_events(sip_endpt, NULL); } }
/* * Perform round-robin/load balance test. */ static int round_robin_test(pj_pool_t *pool) { enum { COUNT = 400, PCT_ALLOWANCE = 5 }; unsigned i; struct server_hit { char *ip_addr; unsigned percent; unsigned hits; } server_hit[] = { { "1.1.1.1", 3, 0 }, { "2.2.2.2", 65, 0 }, { "3.3.3.3", 32, 0 }, { "4.4.4.4", 0, 0 } }; PJ_LOG(3,(THIS_FILE, " Performing round-robin/load-balance test..")); /* Do multiple resolve request to "example.com". * The resolver should select the server based on the weight proportion * the the servers in the SRV entry. */ for (i=0; i<COUNT; ++i) { pjsip_host_info dest; struct result result; unsigned j; dest.type = PJSIP_TRANSPORT_UDP; dest.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP); dest.addr.host = pj_str("example.com"); dest.addr.port = 0; result.status = 0x12345678; pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb); while (result.status == 0x12345678) { int i = 0; pj_time_val timeout = { 1, 0 }; pjsip_endpt_handle_events(endpt, &timeout); if (i == 1) pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE); } /* Find which server was "hit" */ for (j=0; j<PJ_ARRAY_SIZE(server_hit); ++j) { pj_str_t tmp; pj_in_addr a1; pj_sockaddr_in *a2; tmp = pj_str(server_hit[j].ip_addr); a1 = pj_inet_addr(&tmp); a2 = (pj_sockaddr_in*) &result.servers.entry[0].addr; if (a1.s_addr == a2->sin_addr.s_addr) { server_hit[j].hits++; break; } } if (j == PJ_ARRAY_SIZE(server_hit)) { PJ_LOG(1,(THIS_FILE, "..round_robin_test() error 10: returned address mismatch")); return 10; } } /* Print the actual hit rate */ for (i=0; i<PJ_ARRAY_SIZE(server_hit); ++i) { PJ_LOG(3,(THIS_FILE, " ..Server %s: weight=%d%%, hit %d%% times", server_hit[i].ip_addr, server_hit[i].percent, (server_hit[i].hits * 100) / COUNT)); } /* Compare the actual hit with the weight proportion */ for (i=0; i<PJ_ARRAY_SIZE(server_hit); ++i) { int actual_pct = (server_hit[i].hits * 100) / COUNT; if (actual_pct + PCT_ALLOWANCE < (int)server_hit[i].percent || actual_pct - PCT_ALLOWANCE > (int)server_hit[i].percent) { PJ_LOG(1,(THIS_FILE, "..round_robin_test() error 20: " "hit rate difference for server %s (%d%%) is more than " "tolerable allowance (%d%%)", server_hit[i].ip_addr, actual_pct - server_hit[i].percent, PCT_ALLOWANCE)); return 20; } } PJ_LOG(3,(THIS_FILE, " Load balance test success, hit-rate is " "within %d%% allowance", PCT_ALLOWANCE)); return PJ_SUCCESS; }
/***************************************************************************** ** ** TEST10_BRANCH_ID: test transport failure in TRYING state. ** TEST11_BRANCH_ID: test transport failure in PROCEEDING state. ** TEST12_BRANCH_ID: test transport failure in CONNECTED state. ** TEST13_BRANCH_ID: test transport failure in CONFIRMED state. ** ***************************************************************************** */ static int tsx_transport_failure_test(void) { struct test_desc { int transport_delay; int fail_delay; char *branch_id; char *title; } tests[] = { { 0, 10, TEST10_BRANCH_ID, "test10: failed transport in TRYING state (no delay)" }, { 50, 10, TEST10_BRANCH_ID, "test10: failed transport in TRYING state (50 ms delay)" }, { 0, 1500, TEST11_BRANCH_ID, "test11: failed transport in PROCEEDING state (no delay)" }, { 50, 1500, TEST11_BRANCH_ID, "test11: failed transport in PROCEEDING state (50 ms delay)" }, { 0, 2500, TEST12_BRANCH_ID, "test12: failed transport in COMPLETED state (no delay)" }, { 50, 2500, TEST12_BRANCH_ID, "test12: failed transport in COMPLETED state (50 ms delay)" }, }; int i, status; for (i=0; i<(int)PJ_ARRAY_SIZE(tests); ++i) { pj_time_val fail_time, end_test, now; PJ_LOG(3,(THIS_FILE, " %s", tests[i].title)); pjsip_loop_set_failure(loop, 0, NULL); pjsip_loop_set_delay(loop, tests[i].transport_delay); status = perform_test(TARGET_URI, FROM_URI, tests[i].branch_id, 0, &pjsip_invite_method, 1, 0, 1); if (status && status != TEST_TIMEOUT_ERROR) return status; if (!status) { PJ_LOG(3,(THIS_FILE, " error: expecting timeout")); return -40; } pj_gettimeofday(&fail_time); fail_time.msec += tests[i].fail_delay; pj_time_val_normalize(&fail_time); do { pj_time_val interval = { 0, 1 }; pj_gettimeofday(&now); pjsip_endpt_handle_events(endpt, &interval); } while (PJ_TIME_VAL_LT(now, fail_time)); pjsip_loop_set_failure(loop, 1, NULL); end_test = now; end_test.sec += 5; do { pj_time_val interval = { 0, 1 }; pj_gettimeofday(&now); pjsip_endpt_handle_events(endpt, &interval); } while (!test_complete && PJ_TIME_VAL_LT(now, end_test)); if (test_complete == 0) { PJ_LOG(3,(THIS_FILE, " error: test has timed out")); return -41; } if (test_complete != 1) return test_complete; } return 0; }
/* * The generic test framework, used by most of the tests. */ static int perform_test( char *target_uri, char *from_uri, char *branch_param, int test_time, const pjsip_method *method, int request_cnt, int request_interval_msec, int expecting_timeout) { pjsip_tx_data *tdata; pj_str_t target, from; pjsip_via_hdr *via; pj_time_val timeout, next_send; int sent_cnt; pj_status_t status; PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", test_time)); /* Reset test. */ recv_count = 0; test_complete = 0; tsx_key.slen = 0; /* Init headers. */ target = pj_str(target_uri); from = pj_str(from_uri); /* Create request. */ status = pjsip_endpt_create_request( endpt, method, &target, &from, &target, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create request", status); return -10; } /* Set the branch param for test 1. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param = pj_str(branch_param); /* Schedule first send. */ sent_cnt = 0; pj_gettimeofday(&next_send); pj_time_val_normalize(&next_send); /* Set test completion time. */ pj_gettimeofday(&timeout); timeout.sec += test_time; /* Wait until test complete. */ while (!test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); pj_gettimeofday(&now); if (sent_cnt < request_cnt && PJ_TIME_VAL_GTE(now, next_send)) { /* Add additional reference to tdata to prevent transaction from * deleting it. */ pjsip_tx_data_add_ref(tdata); /* (Re)Send the request. */ PJ_LOG(4,(THIS_FILE, " (re)sending request %d", sent_cnt)); status = pjsip_endpt_send_request_stateless(endpt, tdata, 0, 0); if (status != PJ_SUCCESS) { app_perror(" Error: unable to send request", status); pjsip_tx_data_dec_ref(tdata); return -20; } /* Schedule next send, if any. */ sent_cnt++; if (sent_cnt < request_cnt) { pj_gettimeofday(&next_send); next_send.msec += request_interval_msec; pj_time_val_normalize(&next_send); } } if (now.sec > timeout.sec) { if (!expecting_timeout) PJ_LOG(3,(THIS_FILE, " Error: test has timed out")); pjsip_tx_data_dec_ref(tdata); return TEST_TIMEOUT_ERROR; } } if (test_complete < 0) { pjsip_transaction *tsx; tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_mutex_unlock(tsx->mutex); flush_events(1000); } pjsip_tx_data_dec_ref(tdata); return test_complete; } /* Allow transaction to destroy itself */ flush_events(500); /* Make sure transaction has been destroyed. */ if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) { PJ_LOG(3,(THIS_FILE, " Error: transaction has not been destroyed")); pjsip_tx_data_dec_ref(tdata); return -40; } /* Check tdata reference counter. */ if (pj_atomic_get(tdata->ref_cnt) != 1) { PJ_LOG(3,(THIS_FILE, " Error: tdata reference counter is %d", pj_atomic_get(tdata->ref_cnt))); pjsip_tx_data_dec_ref(tdata); return -50; } /* Destroy txdata */ pjsip_tx_data_dec_ref(tdata); return PJ_SUCCESS; }