Ejemplo n.º 1
0
/*
 * pj_ioqueue_register_sock()
 *
 * Register a socket to ioqueue.
 */
PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
					      pj_ioqueue_t *ioqueue,
					      pj_sock_t sock,
					      void *user_data,
					      const pj_ioqueue_callback *cb,
                                              pj_ioqueue_key_t **p_key)
{
    pj_ioqueue_key_t *key = NULL;
    pj_uint32_t value;
    struct epoll_event ev;
    int status;
    pj_status_t rc = PJ_SUCCESS;
    
    PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
                     cb && p_key, PJ_EINVAL);

    pj_lock_acquire(ioqueue->lock);

    if (ioqueue->count >= ioqueue->max) {
        rc = PJ_ETOOMANY;
	TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files"));
	goto on_return;
    }

    /* Set socket to nonblocking. */
    value = 1;
    if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) {
	TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d", 
                rc));
        rc = pj_get_netos_error();
	goto on_return;
    }

    /* If safe unregistration (PJ_IOQUEUE_HAS_SAFE_UNREG) is used, get
     * the key from the free list. Otherwise allocate a new one. 
     */
#if PJ_IOQUEUE_HAS_SAFE_UNREG

    /* Scan closing_keys first to let them come back to free_list */
    scan_closing_keys(ioqueue);

    pj_assert(!pj_list_empty(&ioqueue->free_list));
    if (pj_list_empty(&ioqueue->free_list)) {
	rc = PJ_ETOOMANY;
	goto on_return;
    }

    key = ioqueue->free_list.next;
    pj_list_erase(key);
#else
    /* Create key. */
    key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
#endif

    rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb);
    if (rc != PJ_SUCCESS) {
	key = NULL;
	goto on_return;
    }

    /* Create key's mutex */
 /*   rc = pj_mutex_create_recursive(pool, NULL, &key->mutex);
    if (rc != PJ_SUCCESS) {
	key = NULL;
	goto on_return;
    }
*/
    /* os_epoll_ctl. */
    ev.events = EPOLLIN | EPOLLERR;
    ev.epoll_data = (epoll_data_type)key;
    status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev);
    if (status < 0) {
	rc = pj_get_os_error();
	pj_mutex_destroy(key->mutex);
	key = NULL;
	TRACE_((THIS_FILE, 
                "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d", 
                status));
	goto on_return;
    }
    
    /* Register */
    pj_list_insert_before(&ioqueue->active_list, key);
    ++ioqueue->count;

    //TRACE_((THIS_FILE, "socket registered, count=%d", ioqueue->count));

on_return:
    *p_key = key;
    pj_lock_release(ioqueue->lock);
    
    return rc;
}
Ejemplo n.º 2
0
PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )
{
    if (ht->lock) {
	pj_lock_release(ht->lock);
    }
}
Ejemplo n.º 3
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);
    }
Ejemplo n.º 4
0
/**
 * Notify TCP client session upon receiving a packet from server.
 * The packet maybe a STUN packet or ChannelData packet.
 */
PJ_DEF(pj_status_t) pj_tcp_session_on_rx_pkt(pj_tcp_session *sess,
        void *pkt,
        unsigned pkt_len,
        pj_size_t *parsed_len)
{
    pj_bool_t is_stun = PJ_FALSE;
    pj_status_t stun_check;
    pj_status_t status;
    char buf[PJ_INET6_ADDRSTRLEN+20];

    pj_uint16_t data_len;

    /* Packet could be ChannelData or STUN message (response or
     * indication).
     */
    /* Start locking the session */
    pj_lock_acquire(sess->lock);

    /* Quickly check if this is STUN message */
    //is_stun = (((pj_uint16_t*)pkt)[0] != NATNL_UDT_HEADER_MAGIC);
    stun_check = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_len,
                                   /*PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET*/ 0);

    if (stun_check == PJ_SUCCESS)
        is_stun = PJ_TRUE;

    if (is_stun) {
#if 1
        /* This looks like STUN, give it to the STUN session */
        unsigned options;
        pj_size_t msg_len;

        options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;

        msg_len = GETVAL16H((const pj_uint8_t*)pkt, 2);

        *parsed_len = (msg_len+sizeof(pj_stun_msg_hdr) <= pkt_len) ?
                      msg_len+sizeof(struct pj_stun_msg_hdr) : 0;

        /* Notify application */
        if (sess->cb.on_rx_data) {
            (*sess->cb.on_rx_data)(sess, pkt, pkt_len,
                                   sess->peer_addr, sess->peer_addr_len);
        }
        //DumpPacket(pkt, pkt_len, 0, 5);

        status = PJ_SUCCESS;
#else
        /* This looks like STUN, give it to the STUN session */
        unsigned options;

        options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
        status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len,
                                         options, NULL, parsed_len,
                                         sess->peer_addr, sess->peer_addr_len);
