Example #1
0
static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,
                        size_t slot, size_t child)
{
    PJ_CHECK_STACK();

    // Restore the heap property after a deletion.
    
    while (child < ht->cur_size)
    {
	// Choose the smaller of the two children.
	if (child + 1 < ht->cur_size
	    && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value))
	    child++;
	
	// Perform a <copy> if the child has a larger timeout value than
	// the <moved_node>.
	if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value))
        {
	    copy_node( ht, slot, ht->heap[child]);
	    slot = child;
	    child = HEAP_LEFT(child);
        }
	else
	    // We've found our location in the heap.
	    break;
    }
    
    copy_node( ht, slot, moved_node);
}
Example #2
0
static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,
		       size_t slot, size_t parent)
{
    // Restore the heap property after an insertion.
    
    while (slot > 0)
    {
	// If the parent node is greater than the <moved_node> we need
	// to copy it down.
	if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value))
        {
	    copy_node(ht, slot, ht->heap[parent]);
	    slot = parent;
	    parent = HEAP_PARENT(slot);
        }
	else
	    break;
    }
    
    // Insert the new node into its proper resting place in the heap and
    // update the corresponding slot in the parallel <timer_ids> array.
    copy_node(ht, slot, moved_node);
}
Example #3
0
 bool operator <  (const Pj_Time_Val &rhs) const 
 { 
     return PJ_TIME_VAL_LT((*this), rhs);  
 }
Example #4
0
/*
 * Get NTP time.
 */
