static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_regc *regc = (pjsip_regc*) entry->user_data; pjsip_tx_data *tdata; pj_status_t status; PJ_UNUSED_ARG(timer_heap); /* Temporarily increase busy flag to prevent regc from being deleted * in pjsip_regc_send() or in the callback */ pj_atomic_inc(regc->busy_ctr); entry->id = 0; status = pjsip_regc_register(regc, 1, &tdata); if (status == PJ_SUCCESS) { status = pjsip_regc_send(regc, tdata); } if (status != PJ_SUCCESS && regc->cb) { char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg)); call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL); } /* 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); } }
static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, const void *stun_pkt, pj_size_t pkt_size) { pj_stun_tx_data *tdata; pj_stun_session *sess; pj_status_t status; tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); sess = tdata->sess; /* Lock the session and prevent user from destroying us in the callback */ pj_atomic_inc(sess->busy); pj_lock_acquire(sess->lock); status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, pkt_size, tdata->dst_addr, tdata->addr_len); pj_lock_release(sess->lock); if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { pj_stun_session_destroy(sess); return PJNATH_ESTUNDESTROYED; } else { return status; } }
/* Decrement the key's reference counter, and when the counter reach zero, * destroy the key. */ static void decrement_counter(pj_ioqueue_key_t *key) { if (pj_atomic_dec_and_get(key->ref_count) == 0) { pj_lock_acquire(key->ioqueue->lock); pj_assert(key->closing == 1); pj_gettickcount(&key->free_time); key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; pj_time_val_normalize(&key->free_time); pj_list_erase(key); pj_list_push_back(&key->ioqueue->closing_list, key); pj_lock_release(key->ioqueue->lock); } }
/* * Decrease transport data reference, destroy it when the reference count * reaches zero. */ PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) { pj_assert( pj_atomic_get(tdata->ref_cnt) > 0); if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) { PJ_LOG(5,(tdata->obj_name, "Destroying txdata %s", pjsip_tx_data_get_info(tdata))); pjsip_tpselector_dec_ref(&tdata->tp_sel); #if defined(PJ_DEBUG) && PJ_DEBUG!=0 pj_atomic_dec( tdata->mgr->tdata_counter ); #endif pj_atomic_destroy( tdata->ref_cnt ); pj_lock_destroy( tdata->lock ); pjsip_endpt_release_pool( tdata->mgr->endpt, tdata->pool ); return PJSIP_EBUFDESTROYED; } else { return PJ_SUCCESS; } }
static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, pj_status_t status, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_session *sess; pj_bool_t notify_user = PJ_TRUE; pj_stun_tx_data *tdata; tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); sess = tdata->sess; /* Lock the session and prevent user from destroying us in the callback */ pj_atomic_inc(sess->busy); pj_lock_acquire(sess->lock); /* Handle authentication challenge */ handle_auth_challenge(sess, tdata, response, src_addr, src_addr_len, ¬ify_user); if (notify_user && sess->cb.on_request_complete) { (*sess->cb.on_request_complete)(sess, status, tdata->token, tdata, response, src_addr, src_addr_len); } /* Destroy the transmit data. This will remove the transaction * from the pending list too. */ pj_stun_msg_destroy_tdata(sess, tdata); tdata = NULL; pj_lock_release(sess->lock); if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { pj_stun_session_destroy(sess); return; } }
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); } }
/* * pj_atomic_dec() */ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) { pj_atomic_dec_and_get(atomic_var); }
// // Decrement and get the result. // pj_atomic_value_t dec_and_get() { return pj_atomic_dec_and_get(var_); }
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); } }
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; status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT, regc, &tsx_callback); if (status!=PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status)); } 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; }
/* * pj_atomic_dec() */ void pj_atomic_dec(pj_atomic_t *atomic_var) { pj_atomic_dec_and_get(atomic_var); }