⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_transaction.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
    tsx->timeout_timer._timer_id = -1;
    tsx->timeout_timer.user_data = tsx;
    tsx->timeout_timer.cb = &tsx_timer_callback;
    
    status = pj_mutex_create_recursive(pool, tsx->obj_name, &tsx->mutex);
    if (status != PJ_SUCCESS) {
	pjsip_endpt_release_pool(mod_tsx_layer.endpt, pool);
	return status;
    }

    *p_tsx = tsx;
    return PJ_SUCCESS;
}


/* Destroy transaction. */
static pj_status_t tsx_destroy( pjsip_transaction *tsx )
{
    struct tsx_lock_data *lck;

    /* Decrement transport reference counter. */
    if (tsx->transport) {
	pjsip_transport_dec_ref( tsx->transport );
	tsx->transport = NULL;
    }
    /* Decrement reference counter in transport selector */
    pjsip_tpselector_dec_ref(&tsx->tp_sel);

    /* Free last transmitted message. */
    if (tsx->last_tx) {
	pjsip_tx_data_dec_ref( tsx->last_tx );
	tsx->last_tx = NULL;
    }
    /* Cancel timeout timer. */
    if (tsx->timeout_timer._timer_id != -1) {
	pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
	tsx->timeout_timer._timer_id = -1;
    }
    /* Cancel retransmission timer. */
    if (tsx->retransmit_timer._timer_id != -1) {
	pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
	tsx->retransmit_timer._timer_id = -1;
    }

    /* Clear some pending flags. */
    tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED | TSX_HAS_PENDING_SEND);

    /* Refuse to destroy transaction if it has pending resolving. */
    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
	tsx->transport_flag |= TSX_HAS_PENDING_DESTROY;
	tsx->tsx_user = NULL;
	PJ_LOG(4,(tsx->obj_name, "Will destroy later because transport is "
				 "in progress"));
	return PJ_EBUSY;
    }

    /* Clear TLS, so that mutex will not be unlocked */
    lck = pj_thread_local_get(pjsip_tsx_lock_tls_id);
    while (lck) {
	if (lck->tsx == tsx) {
	    lck->is_alive = 0;
	}
	lck = lck->prev;
    }

    pj_mutex_destroy(tsx->mutex);

    PJ_LOG(5,(tsx->obj_name, "Transaction destroyed!"));

    pjsip_endpt_release_pool(tsx->endpt, tsx->pool);

    return PJ_SUCCESS;
}


/*
 * Callback when timer expires.
 */
static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
{
    pjsip_event event;
    pjsip_transaction *tsx = entry->user_data;
    struct tsx_lock_data lck;

    PJ_UNUSED_ARG(theap);

    PJ_LOG(5,(tsx->obj_name, "%s timer event", 
	     (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit":"Timeout")));


    if (entry->id == TSX_TIMER_RETRANSMISSION) {
        PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer);
    } else {
        PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer);
    }

    /* Dispatch event to transaction. */
    lock_tsx(tsx, &lck);
    (*tsx->state_handler)(tsx, &event);
    unlock_tsx(tsx, &lck);
}


/*
 * Set transaction state, and inform TU about the transaction state change.
 */
static void tsx_set_state( pjsip_transaction *tsx,
			   pjsip_tsx_state_e state,
			   pjsip_event_id_e event_src_type,
                           void *event_src )
{
    pjsip_tsx_state_e prev_state = tsx->state;

    /* New state must be greater than previous state */
    pj_assert(state >= tsx->state);

    PJ_LOG(5, (tsx->obj_name, "State changed from %s to %s, event=%s",
	       state_str[tsx->state], state_str[state], 
               pjsip_event_str(event_src_type)));

    /* Change state. */
    tsx->state = state;

    /* Update the state handlers. */
    if (tsx->role == PJSIP_ROLE_UAC) {
	tsx->state_handler = tsx_state_handler_uac[state];
    } else {
	tsx->state_handler = tsx_state_handler_uas[state];
    }

    /* Before informing TU about state changed, inform TU about
     * rx event.
     */
    if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user) {
	pjsip_rx_data *rdata = event_src;

	pj_assert(rdata != NULL);

	if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG &&
		   tsx->tsx_user->on_rx_response)
	{
	    (*tsx->tsx_user->on_rx_response)(rdata);
	}

    }

    /* Inform TU about state changed. */
    if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) {
	pjsip_event e;
	PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src,
				   prev_state);
	(*tsx->tsx_user->on_tsx_state)(tsx, &e);
    }
    

    /* When the transaction is terminated, release transport, and free the
     * saved last transmitted message.
     */
    if (state == PJSIP_TSX_STATE_TERMINATED) {
	pj_time_val timeout = {0, 0};

	/* Reschedule timeout timer to destroy this transaction. */
	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
	    tsx->transport_flag |= TSX_HAS_PENDING_DESTROY;
	} else {
	    /* Cancel timeout timer. */
	    if (tsx->timeout_timer._timer_id != -1) {
		pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
		tsx->timeout_timer._timer_id = -1;
	    }

	    pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
					&timeout);
	}


    } else if (state == PJSIP_TSX_STATE_DESTROYED) {

	/* Unregister transaction. */
	mod_tsx_layer_unregister_tsx(tsx);

	/* Destroy transaction. */
	tsx_destroy(tsx);
    }
}