PJ_DEF(pj_status_t) pjmedia_rtcp_get_ntp_time(const pjmedia_rtcp_session *sess,
					      pjmedia_rtcp_ntp_rec *ntp)
{
/* Seconds between 1900-01-01 to 1970-01-01 */
#define JAN_1970  (2208988800UL)
    pj_timestamp ts;
    pj_status_t status;

    status = pj_get_timestamp(&ts);

    /* Fill up the high 32bit part */
    ntp->hi = (pj_uint32_t)((ts.u64 - sess->ts_base.u64) / sess->ts_freq.u64)
	      + sess->tv_base.sec + JAN_1970;

    /* Calculate seconds fractions */
    ts.u64 = (ts.u64 - sess->ts_base.u64) % sess->ts_freq.u64;
    pj_assert(ts.u64 < sess->ts_freq.u64);
    ts.u64 = (ts.u64 << 32) / sess->ts_freq.u64;

    /* Fill up the low 32bit part */
    ntp->lo = ts.u32.lo;


#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
    (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0)

    /* On Win32, since we use QueryPerformanceCounter() as the backend
     * timestamp API, we need to protect against this bug:
     *   Performance counter value may unexpectedly leap forward
     *   http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
     */
    {
	/*
	 * Compare elapsed time reported by timestamp with actual elapsed 
	 * time. If the difference is too excessive, then we use system
	 * time instead.
	 */

	/* MIN_DIFF needs to be large enough so that "normal" diff caused
	 * by system activity or context switch doesn't trigger the time
	 * correction.
	 */
	enum { MIN_DIFF = 400 };

	pj_time_val ts_time, elapsed, diff;

	pj_gettimeofday(&elapsed);

	ts_time.sec = ntp->hi - sess->tv_base.sec - JAN_1970;
	ts_time.msec = (long)(ntp->lo * 1000.0 / 0xFFFFFFFF);

	PJ_TIME_VAL_SUB(elapsed, sess->tv_base);

	if (PJ_TIME_VAL_LT(ts_time, elapsed)) {
	    diff = elapsed;
	    PJ_TIME_VAL_SUB(diff, ts_time);
	} else {
	    diff = ts_time;
	    PJ_TIME_VAL_SUB(diff, elapsed);
	}

	if (PJ_TIME_VAL_MSEC(diff) >= MIN_DIFF) {

	    TRACE_((sess->name, "RTCP NTP timestamp corrected by %d ms",
		    PJ_TIME_VAL_MSEC(diff)));


	    ntp->hi = elapsed.sec + sess->tv_base.sec + JAN_1970;
	    ntp->lo = (elapsed.msec * 65536 / 1000) << 16;
	}

    }
#endif

    return status;
}
Example #5
0
int timestamp_test(void)
{
    enum { CONSECUTIVE_LOOP = 100 };
    volatile unsigned i;
    pj_timestamp freq, t1, t2;
    pj_time_val tv1, tv2;
    unsigned elapsed;
    pj_status_t rc;

    PJ_LOG(3,(THIS_FILE, "...Testing timestamp (high res time)"));
    
    /* Get and display timestamp frequency. */
    if ((rc=pj_get_timestamp_freq(&freq)) != PJ_SUCCESS) {
	app_perror("...ERROR: get timestamp freq", rc);
	return -1000;
    }

    PJ_LOG(3,(THIS_FILE, "....frequency: hiword=%lu loword=%lu", 
			freq.u32.hi, freq.u32.lo));

    PJ_LOG(3,(THIS_FILE, "...checking if time can run backwards (pls wait).."));

    /*
     * Check if consecutive readings should yield timestamp value
     * that is bigger than previous value.
     * First we get the first timestamp.
     */
    rc = pj_get_timestamp(&t1);
    if (rc != PJ_SUCCESS) {
	app_perror("...ERROR: pj_get_timestamp", rc);
	return -1001;
    }
    rc = pj_gettimeofday(&tv1);
    if (rc != PJ_SUCCESS) {
	app_perror("...ERROR: pj_gettimeofday", rc);
	return -1002;
    }
    for (i=0; i<CONSECUTIVE_LOOP; ++i) {
        
        pj_thread_sleep(pj_rand() % 100);

	rc = pj_get_timestamp(&t2);
	if (rc != PJ_SUCCESS) {
	    app_perror("...ERROR: pj_get_timestamp", rc);
	    return -1003;
	}
	rc = pj_gettimeofday(&tv2);
	if (rc != PJ_SUCCESS) {
	    app_perror("...ERROR: pj_gettimeofday", rc);
	    return -1004;
	}

	/* compare t2 with t1, expecting t2 >= t1. */
	if (t2.u32.hi < t1.u32.hi ||
	    (t2.u32.hi == t1.u32.hi && t2.u32.lo < t1.u32.lo))
	{
	    PJ_LOG(3,(THIS_FILE, "...ERROR: timestamp run backwards!"));
	    return -1005;
	}

	/* compare tv2 with tv1, expecting tv2 >= tv1. */
	if (PJ_TIME_VAL_LT(tv2, tv1)) {
	    PJ_LOG(3,(THIS_FILE, "...ERROR: time run backwards!"));
	    return -1006;
	}
    }

    /* 
     * Simple test to time some loop. 
     */
    PJ_LOG(3,(THIS_FILE, "....testing simple 1000000 loop"));


    /* Mark start time. */
    if ((rc=pj_get_timestamp(&t1)) != PJ_SUCCESS) {
	app_perror("....error: cat't get timestamp", rc);
	return -1010;
    }

    /* Loop.. */
    for (i=0; i<1000000; ++i) {
	/* Try to do something so that smart compilers wont
	 * remove this silly loop.
	 */
	null_func();
    }

    pj_thread_sleep(0);

    /* Mark end time. */
    pj_get_timestamp(&t2);

    /* Get elapsed time in usec. */
    elapsed = pj_elapsed_usec(&t1, &t2);
    PJ_LOG(3,(THIS_FILE, "....elapsed: %u usec", (unsigned)elapsed));

    /* See if elapsed time is "reasonable". 
     * This should be good even on 50Mhz embedded powerpc.
     */
    if (elapsed < 1 || elapsed > 1000000) {
	PJ_LOG(3,(THIS_FILE, "....error: elapsed time outside window (%u, "
			     "t1.u32.hi=%u, t1.u32.lo=%u, "
			     "t2.u32.hi=%u, t2.u32.lo=%u)",
			     elapsed, 
			     t1.u32.hi, t1.u32.lo, t2.u32.hi, t2.u32.lo));
	return -1030;
    }

    /* Testing time/timestamp accuracy */
    rc = timestamp_accuracy();
    if (rc != 0)
	return rc;

    return 0;
}
Example #6
0
/*
 * Keep-alive test.
 */
