Example #1
0
/*
 * This callback is called by UDP listener on incoming packet. This is
 * the first entry for incoming packet (from client) to the server. From
 * here, the packet may be handed over to an allocation if an allocation
 * is found for the client address, or handed over to owned STUN session
 * if an allocation is not found.
 */
PJ_DEF(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv, 
				   pj_turn_pkt *pkt)
{
    pj_turn_allocation *alloc;

    /* Get TURN allocation from the source address */
    pj_lock_acquire(srv->core.lock);
    alloc = (pj_turn_allocation*)
	    pj_hash_get(srv->tables.alloc, &pkt->src, sizeof(pkt->src), NULL);
    pj_lock_release(srv->core.lock);

    /* If allocation is found, just hand over the packet to the
     * allocation.
     */
    if (alloc) {
	pj_turn_allocation_on_rx_client_pkt(alloc, pkt);
    } else {
	/* Otherwise this is a new client */
	unsigned options;
	pj_size_t parsed_len;
	pj_status_t status;

	/* Check that this is a STUN message */
	options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
	if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP)
	    options |= PJ_STUN_IS_DATAGRAM;

	status = pj_stun_msg_check(pkt->pkt, pkt->len, options);
	if (status != PJ_SUCCESS) {
	    /* If the first byte are not STUN, drop the packet. First byte
	     * of STUN message is always 0x00 or 0x01. Otherwise wait for
	     * more data as the data might have come from TCP.
	     *
	     * Also drop packet if it's unreasonably too big, as this might
	     * indicate invalid data that's building up in the buffer.
	     *
	     * Or if packet is a datagram.
	     */
	    if ((*pkt->pkt != 0x00 && *pkt->pkt != 0x01) ||
		pkt->len > 1600 ||
		(options & PJ_STUN_IS_DATAGRAM)) 
	    {
		char errmsg[PJ_ERR_MSG_SIZE];
		char ip[PJ_INET6_ADDRSTRLEN+10];

		pkt->len = 0;

		pj_strerror(status, errmsg, sizeof(errmsg));
		PJ_LOG(5,(srv->obj_name, 
			  "Non-STUN packet from %s is dropped: %s",
			  pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
			  errmsg));
	    }
	    return;
	}

	/* Special handling for Binding Request. We won't give it to the 
	 * STUN session since this request is not authenticated.
	 */
	if (pkt->pkt[1] == 1) {
	    handle_binding_request(pkt, options);
	    return;
	}

	/* Hand over processing to STUN session. This will trigger
	 * on_rx_stun_request() callback to be called if the STUN
	 * message is a request.
	 */
	options &= ~PJ_STUN_CHECK_PACKET;
	parsed_len = 0;
	status = pj_stun_session_on_rx_pkt(srv->core.stun_sess, pkt->pkt, 
					   pkt->len, options, pkt->transport,
					   &parsed_len, &pkt->src.clt_addr, 
					   pkt->src_addr_len);
	if (status != PJ_SUCCESS) {
	    char errmsg[PJ_ERR_MSG_SIZE];
	    char ip[PJ_INET6_ADDRSTRLEN+10];

	    pj_strerror(status, errmsg, sizeof(errmsg));
	    PJ_LOG(5,(srv->obj_name, 
		      "Error processing STUN packet from %s: %s",
		      pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
		      errmsg));
	}

	if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) {
	    pkt->len = 0;
	} else if (parsed_len > 0) {
	    if (parsed_len == pkt->len) {
		pkt->len = 0;
	    } else {
		pj_memmove(pkt->pkt, pkt->pkt+parsed_len,
			   pkt->len - parsed_len);
		pkt->len -= parsed_len;
	    }
	}
    }
}
Example #2
0
static void systest_aec_test(void)
{
    const char *ref_wav_paths[] = { add_path(res_path, WAV_PLAYBACK_PATH),
				    ALT_PATH1 WAV_PLAYBACK_PATH };
    pjsua_player_id player_id = PJSUA_INVALID_ID;
    pjsua_recorder_id writer_id = PJSUA_INVALID_ID;
    enum gui_key key;
    test_item_t *ti;
    const char *title = "AEC/AES Test";
    unsigned last_ec_tail = 0;
    pj_status_t status;
    pj_str_t tmp;

    ti = systest_alloc_test_item(title);
    if (!ti)
	return;

    key = gui_msgbox(title,
		     "This test will try to find whether the AEC/AES "
		     "works good on this system. Test will play a file "
		     "while recording from mic. The recording will be "
		     "played back later so you can check if echo is there. "
		     "Press OK to start.",
		     WITH_OKCANCEL);
    if (key != KEY_OK) {
	ti->skipped = PJ_TRUE;
	return;
    }

    /* Save current EC tail */
    status = pjsua_get_ec_tail(&last_ec_tail);
    if (status != PJ_SUCCESS)
	goto on_return;

    /* Set EC tail setting to default */
    status = pjsua_set_ec(PJSUA_DEFAULT_EC_TAIL_LEN, 0);
    if (status != PJ_SUCCESS)
	goto on_return;

    /*
     * Create player and recorder
     */
    status = create_player(PJ_ARRAY_SIZE(ref_wav_paths), ref_wav_paths, 
			   &player_id);
    if (status != PJ_SUCCESS) {
	PJ_PERROR(1,(THIS_FILE, status, "Error opening WAV file %s",
		     WAV_PLAYBACK_PATH));
	goto on_return;
    }

    status = pjsua_recorder_create(
                 pj_cstr(&tmp, add_path(doc_path, AEC_REC_PATH)), 0, 0, -1,
                 0, &writer_id);
    if (status != PJ_SUCCESS) {
	PJ_PERROR(1,(THIS_FILE, status, "Error writing WAV file %s",
		     AEC_REC_PATH));
	goto on_return;
    }

    /*
     * Start playback and recording.
     */
    pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0);
    pj_thread_sleep(100);
    pjsua_conf_connect(0, pjsua_recorder_get_conf_port(writer_id));

    /* Wait user signal */
    gui_msgbox(title, "AEC/AES test is running. Press OK to stop this test.",
	       WITH_OK);

    /*
     * Stop and close playback and recorder
     */
    pjsua_conf_disconnect(0, pjsua_recorder_get_conf_port(writer_id));
    pjsua_conf_disconnect(pjsua_player_get_conf_port(player_id), 0);
    pjsua_recorder_destroy(writer_id);
    pjsua_player_destroy(player_id);
    player_id = PJSUA_INVALID_ID;
    writer_id = PJSUA_INVALID_ID;

    /*
     * Play the result.
     */
    status = pjsua_player_create(
                 pj_cstr(&tmp, add_path(doc_path, AEC_REC_PATH)),
                 0, &player_id);
    if (status != PJ_SUCCESS) {
	PJ_PERROR(1,(THIS_FILE, status, "Error opening WAV file %s", AEC_REC_PATH));
	goto on_return;
    }
    pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0);

    /* Wait user signal */
    gui_msgbox(title, "We are now playing the captured audio from the mic. "
		      "Check if echo (of the audio played back previously) is "
		      "present in the audio. The recording is stored in " 
		      AEC_REC_PATH " for offline analysis. "
		      "Press OK to stop.",
		      WITH_OK);

    pjsua_conf_disconnect(pjsua_player_get_conf_port(player_id), 0);

    key = gui_msgbox(title,
		     "Did you notice any echo in the recording?",
		     WITH_YESNO);


on_return:
    if (player_id != PJSUA_INVALID_ID)
	pjsua_player_destroy(player_id);
    if (writer_id != PJSUA_INVALID_ID)
	pjsua_recorder_destroy(writer_id);

    /* Wait until sound device closed before restoring back EC tail setting */
    while (pjsua_snd_is_active())
	pj_thread_sleep(10);
    pjsua_set_ec(last_ec_tail, 0);


    if (status != PJ_SUCCESS) {
	systest_perror("Sorry we encountered an error: ", status);
	ti->success = PJ_FALSE;
	pj_strerror(status, ti->reason, sizeof(ti->reason));
    } else if (key == KEY_YES) {
	ti->success = PJ_FALSE;
	if (!ti->success) {
	    pj_ansi_strcpy(ti->reason, USER_ERROR);
	}
    } else {
	char msg[200];

	pj_ansi_snprintf(msg, sizeof(msg), "Test succeeded.\r\n");

	ti->success = PJ_TRUE;
	pj_ansi_strncpy(ti->reason, msg, sizeof(ti->reason));
	ti->reason[sizeof(ti->reason)-1] = '\0';
    }
}
Example #3
0
//Get error message
PJ_DECL(pj_str_t) get_error_message(int status) {
    return pj_strerror(status, errmsg, sizeof(errmsg));
}
Example #4
0
/****************************************************************************
 * test: audio system test
 */
