/* 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 */ }
/* * Transmit message. */ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx, pj_bool_t mod_count) { pj_status_t status; PJ_ASSERT_RETURN(tsx->retransmit_timer.id == TIMER_INACTIVE || !tsx->require_retransmit, PJ_EBUSY); if (tsx->require_retransmit && mod_count) { /* Calculate retransmit/timeout delay */ if (tsx->transmit_count == 0) { tsx->retransmit_time.sec = 0; tsx->retransmit_time.msec = tsx->rto_msec; } else if (tsx->transmit_count < PJ_STUN_MAX_TRANSMIT_COUNT-1) { unsigned msec; msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time); msec <<= 1; tsx->retransmit_time.sec = msec / 1000; tsx->retransmit_time.msec = msec % 1000; } else { tsx->retransmit_time.sec = PJ_STUN_TIMEOUT_VALUE / 1000; tsx->retransmit_time.msec = PJ_STUN_TIMEOUT_VALUE % 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_w_grp_lock(tsx->timer_heap, &tsx->retransmit_timer, &tsx->retransmit_time, TIMER_ACTIVE, tsx->grp_lock); if (status != PJ_SUCCESS) { tsx->retransmit_timer.id = TIMER_INACTIVE; return status; } } if (mod_count) tsx->transmit_count++; PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)", tsx->transmit_count)); pj_log_push_indent(); /* Send message */ status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size); if (status == PJNATH_ESTUNDESTROYED) { /* We've been destroyed, don't access the object. */ } else if (status != PJ_SUCCESS) { if (mod_count) { pj_timer_heap_cancel_if_active( tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); } PJ_PERROR(4, (tsx->obj_name, status, "STUN error sending message")); } pj_log_pop_indent(); return status; }