static int keep_alive_test(pj_stun_config *cfg)
{
    struct stun_srv *srv;
    struct stun_client *client;
    pj_sockaddr_in mapped_addr;
    pj_stun_sock_info info;
    pj_str_t srv_addr;
    pj_time_val timeout, t;
    int i, ret = 0;
    pj_status_t status;

    PJ_LOG(3,(THIS_FILE, "  normal operation"));

    status =  create_client(cfg, &client, PJ_TRUE);
    if (status != PJ_SUCCESS)
	return -310;

    status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv);
    if (status != PJ_SUCCESS) {
	destroy_client(client);
	return -320;
    }

    /*
     * Part 1: initial Binding resolution.
     */
    PJ_LOG(3,(THIS_FILE, "    initial Binding request"));
    srv_addr = pj_str("127.0.0.1");
    status = pj_stun_sock_start(client->sock, &srv_addr, 
				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
    if (status != PJ_SUCCESS) {
	destroy_server(srv);
	destroy_client(client);
	return -330;
    }

    /* Wait until on_status() callback is called with success status */
    pj_gettimeofday(&timeout);
    timeout.sec += 60;
    do {
	handle_events(cfg, 100);
	pj_gettimeofday(&t);
    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));

    /* Check that callback with correct operation is called */
    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
	ret = -340;
	goto on_return;
    }
    if (client->last_status != PJ_SUCCESS) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting PJ_SUCCESS status"));
	ret = -350;
	goto on_return;
    }
    /* Check that client doesn't receive anything */
    if (client->on_rx_data_cnt != 0) {
	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
	ret = -370;
	goto on_return;
    }

    /* Get info */
    pj_bzero(&info, sizeof(info));
    pj_stun_sock_get_info(client->sock, &info);

    /* Check that we have server address */
    if (!pj_sockaddr_has_addr(&info.srv_addr)) {
	PJ_LOG(3,(THIS_FILE, "    error: missing server address"));
	ret = -380;
	goto on_return;
    }
    /* .. and bound address port must not be zero */
    if (pj_sockaddr_get_port(&info.bound_addr)==0) {
	PJ_LOG(3,(THIS_FILE, "    error: bound address is zero"));
	ret = -381;
	goto on_return;
    }
    /* .. and mapped address */
    if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
	PJ_LOG(3,(THIS_FILE, "    error: missing mapped address"));
	ret = -382;
	goto on_return;
    }
    /* verify the mapped address */
    pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
    if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
	PJ_LOG(3,(THIS_FILE, "    error: mapped address mismatched"));
	ret = -383;
	goto on_return;
    }

    /* .. and at least one alias */
    if (info.alias_cnt == 0) {
	PJ_LOG(3,(THIS_FILE, "    error: must have at least one alias"));
	ret = -384;
	goto on_return;
    }
    if (!pj_sockaddr_has_addr(&info.aliases[0])) {
	PJ_LOG(3,(THIS_FILE, "    error: missing alias"));
	ret = -386;
	goto on_return;
    }


    /*
     * Part 2: sending and receiving data
     */
    PJ_LOG(3,(THIS_FILE, "    sending/receiving data"));

    /* Change server operation mode to echo back data */
    srv->flag = ECHO;

    /* Reset server */
    srv->rx_cnt = 0;

    /* Client sending data to echo server */
    {
	char txt[100];
	PJ_LOG(3,(THIS_FILE, "     sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3)));
    }
    status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret),
				 0, &info.srv_addr, 
				 pj_sockaddr_get_len(&info.srv_addr));
    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
	app_perror("    error: server sending data", status);
	ret = -390;
	goto on_return;
    }

    /* Wait for a short period until client receives data. We can't wait for
     * too long otherwise the keep-alive will kick in.
     */
    pj_gettimeofday(&timeout);
    timeout.sec += 1;
    do {
	handle_events(cfg, 100);
	pj_gettimeofday(&t);
    } while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout));

    /* Check that data is received in server */
    if (srv->rx_cnt == 0) {
	PJ_LOG(3,(THIS_FILE, "    error: server didn't receive data"));
	ret = -395;
	goto on_return;
    }

    /* Check that status is still OK */
    if (client->last_status != PJ_SUCCESS) {
	app_perror("    error: client has failed", client->last_status);
	ret = -400;
	goto on_return;
    }
    /* Check that data has been received */
    if (client->on_rx_data_cnt == 0) {
	PJ_LOG(3,(THIS_FILE, "    error: client doesn't receive data"));
	ret = -410;
	goto on_return;
    }

    /*
     * Part 3: Successful keep-alive,
     */
    PJ_LOG(3,(THIS_FILE, "    successful keep-alive scenario"));

    /* Change server operation mode to normal mode */
    srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;

    /* Reset server */
    srv->rx_cnt = 0;

    /* Reset client */
    client->on_status_cnt = 0;
    client->last_status = PJ_SUCCESS;
    client->on_rx_data_cnt = 0;

    /* Wait for keep-alive duration to see if client actually sends the
     * keep-alive.
     */
    pj_gettimeofday(&timeout);
    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
    do {
	handle_events(cfg, 100);
	pj_gettimeofday(&t);
    } while (PJ_TIME_VAL_LT(t, timeout));

    /* Check that server receives some packets */
    if (srv->rx_cnt == 0) {
	PJ_LOG(3, (THIS_FILE, "    error: no keep-alive was received"));
	ret = -420;
	goto on_return;
    }
    /* Check that client status is still okay and on_status() callback is NOT
     * called
     */
    /* No longer valid due to this ticket:
     *  http://trac.pjsip.org/repos/ticket/742

    if (client->on_status_cnt != 0) {
	PJ_LOG(3, (THIS_FILE, "    error: on_status() must not be called on successful"
			      "keep-alive when mapped-address does not change"));
	ret = -430;
	goto on_return;
    }
    */
    /* Check that client doesn't receive anything */
    if (client->on_rx_data_cnt != 0) {
	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
	ret = -440;
	goto on_return;
    }


    /*
     * Part 4: Successful keep-alive with IP address change
     */
    PJ_LOG(3,(THIS_FILE, "    mapped IP address change"));

    /* Change server operation mode to normal mode */
    srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;

    /* Change mapped address in the response */
    srv->ip_to_send = pj_str("2.2.2.2");
    srv->port_to_send++;

    /* Reset server */
    srv->rx_cnt = 0;

    /* Reset client */
    client->on_status_cnt = 0;
    client->last_status = PJ_SUCCESS;
    client->on_rx_data_cnt = 0;

    /* Wait for keep-alive duration to see if client actually sends the
     * keep-alive.
     */
    pj_gettimeofday(&timeout);
    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
    do {
	handle_events(cfg, 100);
	pj_gettimeofday(&t);
    } while (PJ_TIME_VAL_LT(t, timeout));

    /* Check that server receives some packets */
    if (srv->rx_cnt == 0) {
	PJ_LOG(3, (THIS_FILE, "    error: no keep-alive was received"));
	ret = -450;
	goto on_return;
    }
    /* Check that on_status() callback is called (because mapped address
     * has changed)
     */
    if (client->on_status_cnt != 1) {
	PJ_LOG(3, (THIS_FILE, "    error: on_status() was not called"));
	ret = -460;
	goto on_return;
    }
    /* Check that callback was called with correct operation */
    if (client->last_op != PJ_STUN_SOCK_MAPPED_ADDR_CHANGE) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting keep-alive operation status"));
	ret = -470;
	goto on_return;
    }
    /* Check that last status is still success */
    if (client->last_status != PJ_SUCCESS) {
	PJ_LOG(3, (THIS_FILE, "    error: expecting successful status"));
	ret = -480;
	goto on_return;
    }
    /* Check that client doesn't receive anything */
    if (client->on_rx_data_cnt != 0) {
	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
	ret = -490;
	goto on_return;
    }

    /* Get info */
    pj_bzero(&info, sizeof(info));
    pj_stun_sock_get_info(client->sock, &info);

    /* Check that we have server address */
    if (!pj_sockaddr_has_addr(&info.srv_addr)) {
	PJ_LOG(3,(THIS_FILE, "    error: missing server address"));
	ret = -500;
	goto on_return;
    }
    /* .. and mapped address */
    if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
	PJ_LOG(3,(THIS_FILE, "    error: missing mapped address"));
	ret = -510;
	goto on_return;
    }
    /* verify the mapped address */
    pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
    if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
	PJ_LOG(3,(THIS_FILE, "    error: mapped address mismatched"));
	ret = -520;
	goto on_return;
    }

    /* .. and at least one alias */
    if (info.alias_cnt == 0) {
	PJ_LOG(3,(THIS_FILE, "    error: must have at least one alias"));
	ret = -530;
	goto on_return;
    }
    if (!pj_sockaddr_has_addr(&info.aliases[0])) {
	PJ_LOG(3,(THIS_FILE, "    error: missing alias"));
	ret = -540;
	goto on_return;
    }


    /*
     * Part 5: Failed keep-alive
     */
    PJ_LOG(3,(THIS_FILE, "    failed keep-alive scenario"));
    
    /* Change server operation mode to respond without attribute */
    srv->flag = RESPOND_STUN;

    /* Reset server */
    srv->rx_cnt = 0;

    /* Reset client */
    client->on_status_cnt = 0;
    client->last_status = PJ_SUCCESS;
    client->on_rx_data_cnt = 0;

    /* Wait until on_status() is called with failure. */
    pj_gettimeofday(&timeout);
    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5);
    do {
	handle_events(cfg, 100);
	pj_gettimeofday(&t);
    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));

    /* Check that callback with correct operation is called */
    if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting keep-alive operation status"));
	ret = -600;
	goto on_return;
    }
    if (client->last_status == PJ_SUCCESS) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting failed keep-alive"));
	ret = -610;
	goto on_return;
    }
    /* Check that client doesn't receive anything */
    if (client->on_rx_data_cnt != 0) {
	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
	ret = -620;
	goto on_return;
    }