static void systest_audio_test(void)
{
    enum {
	GOOD_MAX_INTERVAL = 5,
    };
    const pjmedia_dir dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
    pjmedia_aud_param param;
    pjmedia_aud_test_results result;
    int textbufpos;
    enum gui_key key;
    unsigned problem_count = 0;
    const char *problems[16];
    char drifttext[120];
    test_item_t *ti;
    const char *title = "Audio Device Test";
    pj_status_t status;

    ti = systest_alloc_test_item(title);
    if (!ti)
	return;

    key = gui_msgbox(title,
		     "This will run an automated test for about "
		     "ten seconds or so, and display some "
		     "statistics about your sound device. "
		     "Please don't do anything until the test completes. "
		     "Press OK to start, or CANCEL to skip this test.",
		     WITH_OKCANCEL);
    if (key != KEY_OK) {
	ti->skipped = PJ_TRUE;
	return;
    }

    PJ_LOG(3,(THIS_FILE, "Running %s", title));

    /* Disable sound device in pjsua first */
    pjsua_set_no_snd_dev();

    /* Setup parameters */
    status = pjmedia_aud_dev_default_param(systest.play_id, &param);
    if (status != PJ_SUCCESS) {
	systest_perror("Sorry we had error in pjmedia_aud_dev_default_param()", status);
	pjsua_set_snd_dev(systest.rec_id, systest.play_id);
	ti->success = PJ_FALSE;
	pj_strerror(status, ti->reason, sizeof(ti->reason));
	ti->reason[sizeof(ti->reason)-1] = '\0';
	return;
    }

    param.dir = dir;
    param.rec_id = systest.rec_id;
    param.play_id = systest.play_id;
    param.clock_rate = systest.media_cfg.snd_clock_rate;
    param.channel_count = systest.media_cfg.channel_count;
    param.samples_per_frame = param.clock_rate * param.channel_count * 
			      systest.media_cfg.audio_frame_ptime / 1000;

    /* Latency settings */
    param.flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | 
		    PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY);
    param.input_latency_ms = systest.media_cfg.snd_rec_latency;
    param.output_latency_ms = systest.media_cfg.snd_play_latency;

    /* Run the test */
    status = pjmedia_aud_test(&param, &result);
    if (status != PJ_SUCCESS) {
	systest_perror("Sorry we encountered error with the test", status);
	pjsua_set_snd_dev(systest.rec_id, systest.play_id);
	ti->success = PJ_FALSE;
	pj_strerror(status, ti->reason, sizeof(ti->reason));
	ti->reason[sizeof(ti->reason)-1] = '\0';
	return;
    }

    /* Restore pjsua sound device */
    pjsua_set_snd_dev(systest.rec_id, systest.play_id);

    /* Analyze the result! */
    strcpy(textbuf, "Here are the audio statistics:\r\n");
    textbufpos = strlen(textbuf);

    if (result.rec.frame_cnt==0) {
	problems[problem_count++] = 
	    "No audio frames were captured from the microphone. "
	    "This means the audio device is not working properly.";
    } else {
	pj_ansi_snprintf(textbuf+textbufpos, 
			 sizeof(textbuf)-textbufpos,
			 "Rec : interval (min/max/avg/dev)=\r\n"
			 "         %u/%u/%u/%u (ms)\r\n"
			 "      max burst=%u\r\n",
			 result.rec.min_interval,
			 result.rec.max_interval,
			 result.rec.avg_interval,
			 result.rec.dev_interval,
			 result.rec.max_burst);
	textbufpos = strlen(textbuf);

	if (result.rec.max_burst > GOOD_MAX_INTERVAL) {
	    problems[problem_count++] = 
		"Recording max burst is quite high";
	}
    }

    if (result.play.frame_cnt==0) {
	problems[problem_count++] = 
	    "No audio frames were played to the speaker. "
	    "This means the audio device is not working properly.";
    } else {
	pj_ansi_snprintf(textbuf+textbufpos, 
			 sizeof(textbuf)-textbufpos,
			 "Play: interval (min/max/avg/dev)=\r\n"
			 "         %u/%u/%u/%u (ms)\r\n"
			 "      burst=%u\r\n",
			 result.play.min_interval,
			 result.play.max_interval,
			 result.play.avg_interval,
			 result.play.dev_interval,
			 result.play.max_burst);
	textbufpos = strlen(textbuf);

	if (result.play.max_burst > GOOD_MAX_INTERVAL) {
	    problems[problem_count++] = 
		"Playback max burst is quite high";
	}
    }

    if (result.rec_drift_per_sec) {
	const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower";
	unsigned drift = result.rec_drift_per_sec>=0 ? 
			    result.rec_drift_per_sec :
			    -result.rec_drift_per_sec;

	pj_ansi_snprintf(drifttext, sizeof(drifttext),
			"Clock drifts detected. Capture "
			"is %d samples/sec %s "
			"than the playback device",
			drift, which);
	problems[problem_count++] = drifttext;
    }

    if (problem_count == 0) {
	pj_ansi_snprintf(textbuf+textbufpos, 
			 sizeof(textbuf)-textbufpos,
			 "\r\nThe sound device seems to be okay!");
	textbufpos = strlen(textbuf);

	key = gui_msgbox("Audio Device Test", textbuf, WITH_OK);
    } else {
	unsigned i;

	pj_ansi_snprintf(textbuf+textbufpos,
			 sizeof(textbuf)-textbufpos, 
			 "There could be %d problem(s) with the "
			 "sound device:\r\n",
			 problem_count);
	textbufpos = strlen(textbuf);

	for (i=0; i<problem_count; ++i) {
	    pj_ansi_snprintf(textbuf+textbufpos,
			     sizeof(textbuf)-textbufpos, 
			     " %d: %s\r\n", i+1, problems[i]);
	    textbufpos = strlen(textbuf);
	}

	key = gui_msgbox(title, textbuf, WITH_OK);
    }

    ti->success = PJ_TRUE;
    pj_ansi_strncpy(ti->reason, textbuf, sizeof(ti->reason));
    ti->reason[sizeof(ti->reason)-1] = '\0';
}
Example #5
0
static void systest_latency_test(void)
{
    const char *ref_wav_paths[] = { add_path(res_path, WAV_TOCK8_PATH), ALT_PATH1 WAV_TOCK8_PATH };
    pj_str_t rec_wav_file;
    pjsua_player_id play_id = PJSUA_INVALID_ID;
    pjsua_conf_port_id play_slot = PJSUA_INVALID_ID;
    pjsua_recorder_id rec_id = PJSUA_INVALID_ID;
    pjsua_conf_port_id rec_slot = PJSUA_INVALID_ID;
    pj_pool_t *pool = NULL;
    pjmedia_port *wav_port = NULL;
    unsigned lat_sum=0, lat_cnt=0, lat_min=0, lat_max=0;
    enum gui_key key;
    test_item_t *ti;
    const char *title = "Audio Latency Test";
    pj_status_t status;

    ti = systest_alloc_test_item(title);
    if (!ti)
	return;

    key = gui_msgbox(title,
		     "This test will try to find the audio device's "
		     "latency. We will play a special WAV file to the "
		     "speaker for ten seconds, then at the end "
		     "calculate the latency. Please don't do anything "
		     "until the test is done.", WITH_OKCANCEL);
    if (key != KEY_OK) {
	ti->skipped = PJ_TRUE;
	return;
    }
    key = gui_msgbox(title, 
		     "For this test to work, we must be able to capture "
		     "the audio played in the speaker (the echo), and only"
		     " that audio (i.e. you must be in relatively quiet "
		     "place to run this test). "
		     "Press OK to start, or CANCEL to skip.",
		     WITH_OKCANCEL);
    if (key != KEY_OK) {
	ti->skipped = PJ_TRUE;
	return;
    }

    PJ_LOG(3,(THIS_FILE, "Running %s", title));

    status = create_player(PJ_ARRAY_SIZE(ref_wav_paths), ref_wav_paths, 
			   &play_id);
    if (status != PJ_SUCCESS)
	goto on_return;

    play_slot = pjsua_player_get_conf_port(play_id);

    rec_wav_file = pj_str(add_path(doc_path, WAV_LATENCY_OUT_PATH));
    status = pjsua_recorder_create(&rec_wav_file, 0, NULL, -1, 0, &rec_id);
    if (status != PJ_SUCCESS)
	goto on_return;

    rec_slot = pjsua_recorder_get_conf_port(rec_id);

    /* Setup the test */
    //status = pjsua_conf_connect(0, 0);
    status = pjsua_conf_connect(play_slot, 0);
    status = pjsua_conf_connect(0, rec_slot);
    status = pjsua_conf_connect(play_slot, rec_slot);
    

    /* We're running */
    PJ_LOG(3,(THIS_FILE, "Please wait while test is running (~10 sec)"));
    gui_sleep(10);

    /* Done with the test */
    //status = pjsua_conf_disconnect(0, 0);
    status = pjsua_conf_disconnect(play_slot, rec_slot);
    status = pjsua_conf_disconnect(0, rec_slot);
    status = pjsua_conf_disconnect(play_slot, 0);

    pjsua_recorder_destroy(rec_id);
    rec_id = PJSUA_INVALID_ID;

    pjsua_player_destroy(play_id);
    play_id = PJSUA_INVALID_ID;

    /* Confirm that echo is heard */
    gui_msgbox(title,
	       "Test is done. Now we need to confirm that we indeed "
	       "captured the echo. We will play the captured audio "
	       "and please confirm that you can hear the 'tock' echo.",
	       WITH_OK);

    status = pjsua_player_create(&rec_wav_file, 0, &play_id);
    if (status != PJ_SUCCESS)
	goto on_return;

    play_slot = pjsua_player_get_conf_port(play_id);

    status = pjsua_conf_connect(play_slot, 0);
    if (status != PJ_SUCCESS)
	goto on_return;

    key = gui_msgbox(title,
		     "The captured audio is being played back now. "
		     "Can you hear the 'tock' echo?",
		     WITH_YESNO);

    pjsua_player_destroy(play_id);
    play_id = PJSUA_INVALID_ID;

    if (key != KEY_YES)
	goto on_return;

    /* Now analyze the latency */
    pool = pjsua_pool_create("latency", 512, 512);

    status = pjmedia_wav_player_port_create(pool, rec_wav_file.ptr, 0, 0, 0, &wav_port);
    if (status != PJ_SUCCESS)
	goto on_return;

    status = calculate_latency(pool, wav_port, &lat_sum, &lat_cnt, 
			       &lat_min, &lat_max);
    if (status != PJ_SUCCESS)
	goto on_return;

on_return:
    if (wav_port)
	pjmedia_port_destroy(wav_port);
    if (pool)
	pj_pool_release(pool);
    if (play_id != PJSUA_INVALID_ID)
	pjsua_player_destroy(play_id);
    if (rec_id != PJSUA_INVALID_ID)
	pjsua_recorder_destroy(rec_id);

    if (status != PJ_SUCCESS) {
	systest_perror("Sorry we encountered an error: ", status);
	ti->success = PJ_FALSE;
	pj_strerror(status, ti->reason, sizeof(ti->reason));
    } else if (key != KEY_YES) {
	ti->success = PJ_FALSE;
	if (!ti->success) {
	    pj_ansi_strcpy(ti->reason, USER_ERROR);
	}
    } else {
	char msg[200];
	int msglen;

	pj_ansi_snprintf(msg, sizeof(msg),
			 "The sound device latency:\r\n"
			 " Min=%u, Max=%u, Avg=%u\r\n",
			 lat_min, lat_max, lat_sum/lat_cnt);
	msglen = strlen(msg);

	if (lat_sum/lat_cnt > 500) {
	    pj_ansi_snprintf(msg+msglen, sizeof(msg)-msglen,
			     "The latency is huge!\r\n");
	    msglen = strlen(msg);
	} else if (lat_sum/lat_cnt > 200) {
	    pj_ansi_snprintf(msg+msglen, sizeof(msg)-msglen,
			     "The latency is quite high\r\n");
	    msglen = strlen(msg);
	}
	
	key = gui_msgbox(title, msg, WITH_OK);

	ti->success = PJ_TRUE;
	pj_ansi_strncpy(ti->reason, msg, sizeof(ti->reason));
	ti->reason[sizeof(ti->reason)-1] = '\0';
    }
}
Example #6
0
/*
 * This is the main function for performing server resolution.
 */
PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
			    pj_pool_t *pool,
			    const pjsip_host_info *target,
			    void *token,
			    pjsip_resolver_callback *cb)
{
    pjsip_server_addresses svr_addr;
    pj_status_t status = PJ_SUCCESS;
    int ip_addr_ver;
    struct query *query;
    pjsip_transport_type_e type = target->type;

    /* Is it IP address or hostname? And if it's an IP, which version? */
    ip_addr_ver = get_ip_addr_ver(&target->addr.host);

    /* Set the transport type if not explicitly specified. 
     * RFC 3263 section 4.1 specify rules to set up this.
     */
    if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
	if (ip_addr_ver || (target->addr.port != 0)) {
#if PJ_HAS_TCP
	    if (target->flag & PJSIP_TRANSPORT_SECURE) 
	    {
		type = PJSIP_TRANSPORT_TLS;
	    } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) 
	    {
		type = PJSIP_TRANSPORT_TCP;
	    } else 
#endif
	    {
		type = PJSIP_TRANSPORT_UDP;
	    }
	} else {
	    /* No type or explicit port is specified, and the address is
	     * not IP address.
	     * In this case, full NAPTR resolution must be performed.
	     * But we don't support it (yet).
	     */
#if PJ_HAS_TCP
	    if (target->flag & PJSIP_TRANSPORT_SECURE) 
	    {
		type = PJSIP_TRANSPORT_TLS;
	    } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) 
	    {
		type = PJSIP_TRANSPORT_TCP;
	    } else 
