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; } }
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); } }
/* * Create new transmit buffer. */ PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr, pjsip_tx_data **p_tdata ) { pj_pool_t *pool; pjsip_tx_data *tdata; pj_status_t status; PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL); pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p", PJSIP_POOL_LEN_TDATA, PJSIP_POOL_INC_TDATA ); if (!pool) return PJ_ENOMEM; tdata = PJ_POOL_ZALLOC_T(pool, pjsip_tx_data); tdata->pool = pool; tdata->mgr = mgr; pj_memcpy(tdata->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME); status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt); if (status != PJ_SUCCESS) { pjsip_endpt_release_pool( mgr->endpt, tdata->pool ); return status; } //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock); status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock); if (status != PJ_SUCCESS) { pjsip_endpt_release_pool( mgr->endpt, tdata->pool ); return status; } pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key.key)); #if defined(PJ_DEBUG) && PJ_DEBUG!=0 pj_atomic_inc( tdata->mgr->tdata_counter ); #endif *p_tdata = tdata; 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); } }
/* * pjsip_udp_transport_attach() * * Attach UDP socket and start transport. */ static pj_status_t transport_attach( pjsip_endpoint *endpt, pjsip_transport_type_e type, pj_sock_t sock, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport) { pj_pool_t *pool; struct udp_transport *tp; const char *format, *ipv6_quoteb, *ipv6_quotee; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(endpt && sock!=PJ_INVALID_SOCKET && a_name && async_cnt>0, PJ_EINVAL); /* Object name. */ if (type & PJSIP_TRANSPORT_IPV6) { format = "udpv6%p"; ipv6_quoteb = "["; ipv6_quotee = "]"; } else { format = "udp%p"; ipv6_quoteb = ipv6_quotee = ""; } /* Create pool. */ pool = pjsip_endpt_create_pool(endpt, format, PJSIP_POOL_LEN_TRANSPORT, PJSIP_POOL_INC_TRANSPORT); if (!pool) return PJ_ENOMEM; /* Create the UDP transport object. */ tp = PJ_POOL_ZALLOC_T(pool, struct udp_transport); /* Save pool. */ tp->base.pool = pool; pj_memcpy(tp->base.obj_name, pool->obj_name, PJ_MAX_OBJ_NAME); /* Init reference counter. */ status = pj_atomic_create(pool, 0, &tp->base.ref_cnt); if (status != PJ_SUCCESS) goto on_error; /* Init lock. */ status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &tp->base.lock); if (status != PJ_SUCCESS) goto on_error; /* Set type. */ tp->base.key.type = type; /* Remote address is left zero (except the family) */ tp->base.key.rem_addr.addr.sa_family = (pj_uint16_t) ((type & PJSIP_TRANSPORT_IPV6) ? pj_AF_INET6() : pj_AF_INET()); /* Type name. */ tp->base.type_name = "UDP"; /* Transport flag */ tp->base.flag = pjsip_transport_get_flag_from_type(type); /* Length of addressess. */ tp->base.addr_len = sizeof(tp->base.local_addr); /* Init local address. */ status = pj_sock_getsockname(sock, &tp->base.local_addr, &tp->base.addr_len); if (status != PJ_SUCCESS) goto on_error; /* Init remote name. */ if (type == PJSIP_TRANSPORT_UDP) tp->base.remote_name.host = pj_str("0.0.0.0"); else tp->base.remote_name.host = pj_str("::0"); tp->base.remote_name.port = 0; /* Init direction */ tp->base.dir = PJSIP_TP_DIR_NONE; /* Set endpoint. */ tp->base.endpt = endpt; /* Transport manager and timer will be initialized by tpmgr */ /* Attach socket and assign name. */ udp_set_socket(tp, sock, a_name); /* Register to ioqueue */ status = register_to_ioqueue(tp); if (status != PJ_SUCCESS) goto on_error; /* Set functions. */ tp->base.send_msg = &udp_send_msg; tp->base.do_shutdown = &udp_shutdown; tp->base.destroy = &udp_destroy; /* This is a permanent transport, so we initialize the ref count * to one so that transport manager don't destroy this transport * when there's no user! */ pj_atomic_inc(tp->base.ref_cnt); /* Register to transport manager. */ tp->base.tpmgr = pjsip_endpt_get_tpmgr(endpt); status = pjsip_transport_register( tp->base.tpmgr, (pjsip_transport*)tp); if (status != PJ_SUCCESS) goto on_error; /* Create rdata and put it in the array. */ tp->rdata_cnt = 0; tp->rdata = (pjsip_rx_data**) pj_pool_calloc(tp->base.pool, async_cnt, sizeof(pjsip_rx_data*)); for (i=0; i<async_cnt; ++i) { pj_pool_t *rdata_pool = pjsip_endpt_create_pool(endpt, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC); if (!rdata_pool) { pj_atomic_set(tp->base.ref_cnt, 0); pjsip_transport_destroy(&tp->base); return PJ_ENOMEM; } init_rdata(tp, i, rdata_pool, NULL); tp->rdata_cnt++; } /* Start reading the ioqueue. */ status = start_async_read(tp); if (status != PJ_SUCCESS) { pjsip_transport_destroy(&tp->base); return status; } /* Done. */ if (p_transport) *p_transport = &tp->base; PJ_LOG(4,(tp->base.obj_name, "SIP %s started, published address is %s%.*s%s:%d", pjsip_transport_get_type_desc((pjsip_transport_type_e)tp->base.key.type), ipv6_quoteb, (int)tp->base.local_name.host.slen, tp->base.local_name.host.ptr, ipv6_quotee, tp->base.local_name.port)); return PJ_SUCCESS; on_error: udp_destroy((pjsip_transport*)tp); return status; }
/* * Add reference to tx buffer. */ PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ) { pj_atomic_inc(tdata->ref_cnt); }
/* * Poll the I/O Completion Port, execute callback, * and return the key and bytes transfered of the last operation. */ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, pj_ssize_t *p_bytes, pj_ioqueue_key_t **p_key ) { DWORD dwBytesTransfered, dwKey; generic_overlapped *pOv; pj_ioqueue_key_t *key; pj_ssize_t size_status = -1; BOOL rcGetQueued; /* Poll for completion status. */ rcGetQueued = GetQueuedCompletionStatus(hIocp, &dwBytesTransfered, &dwKey, (OVERLAPPED**)&pOv, dwTimeout); /* The return value is: * - nonzero if event was dequeued. * - zero and pOv==NULL if no event was dequeued. * - zero and pOv!=NULL if event for failed I/O was dequeued. */ if (pOv) { pj_bool_t has_lock; /* Event was dequeued for either successfull or failed I/O */ key = (pj_ioqueue_key_t*)dwKey; size_status = dwBytesTransfered; /* Report to caller regardless */ if (p_bytes) *p_bytes = size_status; if (p_key) *p_key = key; #if PJ_IOQUEUE_HAS_SAFE_UNREG /* We shouldn't call callbacks if key is quitting. */ if (key->closing) return PJ_TRUE; /* If concurrency is disabled, lock the key * (and save the lock status to local var since app may change * concurrency setting while in the callback) */ if (key->allow_concurrent == PJ_FALSE) { pj_mutex_lock(key->mutex); has_lock = PJ_TRUE; } else { has_lock = PJ_FALSE; } /* Now that we get the lock, check again that key is not closing */ if (key->closing) { if (has_lock) { pj_mutex_unlock(key->mutex); } return PJ_TRUE; } /* Increment reference counter to prevent this key from being * deleted */ pj_atomic_inc(key->ref_count); #else PJ_UNUSED_ARG(has_lock); #endif /* Carry out the callback */ switch (pOv->operation) { case PJ_IOQUEUE_OP_READ: case PJ_IOQUEUE_OP_RECV: case PJ_IOQUEUE_OP_RECV_FROM: pOv->operation = 0; if (key->cb.on_read_complete) key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv, size_status); break; case PJ_IOQUEUE_OP_WRITE: case PJ_IOQUEUE_OP_SEND: case PJ_IOQUEUE_OP_SEND_TO: pOv->operation = 0; if (key->cb.on_write_complete) key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv, size_status); break; #if PJ_HAS_TCP case PJ_IOQUEUE_OP_ACCEPT: /* special case for accept. */ ioqueue_on_accept_complete(key, (ioqueue_accept_rec*)pOv); if (key->cb.on_accept_complete) { ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv; pj_status_t status = PJ_SUCCESS; pj_sock_t newsock; newsock = accept_rec->newsock; accept_rec->newsock = PJ_INVALID_SOCKET; if (newsock == PJ_INVALID_SOCKET) { int dwError = WSAGetLastError(); if (dwError == 0) dwError = OSERR_ENOTCONN; status = PJ_RETURN_OS_ERROR(dwError); } key->cb.on_accept_complete(key, (pj_ioqueue_op_key_t*)pOv, newsock, status); } break; case PJ_IOQUEUE_OP_CONNECT: #endif case PJ_IOQUEUE_OP_NONE: pj_assert(0); break; } #if PJ_IOQUEUE_HAS_SAFE_UNREG decrement_counter(key); if (has_lock) pj_mutex_unlock(key->mutex); #endif return PJ_TRUE; } /* No event was queued. */ return PJ_FALSE; }
/* * pj_ioqueue_register_sock() */ 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 **key ) { HANDLE hioq; pj_ioqueue_key_t *rec; u_long value; int rc; PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL); pj_lock_acquire(ioqueue->lock); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing list first to release unused keys. * Must do this with lock acquired. */ scan_closing_keys(ioqueue); /* If safe unregistration is used, then get the key record from * the free list. */ if (pj_list_empty(&ioqueue->free_list)) { pj_lock_release(ioqueue->lock); return PJ_ETOOMANY; } rec = ioqueue->free_list.next; pj_list_erase(rec); /* Set initial reference count to 1 */ pj_assert(pj_atomic_get(rec->ref_count) == 0); pj_atomic_inc(rec->ref_count); rec->closing = 0; #else rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); #endif /* Build the key for this socket. */ rec->ioqueue = ioqueue; rec->hnd = (HANDLE)sock; rec->hnd_type = HND_IS_SOCKET; rec->user_data = user_data; pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback)); /* Set concurrency for this handle */ rc = pj_ioqueue_set_concurrency(rec, ioqueue->default_concurrency); if (rc != PJ_SUCCESS) { pj_lock_release(ioqueue->lock); return rc; } #if PJ_HAS_TCP rec->connecting = 0; #endif /* Set socket to nonblocking. */ value = 1; rc = ioctlsocket(sock, FIONBIO, &value); if (rc != 0) { pj_lock_release(ioqueue->lock); return PJ_RETURN_OS_ERROR(WSAGetLastError()); } /* Associate with IOCP */ hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0); if (!hioq) { pj_lock_release(ioqueue->lock); return PJ_RETURN_OS_ERROR(GetLastError()); } *key = rec; #if PJ_IOQUEUE_HAS_SAFE_UNREG pj_list_push_back(&ioqueue->active_list, rec); #endif pj_lock_release(ioqueue->lock); return PJ_SUCCESS; }
// // Increment. // void inc() { pj_atomic_inc(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; }