on_return:
    destroy_server(srv);
    destroy_client(client);
    for (i=0; i<7; ++i)
	handle_events(cfg, 50);
    return ret;
}
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, j, 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 = FD_SETSIZE-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) {
	    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 #8
0
/*
 * Timeout test: scenario when no response is received from server
 */
static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
{
    struct stun_srv *srv;
    struct stun_client *client;
    pj_str_t srv_addr;
    pj_time_val timeout, t;
    int ret = 0;
    pj_status_t status;

    PJ_LOG(3,(THIS_FILE, "  timeout test [%d]", destroy_on_err));

    status =  create_client(cfg, &client, destroy_on_err);
    if (status != PJ_SUCCESS)
	return -10;

    status = create_server(client->pool, cfg->ioqueue, 0, &srv);
    if (status != PJ_SUCCESS) {
	destroy_client(client);
	return -20;
    }

    srv_addr = pj_str("127.0.0.1");
    status = pj_stun_sock_start(client->sock, &srv_addr, 
				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
    if (status != PJ_SUCCESS) {
	destroy_server(srv);
	destroy_client(client);
	return -30;
    }

    /* Wait until on_status() callback is called with the failure */
    pj_gettimeofday(&timeout);
    timeout.sec += 60;
    do {
	handle_events(cfg, 100);
	pj_gettimeofday(&t);
    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));

    /* Check that callback with correct operation is called */
    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
	ret = -40;
	goto on_return;
    }
    /* .. and with the correct status */
    if (client->last_status != PJNATH_ESTUNTIMEDOUT) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting PJNATH_ESTUNTIMEDOUT"));
	ret = -50;
	goto on_return;
    }
    /* Check that server received correct retransmissions */
    if (srv->rx_cnt != PJ_STUN_MAX_TRANSMIT_COUNT) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting %d retransmissions, got %d",
		   PJ_STUN_MAX_TRANSMIT_COUNT, srv->rx_cnt));
	ret = -60;
	goto on_return;
    }
    /* Check that client doesn't receive anything */
    if (client->on_rx_data_cnt != 0) {
	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
	ret = -70;
	goto on_return;
    }

