/* Scan the closing list, and put pending closing keys to free list.
 * Must do this with ioqueue mutex held.
 */
static void scan_closing_keys(pj_ioqueue_t *ioqueue)
{
    if (!pj_list_empty(&ioqueue->closing_list)) {
	pj_time_val now;
	pj_ioqueue_key_t *key;

	pj_gettickcount(&now);
	
	/* Move closing keys to free list when they've finished the closing
	 * idle time.
	 */
	key = ioqueue->closing_list.next;
	while (key != &ioqueue->closing_list) {
	    pj_ioqueue_key_t *next = key->next;

	    pj_assert(key->closing != 0);

	    if (PJ_TIME_VAL_GTE(now, key->free_time)) {
		pj_list_erase(key);
		pj_list_push_back(&ioqueue->free_list, key);
	    }
	    key = next;
	}
    }
}
Beispiel #2
0
PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
					    pj_timer_entry *entry, 
					    const pj_time_val *delay)
#endif
{
    pj_status_t status;
    pj_time_val expires;

    PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
    PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL);

    /* Prevent same entry from being scheduled more than once */
    PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);

#if PJ_TIMER_DEBUG
    entry->src_file = src_file;
    entry->src_line = src_line;
#endif
    pj_gettickcount(&expires);
    PJ_TIME_VAL_ADD(expires, *delay);
    
    lock_timer_heap(ht);
    status = schedule_entry(ht, entry, &expires);
    unlock_timer_heap(ht);

    return status;
}
Beispiel #3
0
PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, 
                                     pj_time_val *next_delay )
{
    pj_time_val now;
    unsigned count;

    PJ_ASSERT_RETURN(ht, 0);

    lock_timer_heap(ht);
    if (!ht->cur_size && next_delay) {
	next_delay->sec = next_delay->msec = PJ_MAXINT32;
        unlock_timer_heap(ht);
	return 0;
    }

    count = 0;
    pj_gettickcount(&now);

    while ( ht->cur_size && 
	    PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) &&
            count < ht->max_entries_per_poll ) 
    {
	pj_timer_entry *node = remove_node(ht, 0);
	pj_grp_lock_t *grp_lock;

	++count;

	grp_lock = node->_grp_lock;
	node->_grp_lock = NULL;

	unlock_timer_heap(ht);

	PJ_RACE_ME(5);

	if (node->cb)
	    (*node->cb)(ht, node);

	if (grp_lock)
	    pj_grp_lock_dec_ref(grp_lock);

	lock_timer_heap(ht);
    }
    if (ht->cur_size && next_delay) {
	*next_delay = ht->heap[0]->_timer_value;
	PJ_TIME_VAL_SUB(*next_delay, now);
	if (next_delay->sec < 0 || next_delay->msec < 0)
	    next_delay->sec = next_delay->msec = 0;
    } else if (next_delay) {
	next_delay->sec = next_delay->msec = PJ_MAXINT32;
    }
    unlock_timer_heap(ht);

    return count;
}
/* Decrement the key's reference counter, and when the counter reach zero,
 * destroy the key.
 */