#endif
	    {
		type = PJSIP_TRANSPORT_UDP;
	    }
	}

	/* Add IPv6 flag for IPv6 address */
	if (ip_addr_ver == 6)
	    type = (pjsip_transport_type_e)((int)type + PJSIP_TRANSPORT_IPV6);
    }


    /* If target is an IP address, or if resolver is not configured, 
     * we can just finish the resolution now using pj_gethostbyname()
     */
    if (ip_addr_ver || resolver->res == NULL) {
	char addr_str[PJ_INET6_ADDRSTRLEN+10];
	pj_uint16_t srv_port;

	if (ip_addr_ver != 0) {
	    /* Target is an IP address, no need to resolve */
	    if (ip_addr_ver == 4) {
		pj_sockaddr_init(pj_AF_INET(), &svr_addr.entry[0].addr, 
				 NULL, 0);
		pj_inet_aton(&target->addr.host,
			     &svr_addr.entry[0].addr.ipv4.sin_addr);
	    } else {
		pj_sockaddr_init(pj_AF_INET6(), &svr_addr.entry[0].addr, 
				 NULL, 0);
		pj_inet_pton(pj_AF_INET6(), &target->addr.host,
			     &svr_addr.entry[0].addr.ipv6.sin6_addr);
	    }
	} else {
	    pj_addrinfo ai;
	    unsigned count;
	    int af;

	    PJ_LOG(5,(THIS_FILE,
		      "DNS resolver not available, target '%.*s:%d' type=%s "
		      "will be resolved with getaddrinfo()",
		      target->addr.host.slen,
		      target->addr.host.ptr,
		      target->addr.port,
		      pjsip_transport_get_type_name(target->type)));

	    if (type & PJSIP_TRANSPORT_IPV6) {
		af = pj_AF_INET6();
	    } else {
		af = pj_AF_INET();
	    }

	    /* Resolve */
	    count = 1;
	    status = pj_getaddrinfo(af, &target->addr.host, &count, &ai);
	    if (status != PJ_SUCCESS) {
		/* "Normalize" error to PJ_ERESOLVE. This is a special error
		 * because it will be translated to SIP status 502 by
		 * sip_transaction.c
		 */
		status = PJ_ERESOLVE;
		goto on_error;
	    }

	    svr_addr.entry[0].addr.addr.sa_family = (pj_uint16_t)af;
	    pj_memcpy(&svr_addr.entry[0].addr, &ai.ai_addr,
		      sizeof(pj_sockaddr));
	}

	/* Set the port number */
	if (target->addr.port == 0) {
	   srv_port = (pj_uint16_t)
		      pjsip_transport_get_default_port_for_type(type);
	} else {
	   srv_port = (pj_uint16_t)target->addr.port;
	}
	pj_sockaddr_set_port(&svr_addr.entry[0].addr, srv_port);

	/* Call the callback. */
	PJ_LOG(5,(THIS_FILE, 
		  "Target '%.*s:%d' type=%s resolved to "
		  "'%s' type=%s (%s)",
		  (int)target->addr.host.slen,
		  target->addr.host.ptr,
		  target->addr.port,
		  pjsip_transport_get_type_name(target->type),
		  pj_sockaddr_print(&svr_addr.entry[0].addr, addr_str,
				    sizeof(addr_str), 3),
		  pjsip_transport_get_type_name(type),
		  pjsip_transport_get_type_desc(type)));
	svr_addr.count = 1;
	svr_addr.entry[0].priority = 0;
	svr_addr.entry[0].weight = 0;
	svr_addr.entry[0].type = type;
    	svr_addr.entry[0].addr_len = pj_sockaddr_get_len(&svr_addr.entry[0].addr);
	(*cb)(status, token, &svr_addr, target->addr.port);

	/* Done. */
	return;
    }

    /* Target is not an IP address so we need to resolve it. */
#if PJSIP_HAS_RESOLVER

    /* Build the query state */
    query = PJ_POOL_ZALLOC_T(pool, struct query);
    query->objname = THIS_FILE;
    query->token = token;
    query->cb = cb;
    query->req.target = *target;
    pj_strdup(pool, &query->req.target.addr.host, &target->addr.host);

    /* If port is not specified, start with SRV resolution
     * (should be with NAPTR, but we'll do that later)
     */
    PJ_TODO(SUPPORT_DNS_NAPTR);

    /* Build dummy NAPTR entry */
    query->naptr_cnt = 1;
    pj_bzero(&query->naptr[0], sizeof(query->naptr[0]));
    query->naptr[0].order = 0;
    query->naptr[0].pref = 0;
    query->naptr[0].type = type;
    pj_strdup(pool, &query->naptr[0].name, &target->addr.host);


    /* Start DNS SRV or A resolution, depending on whether port is specified */
    if (target->addr.port == 0) {
	query->query_type = PJ_DNS_TYPE_SRV;

	query->req.def_port = 5060;

	if (type == PJSIP_TRANSPORT_TLS) {
	    query->naptr[0].res_type = pj_str("_sips._tcp.");
	    query->req.def_port = 5061;
	} else if (type == PJSIP_TRANSPORT_TCP)
	    query->naptr[0].res_type = pj_str("_sip._tcp.");
	else if (type == PJSIP_TRANSPORT_UDP)
	    query->naptr[0].res_type = pj_str("_sip._udp.");
	else {
	    pj_assert(!"Unknown transport type");
	    query->naptr[0].res_type = pj_str("_sip._udp.");
	    
	}

    } else {
	/* Otherwise if port is specified, start with A (or AAAA) host 
	 * resolution 
	 */
	query->query_type = PJ_DNS_TYPE_A;
	query->naptr[0].res_type.slen = 0;
	query->req.def_port = target->addr.port;
    }

    /* Start the asynchronous query */
    PJ_LOG(5, (query->objname, 
	       "Starting async DNS %s query: target=%.*s%.*s, transport=%s, "
	       "port=%d",
	       pj_dns_get_type_name(query->query_type),
	       (int)query->naptr[0].res_type.slen,
	       query->naptr[0].res_type.ptr,
	       (int)query->naptr[0].name.slen, query->naptr[0].name.ptr,
	       pjsip_transport_get_type_name(target->type),
	       target->addr.port));

    if (query->query_type == PJ_DNS_TYPE_SRV) {

	status = pj_dns_srv_resolve(&query->naptr[0].name,
				    &query->naptr[0].res_type,
				    query->req.def_port, pool, resolver->res,
				    PJ_TRUE, query, &srv_resolver_cb, NULL);

    } else if (query->query_type == PJ_DNS_TYPE_A) {

	status = pj_dns_resolver_start_query(resolver->res, 
					     &query->naptr[0].name,
					     PJ_DNS_TYPE_A, 0, 
					     &dns_a_callback,
    					     query, &query->object);

    } else {
	pj_assert(!"Unexpected");
	status = PJ_EBUG;
    }

    if (status != PJ_SUCCESS)
	goto on_error;

    return;

#else /* PJSIP_HAS_RESOLVER */
    PJ_UNUSED_ARG(pool);
    PJ_UNUSED_ARG(query);
    PJ_UNUSED_ARG(srv_name);
#endif /* PJSIP_HAS_RESOLVER */