on_return:
    destroy_server(srv);
    destroy_client(client);
    return ret;
}
Example #9
0
/*
 * Invalid response scenario: when server returns no MAPPED-ADDRESS or
 * XOR-MAPPED-ADDRESS attribute.
 */
static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
{
    struct stun_srv *srv;
    struct stun_client *client;
    pj_str_t srv_addr;
    pj_time_val timeout, t;
    int i, ret = 0;
    pj_status_t status;

    PJ_LOG(3,(THIS_FILE, "  missing attribute test [%d]", destroy_on_err));

    status =  create_client(cfg, &client, destroy_on_err);
    if (status != PJ_SUCCESS)
	return -110;

    status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, &srv);
    if (status != PJ_SUCCESS) {
	destroy_client(client);
	return -120;
    }

    srv_addr = pj_str("127.0.0.1");
    status = pj_stun_sock_start(client->sock, &srv_addr, 
				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
    if (status != PJ_SUCCESS) {
	destroy_server(srv);
	destroy_client(client);
	return -130;
    }

    /* Wait until on_status() callback is called with the failure */
    pj_gettimeofday(&timeout);
    timeout.sec += 60;
    do {
	handle_events(cfg, 100);
	pj_gettimeofday(&t);
    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));

    /* Check that callback with correct operation is called */
    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
	ret = -140;
	goto on_return;
    }
    if (client->last_status != PJNATH_ESTUNNOMAPPEDADDR) {
	PJ_LOG(3,(THIS_FILE, "    error: expecting PJNATH_ESTUNNOMAPPEDADDR"));
	ret = -150;
	goto on_return;
    }
    /* Check that client doesn't receive anything */
    if (client->on_rx_data_cnt != 0) {
	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
	ret = -170;
	goto on_return;
    }

