//stun_server: // xxx.xxx.xxx[:port] //return: bits 0~3: nat type // bit 4: hairpin // bit 5: preserve port int detect_nat(const char *stun_server) { StunAddress4 stunServerAddr; stunServerAddr.addr=0; if(!stunParseServerName( stun_server, &stunServerAddr)) return -1; BOOL presPort=FALSE; BOOL hairpin=FALSE; int stype = stunNatType( &stunServerAddr, &presPort, &hairpin, 0, NULL ); if(presPort) stype |= 0x10; if(hairpin) stype |= 0x20; return stype; }
NatType get_nat_type(const char *stun_server) { static const int CTRL_PORT = 3478; initNetwork(); StunAddress4 stunServerAddr; stunServerAddr.addr = 0; StunAddress4 sAddr[1]; sAddr[0].addr = 0; sAddr[0].port = 0; bool ret = stunParseServerName((char *) stun_server, stunServerAddr, CTRL_PORT); if (!ret) { return StunTypeFailure; } bool presPort = false; bool hairpin = false; return stunNatType(stunServerAddr, true, & presPort, & hairpin, 0, & sAddr[0]); }
bool stun_discover_nat(t_phone_user *pu, string &err_msg) { t_user *user_config = pu->get_user_profile(); // By default enable STUN. If for some reason we cannot perform // NAT discovery, then enable STUN. It will not harm, but only // create non-needed STUN transactions if we are not behind a NAT. pu->use_stun = true; pu->use_nat_keepalive = true; 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("::main", 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_footer(); err_msg = TRANSLATE("Cannot resolve STUN server: %1"); err_msg = replace_first(err_msg, "%1", user_config->get_stun_server().encode()); return false; } while (!destinations.empty()) { StunAddress4 stun_ip4; stun_ip4.addr = destinations.front().ipaddr; stun_ip4.port = destinations.front().port; NatType nat_type = stunNatType(stun_ip4, false); log_file->write_header("::main"); log_file->write_raw("STUN NAT type discovery for "); log_file->write_raw(user_config->get_profile_name()); log_file->write_endl(); log_file->write_raw("NAT type: "); log_file->write_raw(stunNatType2Str(nat_type)); log_file->write_endl(); log_file->write_footer(); switch (nat_type) { case StunTypeOpen: // STUN is not needed. pu->use_stun = false; pu->use_nat_keepalive = false; return true; case StunTypeSymNat: err_msg = TRANSLATE("You are behind a symmetric NAT.\nSTUN will not work.\nConfigure a public IP address in the user profile\nand create the following static bindings (UDP) in your NAT."); err_msg += "\n\n"; err_msg += TRANSLATE("public IP: %1 --> private IP: %2 (SIP signaling)"); err_msg = replace_first(err_msg, "%1", int2str(sys_config->get_sip_port())); err_msg = replace_first(err_msg, "%2", int2str(sys_config->get_sip_port())); err_msg += "\n"; err_msg += TRANSLATE("public IP: %1-%2 --> private IP: %3-%4 (RTP/RTCP)"); err_msg = replace_first(err_msg, "%1", int2str(sys_config->get_rtp_port())); err_msg = replace_first(err_msg, "%2", int2str(sys_config->get_rtp_port() + 5)); err_msg = replace_first(err_msg, "%3", int2str(sys_config->get_rtp_port())); err_msg = replace_first(err_msg, "%4", int2str(sys_config->get_rtp_port() + 5)); pu->use_stun = false; pu->use_nat_keepalive = false; return false; case StunTypeSymFirewall: // STUN is not needed as we are on a pubic IP. // NAT keep alive is needed however to keep the firewall open. pu->use_stun = false; return true; case StunTypeBlocked: destinations.pop_front(); // The code for NAT type discovery does not handle // ICMP errors. So if the conclusion is that the network // connection is blocked, it might be due to a down STUN // server. Try alternative destination if avaliable. if (destinations.empty()) { err_msg = TRANSLATE("Cannot reach the STUN server: %1"); err_msg = replace_first(err_msg, "%1", user_config->get_stun_server().encode()); err_msg += "\n\n"; err_msg += TRANSLATE("If you are behind a firewall then you need to open the following UDP ports."); err_msg += "\n"; err_msg += TRANSLATE("Port %1 (SIP signaling)"); err_msg = replace_first(err_msg, "%1", int2str(sys_config->get_sip_port())); err_msg += "\n"; err_msg += TRANSLATE("Ports %1-%2 (RTP/RTCP)"); err_msg = replace_first(err_msg, "%1", int2str(sys_config->get_rtp_port())); err_msg = replace_first(err_msg, "%2", int2str(sys_config->get_rtp_port() + 5)); return false; } log_file->write_report("Failover to next destination.", "::stun_discover_nat"); break; case StunTypeFailure: destinations.pop_front(); log_file->write_report("Failover to next destination.", "::stun_discover_nat"); break; default: // Use STUN. return true; } } err_msg = TRANSLATE("NAT type discovery via STUN failed."); return false; }
STUN_NAT_TYPE_T StunResolver::ResolveInternal(const std::string &server) { STUN_NAT_TYPE_T type = STUN_NAT_TYPE_UNKNOWN; do { StunAddress4 stunServerAddr; stunServerAddr.addr = 0; // resolve server address bool ret = stunParseServerName(const_cast<char *>(server.c_str()), stunServerAddr); if (!ret || 0 == stunServerAddr.addr) { LOGE("Cannot parser stun server address: %s", server.c_str()); break; } int srcPort = stunRandomPort(); bool presPort = false; bool hairpin = false; StunAddress4 sAddr; sAddr.addr = 0; sAddr.port = 0; NatType stype = stunNatType(stunServerAddr, false, &presPort, &hairpin, srcPort, &sAddr); switch (stype) { case StunTypeIndependentFilter: LOGD("Independent Mapping, Independent Filter: presPort %d hairpin %d", presPort, hairpin); type = STUN_NAT_TYPE_CONE; break; case StunTypeDependentFilter: LOGD("Independent Mapping, Address Dependent Filter: presPort %d hairpin %d", presPort, hairpin); type = STUN_NAT_TYPE_RESTRICTED; break; case StunTypePortDependedFilter: LOGD("Independent Mapping, Port Dependent Filter: presPort %d hairpin %d", presPort, hairpin); type = STUN_NAT_TYPE_PORT_RESTRICTED; break; case StunTypeDependentMapping: LOGD("Dependent Mapping: presPort %d hairpin %d", presPort, hairpin); type = STUN_NAT_TYPE_SYM; break; case StunTypeFailure: LOGE("Some stun error detetecting NAT type"); break; case StunTypeUnknown: LOGE("Some unknown type error detetecting NAT type"); break; case StunTypeOpen: LOGE("Open stun type"); break; case StunTypeFirewall: cout << "Firewall"; break; case StunTypeBlocked: cout << "Blocked or could not reach STUN server"; break; default: LOGD("Unknown NAT stype: %d", stype); break; } } while (false); return type; }