on_error:
    if (status != PJ_SUCCESS) {
	char errmsg[PJ_ERR_MSG_SIZE];
	PJ_LOG(4,(THIS_FILE, "Failed to resolve '%.*s'. Err=%d (%s)",
			     (int)target->addr.host.slen,
			     target->addr.host.ptr,
			     status,
			     pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
	(*cb)(status, token, NULL,  target->addr.port);
	return;
    }
}
Example #7
0
static void read_rtp(pj_uint8_t *buf, pj_size_t bufsize,
		     pjmedia_rtp_hdr **rtp,
		     pj_uint8_t **payload,
		     unsigned *payload_size,
		     pj_bool_t check_pt)
{
    pj_status_t status;

    /* Init RTP session */
    if (!app.rtp_sess_init) {
	T(pjmedia_rtp_session_init(&app.rtp_sess, 0, 0));
	app.rtp_sess_init = PJ_TRUE;
    }

    /* Loop reading until we have a good RTP packet */
    for (;;) {
	pj_size_t sz = bufsize;
	const pjmedia_rtp_hdr *r;
	const void *p;
	pjmedia_rtp_status seq_st;

	status = pj_pcap_read_udp(app.pcap, NULL, buf, &sz);
	if (status != PJ_SUCCESS)
	    err_exit("Error reading PCAP file", status);

	/* Decode RTP packet to make sure that this is an RTP packet.
	 * We will decode it again to get the payload after we do
	 * SRTP decoding
	 */
	status = pjmedia_rtp_decode_rtp(&app.rtp_sess, buf, (int)sz, &r, 
					&p, payload_size);
	if (status != PJ_SUCCESS) {
	    char errmsg[PJ_ERR_MSG_SIZE];
	    pj_strerror(status, errmsg, sizeof(errmsg));
	    printf("Not RTP packet, skipping packet: %s\n", errmsg);
	    continue;
	}

	/* Decrypt SRTP */
#if PJMEDIA_HAS_SRTP
	if (app.srtp) {
	    int len = (int)sz;
	    status = pjmedia_transport_srtp_decrypt_pkt(app.srtp, PJ_TRUE, 
						        buf, &len);
	    if (status != PJ_SUCCESS) {
		char errmsg[PJ_ERR_MSG_SIZE];
		pj_strerror(status, errmsg, sizeof(errmsg));
		printf("SRTP packet decryption failed, skipping packet: %s\n", 
			errmsg);
		continue;
	    }
	    sz = len;

	    /* Decode RTP packet again */
	    status = pjmedia_rtp_decode_rtp(&app.rtp_sess, buf, (int)sz, &r,
					    &p, payload_size);
	    if (status != PJ_SUCCESS) {
		char errmsg[PJ_ERR_MSG_SIZE];
		pj_strerror(status, errmsg, sizeof(errmsg));
		printf("Not RTP packet, skipping packet: %s\n", errmsg);
		continue;
	    }
	}
#endif

	/* Update RTP session */
	pjmedia_rtp_session_update2(&app.rtp_sess, r, &seq_st, PJ_FALSE);

	/* Skip out-of-order packet */
	if (seq_st.diff == 0) {
	    printf("Skipping out of order packet\n");
	    continue;
	}

	/* Skip if payload type is different */
	if (check_pt && r->pt != app.pt) {
	    printf("Skipping RTP packet with bad payload type\n");
	    continue;
	}

	/* Skip bad packet */
	if (seq_st.status.flag.bad) {
	    printf("Skipping bad RTP\n");
	    continue;
	}


	*rtp = (pjmedia_rtp_hdr*)r;
	*payload = (pj_uint8_t*)p;

	/* We have good packet */
	break;
    }
}
Example #8
0
/* perror util */
static void app_perror(const char *title, pj_status_t status)
{
    char errmsg[PJ_ERR_MSG_SIZE];
    pj_strerror(status, errmsg, sizeof(errmsg));
    PJ_LOG(1,(THIS_FILE, "Error: %s: %s", title, errmsg));
}
Example #9
0
static void systest_display_settings(void)
{
    pjmedia_aud_dev_info di;
    int len = 0;
    enum gui_key key;
    test_item_t *ti;
    const char *title = "Audio Settings";
    pj_status_t status;

    ti = systest_alloc_test_item(title);
    if (!ti)
	return;

    PJ_LOG(3,(THIS_FILE, "Running %s", title));

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Version: %s\r\n",
		     pj_get_version());
    len = strlen(textbuf);

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Test clock rate: %d\r\n",
		     systest.media_cfg.clock_rate);
    len = strlen(textbuf);

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Device clock rate: %d\r\n",
		     systest.media_cfg.snd_clock_rate);
    len = strlen(textbuf);

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Aud frame ptime: %d\r\n",
		     systest.media_cfg.audio_frame_ptime);
    len = strlen(textbuf);

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Channel count: %d\r\n",
		     systest.media_cfg.channel_count);
    len = strlen(textbuf);

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Audio switching: %s\r\n",
	    (PJMEDIA_CONF_USE_SWITCH_BOARD ? "Switchboard" : "Conf bridge"));
    len = strlen(textbuf);

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Snd buff count: %d\r\n",
		     PJMEDIA_SOUND_BUFFER_COUNT);
    len = strlen(textbuf);

    /* Capture device */
    status = pjmedia_aud_dev_get_info(systest.rec_id, &di);
    if (status != PJ_SUCCESS) {
	systest_perror("Error querying device info", status);
	ti->success = PJ_FALSE;
	pj_strerror(status, ti->reason, sizeof(ti->reason));
	return;
    }

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len,
		     "Rec dev : %d (%s) [%s]\r\n",
		     systest.rec_id,
		     di.name,
		     di.driver);
    len = strlen(textbuf);

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len,
		     "Rec  buf : %d msec\r\n",
		     systest.media_cfg.snd_rec_latency);
    len = strlen(textbuf);

    /* Playback device */
    status = pjmedia_aud_dev_get_info(systest.play_id, &di);
    if (status != PJ_SUCCESS) {
	systest_perror("Error querying device info", status);
	return;
    }

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len,
		     "Play dev: %d (%s) [%s]\r\n",
		     systest.play_id,
		     di.name,
		     di.driver);
    len = strlen(textbuf);

    pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len,
		     "Play buf: %d msec\r\n",
		     systest.media_cfg.snd_play_latency);
    len = strlen(textbuf);

    ti->success = PJ_TRUE;
    pj_ansi_strncpy(ti->reason, textbuf, sizeof(ti->reason));
    ti->reason[sizeof(ti->reason)-1] = '\0';
    key = gui_msgbox(title, textbuf, WITH_OK);

}
Example #10
0
static int fingerprint_test_vector()
{
    pj_pool_t *pool;
    pj_status_t status;
    unsigned i;
    int rc = 0;

    /* To avoid function not referenced warnings */
    (void)create_msgint2;
    (void)create_msgint3;

    PJ_LOG(3,(THIS_FILE, "  draft-denis-behave-rfc3489bis-test-vectors-02"));

    pool = pj_pool_create(mem, "fingerprint", 1024, 1024, NULL);

    for (i=0; i<PJ_ARRAY_SIZE(test_vectors); ++i) {
	struct test_vector *v;
	pj_stun_msg *ref_msg, *msg;
	pj_size_t parsed_len;
	pj_size_t len;
	unsigned pos;
	pj_uint8_t buf[1500];
	char print[1500];
	pj_str_t key;

	PJ_LOG(3,(THIS_FILE, "    Running test %d/%d", i, 
	          PJ_ARRAY_SIZE(test_vectors)));

	v = &test_vectors[i];

	/* Print reference message */
	PJ_LOG(4,(THIS_FILE, "Reference message PDU:\n%s",
	          print_binary((pj_uint8_t*)v->pdu, v->pdu_len)));

	/* Try to parse the reference message first */
	status = pj_stun_msg_decode(pool, (pj_uint8_t*)v->pdu, v->pdu_len,
				    PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, 
				    &ref_msg, &parsed_len, NULL);
	if (status != PJ_SUCCESS) {
	    PJ_LOG(1,(THIS_FILE, "    Error decoding reference message"));
	    rc = -1010;
	    goto on_return;
	}

	if (parsed_len != v->pdu_len) {
	    PJ_LOG(1,(THIS_FILE, "    Parsed len error"));
	    rc = -1020;
	    goto on_return;
	}

	/* Print the reference message */
	pj_stun_msg_dump(ref_msg, print, sizeof(print), NULL);
	PJ_LOG(4,(THIS_FILE, "Reference message:\n%s", print));

	/* Create our message */
	msg = v->create(pool, v);
	if (msg == NULL) {
	    PJ_LOG(1,(THIS_FILE, "    Error creating stun message"));
	    rc = -1030;
	    goto on_return;
	}

	/* Encode message */
	if (v->options & USE_MESSAGE_INTEGRITY) {
	    pj_str_t s1, s2, r;

	    pj_stun_create_key(pool, &key, pj_cstr(&r, v->realm), 
			       pj_cstr(&s1, v->username), 
			       PJ_STUN_PASSWD_PLAIN, 
			       pj_cstr(&s2, v->password));
	    pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len);

	} else {
	    pj_stun_msg_encode(msg, buf, sizeof(buf), 0, NULL, &len);
	}

	/* Print our raw message */
	PJ_LOG(4,(THIS_FILE, "Message PDU:\n%s",
	          print_binary((pj_uint8_t*)buf, (unsigned)len)));

	/* Print our message */
	pj_stun_msg_dump(msg, print, sizeof(print), NULL);
	PJ_LOG(4,(THIS_FILE, "Message is:\n%s", print));

	/* Compare message length */
	if (len != v->pdu_len) {
	    PJ_LOG(1,(THIS_FILE, "    Message length mismatch"));
	    rc = -1050;
	    goto on_return;
	}

	pos = cmp_buf(buf, (const pj_uint8_t*)v->pdu, (unsigned)len);
	if (pos != (unsigned)-1) {
	    PJ_LOG(1,(THIS_FILE, "    Message mismatch at byte %d", pos));
	    rc = -1060;
	    goto on_return;
	}

	/* Authenticate the request/response */
	if (v->options & USE_MESSAGE_INTEGRITY) {
	    if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
		pj_stun_auth_cred cred;
		pj_status_t status2;

		pj_bzero(&cred, sizeof(cred));
		cred.type = PJ_STUN_AUTH_CRED_STATIC;
		cred.data.static_cred.realm = pj_str(v->realm);
		cred.data.static_cred.username = pj_str(v->username);
		cred.data.static_cred.data = pj_str(v->password);
		cred.data.static_cred.nonce = pj_str(v->nonce);

		status2 = pj_stun_authenticate_request(buf, (unsigned)len, msg, 
						      &cred, pool, NULL, NULL);
		if (status2 != PJ_SUCCESS) {
		    char errmsg[PJ_ERR_MSG_SIZE];
		    pj_strerror(status2, errmsg, sizeof(errmsg));
		    PJ_LOG(1,(THIS_FILE, 
			      "    Request authentication failed: %s",
			      errmsg));
		    rc = -1070;
		    goto on_return;
		}

	    } else if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) {
		pj_status_t status2;
		status2 = pj_stun_authenticate_response(buf, (unsigned)len, 
						       msg, &key);
		if (status2 != PJ_SUCCESS) {
		    char errmsg[PJ_ERR_MSG_SIZE];
		    pj_strerror(status2, errmsg, sizeof(errmsg));
		    PJ_LOG(1,(THIS_FILE, 
			      "    Response authentication failed: %s",
			      errmsg));
		    rc = -1080;
		    goto on_return;
		}
	    }
	}	
    }