on_return:
    destroy_server(srv);
    destroy_client(client);
    for (i=0; i<7; ++i)
	handle_events(cfg, 50);
    return ret;
}
Example #10
0
PJ_DECL(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[])
{
    pj_sockaddr_in srv_addr[2];
    int i, j, send_cnt = 0;
    pj_pool_t *pool;
    struct {
	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();

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


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


    /* 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;

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

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

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

    /* 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) {
	    for (j=0; j<2 && status==PJ_SUCCESS; ++j) {
		pjstun_msg_hdr *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));
		if (status == PJ_SUCCESS)
		    ++wait_resp;
	    }
	}

	/* 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.
	 */

	/* 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(FD_SETSIZE, &r, NULL, NULL, &timeout);
	    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);

		--wait_resp;

		if (status != PJ_SUCCESS)
		    continue;

		status = pjstun_parse_msg(recv_buf, len, &msg);
		if (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 || 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 (pjstun_msg_find_attr(&msg, PJSTUN_ATTR_ERROR_CODE) != NULL) {
		    status = PJLIB_UTIL_ESTUNRECVERRATTR;
		    continue;
		}

		attr = (void*)pjstun_msg_find_attr(&msg, PJSTUN_ATTR_MAPPED_ADDR);
		if (!attr) {
		    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;
	    }
	}

	/* 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;
    }

    for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
	if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
	    rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
	{
	    mapped_addr[i].sin_family = PJ_AF_INET;
	    mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
	    mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;

	    if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
		status = PJLIB_UTIL_ESTUNNOTRESPOND;
		break;
	    }
	} else {
	    status = PJLIB_UTIL_ESTUNSYMMETRIC;
	    break;
	}
    }

    pj_pool_release(pool);

    return status;

on_error:
    if (pool) pj_pool_release(pool);
    return status;
}
Example #11
0
/* 
 * 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;
}
Example #12
0
/*****************************************************************************
 **
 ** 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;
}
/* Client worker thread */
static int client_thread(void *arg)
{
    pj_time_val end_time, last_report, now;
    unsigned thread_index = (unsigned)(long)arg;
    unsigned cycle = 0, last_cycle = 0;

    pj_thread_sleep(100);

    pj_gettimeofday(&end_time);
    end_time.sec += app.client.timeout;

    pj_gettimeofday(&last_report);

    if (app.client.first_request.sec == 0) {
	pj_gettimeofday(&app.client.first_request);
    }

    /* Submit all jobs */
    while (app.client.job_submitted < app.client.job_count && !app.thread_quit){
	pj_time_val timeout = { 0, 1 };
	unsigned i;
	int outstanding;
	pj_status_t status;

	/* Calculate current outstanding job */
	outstanding = app.client.job_submitted - app.client.job_finished;

	/* Update stats on max outstanding jobs */
	if (outstanding > (int)app.client.stat_max_window)
	    app.client.stat_max_window = outstanding;

	/* Wait if there are more pending jobs than allowed in the
	 * window. But spawn a new job anyway if no events are happening
	 * after we wait for some time.
	 */
	for (i=0; outstanding > (int)app.client.job_window && i<1000; ++i) {
	    pj_time_val wait = { 0, 500 };
	    unsigned count = 0;

	    pjsip_endpt_handle_events2(app.sip_endpt, &wait, &count);
	    outstanding = app.client.job_submitted - app.client.job_finished;

	    if (count == 0)
		break;

	    ++cycle;
	}


	/* Submit one job */
	if (app.client.method.id == PJSIP_INVITE_METHOD) {
	    status = make_call(&app.client.dst_uri);
	} else if (app.client.stateless) {
	    status = submit_stateless_job();
	} else {
	    status = submit_job();
	}

	++app.client.job_submitted;
	++cycle;

	/* Handle event */
	pjsip_endpt_handle_events2(app.sip_endpt, &timeout, NULL);

	/* Check for time out, also print report */
	if (cycle - last_cycle >= 500) {
	    pj_gettimeofday(&now);
	    if (PJ_TIME_VAL_GTE(now, end_time)) {
		break;
	    }
	    last_cycle = cycle;

	    
	    if (thread_index == 0 && now.sec-last_report.sec >= 2) {
		printf("\r%d jobs started, %d completed...   ",
		       app.client.job_submitted, app.client.job_finished);
		fflush(stdout);
		last_report = now;
	    }
	}
    }

    if (app.client.requests_sent.sec == 0) {
	pj_gettimeofday(&app.client.requests_sent);
    }


    if (thread_index == 0) {
	printf("\r%d jobs started, %d completed%s\n",
	       app.client.job_submitted, app.client.job_finished,
	       (app.client.job_submitted!=app.client.job_finished ? 
		", waiting..." : ".") );
	fflush(stdout);
    }

    /* Wait until all jobs completes, or timed out */
    pj_gettimeofday(&now);
    while (PJ_TIME_VAL_LT(now, end_time) && 
	   app.client.job_finished < app.client.job_count && 
	   !app.thread_quit) 
    {
	pj_time_val timeout = { 0, 1 };
	unsigned i;

	for (i=0; i<1000; ++i) {
	    unsigned count;
	    count = 0;
	    pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
	    if (count == 0)
		break;
	}

	pj_gettimeofday(&now);
    }

    /* Wait couple of seconds to let jobs completes (e.g. ACKs to be sent)  */
    pj_gettimeofday(&now);
    end_time = now;
    end_time.sec += 2;
    while (PJ_TIME_VAL_LT(now, end_time)) 
    {
	pj_time_val timeout = { 0, 1 };
	unsigned i;

	for (i=0; i<1000; ++i) {
	    unsigned count;
	    count = 0;
	    pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
	    if (count == 0)
		break;
	}

	pj_gettimeofday(&now);
    }

    return 0;
}
Example #14
0
static int sleep_duration_test(void)
{
    enum { MIS = 20};
    unsigned duration[] = { 2000, 1000, 500, 200, 100 };
    unsigned i;
    pj_status_t rc;

    PJ_LOG(3,(THIS_FILE, "..running sleep duration test"));

    /* Test pj_thread_sleep() and pj_gettimeofday() */
    for (i=0; i<PJ_ARRAY_SIZE(duration); ++i) {
        pj_time_val start, stop;
	pj_uint32_t msec;

        /* Mark start of test. */
        rc = pj_gettimeofday(&start);
        if (rc != PJ_SUCCESS) {
            app_perror("...error: pj_gettimeofday()", rc);
            return -10;
        }

        /* Sleep */
        rc = pj_thread_sleep(duration[i]);
        if (rc != PJ_SUCCESS) {
            app_perror("...error: pj_thread_sleep()", rc);
            return -20;
        }

        /* Mark end of test. */
        rc = pj_gettimeofday(&stop);

        /* Calculate duration (store in stop). */
        PJ_TIME_VAL_SUB(stop, start);

	/* Convert to msec. */
	msec = PJ_TIME_VAL_MSEC(stop);

	/* Check if it's within range. */
	if (msec < duration[i] * (100-MIS)/100 ||
	    msec > duration[i] * (100+MIS)/100)
	{
	    PJ_LOG(3,(THIS_FILE, 
		      "...error: slept for %d ms instead of %d ms "
		      "(outside %d%% err window)",
		      msec, duration[i], MIS));
	    return -30;
	}
    }


    /* Test pj_thread_sleep() and pj_get_timestamp() and friends */
    for (i=0; i<PJ_ARRAY_SIZE(duration); ++i) {
	pj_time_val t1, t2;
        pj_timestamp start, stop;
	pj_uint32_t msec;

	pj_thread_sleep(0);

        /* Mark start of test. */
        rc = pj_get_timestamp(&start);
        if (rc != PJ_SUCCESS) {
            app_perror("...error: pj_get_timestamp()", rc);
            return -60;
        }

	/* ..also with gettimeofday() */
	pj_gettimeofday(&t1);

        /* Sleep */
        rc = pj_thread_sleep(duration[i]);
        if (rc != PJ_SUCCESS) {
            app_perror("...error: pj_thread_sleep()", rc);
            return -70;
        }

        /* Mark end of test. */
        pj_get_timestamp(&stop);

	/* ..also with gettimeofday() */
	pj_gettimeofday(&t2);

	/* Compare t1 and t2. */
	if (PJ_TIME_VAL_LT(t2, t1)) {
	    PJ_LOG(3,(THIS_FILE, "...error: t2 is less than t1!!"));
	    return -75;
	}

        /* Get elapsed time in msec */
        msec = pj_elapsed_msec(&start, &stop);

	/* Check if it's within range. */
	if (msec < duration[i] * (100-MIS)/100 ||
	    msec > duration[i] * (100+MIS)/100)
	{
	    PJ_LOG(3,(THIS_FILE, 
		      "...error: slept for %d ms instead of %d ms "
		      "(outside %d%% err window)",
		      msec, duration[i], MIS));
	    PJ_TIME_VAL_SUB(t2, t1);
	    PJ_LOG(3,(THIS_FILE, 
		      "...info: gettimeofday() reported duration is "
		      "%d msec",
		      PJ_TIME_VAL_MSEC(t2)));

	    return -76;
	}
    }

    /* All done. */
    return 0;
}