static void decrement_counter(pj_ioqueue_key_t *key)
{
    if (pj_atomic_dec_and_get(key->ref_count) == 0) {

	pj_lock_acquire(key->ioqueue->lock);

	pj_assert(key->closing == 1);
	pj_gettickcount(&key->free_time);
	key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY;
	pj_time_val_normalize(&key->free_time);

	pj_list_erase(key);
	pj_list_push_back(&key->ioqueue->closing_list, key);

	pj_lock_release(key->ioqueue->lock);
    }
}
Beispiel #5
0
/* Scan closing keys to be put to free list again */
static void scan_closing_keys(pj_ioqueue_t *ioqueue)
{
    pj_time_val now;
    pj_ioqueue_key_t *h;

    pj_gettickcount(&now);
    h = ioqueue->closing_list.next;
    while (h != &ioqueue->closing_list) {
	pj_ioqueue_key_t *next = h->next;

	pj_assert(h->closing != 0);

	if (PJ_TIME_VAL_GTE(now, h->free_time)) {
	    pj_list_erase(h);
	    pj_list_push_back(&ioqueue->free_list, h);
	}
	h = next;
    }
}
Beispiel #6
0
/* Flush all delayed transmision once the socket is connected. */
static void tcp_flush_pending_tx(struct tcp_transport *tcp)
{
    pj_time_val now;

    pj_gettickcount(&now);
    pj_lock_acquire(tcp->base.lock);
    while (!pj_list_empty(&tcp->delayed_list)) {
	struct delayed_tdata *pending_tx;
	pjsip_tx_data *tdata;
	pj_ioqueue_op_key_t *op_key;
	pj_ssize_t size;
	pj_status_t status;

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

	tdata = pending_tx->tdata_op_key->tdata;
	op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;

        if (pending_tx->timeout.sec > 0 &&
            PJ_TIME_VAL_GT(now, pending_tx->timeout))
        {
            continue;
        }

	/* send! */
	size = tdata->buf.cur - tdata->buf.start;
	status = pj_activesock_send(tcp->asock, op_key, tdata->buf.start, 
				    &size, 0);
	if (status != PJ_EPENDING) {
            pj_lock_release(tcp->base.lock);
	    on_data_sent(tcp->asock, op_key, size);
            pj_lock_acquire(tcp->base.lock);
	}

    }
    pj_lock_release(tcp->base.lock);
}
Beispiel #7
0
PJ_DEF(void) pj_timer_heap_dump(pj_timer_heap_t *ht)
{
    lock_timer_heap(ht);

    PJ_LOG(3,(THIS_FILE, "Dumping timer heap:"));
    PJ_LOG(3,(THIS_FILE, "  Cur size: %d entries, max: %d",
			 (int)ht->cur_size, (int)ht->max_size));

    if (ht->cur_size) {
	unsigned i;
	pj_time_val now;

	PJ_LOG(3,(THIS_FILE, "  Entries: "));
	PJ_LOG(3,(THIS_FILE, "    _id\tId\tElapsed\tSource"));
	PJ_LOG(3,(THIS_FILE, "    ----------------------------------"));

	pj_gettickcount(&now);

	for (i=0; i<(unsigned)ht->cur_size; ++i) {
	    pj_timer_entry *e = ht->heap[i];
	    pj_time_val delta;

	    if (PJ_TIME_VAL_LTE(e->_timer_value, now))
		delta.sec = delta.msec = 0;
	    else {
		delta = e->_timer_value;
		PJ_TIME_VAL_SUB(delta, now);
	    }

	    PJ_LOG(3,(THIS_FILE, "    %d\t%d\t%d.%03d\t%s:%d",
		      e->_timer_id, e->id,
		      (int)delta.sec, (int)delta.msec,
		      e->src_file, e->src_line));
	}
    }

    unlock_timer_heap(ht);
}
Beispiel #8
0
static pj_status_t schedule_w_grp_lock(pj_timer_heap_t *ht,
                                       pj_timer_entry *entry,
                                       const pj_time_val *delay,
                                       pj_bool_t set_id,
                                       int id_val,
                                       pj_grp_lock_t *grp_lock)
#endif
{
    pj_status_t status;
    pj_time_val expires;

    PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
    PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL);

    /* Prevent same entry from being scheduled more than once */
    PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);

#if PJ_TIMER_DEBUG
    entry->src_file = src_file;
    entry->src_line = src_line;