on_return:
    pj_pool_release(pool);
    return rc;
}
Example #11
0
static int run_client_test(const char *title,

			   pj_bool_t server_responding,
			   pj_stun_auth_type server_auth_type,

			   pj_stun_auth_type client_auth_type,
			   const char *realm,
			   const char *username,
			   const char *nonce,
			   const char *password,
			   pj_bool_t dummy_mi,
			   pj_bool_t use_ipv6,
			   pj_bool_t expected_error,
			   pj_status_t expected_code,
			   const char *expected_realm,
			   const char *expected_nonce,
			   
			   int (*more_check)(void))
{
    pj_pool_t *pool;
    pj_stun_session_cb sess_cb;
    pj_stun_auth_cred cred;
    pj_stun_tx_data *tdata;
    pj_status_t status;
    pj_sockaddr addr;
    int rc = 0;
    
    PJ_LOG(3,(THIS_FILE, "   %s test (%s)", title, use_ipv6?"IPv6":"IPv4"));

    /* Create client */
    pool = pj_pool_create(mem, "client", 1000, 1000, NULL);
    client = PJ_POOL_ZALLOC_T(pool, struct client);
    client->pool = pool;
    client->responding = PJ_TRUE;

    /* Create STUN session */
    pj_bzero(&sess_cb, sizeof(sess_cb));
    sess_cb.on_request_complete = &client_on_request_complete;
    sess_cb.on_send_msg = &client_send_msg;
    status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, NULL, &client->sess);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -200;
    }

    /* Create semaphore */
    status = pj_sem_create(pool, "client", 0, 1, &client->test_complete);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -205;
    }

    /* Create client socket */
    status = pj_sock_socket(GET_AF(use_ipv6), pj_SOCK_DGRAM(), 0, &client->sock);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -210;
    }

    /* Bind client socket */
    pj_sockaddr_init(GET_AF(use_ipv6), &addr, NULL, 0);
    status = pj_sock_bind(client->sock, &addr, pj_sockaddr_get_len(&addr));
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -220;
    }

    /* Create client thread */
    status = pj_thread_create(pool, "client", &client_thread, NULL, 0, 0, &client->thread);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -230;
    }

    /* Initialize credential */
    pj_bzero(&cred, sizeof(cred));
    cred.type = PJ_STUN_AUTH_CRED_STATIC;
    if (realm) cred.data.static_cred.realm = pj_str((char*)realm);
    if (username) cred.data.static_cred.username = pj_str((char*)username);
    if (nonce) cred.data.static_cred.nonce = pj_str((char*)nonce);
    if (password) cred.data.static_cred.data = pj_str((char*)password);
    cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
    status = pj_stun_session_set_credential(client->sess, client_auth_type, &cred);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -240;
    }

    /* Create the server */
    status = create_std_server(server_auth_type, server_responding, use_ipv6);
    if (status != 0) {
	destroy_client_server();
	return status;
    }

    /* Create request */
    status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST, 
					PJ_STUN_MAGIC, NULL, &tdata);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -250;
    }

    /* Add our own attributes if client authentication is set to none */
    if (client_auth_type == PJ_STUN_AUTH_NONE) {
	pj_str_t tmp;
	if (realm)
	    pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_REALM, pj_cstr(&tmp, realm));
	if (username)
	    pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&tmp, username));
	if (nonce)
	    pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, nonce));
	if (password) {
	    // ignored
	}
	if (dummy_mi) {
	    pj_stun_msgint_attr *mi;

	    pj_stun_msgint_attr_create(tdata->pool, &mi);
	    pj_stun_msg_add_attr(tdata->msg, &mi->hdr);
	}
	   
    }

    /* Send the request */
    status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr,
				      pj_sockaddr_get_len(&server->addr), tdata);
    if (status != PJ_SUCCESS) {
	destroy_client_server();
	return -270;
    }

    /* Wait until test complete */
    pj_sem_wait(client->test_complete);


    /* Verify response */
    if (expected_error) {
	if (expected_code != client->response_status) {
	    char e1[PJ_ERR_MSG_SIZE], e2[PJ_ERR_MSG_SIZE];

	    pj_strerror(expected_code, e1, sizeof(e1));
	    pj_strerror(client->response_status, e2, sizeof(e2));

	    PJ_LOG(3,(THIS_FILE, "    err: expecting %d (%s) but got %d (%s) response",
		      expected_code, e1, client->response_status, e2));
	    rc = -500;
	} 

    } else {
	int res_code = 0;
	pj_stun_realm_attr *arealm;
	pj_stun_nonce_attr *anonce;

	if (client->response_status != 0) {
	    PJ_LOG(3,(THIS_FILE, "    err: expecting successful operation but got error %d", 
		      client->response_status));
	    rc = -600;
	    goto done;
	} 

	if (PJ_STUN_IS_ERROR_RESPONSE(client->response->hdr.type)) {
	    pj_stun_errcode_attr *aerr = NULL;

	    aerr = (pj_stun_errcode_attr*)
		   pj_stun_msg_find_attr(client->response, 
					 PJ_STUN_ATTR_ERROR_CODE, 0);
	    if (aerr == NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: received error response without ERROR-CODE"));
		rc = -610;
		goto done;
	    }

	    res_code = aerr->err_code;
	} else {
	    res_code = 0;
	}

	/* Check that code matches */
	if (expected_code != res_code) {
	    PJ_LOG(3,(THIS_FILE, "    err: expecting response code %d but got %d",
		      expected_code, res_code));
	    rc = -620;
	    goto done;
	}

	/* Find REALM and NONCE attributes */
	arealm = (pj_stun_realm_attr*)
	         pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0);
	anonce = (pj_stun_nonce_attr*)
	         pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0);

	if (expected_realm) {
	    if (arealm == NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: expecting REALM in esponse"));
		rc = -630;
		goto done;
	    }
	    if (pj_strcmp2(&arealm->value, expected_realm)!=0) {
		PJ_LOG(3,(THIS_FILE, "    err: REALM mismatch in response"));
		rc = -640;
		goto done;
	    }
	} else {
	    if (arealm != NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: non expecting REALM in response"));
		rc = -650;
		goto done;
	    }
	}

	if (expected_nonce) {
	    if (anonce == NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: expecting NONCE in esponse"));
		rc = -660;
		goto done;
	    }
	    if (pj_strcmp2(&anonce->value, expected_nonce)!=0) {
		PJ_LOG(3,(THIS_FILE, "    err: NONCE mismatch in response"));
		rc = -670;
		goto done;
	    }
	} else {
	    if (anonce != NULL) {
		PJ_LOG(3,(THIS_FILE, "    err: non expecting NONCE in response"));
		rc = -680;
		goto done;
	    }
	}
    }

    /* Our tests are okay so far. Let caller do some more tests if
     * it wants to.
     */
    if (rc==0 && more_check) {
	rc = (*more_check)();
    }


done:
    destroy_client_server();

    /* If IPv6 is enabled, test again for IPv4. */
    if ((rc == 0) && use_ipv6) {
	rc = run_client_test(title,
			     server_responding,
			     server_auth_type,
			     client_auth_type,
			     realm,
			     username,
			     nonce,
			     password,
			     dummy_mi,
			     0,
			     expected_error,
			     expected_code,
			     expected_realm,
			     expected_nonce,
			     more_check);
    }

    return rc;
}
Example #12
0
static const char *err(pj_status_t status)
{
    static char errmsg[PJ_ERR_MSG_SIZE];
    pj_strerror(status, errmsg, sizeof(errmsg));
    return errmsg;
}
PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
					    int sock_cnt, pj_sock_t sock[],
					    const pj_str_t *srv1, int port1,
					    const pj_str_t *srv2, int port2,
					    pj_sockaddr_in mapped_addr[])
{
    unsigned srv_cnt;
    pj_sockaddr_in srv_addr[2];
    int i, send_cnt = 0, nfds;
    pj_pool_t *pool;
    struct query_rec {
	struct {
	    pj_uint32_t	mapped_addr;
	    pj_uint32_t	mapped_port;
	} srv[2];
    } *rec;
    void       *out_msg;
    pj_size_t	out_msg_len;
    int wait_resp = 0;
    pj_status_t status;

    PJ_CHECK_STACK();

    TRACE_((THIS_FILE, "Entering pjstun_get_mapped_addr()"));

    /* Create pool. */
    pool = pj_pool_create(pf, "stun%p", 400, 400, NULL);
    if (!pool)
	return PJ_ENOMEM;


    /* Allocate client records */
    rec = (struct query_rec*) pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
    if (!rec) {
	status = PJ_ENOMEM;
	goto on_error;
    }

    TRACE_((THIS_FILE, "  Memory allocated."));

    /* Create the outgoing BIND REQUEST message template */
    status = pjstun_create_bind_req( pool, &out_msg, &out_msg_len, 
				      pj_rand(), pj_rand());
    if (status != PJ_SUCCESS)
	goto on_error;

    TRACE_((THIS_FILE, "  Binding request created."));

    /* Resolve servers. */
    status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
    if (status != PJ_SUCCESS)
	goto on_error;

    srv_cnt = 1;

    if (srv2 && port2) {
	status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
	if (status != PJ_SUCCESS)
	    goto on_error;

	if (srv_addr[1].sin_addr.s_addr != srv_addr[0].sin_addr.s_addr &&
	    srv_addr[1].sin_port != srv_addr[0].sin_port)
	{
	    srv_cnt++;
	}
    }

    TRACE_((THIS_FILE, "  Server initialized, using %d server(s)", srv_cnt));

    /* Init mapped addresses to zero */
    pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));

    /* We need these many responses */
    wait_resp = sock_cnt * srv_cnt;

    TRACE_((THIS_FILE, "  Done initialization."));

#if defined(PJ_SELECT_NEEDS_NFDS) && PJ_SELECT_NEEDS_NFDS!=0
    nfds = -1;
    for (i=0; i<sock_cnt; ++i) {
	if (sock[i] > nfds) {
	    nfds = sock[i];
	}
    }
#else
    nfds = PJ_IOQUEUE_MAX_HANDLES-1;
#endif

    /* Main retransmission loop. */
    for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
	pj_time_val next_tx, now;
	pj_fd_set_t r;
	int select_rc;

	PJ_FD_ZERO(&r);

	/* Send messages to servers that has not given us response. */
	for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
	    unsigned j;
	    for (j=0; j<srv_cnt && status==PJ_SUCCESS; ++j) {
		pjstun_msg_hdr *msg_hdr = (pjstun_msg_hdr*) out_msg;
                pj_ssize_t sent_len;

		if (rec[i].srv[j].mapped_port != 0)
		    continue;

		/* Modify message so that we can distinguish response. */
		msg_hdr->tsx[2] = pj_htonl(i);
		msg_hdr->tsx[3] = pj_htonl(j);

		/* Send! */
                sent_len = out_msg_len;
		status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
					(pj_sockaddr_t*)&srv_addr[j],
					sizeof(pj_sockaddr_in));
	    }
	}

	/* All requests sent.
	 * The loop below will wait for responses until all responses have
	 * been received (i.e. wait_resp==0) or timeout occurs, which then
	 * we'll go to the next retransmission iteration.
	 */
	TRACE_((THIS_FILE, "  Request(s) sent, counter=%d", send_cnt));

	/* Calculate time of next retransmission. */
	pj_gettimeofday(&next_tx);
	next_tx.sec += (stun_timer[send_cnt]/1000);
	next_tx.msec += (stun_timer[send_cnt]%1000);
	pj_time_val_normalize(&next_tx);

	for (pj_gettimeofday(&now), select_rc=1; 
	     status==PJ_SUCCESS && select_rc>=1 && wait_resp>0 
	       && PJ_TIME_VAL_LT(now, next_tx); 
	     pj_gettimeofday(&now)) 
	{
	    pj_time_val timeout;

	    timeout = next_tx;
	    PJ_TIME_VAL_SUB(timeout, now);

	    for (i=0; i<sock_cnt; ++i) {
		PJ_FD_SET(sock[i], &r);
	    }

	    select_rc = pj_sock_select(nfds+1, &r, NULL, NULL, &timeout);
	    TRACE_((THIS_FILE, "  select() rc=%d", select_rc));
	    if (select_rc < 1)
		continue;

	    for (i=0; i<sock_cnt; ++i) {
		int sock_idx, srv_idx;
                pj_ssize_t len;
		pjstun_msg msg;
		pj_sockaddr_in addr;
		int addrlen = sizeof(addr);
		pjstun_mapped_addr_attr *attr;
		char recv_buf[128];

		if (!PJ_FD_ISSET(sock[i], &r))
		    continue;

                len = sizeof(recv_buf);
		status = pj_sock_recvfrom( sock[i], recv_buf, 
				           &len, 0,
				           (pj_sockaddr_t*)&addr,
					   &addrlen);

		if (status != PJ_SUCCESS) {
		    char errmsg[PJ_ERR_MSG_SIZE];

		    PJ_LOG(4,(THIS_FILE, "recvfrom() error ignored: %s",
			      pj_strerror(status, errmsg,sizeof(errmsg)).ptr));

		    /* Ignore non-PJ_SUCCESS status.
		     * It possible that other SIP entity is currently 
		     * sending SIP request to us, and because SIP message
		     * is larger than STUN, we could get EMSGSIZE when
		     * we call recvfrom().
		     */
		    status = PJ_SUCCESS;
		    continue;
		}

		status = pjstun_parse_msg(recv_buf, len, &msg);
		if (status != PJ_SUCCESS) {
		    char errmsg[PJ_ERR_MSG_SIZE];

		    PJ_LOG(4,(THIS_FILE, "STUN parsing error ignored: %s",
			      pj_strerror(status, errmsg,sizeof(errmsg)).ptr));

		    /* Also ignore non-successful parsing. This may not
		     * be STUN response at all. See the comment above.
		     */
		    status = PJ_SUCCESS;
		    continue;
		}

		sock_idx = pj_ntohl(msg.hdr->tsx[2]);
		srv_idx = pj_ntohl(msg.hdr->tsx[3]);

		if (sock_idx<0 || sock_idx>=sock_cnt || sock_idx!=i ||
			srv_idx<0 || srv_idx>=2)
		{
		    status = PJLIB_UTIL_ESTUNININDEX;
		    continue;
		}

		if (pj_ntohs(msg.hdr->type) != PJSTUN_BINDING_RESPONSE) {
		    status = PJLIB_UTIL_ESTUNNOBINDRES;
		    continue;
		}

		if (rec[sock_idx].srv[srv_idx].mapped_port != 0) {
		    /* Already got response */
		    continue;
		}

		/* From this part, we consider the packet as a valid STUN
		 * response for our request.
		 */
		--wait_resp;

		if (pjstun_msg_find_attr(&msg, PJSTUN_ATTR_ERROR_CODE) != NULL) {
		    status = PJLIB_UTIL_ESTUNRECVERRATTR;
		    continue;
		}

		attr = (pjstun_mapped_addr_attr*) 
		       pjstun_msg_find_attr(&msg, PJSTUN_ATTR_MAPPED_ADDR);
		if (!attr) {
		    attr = (pjstun_mapped_addr_attr*) 
			   pjstun_msg_find_attr(&msg, PJSTUN_ATTR_XOR_MAPPED_ADDR);
		    if (!attr || attr->family != 1) {
			status = PJLIB_UTIL_ESTUNNOMAP;
			continue;
		    }
		}

		rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
		rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
		if (pj_ntohs(attr->hdr.type) == PJSTUN_ATTR_XOR_MAPPED_ADDR) {
		    rec[sock_idx].srv[srv_idx].mapped_addr ^= pj_htonl(STUN_MAGIC);
		    rec[sock_idx].srv[srv_idx].mapped_port ^= pj_htons(STUN_MAGIC >> 16);
		}
	    }
	}

	/* The best scenario is if all requests have been replied.
	 * Then we don't need to go to the next retransmission iteration.
	 */
	if (wait_resp <= 0)
	    break;
    }