#endif
        PJ_LOG(5, (THIS_FILE, "pj_tcp_session_on_rx_pkt() is_stun %s",
                   sess->peer_addr == NULL ? "NULL" :
                   pj_sockaddr_print(sess->peer_addr, buf, PJ_INET6_ADDRSTRLEN, 3)));
    } else {
        PJ_LOG(5, (THIS_FILE, "pj_tcp_session_on_rx_pkt() not_stun %s",
                   sess->peer_addr == NULL ? "NULL" :
                   pj_sockaddr_print(sess->peer_addr, buf, PJ_INET6_ADDRSTRLEN, 3)));

        //DumpPacket(pkt, pkt_len, 0, 7);

        if (((pj_uint16_t*)pkt)[0] != NATNL_UDT_HEADER_MAGIC) {

            if (pkt_len < NATNL_DTLS_HEADER_SIZE) {
                if (parsed_len) {
                    *parsed_len = 0;
                }
                return PJ_ETOOSMALL;
            }

            /* Check that size is sane */
            data_len = pj_ntohs(((pj_uint16_t*)(((pj_uint8_t*)pkt)+11))[0]);
            if (pkt_len < data_len+NATNL_DTLS_HEADER_SIZE) {
                if (parsed_len) {
                    /* Insufficient fragment */
                    *parsed_len = 0;
                }
                status = PJ_ETOOSMALL;
                goto on_return;
            } else {
                if (parsed_len) {
                    /* Apply padding too */
                    *parsed_len = data_len+NATNL_DTLS_HEADER_SIZE;
                }
            }
        } else {

            if (pkt_len < NATNL_UDT_HEADER_SIZE) {
                if (parsed_len) {
                    *parsed_len = 0;
                }
                return PJ_ETOOSMALL;
            }

            /* Check that size is sane */
            data_len = pj_ntohs(((pj_uint16_t*)pkt)[1]);
            if (pkt_len < data_len+NATNL_UDT_HEADER_SIZE) {
                if (parsed_len) {
                    /* Insufficient fragment */
                    *parsed_len = 0;
                }
                status = PJ_ETOOSMALL;
                goto on_return;
            } else {
                if (parsed_len) {
                    /* Apply padding too */
                    *parsed_len = data_len+NATNL_UDT_HEADER_SIZE;
                }
            }
        }

        /* Notify application */
        if (sess->cb.on_rx_data) {
            (*sess->cb.on_rx_data)(sess, pkt, *parsed_len,
                                   sess->peer_addr, sess->peer_addr_len);
        }
        //DumpPacket(pkt, pkt_len, 0, 6);
        status = PJ_SUCCESS;
    }

on_return:
    pj_lock_release(sess->lock);
    return status;
}
Ejemplo n.º 5
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;
	    }
	}
    }
}
Ejemplo n.º 6
0
/*
 * pj_ioqueue_poll()
 *
 */
PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
{
    int i, count, processed;
    int msec;
    //struct epoll_event *events = ioqueue->events;
    //struct queue *queue = ioqueue->queue;
    struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
    struct queue queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
    pj_timestamp t1, t2;
    
    PJ_CHECK_STACK();

    msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000;

    TRACE_((THIS_FILE, "start os_epoll_wait, msec=%d", msec));
    pj_get_timestamp(&t1);
 
    //count = os_epoll_wait( ioqueue->epfd, events, ioqueue->max, msec);
    count = os_epoll_wait( ioqueue->epfd, events, PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL, msec);
    if (count == 0) {
#if PJ_IOQUEUE_HAS_SAFE_UNREG
    /* Check the closing keys only when there's no activity and when there are
     * pending closing keys.
     */
    if (count == 0 && !pj_list_empty(&ioqueue->closing_list)) {
	pj_lock_acquire(ioqueue->lock);
	scan_closing_keys(ioqueue);
	pj_lock_release(ioqueue->lock);
    }
#endif
	TRACE_((THIS_FILE, "os_epoll_wait timed out"));
	return count;
    }
    else if (count < 0) {
	TRACE_((THIS_FILE, "os_epoll_wait error"));
	return -pj_get_netos_error();
    }

    pj_get_timestamp(&t2);
    TRACE_((THIS_FILE, "os_epoll_wait returns %d, time=%d usec",
		       count, pj_elapsed_usec(&t1, &t2)));

    /* Lock ioqueue. */
    pj_lock_acquire(ioqueue->lock);

    for (processed=0, i=0; i<count; ++i) {
	pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type)
				events[i].epoll_data;

	TRACE_((THIS_FILE, "event %d: events=%d", i, events[i].events));

	/*
	 * Check readability.
	 */
	if ((events[i].events & EPOLLIN) && 
	    (key_has_pending_read(h) || key_has_pending_accept(h)) && !IS_CLOSING(h) ) {

#if PJ_IOQUEUE_HAS_SAFE_UNREG
	    increment_counter(h);
#endif
	    queue[processed].key = h;
	    queue[processed].event_type = READABLE_EVENT;
	    ++processed;
	}

	/*
	 * Check for writeability.
	 */
	if ((events[i].events & EPOLLOUT) && key_has_pending_write(h) && !IS_CLOSING(h)) {

#if PJ_IOQUEUE_HAS_SAFE_UNREG
	    increment_counter(h);
#endif
	    queue[processed].key = h;
	    queue[processed].event_type = WRITEABLE_EVENT;
	    ++processed;
	}

#if PJ_HAS_TCP
	/*
	 * Check for completion of connect() operation.
	 */
	if ((events[i].events & EPOLLOUT) && (h->connecting) && !IS_CLOSING(h)) {

#if PJ_IOQUEUE_HAS_SAFE_UNREG
	    increment_counter(h);
#endif
	    queue[processed].key = h;
	    queue[processed].event_type = WRITEABLE_EVENT;
	    ++processed;
	}
#endif /* PJ_HAS_TCP */
	
	/*
	 * Check for error condition.
	 */
	if (events[i].events & EPOLLERR && (h->connecting) && !IS_CLOSING(h)) {
		
#if PJ_IOQUEUE_HAS_SAFE_UNREG
	    increment_counter(h);
#endif		
	    queue[processed].key = h;
	    queue[processed].event_type = EXCEPTION_EVENT;
	    ++processed;
	}
    }
    pj_lock_release(ioqueue->lock);

    /* Now process the events. */
    for (i=0; i<processed; ++i) {
	switch (queue[i].event_type) {
        case READABLE_EVENT:
            ioqueue_dispatch_read_event(ioqueue, queue[i].key);
            break;
        case WRITEABLE_EVENT:
            ioqueue_dispatch_write_event(ioqueue, queue[i].key);
            break;
        case EXCEPTION_EVENT:
            ioqueue_dispatch_exception_event(ioqueue, queue[i].key);
            break;
        case NO_EVENT:
            pj_assert(!"Invalid event!");
            break;
        }

#if PJ_IOQUEUE_HAS_SAFE_UNREG
	decrement_counter(queue[i].key);
#endif
    }

    /* Special case:
     * When epoll returns > 0 but no descriptors are actually set!
     */
    if (count > 0 && !processed && msec > 0) {
	pj_thread_sleep(msec);
    }

    pj_get_timestamp(&t1);
    TRACE_((THIS_FILE, "ioqueue_poll() returns %d, time=%d usec",
		       processed, pj_elapsed_usec(&t2, &t1)));

    return processed;
}
Ejemplo n.º 7
0
/**
 * Set the server or domain name of the server.
 */
