Ejemplo n.º 1
0
// Main function for STUN listener thread for media STUN requests.
void *stun_listen_main(void *arg) {
	char		buf[STUN_MAX_MESSAGE_SIZE + 1];
	int		data_size;
	
	t_socket_udp *sock = (t_socket_udp *)arg;
	
	while(true) {
		try {
			data_size = sock->recv(buf, STUN_MAX_MESSAGE_SIZE + 1);
		} catch (int err) {
			string msg("Failed to receive STUN response for media.\n");
			msg += get_error_str(err);
			log_file->write_report(msg, "::stun_listen_main",
				LOG_NORMAL, LOG_CRITICAL);
				
			// The request will timeout, no need to send a response now.
				
			return NULL;
		}
		
		StunMessage m;
		
		if (!stunParseMessage(buf, data_size, m, false)) {
			log_file->write_report("Faulty STUN message", "::stun_listen_main");
			continue;
		}
		
		log_file->write_header("::stun_listen_main", LOG_STUN);
		log_file->write_raw("Received: ");
		log_file->write_raw(stunMsg2Str(m));
		log_file->write_footer();
	
		evq_trans_mgr->push_stun_response(&m, 0, 0);
	}
}
Ejemplo n.º 2
0
/* parses a given stun message and sets the refelxive address  */
int recv_stun_message(char *msgbuf,int msgLen,StunMessage *response){

  //StunMessage resp;
  //memset(&resp, 0, sizeof(StunMessage));
  int returnValue = 0;
  //bool verbose = true;
  bool verbose = STUN_VERBOSE;

  //debug("still alive 4 \n ");
  //if ( verbose ) info("Got a response \n");

  bool ok = stunParseMessage(msgbuf, msgLen, response, verbose );

  if (!ok){
    
    returnValue = 1;
    error("parse STUN message failed \n");

  }

      UInt32 ip_addr = response->mappedAddress.ipv4.addr;
      char mapped_addr[16];
      int length;
      length = sprintf(mapped_addr,"%d.%d.%d.%d",(ip_addr >> 24) & 0xff,(ip_addr >> 16) & 0xff,(ip_addr >> 8) & 0xff,ip_addr & 0xff);

      ip_addr = response->changedAddress.ipv4.addr;
      char changed_addr[16];
      length = sprintf(changed_addr,"%d.%d.%d.%d",(ip_addr >> 24) & 0xff,(ip_addr >> 16)\
	      & 0xff,(ip_addr >> 8) & 0xff,ip_addr & 0xff);

  if ( verbose )
  {

      info( "\t mappedAddr= ipv4 %s port %d \n \t changedAddr= ipv4 %s port %d \n",
              mapped_addr ,
              response->mappedAddress.ipv4.port,
              changed_addr,
              response->changedAddress.ipv4.port);

      }

  //reflexiveAddr.sin_addr.s_addr = inet_addr(changed_addr);
  //reflexiveAddr->sin_addr.s_addr = inet_addr("1.1.1.1");
  //reflexiveAddr->sin_port = htons(resp.mappedAddress.ipv4.port);
  
  return returnValue;
}
Ejemplo n.º 3
0
static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
	char buf[STUN_MAX_MESSAGE_SIZE];
   	int len = STUN_MAX_MESSAGE_SIZE;
	StunMessage resp;
	len=recv(sock,buf,len,0);
	if (len>0){
		struct in_addr ia;
		stunParseMessage(buf,len, &resp );
		*id=resp.msgHdr.tr_id.octet[0];
		if (resp.hasXorMappedAddress){
			*port = resp.xorMappedAddress.ipv4.port;
			ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
		}else if (resp.hasMappedAddress){
			*port = resp.mappedAddress.ipv4.port;
			ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
		}else return -1;
		strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
	}
	return len;
}
Ejemplo n.º 4
0
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);
		}
	}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
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;
}