Example #14
0
pj_status_t UE::init_int(pj_log_func* logger,
             std::string realm,
             std::string myurl,
             std::string username,
             std::string password,
             std::string outbound_proxy)
{
  pj_status_t status;
  char errmsg[PJ_ERR_MSG_SIZE];
  pj_sockaddr addr;
  pj_str_t remote;
  pj_sockaddr remote_addr;

  init_pjsip(logger);
  std::string server_uri = std::string("sip:") + realm + std::string(";lr;transport=tcp");
  _pool = pj_pool_create(get_global_pool_factory(), "a", 256, 256, NULL);

  pj_sockaddr_init(pj_AF_INET(), &addr, NULL, (pj_uint16_t)0);

  //status = pjsip_udp_transport_start(get_global_endpoint(), &addr.ipv4, NULL, 1, &_transport);
  //assert(status == PJ_SUCCESS);

  if (outbound_proxy.empty())
  {
    outbound_proxy = realm;
  }
  
  pj_cstr(&remote, outbound_proxy.c_str());
  pj_sockaddr_init(pj_AF_INET(), &remote_addr, &remote, (pj_uint16_t)5060);

  pjsip_tpselector sel2;
  sel2.type = PJSIP_TPSELECTOR_LISTENER;
  sel2.u.listener = get_global_tcp_factory();
  status = pjsip_endpt_acquire_transport(get_global_endpoint(),
      PJSIP_TRANSPORT_TCP,
      &remote_addr,
      pj_sockaddr_get_len(&remote_addr),
      &sel2,
      &_transport);
  
  if (status != PJ_SUCCESS)
  {
    pj_strerror(status, errmsg, sizeof(errmsg));
    PJ_LOG(1, (__FILE__, "TCP connection to %s failed: %s (%d)", outbound_proxy.c_str(), errmsg, status));
    return status;
  }
  
  transport_mapping[_transport] = this;

  status = pjsip_regc_create(get_global_endpoint(), this, &regc_cb, &_regc);

  if (status != PJ_SUCCESS)
  {
    pj_strerror(status, errmsg, sizeof(errmsg));
    PJ_LOG(1, (__FILE__, "Creating the REGISTER session failed: %s (%d)", errmsg, status));
    return status;
  }

  pjsip_regc_set_reg_tsx_cb(_regc, &reg_tsx_cb);

  pjsip_tpselector sel;
  sel.type = PJSIP_TPSELECTOR_TRANSPORT;
  sel.u.transport = _transport;
  pjsip_regc_set_transport(_regc, &sel);

  pjsip_auth_clt_pref prefs = {};
  prefs.initial_auth = PJ_TRUE;

  pjsip_regc_set_prefs(_regc, &prefs);

  pjsip_cred_info cred;
  stra(&cred.realm, realm.c_str());
  stra(&cred.scheme, "Digest");
  stra(&cred.username, username.c_str());
  stra(&cred.data, password.c_str());
  cred.data_type = 0; // Plaintext password
  pjsip_cred_info creds[1] = {cred};
  pjsip_regc_set_credentials(_regc, 1, creds);

  char contact[32];
  snprintf(contact, 32, "sip:phone@%.*s:%d", (int)_transport->local_name.host.slen, _transport->local_name.host.ptr, _transport->local_name.port);

  stra(&_realm, realm.c_str());
  stra(&_server, server_uri.c_str());
  _server_uri = pjsip_parse_uri(_pool, _server.ptr, _server.slen, 0);
  stra(&_my_uri, myurl.c_str());
  stra(&_username, username.c_str());
  stra(&_contact, contact);

  return PJ_SUCCESS;
}
Example #15
0
/*
 * Parse SDP message.
 */
PJ_DEF(pj_status_t) pjmedia_sdp_parse( pj_pool_t *pool,
				       char *buf, pj_size_t len, 
				       pjmedia_sdp_session **p_sdp)
{
    pj_scanner scanner;
    pjmedia_sdp_session *session;
    pjmedia_sdp_media *media = NULL;
    pjmedia_sdp_attr *attr;
    pjmedia_sdp_conn *conn;
    pjmedia_sdp_bandw *bandw;
    pj_str_t dummy;
    int cur_name = 254;
    parse_context ctx;
    PJ_USE_EXCEPTION;

    ctx.last_error = PJ_SUCCESS;

    init_sdp_parser();

    pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
    session = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
    PJ_ASSERT_RETURN(session != NULL, PJ_ENOMEM);

    /* Ignore leading newlines */
    while (*scanner.curptr=='\r' || *scanner.curptr=='\n')
	pj_scan_get_char(&scanner);

    PJ_TRY {
	while (!pj_scan_is_eof(&scanner)) {
		cur_name = *scanner.curptr;
		switch (cur_name) {
		case 'a':
		    attr = parse_attr(pool, &scanner, &ctx);
		    if (attr) {
			if (media) {
			    if (media->attr_count < PJMEDIA_MAX_SDP_ATTR)
				pjmedia_sdp_media_add_attr(media, attr);
			    else
				PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
					      "Error adding media attribute, "
					      "attribute is ignored"));
			} else {
			    if (session->attr_count < PJMEDIA_MAX_SDP_ATTR)
				pjmedia_sdp_session_add_attr(session, attr);
			    else
				PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
					      "Error adding session attribute"
					      ", attribute is ignored"));
			}
		    }
		    break;
		case 'o':
		    parse_origin(&scanner, session, &ctx);
		    break;
		case 's':
		    parse_generic_line(&scanner, &session->name, &ctx);
		    break;
		case 'c':
		    conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
		    parse_connection_info(&scanner, conn, &ctx);
		    if (media) {
			media->conn = conn;
		    } else {
			session->conn = conn;
		    }
		    break;
		case 't':
		    parse_time(&scanner, session, &ctx);
		    break;
		case 'm':
		    media = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
		    parse_media(&scanner, media, &ctx);
		    if (session->media_count < PJMEDIA_MAX_SDP_MEDIA)
			session->media[ session->media_count++ ] = media;
		    else
			PJ_PERROR(2,(THIS_FILE, PJ_ETOOMANY,
				     "Error adding media, media is ignored"));
		    break;
		case 'v':
		    parse_version(&scanner, &ctx);
		    break;
		case 13:
		case 10:
		    pj_scan_get_char(&scanner);
		    /* Allow empty newlines at the end of the message */
		    while (!pj_scan_is_eof(&scanner)) {
			if (*scanner.curptr != 13 && *scanner.curptr != 10) {
			    ctx.last_error = PJMEDIA_SDP_EINSDP;
			    on_scanner_error(&scanner);
			}
			pj_scan_get_char(&scanner);
		    }
		    break;
		case 'b':
		    bandw = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_bandw);
		    parse_bandwidth_info(&scanner, bandw, &ctx);
		    if (media) {
			if (media->bandw_count < PJMEDIA_MAX_SDP_BANDW)
			    media->bandw[media->bandw_count++] = bandw;
			else
			    PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
					  "Error adding media bandwidth "
					  "info, info is ignored"));
		    } else {
			if (session->bandw_count < PJMEDIA_MAX_SDP_BANDW)
			    session->bandw[session->bandw_count++] = bandw;
			else
			    PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
					  "Error adding session bandwidth "
					  "info, info is ignored"));
		    }
		    break;
		default:
		    if (cur_name >= 'a' && cur_name <= 'z')
			parse_generic_line(&scanner, &dummy, &ctx);
		    else  {
			ctx.last_error = PJMEDIA_SDP_EINSDP;
			on_scanner_error(&scanner);
		    }
		    break;
		}
	}

	ctx.last_error = PJ_SUCCESS;

    }
    PJ_CATCH_ANY {
	
	char errmsg[PJ_ERR_MSG_SIZE];
	pj_strerror(ctx.last_error, errmsg, sizeof(errmsg));

	PJ_LOG(4, (THIS_FILE, "Error parsing SDP in line %d col %d: %s",
		   scanner.line, pj_scan_get_col(&scanner),
		   errmsg));

	session = NULL;

	pj_assert(ctx.last_error != PJ_SUCCESS);
    }
    PJ_END;

    pj_scan_fini(&scanner);

    if (session)
	apply_media_direction(session);

    *p_sdp = session;
    return ctx.last_error;
}
Example #16
0
/*****************************************************************************
 * test: play simple ringback tone and hear it 
 */