PJ_DEF(pj_status_t) pj_tcp_session_set_server( pj_tcp_session *sess,
        const pj_str_t *domain,
        int default_port,
        pj_dns_resolver *resolver)
{
    pj_sockaddr tmp_addr;
    pj_bool_t is_ip_addr;
    pj_status_t status;

    PJ_ASSERT_RETURN(sess && domain, PJ_EINVAL);
    PJ_ASSERT_RETURN(sess->state == PJ_TCP_STATE_NULL, PJ_EINVALIDOP);

    pj_lock_acquire(sess->lock);

    /* See if "domain" contains just IP address */
    tmp_addr.addr.sa_family = sess->af;
    status = pj_inet_pton(sess->af, domain,
                          pj_sockaddr_get_addr(&tmp_addr));
    is_ip_addr = (status == PJ_SUCCESS);

    if (!is_ip_addr && resolver) {
        /* Resolve with DNS SRV resolution, and fallback to DNS A resolution
         * if default_port is specified.
         */
        unsigned opt = 0;
        pj_str_t res_name;

        res_name = pj_str("_tcps._tcp.");

        /* Fallback to DNS A only if default port is specified */
        if (default_port>0 && default_port<65536) {
            opt = PJ_DNS_SRV_FALLBACK_A;
            sess->default_port = (pj_uint16_t)default_port;
        }

        PJ_LOG(5,(sess->obj_name, "Resolving %.*s%.*s with DNS SRV",
                  (int)res_name.slen, res_name.ptr,
                  (int)domain->slen, domain->ptr));
        pj_tcp_session_set_state(sess, PJ_TCP_STATE_RESOLVING);

        /* User may have destroyed us in the callback */
        if (sess->state != PJ_TCP_STATE_RESOLVING) {
            status = PJ_ECANCELLED;
            goto on_return;
        }

        status = pj_dns_srv_resolve(domain, &res_name, default_port,
                                    sess->pool, resolver, opt, sess,
                                    &dns_srv_resolver_cb, &sess->dns_async);
        if (status != PJ_SUCCESS) {
            pj_tcp_session_set_state(sess, PJ_TCP_STATE_NULL);
            goto on_return;
        }

    } else {
        /* Resolver is not specified, resolve with standard gethostbyname().
         * The default_port MUST be specified in this case.
         */
        pj_addrinfo *ai;
        unsigned i, cnt;

        /* Default port must be specified */
        PJ_ASSERT_RETURN(default_port>0 && default_port<65536, PJ_EINVAL);
        sess->default_port = (pj_uint16_t)default_port;

        cnt = PJ_TCP_MAX_DNS_SRV_CNT;
        ai = (pj_addrinfo*)
             pj_pool_calloc(sess->pool, cnt, sizeof(pj_addrinfo));

        PJ_LOG(5,(sess->obj_name, "Resolving %.*s with DNS A",
                  (int)domain->slen, domain->ptr));
        pj_tcp_session_set_state(sess, PJ_TCP_STATE_RESOLVING);

        /* User may have destroyed us in the callback */
        if (sess->state != PJ_TCP_STATE_RESOLVING) {
            status = PJ_ECANCELLED;
            goto on_return;
        }

        status = pj_getaddrinfo(sess->af, domain, &cnt, ai);
        if (status != PJ_SUCCESS)
            goto on_return;

        sess->srv_addr_cnt = (pj_uint16_t)cnt;
        sess->srv_addr_list = (pj_sockaddr*)
                              pj_pool_calloc(sess->pool, cnt,
                                             sizeof(pj_sockaddr));
        for (i=0; i<cnt; ++i) {
            pj_sockaddr *addr = &sess->srv_addr_list[i];
            pj_memcpy(addr, &ai[i].ai_addr, sizeof(pj_sockaddr));
            addr->addr.sa_family = sess->af;
            addr->ipv4.sin_port = pj_htons(sess->default_port);
        }

        sess->srv_addr = &sess->srv_addr_list[0];
        pj_tcp_session_set_state(sess, PJ_TCP_STATE_RESOLVED);
    }

on_return:
    pj_lock_release(sess->lock);
    return status;
}
Ejemplo n.º 8
0
PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
{
    pj_status_t status;
    pjsip_cseq_hdr *cseq_hdr;
    pjsip_expires_hdr *expires_hdr;
    pj_uint32_t cseq;

    pj_atomic_inc(regc->busy_ctr);
    pj_lock_acquire(regc->lock);

    /* Make sure we don't have pending transaction. */
    if (regc->has_tsx) {
	PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
			     "transaction pending"));
	pjsip_tx_data_dec_ref( tdata );
	pj_lock_release(regc->lock);
	pj_atomic_dec(regc->busy_ctr);
	return PJSIP_EBUSY;
    }

    /* Just regc->has_tsx check above should be enough. This assertion check
     * may cause problem, e.g: when regc_tsx_callback() invokes callback,
     * lock is released and 'has_tsx' is set to FALSE and 'current_op' has
     * not been updated to REGC_IDLE yet.
     */
    //pj_assert(regc->current_op == REGC_IDLE);

    /* Invalidate message buffer. */
    pjsip_tx_data_invalidate_msg(tdata);

    /* Increment CSeq */
    cseq = ++regc->cseq_hdr->cseq;
    cseq_hdr = (pjsip_cseq_hdr*)
	       pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
    cseq_hdr->cseq = cseq;

    /* Find Expires header */
    expires_hdr = (pjsip_expires_hdr*)
		  pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);

    /* Bind to transport selector */
    pjsip_tx_data_set_transport(tdata, &regc->tp_sel);

    regc->has_tsx = PJ_TRUE;

    /* Set current operation based on the value of Expires header */
    if (expires_hdr && expires_hdr->ivalue==0)
	regc->current_op = REGC_UNREGISTERING;
    else
	regc->current_op = REGC_REGISTERING;
    
    if (expires_hdr && expires_hdr->ivalue)
	regc->expires_requested = expires_hdr->ivalue;

    /* Prevent deletion of tdata, e.g: when something wrong in sending,
     * we need tdata to retrieve the transport.
     */
    pjsip_tx_data_add_ref(tdata);

    /* If via_addr is set, use this address for the Via header. */
    if (regc->via_addr.host.slen > 0) {
        tdata->via_addr = regc->via_addr;
        tdata->via_tp = regc->via_tp;
    }

    /* Need to unlock the regc temporarily while sending the message to
     * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247).
     * It should be safe to do this since the regc's refcount has been
     * incremented.
     */
    pj_lock_release(regc->lock);

    /* Now send the message */
    status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
				      regc, &regc_tsx_callback);
    if (status!=PJ_SUCCESS) {
	/* On failure, regc_tsx_callback() may not be called, so we need
	 * to reset regc->has_tsx here (see also ticket #1936).
	 */
	regc->has_tsx = PJ_FALSE;

	PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
    }

    /* Reacquire the lock */
    pj_lock_acquire(regc->lock);

    /* Get last transport used and add reference to it */
    if (tdata->tp_info.transport != regc->last_transport &&
	status==PJ_SUCCESS)
    {
	if (regc->last_transport) {
	    pjsip_transport_dec_ref(regc->last_transport);
	    regc->last_transport = NULL;
	}

	if (tdata->tp_info.transport) {
	    regc->last_transport = tdata->tp_info.transport;
	    pjsip_transport_add_ref(regc->last_transport);
	}
    }

    /* Release tdata */
    pjsip_tx_data_dec_ref(tdata);

    pj_lock_release(regc->lock);

    /* Delete the record if user destroy regc during the callback. */
    if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
	pjsip_regc_destroy(regc);
    }

    return status;
}
Ejemplo n.º 9
0
PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
					pjsip_tx_data **p_tdata)
{
    pjsip_msg *msg;
    pjsip_contact_hdr *hdr;
    const pjsip_hdr *h_allow;
    pj_status_t status;
    pjsip_tx_data *tdata;

    PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);

    pj_lock_acquire(regc->lock);
    
    regc->expires_requested = 1;

    status = create_request(regc, &tdata);
    if (status != PJ_SUCCESS) {
	pj_lock_release(regc->lock);
	return status;
    }

    msg = tdata->msg;

    /* Add Contact headers. */
    hdr = regc->contact_hdr_list.next;
    while (hdr != &regc->contact_hdr_list) {
	pjsip_msg_add_hdr(msg, (pjsip_hdr*)
			       pjsip_hdr_clone(tdata->pool, hdr));
	hdr = hdr->next;
    }

    /* Also add bindings which are to be removed */
    while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
	hdr = regc->removed_contact_hdr_list.next;
	pjsip_msg_add_hdr(msg, (pjsip_hdr*)
			       pjsip_hdr_clone(tdata->pool, hdr));
	pj_list_erase(hdr);
    }
    

    if (regc->expires_hdr)
	pjsip_msg_add_hdr(msg, (pjsip_hdr*)
			       pjsip_hdr_clone(tdata->pool,
					       regc->expires_hdr));

    if (regc->timer.id != 0) {
	pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
	regc->timer.id = 0;
    }

    /* Add Allow header (http://trac.pjsip.org/repos/ticket/1039) */
    h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL);
    if (h_allow) {
	pjsip_msg_add_hdr(msg, (pjsip_hdr*)
			       pjsip_hdr_clone(tdata->pool, h_allow));

    }

    regc->auto_reg = autoreg;

    pj_lock_release(regc->lock);

    /* Done */
    *p_tdata = tdata;
    return PJ_SUCCESS;
}
Ejemplo n.º 10
0
static void regc_tsx_callback(void *token, pjsip_event *event)
{
    pj_status_t status;
    pjsip_regc *regc = (pjsip_regc*) token;
    pjsip_transaction *tsx = event->body.tsx_state.tsx;
    pj_bool_t handled = PJ_TRUE;
    pj_bool_t update_contact = PJ_FALSE;

    pj_atomic_inc(regc->busy_ctr);
    pj_lock_acquire(regc->lock);

    /* Decrement pending transaction counter. */
    pj_assert(regc->has_tsx);
    regc->has_tsx = PJ_FALSE;

    /* Add reference to the transport */
    if (tsx->transport != regc->last_transport) {
	if (regc->last_transport) {
	    pjsip_transport_dec_ref(regc->last_transport);
	    regc->last_transport = NULL;
	}

	if (tsx->transport) {
	    regc->last_transport = tsx->transport;
	    pjsip_transport_add_ref(regc->last_transport);
	}
    }

    if (regc->_delete_flag == 0 && regc->tsx_cb &&
        regc->current_op == REGC_REGISTERING)
    {
        struct pjsip_regc_tsx_cb_param param;

        param.contact_cnt = -1;
        cbparam_init(&param.cbparam, regc, PJ_SUCCESS, tsx->status_code,
		     &tsx->status_text,
                     (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? 
	              event->body.tsx_state.src.rdata : NULL,
                     -1, 0, NULL, PJ_FALSE);

        /* Call regc tsx callback before handling any response */
        pj_lock_release(regc->lock);
        (*regc->tsx_cb)(&param);
        pj_lock_acquire(regc->lock);

        if (param.contact_cnt >= 0) {
            /* Since we receive non-2xx response, it means that (some) contact
             * bindings haven't been established so we can safely remove these
             * contact headers. This is to avoid removing non-existent contact
             * bindings later.
             */
            if (tsx->status_code/100 != 2) {
                pjsip_contact_hdr *h;

	        h = regc->contact_hdr_list.next;
	        while (h != &regc->contact_hdr_list) {
                    pjsip_contact_hdr *next = h->next;

                    if (h->expires == -1) {
                        pj_list_erase(h);
                    }
                    h = next;
                }
	    }

            /* Update contact address */
            pjsip_regc_update_contact(regc, param.contact_cnt, param.contact);
            update_contact = PJ_TRUE;
        }
    }

    /* Handle 401/407 challenge (even when _delete_flag is set) */
    if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
	tsx->status_code == PJSIP_SC_UNAUTHORIZED)
    {
	pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
	pjsip_tx_data *tdata;
	pj_bool_t is_unreg;

	/* reset current op */
	is_unreg = (regc->current_op == REGC_UNREGISTERING);
	regc->current_op = REGC_IDLE;

        if (update_contact) {
            pjsip_msg *msg;
            pjsip_hdr *hdr, *ins_hdr;
            pjsip_contact_hdr *chdr;

            /* Delete Contact headers, but we shouldn't delete headers
             * which are supposed to remove contact bindings since
             * we cannot reconstruct those headers.
             */
            msg = tsx->last_tx->msg;
            hdr = msg->hdr.next;
            ins_hdr = &msg->hdr;
            while (hdr != &msg->hdr) {
                pjsip_hdr *next = hdr->next;

                if (hdr->type == PJSIP_H_CONTACT) {
                    chdr = (pjsip_contact_hdr *)hdr;
                    if (chdr->expires != 0) {
                        pj_list_erase(hdr);
                        ins_hdr = next;
                    }
                }
                hdr = next;
            }

            /* Add Contact headers. */
            chdr = regc->contact_hdr_list.next;
            while (chdr != &regc->contact_hdr_list) {
	        pj_list_insert_before(ins_hdr, (pjsip_hdr*)
                    pjsip_hdr_clone(tsx->last_tx->pool, chdr));
	        chdr = chdr->next;
            }

            /* Also add bindings which are to be removed */
            while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
	        chdr = regc->removed_contact_hdr_list.next;
	        pj_list_insert_before(ins_hdr, (pjsip_hdr*)
                    pjsip_hdr_clone(tsx->last_tx->pool, chdr));
	        pj_list_erase(chdr);
            }
        }

        status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
					    rdata, 
					    tsx->last_tx,  
					    &tdata);

	if (status == PJ_SUCCESS) {
	    status = pjsip_regc_send(regc, tdata);
	}
	
	if (status != PJ_SUCCESS) {

	    /* Only call callback if application is still interested
	     * in it.
	     */
	    if (regc->_delete_flag == 0) {
		/* Should be safe to release the lock temporarily.
		 * We do this to avoid deadlock. 
		 */
		pj_lock_release(regc->lock);
		call_callback(regc, status, tsx->status_code, 
			      &rdata->msg_info.msg->line.status.reason,
			      rdata, -1, 0, NULL, is_unreg);
		pj_lock_acquire(regc->lock);
	    }
	}

    } else if (regc->_delete_flag) {

	/* User has called pjsip_regc_destroy(), so don't call callback. 
	 * This regc will be destroyed later in this function.
	 */

	/* Just reset current op */
	regc->current_op = REGC_IDLE;

    } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
	       regc->current_op == REGC_REGISTERING)
    {
	/* Handle 423 response automatically:
	 *  - set requested expiration to Min-Expires header, ONLY IF
	 *    the original request is a registration (as opposed to
	 *    unregistration) and the requested expiration was indeed
	 *    lower than Min-Expires)
	 *  - resend the request
	 */
	pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
	pjsip_min_expires_hdr *me_hdr;
	pjsip_tx_data *tdata;
	pj_int32_t min_exp;

	/* reset current op */
	regc->current_op = REGC_IDLE;

	/* Update requested expiration */
	me_hdr = (pjsip_min_expires_hdr*)
		 pjsip_msg_find_hdr(rdata->msg_info.msg,
				    PJSIP_H_MIN_EXPIRES, NULL);
	if (me_hdr) {
	    min_exp = me_hdr->ivalue;
	} else {
	    /* Broken server, Min-Expires doesn't exist.
	     * Just guestimate then, BUT ONLY if if this is the
	     * first time we received such response.
	     */
	    enum {
		/* Note: changing this value would require changing couple of
		 *       Python test scripts.
		 */
		UNSPECIFIED_MIN_EXPIRES = 3601
	    };
	    if (!regc->expires_hdr ||
		 regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
	    {
		min_exp = UNSPECIFIED_MIN_EXPIRES;
	    } else {
		handled = PJ_FALSE;
		PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
				     "without Min-Expires header is invalid"));
		goto handle_err;
	    }
	}

	if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
	    /* But we already send with greater expiration time, why does
	     * the server send us with 423? Oh well, just fail the request.
	     */
	    handled = PJ_FALSE;
	    PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
			         "Min-Expires header value in response"));
	    goto handle_err;
	}

	set_expires(regc, min_exp);

	status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
	if (status == PJ_SUCCESS) {
	    status = pjsip_regc_send(regc, tdata);
	}

	if (status != PJ_SUCCESS) {
	    /* Only call callback if application is still interested
	     * in it.
	     */
	    if (!regc->_delete_flag) {
		/* Should be safe to release the lock temporarily.
		 * We do this to avoid deadlock.
		 */
		pj_lock_release(regc->lock);
		call_callback(regc, status, tsx->status_code,
			      &rdata->msg_info.msg->line.status.reason,
			      rdata, -1, 0, NULL, PJ_FALSE);
		pj_lock_acquire(regc->lock);
	    }
	}

    } else {
	handled = PJ_FALSE;
    }