/*
 * Create, initialize, and register UAC transaction.
 */
PJ_DEF(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user,
					  pjsip_tx_data *tdata,
					  pjsip_transaction **p_tsx)
{
    pjsip_transaction *tsx;
    pjsip_msg *msg;
    pjsip_cseq_hdr *cseq;
    pjsip_via_hdr *via;
    pjsip_host_info dst_info;
    struct tsx_lock_data lck;
    pj_status_t status;

    /* Validate arguments. */
    PJ_ASSERT_RETURN(tdata && tdata->msg && p_tsx, PJ_EINVAL);
    PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
		     PJSIP_ENOTREQUESTMSG);

    /* Method MUST NOT be ACK! */
    PJ_ASSERT_RETURN(tdata->msg->line.req.method.id != PJSIP_ACK_METHOD,
		     PJ_EINVALIDOP);

    /* Keep shortcut */
    msg = tdata->msg;

    /* Make sure CSeq header is present. */
    cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
    if (!cseq) {
	pj_assert(!"CSeq header not present in outgoing message!");
	return PJSIP_EMISSINGHDR;
    }


    /* Create transaction instance. */
    status = tsx_create( tsx_user, &tsx);
    if (status != PJ_SUCCESS)
	return status;


    /* Lock transaction. */
    lock_tsx(tsx, &lck);

    /* Role is UAC. */
    tsx->role = PJSIP_ROLE_UAC;

    /* Save method. */
    pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);

    /* Save CSeq. */
    tsx->cseq = cseq->cseq;

    /* Generate Via header if it doesn't exist. */
    via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
    if (via == NULL) {
	via = pjsip_via_hdr_create(tdata->pool);
	pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
    }

    /* Generate branch parameter if it doesn't exist. */
    if (via->branch_param.slen == 0) {
	pj_str_t tmp;
	via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
	via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
	pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, 
		  PJSIP_RFC3261_BRANCH_LEN);
	tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN + 2;
	*(tmp.ptr-2) = 80; *(tmp.ptr-1) = 106;
	pj_generate_unique_string( &tmp );

        /* Save branch parameter. */
        tsx->branch = via->branch_param;

    } else {
        /* Copy branch parameter. */
        pj_strdup(tsx->pool, &tsx->branch, &via->branch_param);
    }

   /* Generate transaction key. */
    create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
			 PJSIP_ROLE_UAC, &tsx->method, 
			 &via->branch_param);

    /* Calculate hashed key value. */
#ifdef PRECALC_HASH
    tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr,
				   tsx->transaction_key.slen);
#endif

    PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
	       tsx->transaction_key.ptr));

    /* Begin with State_Null.
     * Manually set-up the state becase we don't want to call the callback.
     */
    tsx->state = PJSIP_TSX_STATE_NULL;
    tsx->state_handler = &tsx_on_state_null;

    /* Save the message. */
    tsx->last_tx = tdata;
    pjsip_tx_data_add_ref(tsx->last_tx);

    /* Determine whether reliable transport should be used initially.
     * This will be updated whenever transport has changed.
     */
    status = pjsip_get_request_dest(tdata, &dst_info);
    if (status != PJ_SUCCESS) {
	tsx_destroy(tsx);
	return status;
    }
    tsx->is_reliable = (dst_info.flag & PJSIP_TRANSPORT_RELIABLE);

    /* Register transaction to hash table. */
    status = mod_tsx_layer_register_tsx(tsx);
    if (status != PJ_SUCCESS) {
	pj_assert(!"Bug in branch_param generator (i.e. not unique)");
	tsx_destroy(tsx);
	return status;
    }


    /* Unlock transaction and return. */
    unlock_tsx(tsx, &lck);

    PJ_LOG(5,(tsx->obj_name, "Transaction created for %s",
	      pjsip_tx_data_get_info(tdata)));

    *p_tsx = tsx;
    return PJ_SUCCESS;
}