#endif
    pj_gettickcount(&expires);
    PJ_TIME_VAL_ADD(expires, *delay);
    
    lock_timer_heap(ht);
    status = schedule_entry(ht, entry, &expires);
    if (status == PJ_SUCCESS) {
	if (set_id)
	    entry->id = id_val;
	entry->_grp_lock = grp_lock;
	if (entry->_grp_lock) {
	    pj_grp_lock_add_ref(entry->_grp_lock);
	}
    }
    unlock_timer_heap(ht);

    return status;
}
Beispiel #9
0
/* Scan closing keys to be put to free list again */
static void scan_closing_keys(pj_ioqueue_t *ioqueue)
{
    pj_time_val now;
    pj_ioqueue_key_t *h;

    pj_gettickcount(&now);
    h = ioqueue->closing_list.next;
    while (h != &ioqueue->closing_list) {
	pj_ioqueue_key_t *next = h->next;

	pj_assert(h->closing != 0);

	if (PJ_TIME_VAL_GTE(now, h->free_time)) {
	    pj_list_erase(h);
	    // Don't set grp_lock to NULL otherwise the other thread
	    // will crash. Just leave it as dangling pointer, but this
	    // should be safe
	    //h->grp_lock = NULL;
	    pj_list_push_back(&ioqueue->free_list, h);
	}
	h = next;
    }
}
Beispiel #10
0
/* 
 * This callback is called by transport manager to send SIP message 
 */
static pj_status_t tcp_send_msg(pjsip_transport *transport, 
				pjsip_tx_data *tdata,
				const pj_sockaddr_t *rem_addr,
				int addr_len,
				void *token,
				pjsip_transport_callback callback)
{
    struct tcp_transport *tcp = (struct tcp_transport*)transport;
    pj_ssize_t size;
    pj_bool_t delayed = PJ_FALSE;
    pj_status_t status = PJ_SUCCESS;

    /* Sanity check */
    PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);

    /* Check that there's no pending operation associated with the tdata */
    PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
    
    /* Check the address is supported */
    PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) ||
	                          addr_len==sizeof(pj_sockaddr_in6)),
	             PJ_EINVAL);

    /* Init op key. */
    tdata->op_key.tdata = tdata;
    tdata->op_key.token = token;
    tdata->op_key.callback = callback;

    /* If asynchronous connect() has not completed yet, just put the
     * transmit data in the pending transmission list since we can not
     * use the socket yet.
     */
    if (tcp->has_pending_connect) {

	/*
	 * Looks like connect() is still in progress. Check again (this time
	 * with holding the lock) to be sure.
	 */
	pj_lock_acquire(tcp->base.lock);

	if (tcp->has_pending_connect) {
	    struct delayed_tdata *delayed_tdata;

	    /*
	     * connect() is still in progress. Put the transmit data to
	     * the delayed list.
             * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583),
             * we also add timeout value for the transmit data. When the
             * connect() is completed, the timeout value will be checked to
             * determine whether the transmit data needs to be sent.
	     */
	    delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool, 
					     struct delayed_tdata);
	    delayed_tdata->tdata_op_key = &tdata->op_key;
            if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) {
                pj_gettickcount(&delayed_tdata->timeout);
                delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td;
                pj_time_val_normalize(&delayed_tdata->timeout);
            }

	    pj_list_push_back(&tcp->delayed_list, delayed_tdata);
	    status = PJ_EPENDING;

	    /* Prevent pj_ioqueue_send() to be called below */
	    delayed = PJ_TRUE;
	}

	pj_lock_release(tcp->base.lock);
    } 
Beispiel #11
0
PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
        const pjstun_setting *opt,
        int sock_cnt,
        pj_sock_t sock[],
        pj_sockaddr_in mapped_addr[])
{
    unsigned srv_cnt;
    const pj_str_t *srv1, *srv2;
    int port1, port2;
    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();

    srv1 = &opt->srv1;
    port1 = opt->port1;
    srv2 = &opt->srv1;
    port2 = opt->port2;

    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;

    /* Insert magic cookie (specified in RFC 5389) when requested to. */
    if (opt->use_stun2) {
        pjstun_msg_hdr *hdr = (pjstun_msg_hdr*)out_msg;
        hdr->tsx[0] = pj_htonl(STUN_MAGIC);
    }

    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_gettickcount(&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_gettickcount(&now), select_rc=1;
                status==PJ_SUCCESS && select_rc>=1 && wait_resp>0
                && PJ_TIME_VAL_LT(now, next_tx);
                pj_gettickcount(&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;
    }