static void ice_sendtest( Socket myFd, StunAddress4 *dest, const StunAtrString *username, const StunAtrString *password, int testNum, bool verbose , UInt128 *tid) { bool changePort=false; bool changeIP=false; bool discard=false; StunMessage req; char buf[STUN_MAX_MESSAGE_SIZE]; int len = STUN_MAX_MESSAGE_SIZE; switch (testNum) { case 1: case 10: case 11: break; case 2: /* changePort=true; */ changeIP=true; break; case 3: changePort=true; break; case 4: changeIP=true; break; case 5: discard=true; break; default: printf("Test %i is unkown\n", testNum); return ; /* error */ } memset(&req, 0, sizeof(StunMessage)); stunBuildReqSimple( &req, username, changePort , changeIP , testNum ); len = stunEncodeMessage( &req, buf, len, password,verbose ); memcpy(tid , &(req.msgHdr.id), sizeof(req.msgHdr.id)); sendMessage( myFd, buf, len, dest->addr, dest->port, verbose ); }
static void ice_sendtest( struct IceCheckList *checklist, struct CandidatePair *remote_candidate, Socket myFd, StunAddress4 *dest, const StunAtrString *username, const StunAtrString *password, UInt96 *tid) { StunMessage req; char buf[STUN_MAX_MESSAGE_SIZE]; int len = STUN_MAX_MESSAGE_SIZE; memset(&req, 0, sizeof(StunMessage)); stunBuildReqSimple( &req, username, FALSE, FALSE, 1); req.hasMessageIntegrity=TRUE; /* 7.1.1.1 The attribute MUST be set equal to the priority that would be assigned, based on the algorithm in Section 4.1.2, to a peer reflexive candidate, should one be learned as a consequence of this check */ req.hasPriority = TRUE; req.priority.priority = (110 << 24) | (255 << 16) | (255 << 8) | (256 - remote_candidate->remote_candidate.component_id); /* TODO: put this parameter only for the candidate selected */ if (remote_candidate->nominated_pair==1) req.hasUseCandidate = TRUE; if (remote_candidate->rem_controlling==1) { req.hasIceControlled = TRUE; req.iceControlled.value = checklist->tiebreak_value; } else { req.hasIceControlling = TRUE; req.iceControlling.value = checklist->tiebreak_value; } /* TODO: not yet implemented? */ req.hasFingerprint = TRUE; len = stunEncodeMessage( &req, buf, len, password ); memcpy(tid , &(req.msgHdr.tr_id), sizeof(req.msgHdr.tr_id)); sendMessage( myFd, buf, len, dest->addr, dest->port ); }
void t_phone_user::send_stun_request(void) { if (r_stun) { log_file->write_report("STUN request already in progress.", "t_phone_user::send_stun_request", LOG_NORMAL, LOG_DEBUG); return; } StunMessage req; StunAtrString username; username.sizeValue = 0; stunBuildReqSimple(&req, username, false, false); r_stun = new t_client_request(user_config, &req, 0); MEMMAN_NEW(r_stun); phone->send_request(user_config, &req, r_stun->get_tuid()); return; }
/* Send dummy STUN packet to open NAT ports ASAP. */ static void send_stun_packet(RtpSession *s) { StunMessage msg; mblk_t *mp; char buf[STUN_MAX_MESSAGE_SIZE]; int len = STUN_MAX_MESSAGE_SIZE; memset(&msg, 0, sizeof(StunMessage)); stunBuildReqSimple(&msg, NULL, FALSE, FALSE, 1); len = stunEncodeMessage(&msg, buf, len, NULL); if (len > 0) { mp = allocb(len, BPRI_MED); memcpy(mp->b_wptr, buf, len); mp->b_wptr += len; rtp_session_sendm_with_ts(s, mp, 0); } }
static int test_stun_encode( char*buffer, size_t len, bool_t expect_fail ) { StunAtrString username; StunAtrString password; StunMessage req; memset(&req, 0, sizeof(StunMessage)); memset(&username,0,sizeof(username)); memset(&password,0,sizeof(password)); stunBuildReqSimple( &req, &username, TRUE , TRUE , 11); len = stunEncodeMessage( &req, buffer, (unsigned int)len, &password); if (len<=0){ if( expect_fail ) ms_message("Fail to encode stun message (EXPECTED).\n"); else ms_error("Fail to encode stun message.\n"); return -1; } return (int)len; }
/* sends a request to a stun server */ int send_stun_request(const int udpSocket,struct sockaddr_in *stunServ) { assert( sizeof(UInt8 ) == 1 ); assert( sizeof(UInt16) == 2 ); assert( sizeof(UInt32) == 4 ); bool verbose = STUN_VERBOSE; //set the verbose mode verbose = true; StunMessage req; memset(&req, 0, sizeof(StunMessage)); StunAtrString username; StunAtrString password; username.sizeValue = 0; password.sizeValue = 0; /* build a stun request message */ /* creates a stun message struct with the given atributes */ stunBuildReqSimple( &req, username, false , false , 0x0c ); /* buffer for the stun request */ char buf[STUN_MAX_MESSAGE_SIZE]; int len = STUN_MAX_MESSAGE_SIZE; /* encode the stun message */ len = stunEncodeMessage( req, buf, len, password, verbose); struct iovec iov; iov.iov_len = len; iov.iov_base = buf; debug("Sending STUN with sendPacketFinal %d, %d!!!!!\n",len, udpSocket); sendPacketFinal(udpSocket,&iov,1,stunServ); return 0; }
/* Send dummy STUN packet to open NAT ports ASAP. */ static void send_stun_packet(RtpSession *s) { StunMessage msg; mblk_t *mp; char buf[STUN_MAX_MESSAGE_SIZE]; int len = STUN_MAX_MESSAGE_SIZE; if (ms_is_multicast_addr((const struct sockaddr *)&s->rtcp.gs.loc_addr)) { ms_debug("Stun packet not sent for session [%p] because of multicast",s); return; } memset(&msg, 0, sizeof(StunMessage)); stunBuildReqSimple(&msg, NULL, FALSE, FALSE, 1); len = stunEncodeMessage(&msg, buf, len, NULL); if (len > 0) { mp = allocb(len, BPRI_MED); memcpy(mp->b_wptr, buf, len); mp->b_wptr += len; ms_message("Stun packet sent for session [%p]",s); rtp_session_sendm_with_ts(s, mp, 0); } }
static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){ char buf[STUN_MAX_MESSAGE_SIZE]; int len = STUN_MAX_MESSAGE_SIZE; StunAtrString username; StunAtrString password; StunMessage req; int err; memset(&req, 0, sizeof(StunMessage)); memset(&username,0,sizeof(username)); memset(&password,0,sizeof(password)); stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id); len = stunEncodeMessage( &req, buf, len, &password); if (len<=0){ ms_error("Fail to encode stun message."); return -1; } err=sendto(sock,buf,len,0,server,addrlen); if (err<0){ ms_error("sendto failed: %s",strerror(errno)); return -1; } return 0; }
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; }
bool get_stun_binding(t_user *user_config, unsigned short src_port, unsigned long &mapped_ip, unsigned short &mapped_port, int &err_code, string &err_reason) { list<t_ip_port> destinations = user_config->get_stun_server().get_h_ip_srv("udp"); if (destinations.empty()) { // Cannot resolve STUN server address. log_file->write_header("::get_stun_binding", LOG_NORMAL, LOG_CRITICAL); log_file->write_raw("Failed to resolve: "); log_file->write_raw(user_config->get_stun_server().encode()); log_file->write_endl(); log_file->write_raw("Return internal STUN bind error: 404 Not Found"); log_file->write_endl(); log_file->write_footer(); err_code = 404; err_reason = "Not Found"; return false; } int num_transmissions = 0; int wait_intval = DUR_STUN_START_INTVAL; t_socket_udp sock(src_port); sock.connect(destinations.front().ipaddr, destinations.front().port); // Build STUN request char buf[STUN_MAX_MESSAGE_SIZE + 1]; StunMessage req_bind; StunAtrString stun_null_str; stun_null_str.sizeValue = 0; stunBuildReqSimple(&req_bind, stun_null_str, false, false); char req_msg[STUN_MAX_MESSAGE_SIZE]; int req_msg_size = stunEncodeMessage(req_bind, req_msg, STUN_MAX_MESSAGE_SIZE, stun_null_str, false); // Send STUN request and retransmit till a response is received. while (num_transmissions < STUN_MAX_TRANSMISSIONS) { bool ret; try { sock.send(req_msg, req_msg_size); } catch (int err) { // Socket error (probably ICMP error) // Failover to next destination log_file->write_report("Send failed. Failover to next destination.", "::get_stun_binding"); destinations.pop_front(); if (destinations.empty()) { log_file->write_report("No next destination for failover.", "::get_stun_binding"); break; } num_transmissions = 0; wait_intval = DUR_STUN_START_INTVAL; sock.connect(destinations.front().ipaddr, destinations.front().port); continue; } log_file->write_header("::get_stun_binding", LOG_STUN); log_file->write_raw("Send to: "); log_file->write_raw(h_ip2str(destinations.front().ipaddr)); log_file->write_raw(":"); log_file->write_raw(destinations.front().port); log_file->write_endl(); log_file->write_raw(stunMsg2Str(req_bind)); log_file->write_footer(); try { ret = sock.select_read(wait_intval); } catch (int err) { // Socket error (probably ICMP error) // Failover to next destination log_file->write_report("Select failed. Failover to next destination.", "::get_stun_binding"); destinations.pop_front(); if (destinations.empty()) { log_file->write_report("No next destination for failover.", "::get_stun_binding"); break; } num_transmissions = 0; wait_intval = DUR_STUN_START_INTVAL; sock.connect(destinations.front().ipaddr, destinations.front().port); continue; } if (!ret) { // Time out num_transmissions++; if (wait_intval < DUR_STUN_MAX_INTVAL) { wait_intval *= 2; } continue; } // A message has been received int resp_msg_size; try { resp_msg_size = sock.recv(buf, STUN_MAX_MESSAGE_SIZE + 1); } catch (int err) { // Socket error (probably ICMP error) // Failover to next destination log_file->write_report("Recv failed. Failover to next destination.", "::get_stun_binding"); destinations.pop_front(); if (destinations.empty()) { log_file->write_report("No next destination for failover.", "::get_stun_binding"); break; } num_transmissions = 0; wait_intval = DUR_STUN_START_INTVAL; sock.connect(destinations.front().ipaddr, destinations.front().port); continue; } StunMessage resp_bind; if (!stunParseMessage(buf, resp_msg_size, resp_bind, false)) { log_file->write_report( "Received faulty STUN message", "::get_stun_binding", LOG_STUN); num_transmissions++; if (wait_intval < DUR_STUN_MAX_INTVAL) { wait_intval *= 2; } continue; } log_file->write_header("::get_stun_binding", LOG_STUN); log_file->write_raw("Received from: "); log_file->write_raw(h_ip2str(destinations.front().ipaddr)); log_file->write_raw(":"); log_file->write_raw(destinations.front().port); log_file->write_endl(); log_file->write_raw(stunMsg2Str(resp_bind)); log_file->write_footer(); // Check if id in msgHdr matches if (!stunEqualId(resp_bind, req_bind)) { num_transmissions++; if (wait_intval < DUR_STUN_MAX_INTVAL) { wait_intval *= 2; } continue; } if (resp_bind.msgHdr.msgType == BindResponseMsg && resp_bind.hasMappedAddress) { // Bind response received mapped_ip = resp_bind.mappedAddress.ipv4.addr; mapped_port = resp_bind.mappedAddress.ipv4.port; return true; } if (resp_bind.msgHdr.msgType == BindErrorResponseMsg && resp_bind.hasErrorCode) { // Bind error received err_code = resp_bind.errorCode.errorClass * 100 + resp_bind.errorCode.number; char s[STUN_MAX_STRING + 1]; strncpy(s, resp_bind.errorCode.reason, STUN_MAX_STRING); s[STUN_MAX_STRING] = 0; err_reason = s; return false; } // A wrong response has been received. log_file->write_report( "Invalid STUN response received", "::get_stun_binding", LOG_NORMAL); err_code = 500; err_reason = "Server Error"; return false; } // Request timed out log_file->write_report("STUN request timeout", "::get_stun_binding", LOG_NORMAL); err_code = 408; err_reason = "Request Timeout"; return false; }