/*
 * Create, initialize, and register UAS transaction.
 */
PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
					  pjsip_rx_data *rdata,
					  pjsip_transaction **p_tsx)
{
    pjsip_transaction *tsx;
    pjsip_msg *msg;
    pj_str_t *branch;
    pjsip_cseq_hdr *cseq;
    pj_status_t status;
    struct tsx_lock_data lck;

    /* Validate arguments. */
    PJ_ASSERT_RETURN(rdata && rdata->msg_info.msg && p_tsx, PJ_EINVAL);

    /* Keep shortcut to message */
    msg = rdata->msg_info.msg;
    
    /* Make sure this is a request message. */
    PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);

    /* Make sure method is not ACK */
    PJ_ASSERT_RETURN(msg->line.req.method.id != PJSIP_ACK_METHOD,
		     PJ_EINVALIDOP);

    /* Make sure CSeq header is present. */
    cseq = rdata->msg_info.cseq;
    if (!cseq)
	return PJSIP_EMISSINGHDR;

    /* Make sure Via header is present. */
    if (rdata->msg_info.via == NULL)
	return PJSIP_EMISSINGHDR;

    /* Check that method in CSeq header match request method.
     * Reference: PROTOS #1922
     */
    if (pjsip_method_cmp(&msg->line.req.method, 
			 &rdata->msg_info.cseq->method) != 0)
    {
	PJ_LOG(4,(THIS_FILE, "Error: CSeq header contains different "
			     "method than the request line"));
	return PJSIP_EINVALIDHDR;
    }

    /* 
     * Create transaction instance. 
     */
    status = tsx_create( tsx_user, &tsx);
    if (status != PJ_SUCCESS)
	return status;


    /* Lock transaction. */
    lock_tsx(tsx, &lck);

    /* Role is UAS */
    tsx->role = PJSIP_ROLE_UAS;

    /* Save method. */
    pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);

    /* Save CSeq */
    tsx->cseq = cseq->cseq;

    /* Get transaction key either from branch for RFC3261 message, or
     * create transaction key.
     */
    status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, 
                                  PJSIP_ROLE_UAS, &tsx->method, rdata);
    if (status != PJ_SUCCESS) {
        tsx_destroy(tsx);
        return status;
    }

    /* Calculate hashed key value. */
#ifdef PRECALC_HASH
    tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr,
				   tsx->transaction_key.slen);
#endif

    /* Duplicate branch parameter for transaction. */
    branch = &rdata->msg_info.via->branch_param;
    pj_strdup(tsx->pool, &tsx->branch, branch);

    PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
	       tsx->transaction_key.ptr));


    /* Begin with state NULL.
     * Manually set-up the state becase we don't want to call the callback.
     */
    tsx->state = PJSIP_TSX_STATE_NULL; 
    tsx->state_handler = &tsx_on_state_null;

    /* Get response address. */
    status = pjsip_get_response_addr( tsx->pool, rdata, &tsx->res_addr );
    if (status != PJ_SUCCESS) {
	tsx_destroy(tsx);
	return status;
    }

    /* If it's decided that we should use current transport, keep the
     * transport.
     */
    if (tsx->res_addr.transport) {
	tsx->transport = tsx->res_addr.transport;
	pjsip_transport_add_ref(tsx->transport);
	pj_memcpy(&tsx->addr, &tsx->res_addr.addr, tsx->res_addr.addr_len);
	tsx->addr_len = tsx->res_addr.addr_len;
	tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport);
    }


    /* Register the transaction. */
    status = mod_tsx_layer_register_tsx(tsx);
    if (status != PJ_SUCCESS) {
	tsx_destroy(tsx);
	return status;
    }

    /* Put this transaction in rdata's mod_data. */
    rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx;

    /* Unlock transaction and return. */
    unlock_tsx(tsx, &lck);


    PJ_LOG(5,(tsx->obj_name, "Transaction created for %s",
	      pjsip_rx_data_get_info(rdata)));


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -