void UDT_transport_set_rtp_session(UDT_transport *udt,RtpSession *session){ char *local_port = ortp_strdup_printf("%d", session->rtp.loc_port); udt->rtp_session = session; udt->ortp_transport.session = session; if (0 != getaddrinfo(NULL, local_port, &udt->hints, &udt->local)){ ms_message("incorrect network address."); return; } udt->udt_socket = UDT::socket(udt->local->ai_family, udt->local->ai_socktype, udt->local->ai_protocol); /*设置传输参数*/ UDT::setsockopt(udt->udt_socket, 0, UDT_MSS, new int(ms_get_payload_max_size()), sizeof(int)); //MTU size UDT::setsockopt(udt->udt_socket, 0, UDT_RCVSYN, new bool(true), sizeof(bool)); UDT::setsockopt(udt->udt_socket, 0, UDT_SNDSYN, new bool(true), sizeof(bool)); //非阻塞模式 UDT::setsockopt(udt->udt_socket, 0, UDT_SNDBUF, new int(1024000), sizeof(int)); //接受缓冲 UDT::setsockopt(udt->udt_socket, 0, UDT_RCVBUF, new int(1024000), sizeof(int)); //发送缓冲 UDT::setsockopt(udt->udt_socket, 0, UDT_RCVTIMEO, new int(1), sizeof(int)); //阻塞模式超时设置 UDT::setsockopt(udt->udt_socket, 0, UDT_SNDTIMEO, new int(1), sizeof(int)); //阻塞模式超时设置 UDT::setsockopt(udt->udt_socket, 0, UDT_RENDEZVOUS, new bool(true), sizeof(bool)); if (UDT::ERROR == UDT::bind(udt->udt_socket, rtp_session_get_rtp_socket(udt->rtp_session))){ ms_error("bind: %s",UDT::getlasterror().getErrorMessage()); } ms_free(local_port); }
void VodWnd::active_rtp_sock() { if (!rtp_) return; int sock = rtp_session_get_rtp_socket(rtp_); send_something(sock, server_ip_.c_str(), server_rtp_port_); }
void SinkBase::RunOnce() { // FIXME: 不处理 rtcp 数据了 // FIXME: 此时简单的发送 rtp, rtcp 激活包,更好的方式应该是受到 rtp, rtcp 包之后,就不再发送了 active_sock(rtp_session_get_rtp_socket(rtp_), mcu_ip_.c_str(), mcu_rtp_port_); active_sock(rtp_session_get_rtcp_socket(rtp_), mcu_ip_.c_str(), mcu_rtcp_port_); }
RtpSession * create_duplex_rtpsession(int loc_rtp_port, int loc_rtcp_port, bool_t ipv6) { RtpSession *rtpr; rtpr = rtp_session_new(RTP_SESSION_SENDRECV); rtp_session_set_recv_buf_size(rtpr, MAX(ms_get_mtu() , MS_MINIMAL_MTU)); rtp_session_set_scheduling_mode(rtpr, 0); rtp_session_set_blocking_mode(rtpr, 0); rtp_session_enable_adaptive_jitter_compensation(rtpr, TRUE); rtp_session_set_symmetric_rtp(rtpr, TRUE); rtp_session_set_local_addr(rtpr, ipv6 ? "::" : "0.0.0.0", loc_rtp_port, loc_rtcp_port); rtp_session_signal_connect(rtpr, "timestamp_jump", (RtpCallback)rtp_session_resync, (long)NULL); rtp_session_signal_connect(rtpr, "ssrc_changed", (RtpCallback)rtp_session_resync, (long)NULL); rtp_session_set_ssrc_changed_threshold(rtpr, 0); disable_checksums(rtp_session_get_rtp_socket(rtpr)); return rtpr; }
/* Wait for an RTP packet to arrive and return it */ mblk_t *MastTool::wait_for_rtp_packet( int seconds ) { struct timeval timeout; fd_set readfds; int retval = -1; /* reset the session */ rtp_session_reset( session ); /* set the timeout */ timeout.tv_sec = seconds; timeout.tv_usec = 0; /* Watch socket to see when it has input */ FD_ZERO(&readfds); FD_SET( rtp_session_get_rtp_socket(session), &readfds); retval = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout); // Check return value if (retval == -1) { MAST_ERROR("select() failed: %s", strerror(errno)); return NULL; } else if (retval==0) { MAST_ERROR("Timed out waiting for packet after %d seconds", seconds); return NULL; } /* recieve packet and put it in queue */ rtp_session_rtp_recv( session, 0 ); /* check that there is something in the queue */ if (qempty(&session->rtp.rq) ) { MAST_ERROR("Queue is empty after trying to recieve packet"); return NULL; } /* take the packet off the queue and return it */ return getq(&session->rtp.rq); }
RtpSession * ms_create_duplex_rtp_session(const char* local_ip, int loc_rtp_port, int loc_rtcp_port, int mtu) { RtpSession *rtpr; rtpr = rtp_session_new(RTP_SESSION_SENDRECV); rtp_session_set_recv_buf_size(rtpr, MAX(mtu , MS_MINIMAL_MTU)); rtp_session_set_scheduling_mode(rtpr, 0); rtp_session_set_blocking_mode(rtpr, 0); rtp_session_enable_adaptive_jitter_compensation(rtpr, TRUE); rtp_session_set_symmetric_rtp(rtpr, TRUE); rtp_session_set_local_addr(rtpr, local_ip, loc_rtp_port, loc_rtcp_port); rtp_session_signal_connect(rtpr, "timestamp_jump", (RtpCallback)rtp_session_resync, NULL); rtp_session_signal_connect(rtpr, "ssrc_changed", (RtpCallback)rtp_session_resync, NULL); rtp_session_set_ssrc_changed_threshold(rtpr, 0); rtp_session_set_rtcp_report_interval(rtpr, 2500); /* At the beginning of the session send more reports. */ rtp_session_set_multicast_loopback(rtpr,TRUE); /*very useful, specially for testing purposes*/ disable_checksums(rtp_session_get_rtp_socket(rtpr)); return rtpr; }
static int ice_process_stun_message(RtpSession *session, struct IceCheckList *checklist, OrtpEvent *evt) { struct CandidatePair *remote_candidates = NULL; StunMessage msg; bool_t res; int highest_priority_success=-1; OrtpEventData *evt_data = ortp_event_get_data(evt); mblk_t *mp = evt_data->packet; struct sockaddr_in *udp_remote; char src6host[NI_MAXHOST]; int recvport = 0; int i; udp_remote = (struct sockaddr_in*)&evt_data->ep->addr; memset( &msg, 0 , sizeof(msg) ); res = stunParseMessage((char*)mp->b_rptr, mp->b_wptr-mp->b_rptr, &msg); if (!res) { ms_error("ice.c: Malformed STUN packet."); return -1; } if (checklist==NULL) { ms_error("ice.c: dropping STUN packet: ice is not configured"); return -1; } remote_candidates = checklist->cand_pairs; if (remote_candidates==NULL) { ms_error("ice.c: dropping STUN packet: ice is not configured"); return -1; } /* prepare ONCE tie-break value */ if (checklist->tiebreak_value==0) { checklist->tiebreak_value = random() * (0x7fffffffffffffffLL/0x7fff); } memset (src6host, 0, sizeof (src6host)); { struct sockaddr_storage *aaddr = (struct sockaddr_storage *)&evt_data->ep->addr; if (aaddr->ss_family==AF_INET) recvport = ntohs (((struct sockaddr_in *) udp_remote)->sin_port); else recvport = ntohs (((struct sockaddr_in6 *) &evt_data->ep->addr)->sin6_port); } i = getnameinfo ((struct sockaddr*)&evt_data->ep->addr, evt_data->ep->addrlen, src6host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (i != 0) { ms_error("ice.c: Error with getnameinfo"); return -1; } if (STUN_IS_REQUEST(msg.msgHdr.msgType)) ms_message("ice.c: STUN_CONNECTIVITYCHECK: Request received from: %s:%i", src6host, recvport); else if (STUN_IS_INDICATION(msg.msgHdr.msgType)) ms_message("ice.c: SUN_INDICATION: Request Indication received from: %s:%i", src6host, recvport); else ms_message("ice.c: STUN_ANSWER: Answer received from: %s:%i", src6host, recvport); { int pos; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { struct CandidatePair *cand_pair = &remote_candidates[pos]; if (cand_pair->connectivity_check == ICE_SUCCEEDED) { highest_priority_success=pos; break; } } } if (STUN_IS_INDICATION(msg.msgHdr.msgType)) { ms_message("ice.c: STUN INDICATION <- (?:?:? <- %s:%i:?)", src6host, recvport); return 0; } else if (STUN_IS_REQUEST(msg.msgHdr.msgType)) { StunMessage resp; StunAtrString hmacPassword; StunAddress4 remote_addr; int rtp_socket; memset( &resp, 0 , sizeof(resp)); remote_addr.addr = ntohl(udp_remote->sin_addr.s_addr); remote_addr.port = ntohs(udp_remote->sin_port); rtp_socket = rtp_session_get_rtp_socket(session); resp.msgHdr.magic_cookie = ntohl(msg.msgHdr.magic_cookie); for (i=0; i<12; i++ ) { resp.msgHdr.tr_id.octet[i] = msg.msgHdr.tr_id.octet[i]; } /* check mandatory params */ if (!msg.hasUsername) { char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); ms_error("ice.c: STUN REQ <- Missing USERNAME attribute in connectivity check"); _ice_createErrorResponse(&resp, 4, 32, "Missing USERNAME attribute"); len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); if (len) sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); return -1; } if (!msg.hasMessageIntegrity) { char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); ms_error("ice.c: STUN REQ <- Missing MESSAGEINTEGRITY attribute in connectivity check"); _ice_createErrorResponse(&resp, 4, 1, "Missing MESSAGEINTEGRITY attribute"); len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); if (len) sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); return -1; } /* The password associated with that transport address ID is used to verify the MESSAGE-INTEGRITY attribute, if one was present in the request. */ { char hmac[20]; /* remove length of fingerprint if present */ if (msg.hasFingerprint==TRUE) { char *lenpos = (char *)mp->b_rptr + sizeof(uint16_t); uint16_t newlen = htons(msg.msgHdr.msgLength-8); /* remove fingerprint size */ memcpy(lenpos, &newlen, sizeof(uint16_t)); stunCalculateIntegrity_shortterm(hmac, (char*)mp->b_rptr, mp->b_wptr-mp->b_rptr-24-8, checklist->loc_ice_pwd); } else stunCalculateIntegrity_shortterm(hmac, (char*)mp->b_rptr, mp->b_wptr-mp->b_rptr-24, checklist->loc_ice_pwd); if (memcmp(msg.messageIntegrity.hash, hmac, 20)!=0) { char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); ms_error("ice.c: STUN REQ <- Wrong MESSAGEINTEGRITY attribute in connectivity check"); _ice_createErrorResponse(&resp, 4, 1, "Wrong MESSAGEINTEGRITY attribute"); len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); if (len) sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); return -1; } if (msg.hasFingerprint==TRUE) { char *lenpos = (char *)mp->b_rptr + sizeof(uint16_t); uint16_t newlen = htons(msg.msgHdr.msgLength); /* add back fingerprint size */ memcpy(lenpos, &newlen, sizeof(uint16_t)); } } /* 7.2.1.1. Detecting and Repairing Role Conflicts */ /* TODO */ if (!msg.hasIceControlling && !msg.hasIceControlled) { char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); ms_error("ice.c: STUN REQ <- Missing either ICE-CONTROLLING or ICE-CONTROLLED attribute"); _ice_createErrorResponse(&resp, 4, 87, "Missing either ICE-CONTROLLING or ICE-CONTROLLED attribute"); len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); if (len) sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); return -1; } if (checklist->rem_controlling==0 && msg.hasIceControlling) { /* If the agent's tie-breaker is larger than or equal to the contents of the ICE-CONTROLLING attribute -> send 487, and do not change ROLE */ if (checklist->tiebreak_value >= msg.iceControlling.value) { char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); ms_error("ice.c: STUN REQ <- 487 Role Conflict"); _ice_createErrorResponse(&resp, 4, 87, "Role Conflict"); len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); if (len) sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); return -1; } else { int pos; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { /* controller agent */ uint64_t G = remote_candidates[pos].remote_candidate.priority; /* controlled agent */ uint64_t D = remote_candidates[pos].local_candidate.priority; remote_candidates[pos].pair_priority = (MIN(G, D))<<32 | (MAX(G, D))<<1 | (G>D?1:0); } checklist->rem_controlling = 1; /* reset all to initial WAITING state? */ ms_message("ice.c: STUN REQ <- tiebreaker -> reset all to ICE_WAITING state"); for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { if (remote_candidates[pos].connectivity_check == ICE_PRUNED) continue; remote_candidates[pos].connectivity_check = ICE_WAITING; memset(&remote_candidates[pos].tid , 0, sizeof(remote_candidates[pos].tid)); remote_candidates[pos].retransmission_time = 0; remote_candidates[pos].retransmission_number = 0; } } } if (checklist->rem_controlling==1 && msg.hasIceControlled) { /* If the agent's tie-breaker is larger than or equal to the contents of the ICE-CONTROLLED attribute -> change ROLE */ if (checklist->tiebreak_value >= msg.iceControlled.value) { int pos; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { /* controller agent */ uint64_t G = remote_candidates[pos].local_candidate.priority; /* controlled agent */ uint64_t D = remote_candidates[pos].remote_candidate.priority; remote_candidates[pos].pair_priority = (MIN(G, D))<<32 | (MAX(G, D))<<1 | (G>D?1:0); } checklist->rem_controlling = 0; /* reset all to initial WAITING state? */ ms_message("ice.c: STUN REQ <- tiebreaker -> reset all to ICE_WAITING state"); for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { if (remote_candidates[pos].connectivity_check == ICE_PRUNED) continue; remote_candidates[pos].connectivity_check = ICE_WAITING; memset(&remote_candidates[pos].tid , 0, sizeof(remote_candidates[pos].tid)); remote_candidates[pos].retransmission_time = 0; remote_candidates[pos].retransmission_number = 0; } } else { char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); ms_error("ice.c: STUN REQ <- 487 Role Conflict"); _ice_createErrorResponse(&resp, 4, 87, "Role Conflict"); len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); if (len) sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); return -1; } } { struct CandidatePair *cand_pair; int pos; cand_pair=NULL; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { cand_pair = &remote_candidates[pos]; /* connectivity check is coming from a known remote candidate? we should also check the port... */ if (strcmp(cand_pair->remote_candidate.conn_addr, src6host)==0 && cand_pair->remote_candidate.conn_port==recvport) { ms_message("ice.c: STUN REQ (%s) <- %i (%s:%i:%s <- %s:%i:%s) from known peer", msg.hasUseCandidate==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); if (cand_pair->connectivity_check==ICE_FROZEN || cand_pair->connectivity_check==ICE_IN_PROGRESS || cand_pair->connectivity_check==ICE_FAILED) { cand_pair->connectivity_check = ICE_WAITING; if (msg.hasUseCandidate==TRUE && checklist->rem_controlling==0) cand_pair->nominated_pair = 1; } else if (cand_pair->connectivity_check==ICE_SUCCEEDED) { if (msg.hasUseCandidate==TRUE && checklist->rem_controlling==0) { cand_pair->nominated_pair = 1; /* USE-CANDIDATE is in STUN request and we already succeeded on that link */ ms_message("ice.c: ICE CONCLUDED == %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"); memcpy(&session->rtp.rem_addr, &evt_data->ep->addr, evt_data->ep->addrlen); session->rtp.rem_addrlen=evt_data->ep->addrlen; } } break; } cand_pair=NULL; } if (cand_pair==NULL) { struct CandidatePair new_pair; memset(&new_pair, 0, sizeof(struct CandidatePair)); ms_message("ice.c: STUN REQ <- connectivity check received from an unknow candidate (%s:%i)", src6host, recvport); /* TODO: add the peer-reflexive candidate */ memcpy(&new_pair.local_candidate, &remote_candidates[0].local_candidate, sizeof(new_pair.local_candidate)); new_pair.remote_candidate.foundation = 6; new_pair.remote_candidate.component_id = remote_candidates[0].remote_candidate.component_id; /* -> no known base address for peer */ new_pair.remote_candidate.conn_port = recvport; snprintf(new_pair.remote_candidate.conn_addr, sizeof(new_pair.remote_candidate.conn_addr), "%s", src6host); /* take it from PRIORITY STUN attr */ new_pair.remote_candidate.priority = msg.priority.priority; if (new_pair.remote_candidate.priority==0) { uint32_t type_preference = 110; uint32_t interface_preference = 255; uint32_t stun_priority=255; new_pair.remote_candidate.priority = (type_preference << 24) | (interface_preference << 16) | (stun_priority << 8) | (256 - new_pair.remote_candidate.component_id); } snprintf(new_pair.remote_candidate.cand_type, sizeof(cand_pair->remote_candidate.cand_type), "prflx"); snprintf (new_pair.remote_candidate.transport, sizeof (new_pair.remote_candidate.transport), "UDP"); if (checklist->rem_controlling==0) { uint64_t G = new_pair.local_candidate.priority; /* controlled agent */ uint64_t D = new_pair.remote_candidate.priority; new_pair.pair_priority = (MIN(G, D))<<32 | (MAX(G, D))<<1 | (G>D?1:0); } else { uint64_t G = new_pair.remote_candidate.priority; /* controlled agent */ uint64_t D = new_pair.local_candidate.priority; new_pair.pair_priority = (MIN(G, D))<<32 | (MAX(G, D))<<1 | (G>D?1:0); } new_pair.connectivity_check = ICE_WAITING; /* insert new pair candidate */ if (msg.hasUseCandidate==TRUE && checklist->rem_controlling==0) { new_pair.nominated_pair = 1; } for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) { if (pos==9) { ms_message("ice.c: STUN REQ (%s) <- X (%s:%i:%s <- %s:%i:%s) no room for new remote reflexive candidate", msg.hasUseCandidate==0?"":"USE-CANDIDATE", new_pair.local_candidate.conn_addr, new_pair.local_candidate.conn_port, new_pair.local_candidate.cand_type, new_pair.remote_candidate.conn_addr, new_pair.remote_candidate.conn_port, new_pair.remote_candidate.cand_type); break; } if (new_pair.pair_priority > remote_candidates[pos].pair_priority) { /* move upper data */ memmove(&remote_candidates[pos+1], &remote_candidates[pos], sizeof(struct CandidatePair)*(10-pos-1)); memcpy(&remote_candidates[pos], &new_pair, sizeof(struct CandidatePair)); if (checklist->nominated_pair_index>=pos) checklist->nominated_pair_index++; ms_message("ice.c: STUN REQ (%s) <- %i (%s:%i:%s <- %s:%i:%s) new learned remote reflexive candidate", msg.hasUseCandidate==0?"":"USE-CANDIDATE", pos, new_pair.local_candidate.conn_addr, new_pair.local_candidate.conn_port, new_pair.local_candidate.cand_type, new_pair.remote_candidate.conn_addr, new_pair.remote_candidate.conn_port, new_pair.remote_candidate.cand_type); break; } } } } { uint32_t cookie = 0x2112A442; resp.hasXorMappedAddress = TRUE; resp.xorMappedAddress.ipv4.port = remote_addr.port^(cookie>>16); resp.xorMappedAddress.ipv4.addr = remote_addr.addr^cookie; } resp.msgHdr.msgType = (STUN_METHOD_BINDING | STUN_SUCCESS_RESP); resp.hasUsername = TRUE; memcpy(resp.username.value, msg.username.value, msg.username.sizeValue ); resp.username.sizeValue = msg.username.sizeValue; /* ? any messageintegrity in response? */ resp.hasMessageIntegrity = TRUE; { const char serverName[] = "mediastreamer2 " STUN_VERSION; resp.hasSoftware = TRUE; memcpy( resp.softwareName.value, serverName, sizeof(serverName)); resp.softwareName.sizeValue = sizeof(serverName); } resp.hasFingerprint = TRUE; { char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); len = stunEncodeMessage( &resp, buf, len, &hmacPassword ); if (len) sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); } }
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; }
ortp_socket_t udt_getsocket(struct _RtpTransport *t){ UDT_transport *myudt = (UDT_transport *)t->data; return rtp_session_get_rtp_socket(myudt->rtp_session); }
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; }
int ice_process_stun_message(RtpSession *session, struct CandidatePair *remote_candidates, OrtpEvent *evt) { bool switch_to_address = -1; StunMessage msg; bool res; int already_worked_once=-1; OrtpEventData *evt_data = ortp_event_get_data(evt); mblk_t *mp = evt_data->packet; struct sockaddr_in *udp_remote; char src6host[NI_MAXHOST]; int recvport = 0; int i; udp_remote = (struct sockaddr_in*)&evt_data->ep->addr; memset( &msg, 0 , sizeof(msg) ); res = stunParseMessage((char*)mp->b_rptr, mp->b_wptr-mp->b_rptr, &msg, 0); if (!res) { ms_error("ice.c: Malformed STUN packet."); return -1; } memset (src6host, 0, sizeof (src6host)); { struct sockaddr_storage *aaddr = (struct sockaddr_storage *)&evt_data->ep->addr; if (aaddr->ss_family==AF_INET) recvport = ntohs (((struct sockaddr_in *) udp_remote)->sin_port); else recvport = ntohs (((struct sockaddr_in6 *) &evt_data->ep->addr)->sin6_port); } i = getnameinfo ((struct sockaddr*)&evt_data->ep->addr, evt_data->ep->addrlen, src6host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (i != 0) { ms_error("ice.c: Error with getnameinfo"); } else { if (msg.msgHdr.msgType == BindRequestMsg) ms_message("ice.c: Request received from: %s:%i", src6host, recvport); else ms_message("ice.c: Answer received from: %s:%i", src6host, recvport); } if (remote_candidates!=NULL) { int pos; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.ipaddr[0]!='\0';pos++) { struct CandidatePair *cand_pair = &remote_candidates[pos]; #ifdef RESTRICTIVE_ICE if (cand_pair->connectivity_check == VALID ||cand_pair->connectivity_check == RECV_VALID) { already_worked_once=pos; break; } #else if (cand_pair->connectivity_check == VALID ||cand_pair->connectivity_check == RECV_VALID ||cand_pair->connectivity_check == SEND_VALID) { already_worked_once=pos; break; } #endif } } if (msg.msgHdr.msgType == BindRequestMsg) { StunMessage resp; StunAddress4 dest; StunAtrString hmacPassword; StunAddress4 from; StunAddress4 secondary; StunAddress4 myAddr; StunAddress4 myAltAddr; bool changePort = false; bool changeIp = false; struct sockaddr_storage name; socklen_t namelen; char localip[128]; int rtp_socket; memset(&name, '\0', sizeof(struct sockaddr_storage)); memset(localip, '\0', sizeof(localip)); _ice_get_localip_for ((struct sockaddr_storage*)&evt_data->ep->addr, evt_data->ep->addrlen, localip, 128); from.addr = ntohl(udp_remote->sin_addr.s_addr); from.port = ntohs(udp_remote->sin_port); secondary.addr = 0; secondary.port = 0; namelen = sizeof(struct sockaddr_storage); rtp_socket = rtp_session_get_rtp_socket(session); i = getsockname(rtp_socket, (struct sockaddr*)&name, &namelen); if (i!=0) { ms_error("ice.c: getsockname failed."); return -1; } myAddr.port = ntohs (((struct sockaddr_in*)&name)->sin_port); i = stunParseHostName(localip, &myAddr.addr, &myAddr.port, myAddr.port); if (!i) { ms_error("ice.c: stunParseHostName failed."); return -1; } myAddr.port = ntohs (((struct sockaddr_in*)&name)->sin_port); /* changed-address set to local address/port */ myAltAddr = myAddr; dest.addr = 0; dest.port = 0; res = stunServerProcessMsg((char*)mp->b_rptr, mp->b_wptr-mp->b_rptr, &from, &secondary, &myAddr, &myAltAddr, &resp, &dest, &hmacPassword, &changePort, &changeIp, false ); if (!res) { ms_error("ice.c: Failed to process STUN request."); return -1; } if (changePort == true || changeIp == true) { ms_error("ice.c: STUN request with changePort or changeIP refused."); return -1; } res=true; if ( dest.addr == 0 ) res=false; if ( dest.port == 0 ) res=false; if (!res) { ms_error("ice.c: Missing destination value for response."); return -1; } if (msg.hasUsername!=true || msg.username.sizeValue<=0) { /* reply 430 */ ms_error("ice.c: Missing or bad username value."); return -1; } /* USERNAME is considered valid if its topmost portion (the part up to, but not including the second colon) corresponds to a transport address ID known to the agent. */ if (remote_candidates!=NULL) { int pos; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.ipaddr[0]!='\0';pos++) { char username[256]; struct CandidatePair *cand_pair = &remote_candidates[pos]; size_t len = strlen(cand_pair->remote_candidate.candidate_id); if (cand_pair->connectivity_check == VALID) { break; } memset(username, '\0', sizeof(username)); snprintf(username, sizeof(username), "%s:%i:%s:%i", cand_pair->remote_candidate.candidate_id, 1, cand_pair->local_candidate.candidate_id, 1); if (len+3<msg.username.sizeValue && strncmp(msg.username.value, cand_pair->remote_candidate.candidate_id, len)==0) { char tmp[10]; int k; snprintf(tmp, 10, "%s", msg.username.value + len +1); for (k=0;k<10;k++) { if (tmp[k]=='\0') break; if (tmp[k]==':') { tmp[k]='\0'; break; } } k = atoi(tmp); /* TODO support for 2 stream RTP+RTCP */ if (k>0 && k<10 && k==1) { /* candidate-id found! */ #if 0 ms_message("ice.c: Find candidate id (index=%i) for incoming STUN request.", pos); #endif if (strncmp(msg.username.value, username, strlen(username))==0) { #ifdef RESTRICTIVE_ICE ms_message("ice.c: Valid STUN request received (to=%s:%i from=%s:%i).", cand_pair->remote_candidate.ipaddr, cand_pair->remote_candidate.port, src6host, recvport); /* We can't be sure the remote end will receive our answer: connection could be only one way... */ if (cand_pair->connectivity_check != VALID) { switch_to_address = pos; } #else switch_to_address = pos; #endif if (cand_pair->connectivity_check == RECV_VALID || cand_pair->connectivity_check == VALID) { if (cand_pair->connectivity_check != VALID) { switch_to_address = pos; ms_message("ice.c: candidate id (index=%i) moved in VALID state (stunbindingrequest received).", pos); cand_pair->connectivity_check = VALID; } } else cand_pair->connectivity_check = SEND_VALID; /* we have a VALID one */ } } } } } /* The password associated with that transport address ID is used to verify the MESSAGE-INTEGRITY attribute, if one was present in the request. */ { char buf[STUN_MAX_MESSAGE_SIZE]; int len = sizeof(buf); len = stunEncodeMessage( &resp, buf, len, &hmacPassword,false ); if (len) sendMessage( rtp_socket, buf, len, dest.addr, dest.port, false ); } } else { /* set state to RECV-VALID or VALID */ StunMessage resp; StunAddress4 mappedAddr; memset(&resp, 0, sizeof(StunMessage)); res = stunParseMessage((char*)mp->b_rptr, mp->b_wptr-mp->b_rptr, &resp, false ); if (!res) { ms_error("ice.c: Bad format for STUN answer."); return -1; } mappedAddr = resp.mappedAddress.ipv4; if (remote_candidates!=NULL) { int pos; for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.ipaddr[0]!='\0';pos++) { struct CandidatePair *cand_pair = &remote_candidates[pos]; if (memcmp(&(cand_pair->tid), &(resp.msgHdr.id), sizeof(resp.msgHdr.id))==0) { /* Youhouhouhou */ if (cand_pair->connectivity_check != VALID) { switch_to_address = pos; } #if 0 ms_message("ice.c: Valid STUN answer received (to=%s:%i from=%s:%i)", cand_pair->remote_candidate.ipaddr, cand_pair->remote_candidate.port, src6host, recvport); #endif if (cand_pair->connectivity_check == SEND_VALID || cand_pair->connectivity_check == VALID) { if (cand_pair->connectivity_check != VALID) { ms_message("ice.c: Switch to VALID mode for (to=%s:%i from=%s:%i)", cand_pair->remote_candidate.ipaddr, cand_pair->remote_candidate.port, src6host, recvport); cand_pair->connectivity_check = VALID; } } else cand_pair->connectivity_check = RECV_VALID; } } } } if (remote_candidates==NULL) { ms_warning("ice.c: STUN connectivity check is disabled but we received a STUN message (%s:%i)\n", src6host, recvport); return 0; } if (switch_to_address == -1) return 0; { /* skip symmetric RTP if any previous connection is working */ if (switch_to_address<already_worked_once || already_worked_once==-1) { /* rtp_in_direct_mode = 1; */ /* current destination address: snprintf(rtp_remote_addr, 256, "%s:%i", src6host, recvport); */ ms_warning("ice.c: Modifying remote socket: symmetric RTP (%s:%i)\n", src6host, recvport); memcpy(&session->rtp.rem_addr, &evt_data->ep->addr, evt_data->ep->addrlen); session->rtp.rem_addrlen=evt_data->ep->addrlen; } } return 0; }