static int ice_sound_send_stun_request(RtpSession *session, struct IceCheckList *checklist, uint64_t ctime) { struct CandidatePair *remote_candidates = NULL; if (checklist==NULL) return 0; remote_candidates = checklist->cand_pairs; if (remote_candidates==NULL) return 0; { struct CandidatePair *cand_pair; int media_socket = rtp_session_get_rtp_socket(session); StunAddress4 stunServerAddr; StunAtrString username; StunAtrString password; bool_t res; int pos; /* prepare ONCE tie-break value */ if (checklist->tiebreak_value==0) { checklist->tiebreak_value = random() * (0x7fffffffffffffffLL /0x7fff); } cand_pair=NULL; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { cand_pair = &remote_candidates[pos]; if (cand_pair->connectivity_check == ICE_PRUNED) { cand_pair=NULL; continue; } if (cand_pair->connectivity_check == ICE_WAITING) break; if (cand_pair->connectivity_check == ICE_IN_PROGRESS) break; if (cand_pair->connectivity_check == ICE_SUCCEEDED) break; cand_pair=NULL; } if (cand_pair==NULL) return 0; /* nothing to do: every pair is FAILED, FROZEN or PRUNED */ /* start first WAITING pair */ cand_pair=NULL; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { cand_pair = &remote_candidates[pos]; if (cand_pair->connectivity_check == ICE_PRUNED) { cand_pair=NULL; continue; } if (cand_pair->connectivity_check == ICE_WAITING) break; cand_pair=NULL; } if (cand_pair!=NULL) { cand_pair->connectivity_check = ICE_IN_PROGRESS; cand_pair->retransmission_number=0; cand_pair->retransmission_time=ctime+checklist->RTO; /* keep same rem_controlling for retransmission */ cand_pair->rem_controlling = checklist->rem_controlling; } /* try no nominate a pair if we are ready */ if (cand_pair==NULL && checklist->nominated_pair_index<0) { for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { cand_pair = &remote_candidates[pos]; if (cand_pair->connectivity_check == ICE_PRUNED) { cand_pair=NULL; continue; } if (cand_pair->connectivity_check == ICE_SUCCEEDED) { break; } cand_pair=NULL; } /* ALWAYS accept "host" candidate that have succeeded */ if (cand_pair!=NULL && (strcasecmp(cand_pair->remote_candidate.cand_type, "host")==0)) { checklist->nominated_pair_index = pos; cand_pair->nominated_pair = 1; cand_pair->connectivity_check = ICE_IN_PROGRESS; cand_pair->retransmission_number=0; cand_pair->retransmission_time=ctime+checklist->RTO; /* keep same rem_controlling for retransmission */ cand_pair->rem_controlling = checklist->rem_controlling; /* send a new STUN with USE-CANDIDATE */ ms_message("ice.c: nominating pair -> %i (%s:%i:%s -> %s:%i:%s) nominated=%s", pos, cand_pair->local_candidate.conn_addr, cand_pair->local_candidate.conn_port, cand_pair->local_candidate.cand_type, cand_pair->remote_candidate.conn_addr, cand_pair->remote_candidate.conn_port, cand_pair->remote_candidate.cand_type, cand_pair->nominated_pair==0?"FALSE":"TRUE"); checklist->keepalive_time=ctime+15*1000; } else if (cand_pair!=NULL) { struct CandidatePair *cand_pair2=NULL; int pos2; for (pos2=0;pos2<pos && remote_candidates[pos2].remote_candidate.conn_addr[0]!='\0';pos2++) { cand_pair2 = &remote_candidates[pos2]; if (cand_pair2->connectivity_check == ICE_PRUNED) { cand_pair2=NULL; continue; } if (cand_pair2->connectivity_check == ICE_IN_PROGRESS ||cand_pair2->connectivity_check == ICE_WAITING) { break; } cand_pair2=NULL; } if (cand_pair2!=NULL) { /* a better candidate is still tested */ cand_pair=NULL; } else { checklist->nominated_pair_index = pos; cand_pair->nominated_pair = 1; cand_pair->connectivity_check = ICE_IN_PROGRESS; cand_pair->retransmission_number=0; cand_pair->retransmission_time=ctime+checklist->RTO; /* keep same rem_controlling for retransmission */ cand_pair->rem_controlling = checklist->rem_controlling; /* send a new STUN with USE-CANDIDATE */ ms_message("ice.c: nominating pair -> %i (%s:%i:%s -> %s:%i:%s) nominated=%s", pos, cand_pair->local_candidate.conn_addr, cand_pair->local_candidate.conn_port, cand_pair->local_candidate.cand_type, cand_pair->remote_candidate.conn_addr, cand_pair->remote_candidate.conn_port, cand_pair->remote_candidate.cand_type, cand_pair->nominated_pair==0?"FALSE":"TRUE"); checklist->keepalive_time=ctime+15*1000; } } } if (cand_pair==NULL) { /* no WAITING pair: retransmit after RTO */ for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { cand_pair = &remote_candidates[pos]; if (cand_pair->connectivity_check == ICE_PRUNED) { cand_pair=NULL; continue; } if (cand_pair->connectivity_check == ICE_IN_PROGRESS && ctime > cand_pair->retransmission_time) { if (cand_pair->retransmission_number>7) { ms_message("ice.c: ICE_FAILED for candidate pair! %s:%i -> %s:%i", cand_pair->local_candidate.conn_addr, cand_pair->local_candidate.conn_port, cand_pair->remote_candidate.conn_addr, cand_pair->remote_candidate.conn_port); cand_pair->connectivity_check = ICE_FAILED; cand_pair=NULL; continue; } cand_pair->retransmission_number++; cand_pair->retransmission_time=ctime+checklist->RTO; break; } cand_pair=NULL; } } if (cand_pair==NULL) { if (checklist->nominated_pair_index<0) return 0; /* send STUN indication each 15 seconds: keepalive */ if (ctime>checklist->keepalive_time) { checklist->keepalive_time=ctime+15*1000; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { cand_pair = &remote_candidates[pos]; if (cand_pair->connectivity_check == ICE_SUCCEEDED) { res = stunParseServerName(cand_pair->remote_candidate.conn_addr, &stunServerAddr); if ( res == TRUE ) { StunMessage req; char buf[STUN_MAX_MESSAGE_SIZE]; int len = STUN_MAX_MESSAGE_SIZE; stunServerAddr.port = cand_pair->remote_candidate.conn_port; memset(&req, 0, sizeof(StunMessage)); stunBuildReqSimple( &req, NULL, FALSE, FALSE, 1); req.msgHdr.msgType = (STUN_METHOD_BINDING|STUN_INDICATION); req.hasFingerprint = TRUE; len = stunEncodeMessage( &req, buf, len, NULL); sendMessage( media_socket, buf, len, stunServerAddr.addr, stunServerAddr.port ); } } } } return 0; } username.sizeValue = 0; password.sizeValue = 0; /* username comes from "ice-ufrag" (rfrag:lfrag) */ /* ufrag and pwd are in first row only */ snprintf(username.value, sizeof(username.value), "%s:%s", checklist->rem_ice_ufrag, checklist->loc_ice_ufrag); username.sizeValue = (uint16_t)strlen(username.value); snprintf(password.value, sizeof(password.value), "%s", checklist->rem_ice_pwd); password.sizeValue = (uint16_t)strlen(password.value); res = stunParseServerName(cand_pair->remote_candidate.conn_addr, &stunServerAddr); if ( res == TRUE ) { ms_message("ice.c: STUN REQ (%s) -> %i (%s:%i:%s -> %s:%i:%s) nominated=%s", cand_pair->nominated_pair==0?"":"USE-CANDIDATE", pos, cand_pair->local_candidate.conn_addr, cand_pair->local_candidate.conn_port, cand_pair->local_candidate.cand_type, cand_pair->remote_candidate.conn_addr, cand_pair->remote_candidate.conn_port, cand_pair->remote_candidate.cand_type, cand_pair->nominated_pair==0?"FALSE":"TRUE"); stunServerAddr.port = cand_pair->remote_candidate.conn_port; ice_sendtest(checklist, cand_pair, media_socket, &stunServerAddr, &username, &password, &(cand_pair->tid)); } } return 0; }
int ice_sound_send_stun_request(RtpSession *session, struct CandidatePair *remote_candidates, int round) { int roll=250; #if 0 /* in "passive" mode (UA not behind a NATor behind a full cone NAT), wait a few delay before sending the first STUN request: this help to traverse */ if (session->setup_passive>0) { return 0; } #endif if (remote_candidates==NULL) return 0; if (round>500) roll=2*roll; if (round%roll==50) { int pos; #if 0 /* do this only with application that support this */ if (osip_strncasecmp(remote_useragent, "linphone/", 8)!=0) { /* use stun only with linphone to linphone softphone */ return 0; } #endif for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.ipaddr[0]!='\0';pos++) { int media_socket = rtp_session_get_rtp_socket(session); StunAddress4 stunServerAddr; StunAtrString username; StunAtrString password; bool res; int pad_size; struct CandidatePair *cand_pair = &remote_candidates[pos]; username.sizeValue = 0; password.sizeValue = 0; /* set username to L3:1:R2:1 */ snprintf(username.value, sizeof(username.value), "%s:%i:%s:%i", cand_pair->local_candidate.candidate_id, 1, cand_pair->remote_candidate.candidate_id, 1); username.sizeValue = (UInt16)strlen(username.value); pad_size = username.sizeValue % 4; username.value[username.sizeValue]='\0'; username.value[username.sizeValue+1]='\0'; username.value[username.sizeValue+2]='\0'; username.value[username.sizeValue+3]='\0'; username.sizeValue = username.sizeValue + 4 - pad_size; snprintf(password.value, sizeof(password.value), "%s", cand_pair->remote_candidate.password); password.sizeValue = (UInt16)strlen(password.value); #if 0 pad_size = password.sizeValue%4; password.value[password.sizeValue]='\0'; password.value[password.sizeValue+1]='\0'; password.value[password.sizeValue+2]='\0'; password.value[password.sizeValue+3]='\0'; password.sizeValue = password.sizeValue + pad_size; #endif res = stunParseServerName(cand_pair->remote_candidate.ipaddr, &stunServerAddr); if ( res == true ) { stunServerAddr.port = cand_pair->remote_candidate.port; ice_sendtest(media_socket, &stunServerAddr, &username, &password, 1, 0/*false*/, &(cand_pair->tid)); } } } return 0; }