/* * Send outgoing message and start STUN transaction. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, pj_bool_t retransmit, void *pkt, unsigned pkt_len) { pj_status_t status; PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL); PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY); /* Encode message */ tsx->last_pkt = pkt; tsx->last_pkt_size = pkt_len; /* Update STUN retransmit flag */ tsx->require_retransmit = retransmit; /* For TCP, schedule timeout timer after PJ_STUN_TIMEOUT_VALUE. * Since we don't have timeout timer, simulate this by using * retransmit timer. */ if (!retransmit) { unsigned timeout; pj_assert(tsx->retransmit_timer.id == 0); tsx->transmit_count = PJ_STUN_MAX_TRANSMIT_COUNT; timeout = tsx->rto_msec * 16; tsx->retransmit_time.sec = timeout / 1000; tsx->retransmit_time.msec = timeout % 1000; /* Schedule timer first because when send_msg() failed we can * cancel it (as opposed to when schedule_timer() failed we cannot * cancel transmission). */; status = pj_timer_heap_schedule(tsx->timer_heap, &tsx->retransmit_timer, &tsx->retransmit_time); if (status != PJ_SUCCESS) { tsx->retransmit_timer.id = 0; return status; } tsx->retransmit_timer.id = TIMER_ACTIVE; } /* Send the message */ status = tsx_transmit_msg(tsx, PJ_TRUE); if (status != PJ_SUCCESS) { if (tsx->retransmit_timer.id != 0) { pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer); tsx->retransmit_timer.id = 0; } return status; } return PJ_SUCCESS; }
/* * Request to retransmit the request. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, pj_bool_t mod_count) { if (tsx->destroy_timer.id != 0) { return PJ_SUCCESS; } pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); return tsx_transmit_msg(tsx, mod_count); }
/* * Request to retransmit the request. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx) { if (tsx->destroy_timer.id != 0) { return PJ_SUCCESS; } if (tsx->retransmit_timer.id != 0) { pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer); tsx->retransmit_timer.id = 0; } return tsx_transmit_msg(tsx); }
/* Retransmit timer callback */ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer) { pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; pj_status_t status; PJ_UNUSED_ARG(timer_heap); pj_grp_lock_acquire(tsx->grp_lock); if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) { /* tsx may be destroyed when calling the callback below */ pj_grp_lock_t *grp_lock = tsx->grp_lock; /* Retransmission count exceeded. Transaction has failed */ tsx->retransmit_timer.id = 0; PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response")); pj_log_push_indent(); if (!tsx->complete) { tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0); } } pj_grp_lock_release(grp_lock); /* We might have been destroyed, don't try to access the object */ pj_log_pop_indent(); return; } tsx->retransmit_timer.id = 0; status = tsx_transmit_msg(tsx, PJ_TRUE); if (status != PJ_SUCCESS) { tsx->retransmit_timer.id = 0; if (!tsx->complete) { tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, status, NULL, NULL, 0); } } } pj_grp_lock_release(tsx->grp_lock); /* We might have been destroyed, don't try to access the object */ }
/* Retransmit timer callback */ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer) { pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; pj_status_t status; PJ_UNUSED_ARG(timer_heap); if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) { /* Retransmission count exceeded. Transaction has failed */ tsx->retransmit_timer.id = 0; PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response")); if (!tsx->complete) { tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0); } } /* We might have been destroyed, don't try to access the object */ return; } tsx->retransmit_timer.id = 0; status = tsx_transmit_msg(tsx, PJ_TRUE); if (status == PJNATH_ESTUNDESTROYED) { /* We've been destroyed, don't try to access the object */ } else if (status != PJ_SUCCESS && status != PJ_EPENDING) { tsx->retransmit_timer.id = 0; if (!tsx->complete) { tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, status, NULL, NULL, 0); } } /* We might have been destroyed, don't try to access the object */ } }