static void systest_play_tone(void)
{
    /* Ringtones  */
    #define RINGBACK_FREQ1	    440	    /* 400 */
    #define RINGBACK_FREQ2	    480	    /* 450 */
    #define RINGBACK_ON		    3000    /* 400 */
    #define RINGBACK_OFF	    4000    /* 200 */
    #define RINGBACK_CNT	    1	    /* 2   */
    #define RINGBACK_INTERVAL	    4000    /* 2000 */

    unsigned i, samples_per_frame;
    pjmedia_tone_desc tone[RINGBACK_CNT];
    pj_pool_t *pool = NULL;
    pjmedia_port *ringback_port = NULL;
    enum gui_key key;
    int ringback_slot = -1;
    test_item_t *ti;
    pj_str_t name;
    const char *title = "Audio Tone Playback Test";
    pj_status_t status;

    ti = systest_alloc_test_item(title);
    if (!ti)
	return;

    key = gui_msgbox(title,
		     "This test will play simple ringback tone to "
		     "the speaker. Please listen carefully for audio "
		     "impairments such as stutter. You may need "
		     "to let this test running for a while to "
		     "make sure that everything is okay. Press "
		     "OK to start, CANCEL to skip",
		     WITH_OKCANCEL);
    if (key != KEY_OK) {
	ti->skipped = PJ_TRUE;
	return;
    }

    PJ_LOG(3,(THIS_FILE, "Running %s", title));

    pool = pjsua_pool_create("ringback", 512, 512);
    samples_per_frame = systest.media_cfg.audio_frame_ptime * 
			systest.media_cfg.clock_rate *
			systest.media_cfg.channel_count / 1000;

    /* Ringback tone (call is ringing) */
    name = pj_str("ringback");
    status = pjmedia_tonegen_create2(pool, &name, 
				     systest.media_cfg.clock_rate,
				     systest.media_cfg.channel_count, 
				     samples_per_frame,
				     16, PJMEDIA_TONEGEN_LOOP, 
				     &ringback_port);
    if (status != PJ_SUCCESS)
	goto on_return;

    pj_bzero(&tone, sizeof(tone));
    for (i=0; i<RINGBACK_CNT; ++i) {
	tone[i].freq1 = RINGBACK_FREQ1;
	tone[i].freq2 = RINGBACK_FREQ2;
	tone[i].on_msec = RINGBACK_ON;
	tone[i].off_msec = RINGBACK_OFF;
    }
    tone[RINGBACK_CNT-1].off_msec = RINGBACK_INTERVAL;

    status = pjmedia_tonegen_play(ringback_port, RINGBACK_CNT, tone,
				  PJMEDIA_TONEGEN_LOOP);
    if (status != PJ_SUCCESS)
	goto on_return;

    status = pjsua_conf_add_port(pool, ringback_port, &ringback_slot);
    if (status != PJ_SUCCESS)
	goto on_return;

    status = pjsua_conf_connect(ringback_slot, 0);
    if (status != PJ_SUCCESS)
	goto on_return;

    key = gui_msgbox(title,
		     "Ringback tone should be playing now in the "
		     "speaker. Press OK to stop. ", WITH_OK);

    status = PJ_SUCCESS;

on_return:
    if (ringback_slot != -1)
	pjsua_conf_remove_port(ringback_slot);
    if (ringback_port)
	pjmedia_port_destroy(ringback_port);
    if (pool)
	pj_pool_release(pool);

    if (status != PJ_SUCCESS) {
	systest_perror("Sorry we encounter error when initializing "
		       "the tone generator: ", status);
	ti->success = PJ_FALSE;
	pj_strerror(status, ti->reason, sizeof(ti->reason));
    } else {
	key = gui_msgbox(title, "Is the audio okay?", WITH_YESNO);
	ti->success = (key == KEY_YES);
	if (!ti->success)
	    pj_ansi_strcpy(ti->reason, USER_ERROR);
    }
    return;
}
/* Destroy TLS transport */
static pj_status_t tls_destroy(pjsip_transport *transport, 
			       pj_status_t reason)
{
    struct tls_transport *tls = (struct tls_transport*)transport;

    if (tls->close_reason == 0)
	tls->close_reason = reason;

    if (tls->is_registered) {
	tls->is_registered = PJ_FALSE;
	pjsip_transport_destroy(transport);

	/* pjsip_transport_destroy will recursively call this function
	 * again.
	 */
	return PJ_SUCCESS;
    }

    /* Mark transport as closing */
    tls->is_closing = PJ_TRUE;

    /* Stop keep-alive timer. */
    if (tls->ka_timer.id) {
	pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer);
	tls->ka_timer.id = PJ_FALSE;
    }

    /* Cancel all delayed transmits */
    while (!pj_list_empty(&tls->delayed_list)) {
	struct delayed_tdata *pending_tx;
	pj_ioqueue_op_key_t *op_key;

	pending_tx = tls->delayed_list.next;
	pj_list_erase(pending_tx);

	op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;

	on_data_sent(tls->ssock, op_key, -reason);
    }

    if (tls->rdata.tp_info.pool) {
	pj_pool_release(tls->rdata.tp_info.pool);
	tls->rdata.tp_info.pool = NULL;
    }

    if (tls->ssock) {
	pj_ssl_sock_close(tls->ssock);
	tls->ssock = NULL;
    }
    if (tls->base.lock) {
	pj_lock_destroy(tls->base.lock);
	tls->base.lock = NULL;
    }

    if (tls->base.ref_cnt) {
	pj_atomic_destroy(tls->base.ref_cnt);
	tls->base.ref_cnt = NULL;
    }

    if (tls->base.pool) {
	pj_pool_t *pool;

	if (reason != PJ_SUCCESS) {
	    char errmsg[PJ_ERR_MSG_SIZE];

	    pj_strerror(reason, errmsg, sizeof(errmsg));
	    PJ_LOG(4,(tls->base.obj_name, 
		      "TLS transport destroyed with reason %d: %s", 
		      reason, errmsg));

	} else {

	    PJ_LOG(4,(tls->base.obj_name, 
		      "TLS transport destroyed normally"));

	}

	pool = tls->base.pool;
	tls->base.pool = NULL;
	pj_pool_release(pool);
    }

    return PJ_SUCCESS;
}
Example #18
0
/*****************************************************************************
 * test: record audio 
 */
static void systest_rec_audio(void)
{
    const pj_str_t filename = pj_str(add_path(doc_path, WAV_REC_OUT_PATH));
    pj_pool_t *pool = NULL;
    enum gui_key key;
    pjsua_recorder_id rec_id = PJSUA_INVALID_ID;
    pjsua_player_id play_id = PJSUA_INVALID_ID;
    pjsua_conf_port_id rec_slot = PJSUA_INVALID_ID;
    pjsua_conf_port_id play_slot = PJSUA_INVALID_ID;
    pj_status_t status = PJ_SUCCESS;
    const char *title = "Audio Recording";
    test_item_t *ti;

    ti = systest_alloc_test_item(title);
    if (!ti)
	return;

    key = gui_msgbox(title,
		     "This test will allow you to record audio "
		     "from the microphone, and playback the "
		     "audio to the speaker. Press OK to start recording, "
		     "CANCEL to skip.", 
		     WITH_OKCANCEL);
    if (key != KEY_OK) {
	ti->skipped = PJ_TRUE;
	return;
    }

    PJ_LOG(3,(THIS_FILE, "Running %s", title));

    pool = pjsua_pool_create("rectest", 512, 512);

    status = pjsua_recorder_create(&filename, 0, NULL, -1, 0, &rec_id);
    if (status != PJ_SUCCESS)
	goto on_return;

    rec_slot = pjsua_recorder_get_conf_port(rec_id);

    status = pjsua_conf_connect(0, rec_slot);
    if (status != PJ_SUCCESS)
	goto on_return;

    key = gui_msgbox(title,
		     "Recording is in progress now, please say "
		     "something in the microphone. Press OK "
		     "to stop recording", WITH_OK);

    pjsua_conf_disconnect(0, rec_slot);
    rec_slot = PJSUA_INVALID_ID;
    pjsua_recorder_destroy(rec_id);
    rec_id = PJSUA_INVALID_ID;

    status = pjsua_player_create(&filename, 0, &play_id);
    if (status != PJ_SUCCESS)
	goto on_return;

    play_slot = pjsua_player_get_conf_port(play_id);

    status = pjsua_conf_connect(play_slot, 0);
    if (status != PJ_SUCCESS)
	goto on_return;

    key = gui_msgbox(title,
		     "Recording has been stopped. "
		     "The recorded audio is being played now to "
		     "the speaker device, in a loop. Listen for "
		     "any audio impairments. Press OK to stop.", 
		     WITH_OK);

on_return:
    if (rec_slot != PJSUA_INVALID_ID)
	pjsua_conf_disconnect(0, rec_slot);
    if (rec_id != PJSUA_INVALID_ID)
	pjsua_recorder_destroy(rec_id);
    if (play_slot != PJSUA_INVALID_ID)
	pjsua_conf_disconnect(play_slot, 0);
    if (play_id != PJSUA_INVALID_ID)
	pjsua_player_destroy(play_id);
    if (pool)
	pj_pool_release(pool);

    if (status != PJ_SUCCESS) {
	systest_perror("Sorry we encountered an error: ", status);
	ti->success = PJ_FALSE;
	pj_strerror(status, ti->reason, sizeof(ti->reason));
    } else {
	key = gui_msgbox(title, "Is the audio okay?", WITH_YESNO);
	ti->success = (key == KEY_YES);
	if (!ti->success) {
	    pj_ansi_snprintf(textbuf, sizeof(textbuf),
			     "You will probably need to copy the recorded "
			     "WAV file %s to a desktop computer and analyze "
			     "it, to find out whether it's a recording "
			     "or playback problem.",
			     WAV_REC_OUT_PATH);
	    gui_msgbox(title, textbuf, WITH_OK);
	    pj_ansi_strcpy(ti->reason, USER_ERROR);
	}
    }
}
Example #19
0
/*
 * Benchmarking IOQueue
 */
