/* * Add transport/listener reference in the selector. */ PJ_DEF(void) pjsip_tpselector_add_ref(pjsip_tpselector *sel) { if (sel->type == PJSIP_TPSELECTOR_TRANSPORT && sel->u.transport != NULL) pjsip_transport_add_ref(sel->u.transport); else if (sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener != NULL) ; /* Hmm.. looks like we don't have reference counter for listener */ }
static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status) { pjsip_tp_state_callback state_cb; if (tls->close_reason == PJ_SUCCESS) tls->close_reason = status; if (tls->base.is_shutdown || tls->base.is_destroying) return; /* Prevent immediate transport destroy by application, as transport * state notification callback may be stacked and transport instance * must remain valid at any point in the callback. */ pjsip_transport_add_ref(&tls->base); /* Notify application of transport disconnected state */ state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pjsip_tls_state_info tls_info; pj_ssl_sock_info ssl_info; /* Init transport state info */ pj_bzero(&state_info, sizeof(state_info)); state_info.status = tls->close_reason; if (tls->ssock && pj_ssl_sock_get_info(tls->ssock, &ssl_info) == PJ_SUCCESS) { pj_bzero(&tls_info, sizeof(tls_info)); tls_info.ssl_sock_info = &ssl_info; state_info.ext_info = &tls_info; } (*state_cb)(&tls->base, PJSIP_TP_STATE_DISCONNECTED, &state_info); } /* check again */ if (tls->base.is_shutdown || tls->base.is_destroying) { pjsip_transport_dec_ref(&tls->base); return; } /* We can not destroy the transport since high level objects may * still keep reference to this transport. So we can only * instruct transport manager to gracefully start the shutdown * procedure for this transport. */ pjsip_transport_shutdown(&tls->base); /* Now, it is ok to destroy the transport. */ pjsip_transport_dec_ref(&tls->base); }
SipTransport::SipTransport(pjsip_transport* t) : transport_(nullptr, deleteTransport) { if (not t or pjsip_transport_add_ref(t) != PJ_SUCCESS) throw std::runtime_error("invalid transport"); // Set pointer here, right after the successful pjsip_transport_add_ref transport_.reset(t); RING_DBG("SipTransport@%p {tr=%p {rc=%ld}}", this, transport_.get(), pj_atomic_get(transport_->ref_cnt)); }
Flow::Flow(FlowTable* flow_table, pjsip_transport* transport, const pj_sockaddr* remote_addr) : _flow_table(flow_table), _transport(transport), _tp_state_listener_key(NULL), _remote_addr(*remote_addr), _token(), _authorized_ids(), _default_id(), _refs(1), _dialogs(0) { // Create the lock for protecting the authorized_ids and default_id. pthread_mutex_init(&_flow_lock, NULL); // Create a random base64 encoded token for the flow. Utils::create_random_token(Flow::TOKEN_LENGTH, _token); if (PJSIP_TRANSPORT_IS_RELIABLE(_transport)) { // We're adding a new reliable transport, so make sure it stays around // until we remove it from the map. pjsip_transport_add_ref(_transport); // Add a state listener so we find out when the flow is destroyed. pjsip_transport_add_state_listener(_transport, &on_transport_state_changed, this, &_tp_state_listener_key); LOG_DEBUG("Added transport listener for flow %p", this); } // Initialize the timer. pj_timer_entry_init(&_timer, PJ_FALSE, (void*)this, &on_timer_expiry); _timer.id = 0; // Start the timer as an idle timer. restart_timer(IDLE_TIMER, IDLE_TIMEOUT); }
pjsip_transport* ConnectionPool::get_connection() { pjsip_transport* tp = NULL; pthread_mutex_lock(&_tp_hash_lock); if (_active_connections > 0) { // Select a transport by starting at a random point in the hash and // stepping through the hash until a connected entry is found. int start_slot = rand() % _num_connections; int ii = start_slot; while (!_tp_hash[ii].connected) { ii = (ii + 1) % _num_connections; if (ii == start_slot) { break; } } tp = _tp_hash[ii].tp; if (tp != NULL) { // Add a reference to the transport to make sure it is not destroyed. // The reference must be decremented once again when the transport is set // on the message. pjsip_transport_add_ref(tp); } } pthread_mutex_unlock(&_tp_hash_lock); return tp; }
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; } 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, ®c->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; /* 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, ®c_tsx_callback); if (status!=PJ_SUCCESS) { 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; }
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(¶m.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); /* Call regc tsx callback before handling any response */ pj_lock_release(regc->lock); (*regc->tsx_cb)(¶m); 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 != ®c->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; /* reset current op */ 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 != ®c->contact_hdr_list) { pj_list_insert_before(ins_hdr, (pjsip_hdr*) pjsip_hdr_shallow_clone(tsx->last_tx->pool, chdr)); chdr = chdr->next; } /* Also add bindings which are to be removed */ while (!pj_list_empty(®c->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( ®c->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); 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_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]; 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 */ 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); 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); } }
/* * This callback is called by SSL socket when pending accept() operation * has completed. */ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock, pj_ssl_sock_t *new_ssock, const pj_sockaddr_t *src_addr, int src_addr_len) { struct tls_listener *listener; struct tls_transport *tls; pj_ssl_sock_info ssl_info; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; pj_bool_t is_shutdown; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); listener = (struct tls_listener*) pj_ssl_sock_get_user_data(ssock); PJ_ASSERT_RETURN(new_ssock, PJ_TRUE); PJ_LOG(4,(listener->factory.obj_name, "TLS listener %.*s:%d: got incoming TLS connection " "from %s, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), new_ssock)); /* Retrieve SSL socket info, close the socket if this is failed * as the SSL socket info availability is rather critical here. */ status = pj_ssl_sock_get_info(new_ssock, &ssl_info); if (status != PJ_SUCCESS) { pj_ssl_sock_close(new_ssock); return PJ_TRUE; } /* * Incoming connection! * Create TLS transport for the new socket. */ status = tls_create( listener, NULL, new_ssock, PJ_TRUE, (const pj_sockaddr_in*)&listener->factory.local_addr, (const pj_sockaddr_in*)src_addr, NULL, &tls); if (status != PJ_SUCCESS) return PJ_TRUE; /* Set the "pending" SSL socket user data */ pj_ssl_sock_set_user_data(new_ssock, tls); /* Prevent immediate transport destroy as application may access it * (getting info, etc) in transport state notification callback. */ pjsip_transport_add_ref(&tls->base); /* If there is verification error and verification is mandatory, shutdown * and destroy the transport. */ if (ssl_info.verify_status && listener->tls_setting.verify_client) { if (tls->close_reason == PJ_SUCCESS) tls->close_reason = PJSIP_TLS_ECERTVERIF; pjsip_transport_shutdown(&tls->base); } /* Notify transport state to application */ state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pjsip_tls_state_info tls_info; pjsip_transport_state tp_state; /* Init transport state info */ pj_bzero(&tls_info, sizeof(tls_info)); pj_bzero(&state_info, sizeof(state_info)); tls_info.ssl_sock_info = &ssl_info; state_info.ext_info = &tls_info; /* Set transport state based on verification status */ if (ssl_info.verify_status && listener->tls_setting.verify_client) { tp_state = PJSIP_TP_STATE_DISCONNECTED; state_info.status = PJSIP_TLS_ECERTVERIF; } else { tp_state = PJSIP_TP_STATE_CONNECTED; state_info.status = PJ_SUCCESS; } (*state_cb)(&tls->base, tp_state, &state_info); } /* Release transport reference. If transport is shutting down, it may * get destroyed here. */ is_shutdown = tls->base.is_shutdown; pjsip_transport_dec_ref(&tls->base); if (is_shutdown) return PJ_TRUE; status = tls_start_read(tls); if (status != PJ_SUCCESS) { PJ_LOG(3,(tls->base.obj_name, "New transport cancelled")); tls_init_shutdown(tls, status); tls_destroy(&tls->base, status); } else { /* Start keep-alive timer */ if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) { pj_time_val delay = {PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tls->last_activity); } } return PJ_TRUE; }
pj_status_t ConnectionPool::create_connection(int hash_slot) { // Resolve the target host to an IP address. pj_sockaddr remote_addr; pj_status_t status = resolve_host(&_target.host, &remote_addr); if (status != PJ_SUCCESS) { LOG_ERROR("Failed to resolve %.*s to an IP address - %s", _target.host.slen, _target.host.ptr, PJUtils::pj_status_to_string(status).c_str()); return status; } pj_sockaddr_set_port(&remote_addr, _target.port); // Call the factory to create a new transport connection. pjsip_transport* tp; status = _tpfactory->create_transport(_tpfactory, pjsip_endpt_get_tpmgr(_endpt), _endpt, &remote_addr, sizeof(pj_sockaddr_in), &tp); if (status != PJ_SUCCESS) { return status; } // Add a reference to the new transport to stop it being destroyed while we // have pointers referencing it. pjsip_transport_add_ref(tp); LOG_DEBUG("Created transport %s in slot %d (%.*s:%d to %.*s:%d)", tp->obj_name, hash_slot, (int)tp->local_name.host.slen, tp->local_name.host.ptr, tp->local_name.port, (int)tp->remote_name.host.slen, tp->remote_name.host.ptr, tp->remote_name.port); // Register for transport state callbacks. pjsip_tp_state_listener_key* key; status = pjsip_transport_add_state_listener(tp, &transport_state, (void*)this, &key); // Store the new transport in the hash slot, but marked as disconnected. pthread_mutex_lock(&_tp_hash_lock); _tp_hash[hash_slot].tp = tp; _tp_hash[hash_slot].state = PJSIP_TP_STATE_DISCONNECTED; _tp_map[tp] = hash_slot; // Don't increment the connection count here, wait until we get confirmation // that the transport is connected. pthread_mutex_unlock(&_tp_hash_lock); return PJ_SUCCESS; }
/* * This is the handler to receive message for this test. It is used to * control and verify the behavior of the message transmitted by the * transaction. */ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) { if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID) == 0) { /* * The TEST1_BRANCH_ID test performs the verifications for transaction * retransmission mechanism. It will not answer the incoming request * with any response. */ pjsip_msg *msg = rdata->msg_info.msg; PJ_LOG(4,(THIS_FILE, " received request")); /* Only wants to take INVITE or OPTIONS method. */ if (msg->line.req.method.id != PJSIP_INVITE_METHOD && msg->line.req.method.id != PJSIP_OPTIONS_METHOD) { PJ_LOG(3,(THIS_FILE, " error: received unexpected method %.*s", msg->line.req.method.name.slen, msg->line.req.method.name.ptr)); test_complete = -600; return PJ_TRUE; } if (recv_count == 0) { recv_count++; //pj_gettimeofday(&recv_last); recv_last = rdata->pkt_info.timestamp; } else { pj_time_val now; unsigned msec_expected, msec_elapsed; int max_received; //pj_gettimeofday(&now); now = rdata->pkt_info.timestamp; PJ_TIME_VAL_SUB(now, recv_last); msec_elapsed = now.sec*1000 + now.msec; ++recv_count; msec_expected = (1<<(recv_count-2))*PJSIP_T1_TIMEOUT; if (msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (msec_expected > PJSIP_T2_TIMEOUT) msec_expected = PJSIP_T2_TIMEOUT; max_received = 11; } else { max_received = 7; } if (DIFF(msec_expected, msec_elapsed) > TEST1_ALLOWED_DIFF) { PJ_LOG(3,(THIS_FILE, " error: expecting retransmission no. %d in %d " "ms, received in %d ms", recv_count-1, msec_expected, msec_elapsed)); test_complete = -610; } if (recv_count > max_received) { PJ_LOG(3,(THIS_FILE, " error: too many messages (%d) received", recv_count)); test_complete = -620; } //pj_gettimeofday(&recv_last); recv_last = rdata->pkt_info.timestamp; } return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID) == 0) { /* * The TEST4_BRANCH_ID test simulates transport failure after several * retransmissions. */ recv_count++; if (recv_count == TEST4_RETRANSMIT_CNT) { /* Simulate transport failure. */ pjsip_loop_set_failure(loop, 2, NULL); } else if (recv_count > TEST4_RETRANSMIT_CNT) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -631; } return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID) == 0) { /* * The TEST5_BRANCH_ID test simulates user terminating the transaction * after several retransmissions. */ recv_count++; if (recv_count == TEST5_RETRANSMIT_CNT+1) { pj_str_t key; pjsip_transaction *tsx; pjsip_tsx_create_key( rdata->tp_info.pool, &key, PJSIP_ROLE_UAC, &rdata->msg_info.msg->line.req.method, rdata); tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_mutex_unlock(tsx->mutex); } else { PJ_LOG(3,(THIS_FILE, " error: uac transaction not found!")); test_complete = -633; } } else if (recv_count > TEST5_RETRANSMIT_CNT+1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -634; } return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID) == 0) { /* * The TEST6_BRANCH_ID test successfull non-INVITE transaction. */ pj_status_t status; recv_count++; if (recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -635; } status = pjsip_endpt_respond_stateless(endpt, rdata, 202, NULL, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send response", status); test_complete = -636; } return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID) == 0) { /* * The TEST7_BRANCH_ID test successfull non-INVITE transaction * with provisional response. */ pj_status_t status; pjsip_response_addr res_addr; struct response *r; pjsip_tx_data *tdata; pj_time_val delay = { 2, 0 }; recv_count++; if (recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -640; return PJ_TRUE; } /* Respond with provisional response */ status = pjsip_endpt_create_response(endpt, rdata, 100, NULL, &tdata); pj_assert(status == PJ_SUCCESS); status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); pj_assert(status == PJ_SUCCESS); status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL); pj_assert(status == PJ_SUCCESS); /* Create the final response. */ status = pjsip_endpt_create_response(endpt, rdata, 202, NULL, &tdata); pj_assert(status == PJ_SUCCESS); /* Schedule sending final response in couple of of secs. */ r = pj_pool_alloc(tdata->pool, sizeof(*r)); r->res_addr = res_addr; r->tdata = tdata; if (r->res_addr.transport) pjsip_transport_add_ref(r->res_addr.transport); timer.entry.cb = &send_response_callback; timer.entry.user_data = r; pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID) == 0) { /* * The TEST8_BRANCH_ID test failed INVITE transaction. */ pjsip_method *method; pj_status_t status; method = &rdata->msg_info.msg->line.req.method; recv_count++; if (method->id == PJSIP_INVITE_METHOD) { if (recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -635; } status = pjsip_endpt_respond_stateless(endpt, rdata, 301, NULL, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send response", status); test_complete = -636; } } else if (method->id == PJSIP_ACK_METHOD) { if (recv_count == 2) { pj_str_t key; pj_time_val delay = { 5, 0 }; /* Schedule timer to destroy transaction after 5 seconds. * This is to make sure that transaction does not * retransmit ACK. */ pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC, &pjsip_invite_method, rdata); pj_strcpy(&timer.tsx_key, &key); timer.entry.id = 301; timer.entry.cb = &terminate_tsx_callback; pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); } if (recv_count > 2) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -638; } } else { PJ_LOG(3,(THIS_FILE," error: not expecting %s", pjsip_rx_data_get_info(rdata))); test_complete = -639; } } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID) == 0) { /* * The TEST9_BRANCH_ID test failed INVITE transaction with * provisional response. */ pjsip_method *method; pj_status_t status; method = &rdata->msg_info.msg->line.req.method; recv_count++; if (method->id == PJSIP_INVITE_METHOD) { pjsip_response_addr res_addr; struct response *r; pjsip_tx_data *tdata; pj_time_val delay = { 2, 0 }; if (recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -650; return PJ_TRUE; } /* Respond with provisional response */ status = pjsip_endpt_create_response(endpt, rdata, 100, NULL, &tdata); pj_assert(status == PJ_SUCCESS); status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); pj_assert(status == PJ_SUCCESS); status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL); pj_assert(status == PJ_SUCCESS); /* Create the final response. */ status = pjsip_endpt_create_response(endpt, rdata, 302, NULL, &tdata); pj_assert(status == PJ_SUCCESS); /* Schedule sending final response in couple of of secs. */ r = pj_pool_alloc(tdata->pool, sizeof(*r)); r->res_addr = res_addr; r->tdata = tdata; if (r->res_addr.transport) pjsip_transport_add_ref(r->res_addr.transport); timer.entry.cb = &send_response_callback; timer.entry.user_data = r; pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); } else if (method->id == PJSIP_ACK_METHOD) { if (recv_count == 2) { pj_str_t key; pj_time_val delay = { 5, 0 }; /* Schedule timer to destroy transaction after 5 seconds. * This is to make sure that transaction does not * retransmit ACK. */ pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC, &pjsip_invite_method, rdata); pj_strcpy(&timer.tsx_key, &key); timer.entry.id = 302; timer.entry.cb = &terminate_tsx_callback; pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); } if (recv_count > 2) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -638; } } else { PJ_LOG(3,(THIS_FILE," error: not expecting %s", pjsip_rx_data_get_info(rdata))); test_complete = -639; } return PJ_TRUE; } return PJ_FALSE; }
SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<TlsListener>& l) : transport(t), tlsListener(l) { pjsip_transport_add_ref(transport); }
static void 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_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); } } /* 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; /* reset current op */ regc->current_op = REGC_IDLE; status = pjsip_auth_clt_reinit_req( ®c->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); 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 { pjsip_rx_data *rdata; pj_int32_t expiration = NOEXP; unsigned contact_cnt = 0; pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; 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); /* Mark operation as complete */ regc->current_op = REGC_IDLE; /* Schedule next registration */ if (regc->auto_reg && expiration > 0) { pj_time_val delay = { 0, 0}; delay.sec = expiration - DELAY_BEFORE_REFRESH; if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && delay.sec > (pj_int32_t)regc->expires) { delay.sec = regc->expires; } if (delay.sec < DELAY_BEFORE_REFRESH) delay.sec = DELAY_BEFORE_REFRESH; regc->timer.cb = ®c_refresh_timer_cb; regc->timer.id = REFRESH_TIMER; regc->timer.user_data = regc; pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay); pj_gettimeofday(®c->last_reg); regc->next_reg = regc->last_reg; regc->next_reg.sec += delay.sec; } } 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; /* 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 : pjsip_get_status_text(tsx->status_code)), rdata, expiration, contact_cnt, contact); 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); } }
/* * This callback is called by active socket when pending accept() operation * has completed. */ static pj_bool_t on_accept_complete(pj_activesock_t *asock, pj_sock_t sock, const pj_sockaddr_t *src_addr, int src_addr_len) { struct tcp_listener *listener; struct tcp_transport *tcp; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; pj_sockaddr tmp_src_addr; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); listener = (struct tcp_listener*) pj_activesock_get_user_data(asock); PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_TRUE); PJ_LOG(4,(listener->factory.obj_name, "TCP listener %.*s:%d: got incoming TCP connection " "from %s, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), sock)); /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "incoming SIP TCP socket"); /* tcp_create() expect pj_sockaddr, so copy src_addr to temporary var, * just in case. */ pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr)); pj_sockaddr_cp(&tmp_src_addr, src_addr); /* * Incoming connection! * Create TCP transport for the new socket. */ status = tcp_create( listener, NULL, sock, PJ_TRUE, &listener->factory.local_addr, &tmp_src_addr, &tcp); if (status == PJ_SUCCESS) { /* Add a reference to prevent the transport from being destroyed while * we're operating on it. */ pjsip_transport_add_ref(&tcp->base); status = tcp_start_read(tcp); if (status != PJ_SUCCESS) { PJ_LOG(3,(tcp->base.obj_name, "New transport cancelled")); pjsip_transport_dec_ref(&tcp->base); tcp_destroy(&tcp->base, status); } else { /* Start keep-alive timer */ if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) { pj_time_val delay = {PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tcp->ka_timer, &delay); tcp->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tcp->last_activity); } /* Notify application of transport state accepted */ state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pj_bzero(&state_info, sizeof(state_info)); (*state_cb)(&tcp->base, PJSIP_TP_STATE_CONNECTED, &state_info); } pjsip_transport_dec_ref(&tcp->base); } } return PJ_TRUE; }