handle_err:
    if (!handled) {
	pjsip_rx_data *rdata;
	pj_int32_t expiration = NOEXP;
	unsigned contact_cnt = 0;
	pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
	pj_bool_t is_unreg;

	if (tsx->status_code/100 == 2) {

	    rdata = event->body.tsx_state.src.rdata;

	    /* Calculate expiration */
	    expiration = calculate_response_expiration(regc, rdata, 
						       &contact_cnt,
						       PJSIP_REGC_MAX_CONTACT,
						       contact);

	    /* Schedule next registration */
            schedule_registration(regc, expiration);

	} else {
	    rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? 
			event->body.tsx_state.src.rdata : NULL;
	}

	/* Update registration */
	if (expiration==NOEXP) expiration=-1;
	regc->expires = expiration;

	/* Mark operation as complete */
	is_unreg = (regc->current_op == REGC_UNREGISTERING);
	regc->current_op = REGC_IDLE;

	/* Call callback. */
	/* Should be safe to release the lock temporarily.
	 * We do this to avoid deadlock. 
	 */
	pj_lock_release(regc->lock);
	call_callback(regc, PJ_SUCCESS, tsx->status_code, 
		      (rdata ? &rdata->msg_info.msg->line.status.reason 
			: &tsx->status_text),
		      rdata, expiration, 
		      contact_cnt, contact, is_unreg);
	pj_lock_acquire(regc->lock);
    }

    pj_lock_release(regc->lock);

    /* Delete the record if user destroy regc during the callback. */
    if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
	pjsip_regc_destroy(regc);
    }
}
Ejemplo n.º 11
0
static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
{
    pj_caching_pool *cp = (pj_caching_pool*)pf;
    unsigned pool_capacity;
    unsigned i;

    PJ_CHECK_STACK();

    PJ_ASSERT_ON_FAIL(pf && pool, return);

    pj_lock_acquire(cp->lock);

#if PJ_SAFE_POOL
    /* Make sure pool is still in our used list */
    if (pj_list_find_node(&cp->used_list, pool) != pool) {
	pj_assert(!"Attempt to destroy pool that has been destroyed before");
	return;
    }
#endif

    /* Erase from the used list. */
    pj_list_erase(pool);

    /* Decrement used count. */
    --cp->used_count;

    pool_capacity = pj_pool_get_capacity(pool);

    /* Destroy the pool if the size is greater than our size or if the total
     * capacity in our recycle list (plus the size of the pool) exceeds 
     * maximum capacity.
   . */
    if (pool_capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] ||
	cp->capacity + pool_capacity > cp->max_capacity)
    {
	pj_pool_destroy_int(pool);
	pj_lock_release(cp->lock);
	return;
    }

    /* Reset pool. */
    PJ_LOG(6, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", 
	       pool_capacity, pj_pool_get_used_size(pool), 
	       pj_pool_get_used_size(pool)*100/pool_capacity));
    pj_pool_reset(pool);

    pool_capacity = pj_pool_get_capacity(pool);

    /*
     * Otherwise put the pool in our recycle list.
     */
    i = (unsigned) (unsigned long) pool->factory_data;

    pj_assert(i<PJ_CACHING_POOL_ARRAY_SIZE);
    if (i >= PJ_CACHING_POOL_ARRAY_SIZE ) {
	/* Something has gone wrong with the pool. */
	pj_pool_destroy_int(pool);
	pj_lock_release(cp->lock);
	return;
    }

    pj_list_insert_after(&cp->free_list[i], pool);
    cp->capacity += pool_capacity;

    pj_lock_release(cp->lock);
}
Ejemplo n.º 12
0
static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, 
					      const char *name, 
					      pj_size_t initial_size, 
					      pj_size_t increment_sz, 
					      pj_pool_callback *callback)
{
    pj_caching_pool *cp = (pj_caching_pool*)pf;
    pj_pool_t *pool;
    int idx;

    PJ_CHECK_STACK();

    pj_lock_acquire(cp->lock);

    /* Use pool factory's policy when callback is NULL */
    if (callback == NULL) {
	callback = pf->policy.callback;
    }

    /* Search the suitable size for the pool. 
     * We'll just do linear search to the size array, as the array size itself
     * is only a few elements. Binary search I suspect will be less efficient
     * for this purpose.
     */
    if (initial_size <= pool_sizes[START_SIZE]) {
	for (idx=START_SIZE-1; 
	     idx >= 0 && pool_sizes[idx] >= initial_size;
	     --idx)
	    ;
	++idx;
    } else {
	for (idx=START_SIZE+1; 
	     idx < PJ_CACHING_POOL_ARRAY_SIZE && 
		  pool_sizes[idx] < initial_size;
	     ++idx)
	    ;
    }

    /* Check whether there's a pool in the list. */
    if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) {
	/* No pool is available. */
	/* Set minimum size. */
	if (idx < PJ_CACHING_POOL_ARRAY_SIZE)
	    initial_size =  pool_sizes[idx];

	/* Create new pool */
	pool = pj_pool_create_int(&cp->factory, name, initial_size, 
				  increment_sz, callback);
	if (!pool) {
	    pj_lock_release(cp->lock);
	    return NULL;
	}

    } else {
	/* Get one pool from the list. */
	pool = (pj_pool_t*) cp->free_list[idx].next;
	pj_list_erase(pool);

	/* Initialize the pool. */
	pj_pool_init_int(pool, name, increment_sz, callback);

	/* Update pool manager's free capacity. */
	cp->capacity -= pj_pool_get_capacity(pool);

	PJ_LOG(6, (pool->obj_name, "pool reused, size=%u", pool->capacity));
    }

    /* Put in used list. */
    pj_list_insert_before( &cp->used_list, pool );

    /* Mark factory data */
    pool->factory_data = (void*) (long) idx;

    /* Increment used count. */
    ++cp->used_count;

    pj_lock_release(cp->lock);
    return pool;
}
Ejemplo n.º 13
0
/*
 * Initialize and start SRTP session with the given parameters.
 */
PJ_DEF(pj_status_t) pjmedia_transport_srtp_start(
			   pjmedia_transport *tp,
			   const pjmedia_srtp_crypto *tx,
			   const pjmedia_srtp_crypto *rx)
{
    transport_srtp  *srtp = (transport_srtp*) tp;
    srtp_policy_t    tx_;
    srtp_policy_t    rx_;
    srtp_err_status_t err;
    int		     cr_tx_idx = 0;
    int		     au_tx_idx = 0;
    int		     cr_rx_idx = 0;
    int		     au_rx_idx = 0;
    pj_status_t	     status = PJ_SUCCESS;

    PJ_ASSERT_RETURN(tp && tx && rx, PJ_EINVAL);

    pj_lock_acquire(srtp->mutex);

    if (srtp->session_inited) {
	pjmedia_transport_srtp_stop(tp);
    }

    /* Get encryption and authentication method */
    cr_tx_idx = au_tx_idx = get_crypto_idx(&tx->name);
    if (tx->flags & PJMEDIA_SRTP_NO_ENCRYPTION)
	cr_tx_idx = 0;
    if (tx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION)
	au_tx_idx = 0;

    cr_rx_idx = au_rx_idx = get_crypto_idx(&rx->name);
    if (rx->flags & PJMEDIA_SRTP_NO_ENCRYPTION)
	cr_rx_idx = 0;
    if (rx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION)
	au_rx_idx = 0;

    /* Check whether the crypto-suite requested is supported */
    if (cr_tx_idx == -1 || cr_rx_idx == -1 || au_tx_idx == -1 ||
	au_rx_idx == -1)
    {
	status = PJMEDIA_SRTP_ENOTSUPCRYPTO;
	goto on_return;
    }

    /* If all options points to 'NULL' method, just bypass SRTP */
    if (cr_tx_idx == 0 && cr_rx_idx == 0 && au_tx_idx == 0 && au_rx_idx == 0) {
	srtp->bypass_srtp = PJ_TRUE;
	goto on_return;
    }

    /* Check key length */
    if (tx->key.slen != (pj_ssize_t)crypto_suites[cr_tx_idx].cipher_key_len ||
        rx->key.slen != (pj_ssize_t)crypto_suites[cr_rx_idx].cipher_key_len)
    {
	status = PJMEDIA_SRTP_EINKEYLEN;
	goto on_return;
    }

    /* Init transmit direction */
    pj_bzero(&tx_, sizeof(srtp_policy_t));
    pj_memmove(srtp->tx_key, tx->key.ptr, tx->key.slen);
    if (cr_tx_idx && au_tx_idx)
	tx_.rtp.sec_serv    = sec_serv_conf_and_auth;
    else if (cr_tx_idx)
	tx_.rtp.sec_serv    = sec_serv_conf;
    else if (au_tx_idx)
	tx_.rtp.sec_serv    = sec_serv_auth;
    else
	tx_.rtp.sec_serv    = sec_serv_none;
    tx_.key		    = (uint8_t*)srtp->tx_key;
    tx_.ssrc.type	    = ssrc_any_outbound;
    tx_.ssrc.value	    = 0;
    tx_.rtp.cipher_type	    = crypto_suites[cr_tx_idx].cipher_type;
    tx_.rtp.cipher_key_len  = crypto_suites[cr_tx_idx].cipher_key_len;
    tx_.rtp.auth_type	    = crypto_suites[au_tx_idx].auth_type;
    tx_.rtp.auth_key_len    = crypto_suites[au_tx_idx].auth_key_len;
    tx_.rtp.auth_tag_len    = crypto_suites[au_tx_idx].srtp_auth_tag_len;
    tx_.rtcp		    = tx_.rtp;
    tx_.rtcp.auth_tag_len   = crypto_suites[au_tx_idx].srtcp_auth_tag_len;
    tx_.next		    = NULL;
    err = srtp_create(&srtp->srtp_tx_ctx, &tx_);
    if (err != srtp_err_status_ok) {
	status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
	goto on_return;
    }
    srtp->tx_policy = *tx;
    pj_strset(&srtp->tx_policy.key,  srtp->tx_key, tx->key.slen);
    srtp->tx_policy.name=pj_str(crypto_suites[get_crypto_idx(&tx->name)].name);


    /* Init receive direction */
    pj_bzero(&rx_, sizeof(srtp_policy_t));
    pj_memmove(srtp->rx_key, rx->key.ptr, rx->key.slen);
    if (cr_rx_idx && au_rx_idx)
	rx_.rtp.sec_serv    = sec_serv_conf_and_auth;
    else if (cr_rx_idx)
	rx_.rtp.sec_serv    = sec_serv_conf;
    else if (au_rx_idx)
	rx_.rtp.sec_serv    = sec_serv_auth;
    else
	rx_.rtp.sec_serv    = sec_serv_none;
    rx_.key		    = (uint8_t*)srtp->rx_key;
    rx_.ssrc.type	    = ssrc_any_inbound;
    rx_.ssrc.value	    = 0;
    rx_.rtp.sec_serv	    = crypto_suites[cr_rx_idx].service;
    rx_.rtp.cipher_type	    = crypto_suites[cr_rx_idx].cipher_type;
    rx_.rtp.cipher_key_len  = crypto_suites[cr_rx_idx].cipher_key_len;
    rx_.rtp.auth_type	    = crypto_suites[au_rx_idx].auth_type;
    rx_.rtp.auth_key_len    = crypto_suites[au_rx_idx].auth_key_len;
    rx_.rtp.auth_tag_len    = crypto_suites[au_rx_idx].srtp_auth_tag_len;
    rx_.rtcp		    = rx_.rtp;
    rx_.rtcp.auth_tag_len   = crypto_suites[au_rx_idx].srtcp_auth_tag_len;
    rx_.next		    = NULL;
    err = srtp_create(&srtp->srtp_rx_ctx, &rx_);
    if (err != srtp_err_status_ok) {
	srtp_dealloc(srtp->srtp_tx_ctx);
	status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
	goto on_return;
    }
    srtp->rx_policy = *rx;
    pj_strset(&srtp->rx_policy.key,  srtp->rx_key, rx->key.slen);
    srtp->rx_policy.name=pj_str(crypto_suites[get_crypto_idx(&rx->name)].name);

    /* Declare SRTP session initialized */
    srtp->session_inited = PJ_TRUE;

    /* Logging stuffs */
#if PJ_LOG_MAX_LEVEL >= 5
    {
	char b64[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)];
	int b64_len;

	/* TX crypto and key */
	b64_len = sizeof(b64);
	status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, (int)tx->key.slen,
				  b64, &b64_len);
	if (status != PJ_SUCCESS)
	    b64_len = pj_ansi_sprintf(b64, "--key too long--");
	else
	    b64[b64_len] = '\0';

	PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s",
		   srtp->tx_policy.name.ptr, b64));
	if (srtp->tx_policy.flags) {
	    PJ_LOG(5,(srtp->pool->obj_name, "TX: disable%s%s",
		      (cr_tx_idx?"":" enc"),
		      (au_tx_idx?"":" auth")));
	}

	/* RX crypto and key */
	b64_len = sizeof(b64);
	status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, (int)rx->key.slen,
				  b64, &b64_len);
	if (status != PJ_SUCCESS)
	    b64_len = pj_ansi_sprintf(b64, "--key too long--");
	else
	    b64[b64_len] = '\0';

	PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s",
		   srtp->rx_policy.name.ptr, b64));
	if (srtp->rx_policy.flags) {
	    PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s",
		      (cr_rx_idx?"":" enc"),
		      (au_rx_idx?"":" auth")));
	}
    }
#endif

on_return:
    pj_lock_release(srtp->mutex);
    return status;
}
Ejemplo n.º 14
0
/*
 * This callback is called by transport when incoming rtp is received
 */
static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size)
{
    transport_srtp *srtp = (transport_srtp *) user_data;
    int len = (int)size;
    srtp_err_status_t err;
    void (*cb)(void*, void*, pj_ssize_t) = NULL;
    void *cb_data = NULL;

    if (srtp->bypass_srtp) {
	srtp->rtp_cb(srtp->user_data, pkt, size);
	return;
    }

    if (size < 0) {
	return;
    }

    /* Give the packet to keying first by invoking its send_rtp() op.
     * Yes, the usage of send_rtp() is rather hacky, but it is convenient
     * as the signature suits the purpose and it is ready to use
     * (no futher registration/setting needed), and it may never be used
     * by any keying method in the future.
     */
    {
	unsigned i;
	pj_status_t status;
	for (i=0; i < srtp->keying_cnt; i++) {
	    if (!srtp->keying[i]->op->send_rtp)
		continue;
	    status = pjmedia_transport_send_rtp(srtp->keying[i], pkt, size);
	    if (status != PJ_EIGNORED) {
		/* Packet is already consumed by the keying method */
		return;
	    }
	}
    }

    /* Make sure buffer is 32bit aligned */
    PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return );

    if (srtp->probation_cnt > 0)
	--srtp->probation_cnt;

    pj_lock_acquire(srtp->mutex);

    if (!srtp->session_inited) {
	pj_lock_release(srtp->mutex);
	return;
    }
    err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len);
    if (srtp->probation_cnt > 0 &&
	(err == srtp_err_status_replay_old ||
	 err == srtp_err_status_replay_fail))
    {
	/* Handle such condition that stream is updated (RTP seq is reinited
	 * & SRTP is restarted), but some old packets are still coming
	 * so SRTP is learning wrong RTP seq. While the newly inited RTP seq
	 * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect()
	 * will return err_status_replay_*. Restarting SRTP can resolve this.
	 */
	pjmedia_srtp_crypto tx, rx;
	pj_status_t status;

	tx = srtp->tx_policy;
	rx = srtp->rx_policy;
	status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp,
					      &tx, &rx);
	if (status != PJ_SUCCESS) {
	    PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s",
		      get_libsrtp_errstr(err)));
	} else if (!srtp->bypass_srtp) {
	    err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len);
	}
    }

    if (err != srtp_err_status_ok) {
	PJ_LOG(5,(srtp->pool->obj_name,
		  "Failed to unprotect SRTP, pkt size=%d, err=%s",
		  size, get_libsrtp_errstr(err)));
    } else {
	cb = srtp->rtp_cb;
	cb_data = srtp->user_data;
    }

    pj_lock_release(srtp->mutex);

    if (cb) {
	(*cb)(cb_data, pkt, len);
    }
}