static int bench_test(pj_bool_t allow_concur, int bufsize, 
		      int inactive_sock_count)
{
    pj_sock_t ssock=-1, csock=-1;
    pj_sockaddr_in addr;
    pj_pool_t *pool = NULL;
    pj_sock_t *inactive_sock=NULL;
    pj_ioqueue_op_key_t *inactive_read_op;
    char *send_buf, *recv_buf;
    pj_ioqueue_t *ioque = NULL;
    pj_ioqueue_key_t *skey, *ckey, *keys[SOCK_INACTIVE_MAX+2];
    pj_timestamp t1, t2, t_elapsed;
    int rc=0, i;    /* i must be signed */
    pj_str_t temp;
    char errbuf[PJ_ERR_MSG_SIZE];

    TRACE__((THIS_FILE, "   bench test %d", inactive_sock_count));

    // Create pool.
    pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);

    // Allocate buffers for send and receive.
    send_buf = (char*)pj_pool_alloc(pool, bufsize);
    recv_buf = (char*)pj_pool_alloc(pool, bufsize);

    // Allocate sockets for sending and receiving.
    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock);
    if (rc == PJ_SUCCESS) {
        rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock);
    } else
        csock = PJ_INVALID_SOCKET;
    if (rc != PJ_SUCCESS) {
	app_perror("...error: pj_sock_socket()", rc);
	goto on_error;
    }

    // Bind server socket.
    pj_bzero(&addr, sizeof(addr));
    addr.sin_family = pj_AF_INET();
    addr.sin_port = pj_htons(PORT);
    if (pj_sock_bind(ssock, &addr, sizeof(addr)))
	goto on_error;

    pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES);

    // Create I/O Queue.
    rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
    if (rc != PJ_SUCCESS) {
	app_perror("...error: pj_ioqueue_create()", rc);
	goto on_error;
    }

    // Set concurrency
    rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
    if (rc != PJ_SUCCESS) {
	app_perror("...error: pj_ioqueue_set_default_concurrency()", rc);
	goto on_error;
    }

    // Allocate inactive sockets, and bind them to some arbitrary address.
    // Then register them to the I/O queue, and start a read operation.
    inactive_sock = (pj_sock_t*)pj_pool_alloc(pool, 
				    inactive_sock_count*sizeof(pj_sock_t));
    inactive_read_op = (pj_ioqueue_op_key_t*)pj_pool_alloc(pool,
                              inactive_sock_count*sizeof(pj_ioqueue_op_key_t));
    pj_bzero(&addr, sizeof(addr));
    addr.sin_family = pj_AF_INET();
    for (i=0; i<inactive_sock_count; ++i) {
        pj_ssize_t bytes;

	rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &inactive_sock[i]);
	if (rc != PJ_SUCCESS || inactive_sock[i] < 0) {
	    app_perror("...error: pj_sock_socket()", rc);
	    goto on_error;
	}
	if ((rc=pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) != 0) {
	    pj_sock_close(inactive_sock[i]);
	    inactive_sock[i] = PJ_INVALID_SOCKET;
	    app_perror("...error: pj_sock_bind()", rc);
	    goto on_error;
	}
	rc = pj_ioqueue_register_sock(pool, ioque, inactive_sock[i], 
			              NULL, &test_cb, &keys[i]);
	if (rc != PJ_SUCCESS) {
	    pj_sock_close(inactive_sock[i]);
	    inactive_sock[i] = PJ_INVALID_SOCKET;
	    app_perror("...error(1): pj_ioqueue_register_sock()", rc);
	    PJ_LOG(3,(THIS_FILE, "....i=%d", i));
	    goto on_error;
	}
        bytes = bufsize;
	rc = pj_ioqueue_recv(keys[i], &inactive_read_op[i], recv_buf, &bytes, 0);
	if (rc != PJ_EPENDING) {
	    pj_sock_close(inactive_sock[i]);
	    inactive_sock[i] = PJ_INVALID_SOCKET;
	    app_perror("...error: pj_ioqueue_read()", rc);
	    goto on_error;
	}
    }

    // Register server and client socket.
    // We put this after inactivity socket, hopefully this can represent the
    // worst waiting time.
    rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, 
			          &test_cb, &skey);
    if (rc != PJ_SUCCESS) {
	app_perror("...error(2): pj_ioqueue_register_sock()", rc);
	goto on_error;
    }

    rc = pj_ioqueue_register_sock(pool, ioque, csock, NULL, 
			          &test_cb, &ckey);
    if (rc != PJ_SUCCESS) {
	app_perror("...error(3): pj_ioqueue_register_sock()", rc);
	goto on_error;
    }

    // Set destination address to send the packet.
    pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT);

    // Test loop.
    t_elapsed.u64 = 0;
    for (i=0; i<LOOP; ++i) {
	pj_ssize_t bytes;
        pj_ioqueue_op_key_t read_op, write_op;

	// Randomize send buffer.
	pj_create_random_string(send_buf, bufsize);

	// Start reading on the server side.
        bytes = bufsize;
	rc = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
	if (rc != PJ_EPENDING) {
	    app_perror("...error: pj_ioqueue_read()", rc);
	    break;
	}

	// Starts send on the client side.
        bytes = bufsize;
	rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0,
			       &addr, sizeof(addr));
	if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
	    app_perror("...error: pj_ioqueue_write()", rc);
	    break;
	}
	if (rc == PJ_SUCCESS) {
	    if (bytes < 0) {
		app_perror("...error: pj_ioqueue_sendto()", -bytes);
		break;
	    }
	}

	// Begin time.
	pj_get_timestamp(&t1);

	// Poll the queue until we've got completion event in the server side.
        callback_read_key = NULL;
        callback_read_size = 0;
	TRACE__((THIS_FILE, "     waiting for key = %p", skey));
	do {
	    pj_time_val timeout = { 1, 0 };
#ifdef PJ_SYMBIAN
	    rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
#else
	    rc = pj_ioqueue_poll(ioque, &timeout);
#endif
	    TRACE__((THIS_FILE, "     poll rc=%d", rc));
	} while (rc >= 0 && callback_read_key != skey);

	// End time.
	pj_get_timestamp(&t2);
	t_elapsed.u64 += (t2.u64 - t1.u64);

	if (rc < 0) {
	    app_perror("   error: pj_ioqueue_poll", -rc);
	    break;
	}

	// Compare recv buffer with send buffer.
	if (callback_read_size != bufsize || 
	    pj_memcmp(send_buf, recv_buf, bufsize)) 
	{
	    rc = -10;
	    PJ_LOG(3,(THIS_FILE, "   error: size/buffer mismatch"));
	    break;
	}

	// Poll until all events are exhausted, before we start the next loop.
	do {
	    pj_time_val timeout = { 0, 10 };
#ifdef PJ_SYMBIAN
	    PJ_UNUSED_ARG(timeout);
	    rc = pj_symbianos_poll(-1, 100);
#else	    
	    rc = pj_ioqueue_poll(ioque, &timeout);
#endif
	} while (rc>0);

	rc = 0;
    }

    // Print results
    if (rc == 0) {
	pj_timestamp tzero;
	pj_uint32_t usec_delay;

	tzero.u32.hi = tzero.u32.lo = 0;
	usec_delay = pj_elapsed_usec( &tzero, &t_elapsed);

	PJ_LOG(3, (THIS_FILE, "...%10d %15d  % 9d", 
	           bufsize, inactive_sock_count, usec_delay));

    } else {
	PJ_LOG(2, (THIS_FILE, "...ERROR rc=%d (buf:%d, fds:%d)", 
			      rc, bufsize, inactive_sock_count+2));
    }

    // Cleaning up.
    for (i=inactive_sock_count-1; i>=0; --i) {
	pj_ioqueue_unregister(keys[i]);
    }

    pj_ioqueue_unregister(skey);
    pj_ioqueue_unregister(ckey);


    pj_ioqueue_destroy(ioque);
    pj_pool_release( pool);
    return rc;

on_error:
    PJ_LOG(1,(THIS_FILE, "...ERROR: %s", 
	      pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf))));
    if (ssock)
	pj_sock_close(ssock);
    if (csock)
	pj_sock_close(csock);
    for (i=0; i<inactive_sock_count && inactive_sock && 
	      inactive_sock[i]!=PJ_INVALID_SOCKET; ++i) 
    {
	pj_sock_close(inactive_sock[i]);
    }
    if (ioque != NULL)
	pj_ioqueue_destroy(ioque);
    pj_pool_release( pool);
    return -1;
}
Example #20
0
/* Timer callback. When the timer is fired, it can be time to refresh
 * the session if UA is the refresher, otherwise it is time to end 
 * the session.
 */
void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
{
    pjsip_inv_session *inv = (pjsip_inv_session*) entry->user_data;
    pjsip_tx_data *tdata = NULL;
    pj_status_t status;
    pj_bool_t as_refresher;

    pj_assert(inv);

    PJ_UNUSED_ARG(timer_heap);

    /* When there is a pending INVITE transaction, delay/reschedule this timer
     * for five seconds to cover the case that pending INVITE fails and the
     * previous session is still active. If the pending INVITE is successful, 
     * timer state will be updated, i.e: restarted or stopped.
     */
    if (inv->invite_tsx != NULL) {
	pj_time_val delay = {5};

	inv->timer->timer.id = 1;
	pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer, &delay);
	return;
    }

    /* Lock dialog. */
    pjsip_dlg_inc_lock(inv->dlg);

    /* Check our role */
    as_refresher = 
	(inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) ||
	(inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS);

    /* Do action based on role, refresher or refreshee */
    if (as_refresher) {

	pj_time_val now;

	/* Refresher, refresh the session */
	if (inv->timer->use_update) {
	    /* Create UPDATE request without offer */
	    status = pjsip_inv_update(inv, NULL, NULL, &tdata);
	} else {
	    /* Create re-INVITE without modifying session */
	    pjsip_msg_body *body;
	    const pjmedia_sdp_session *offer = NULL;

	    pj_assert(pjmedia_sdp_neg_get_state(inv->neg) == 
		      PJMEDIA_SDP_NEG_STATE_DONE);

	    status = pjsip_inv_invite(inv, &tdata);
	    if (status == PJ_SUCCESS)
		status = pjmedia_sdp_neg_send_local_offer(inv->pool_prov, 
							  inv->neg, &offer);
	    if (status == PJ_SUCCESS)
		status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
	    if (status == PJ_SUCCESS) {
		status = pjsip_create_sdp_body(tdata->pool, 
					(pjmedia_sdp_session*)offer, &body);
		tdata->msg->body = body;
	    }
	}

	pj_gettimeofday(&now);
	PJ_LOG(4, (inv->pool->obj_name, 
		   "Refresh session after %ds (expiration period=%ds)",
		   (now.sec-inv->timer->last_refresh.sec),
		   inv->timer->setting.sess_expires));
    } else {
	
	pj_time_val now;

	/* Refreshee, terminate the session */
	status = pjsip_inv_end_session(inv, PJSIP_SC_REQUEST_TIMEOUT, 
				       NULL, &tdata);

	pj_gettimeofday(&now);
	PJ_LOG(3, (inv->pool->obj_name, 
		   "No session refresh received after %ds "
		   "(expiration period=%ds), stopping session now!",
		   (now.sec-inv->timer->last_refresh.sec),
		   inv->timer->setting.sess_expires));
    }

    /* Unlock dialog. */
    pjsip_dlg_dec_lock(inv->dlg);

    /* Send message, if any */
    if (tdata && status == PJ_SUCCESS) {
	status = pjsip_inv_send_msg(inv, tdata);
    }

    /* Print error message, if any */
    if (status != PJ_SUCCESS) {
	char errmsg[PJ_ERR_MSG_SIZE];

	if (tdata)
	    pjsip_tx_data_dec_ref(tdata);

	pj_strerror(status, errmsg, sizeof(errmsg));
	PJ_LOG(2, (inv->pool->obj_name, "Session timer fails in %s session, "
					"err code=%d (%s)",
					(as_refresher? "refreshing" : 
						       "terminating"),
					status, errmsg));					
    }
}