From 1d2e2f564ba007345bba2da94201ef6107aa9ee6 Mon Sep 17 00:00:00 2001 From: Bjoern Sikora Date: Wed, 14 Oct 2009 17:47:39 +0200 Subject: [PATCH] Added support for multiple interfaces. Improved IPv6 Support. --- src/ip_addr_helper.cpp | 246 ++++++++++++++++++++++++++++++++++++++++++------ src/ip_addr_helper.h | 7 +- src/logger.cpp | 32 ++++++ src/logger.h | 4 + src/updater.cpp | 4 + 5 files changed, 260 insertions(+), 33 deletions(-) diff --git a/src/ip_addr_helper.cpp b/src/ip_addr_helper.cpp index a55e0c3..fffb4a0 100644 --- a/src/ip_addr_helper.cpp +++ b/src/ip_addr_helper.cpp @@ -10,6 +10,11 @@ #include "ip_addr_helper.h" #include #include +#include +#include +#include +#include + using namespace std; @@ -55,11 +60,64 @@ IPAddrHelper::~IPAddrHelper() /** - * Tests if a given IP is a local address + * Tests if a given IP is a local IPv6 address * @param ip The IP to test * @return true if given IP is local, false if not. */ -bool IPAddrHelper::is_local(const string ip) const +bool IPAddrHelper::is_local_ipv6(const string ip) const +{ + // IPv6 any + boost::regex expr_any_ipv6("^::$"); + + // IPV6 loopback + boost::regex expr_loopback_ipv6("^::1$"); + + // IPV6 local unicast address + boost::regex expr_local_unicast_ipv6("^fc00:"); + + // IPV6 link local + boost::regex expr_link_local_ipv6("^fe[8,9,a,b]{1}"); + + // IPV6 site local + boost::regex expr_site_local_ipv6("^fe[c,d,e,f]{1}"); + + // It's time to test against the regex patterns + if ( boost::regex_search(ip,expr_any_ipv6) ) + { + Log->print_regex_match(expr_any_ipv6.str(),ip); + return true; + } + else if ( boost::regex_search(ip,expr_loopback_ipv6) ) + { + Log->print_regex_match(expr_loopback_ipv6.str(),ip); + return true; + } + else if ( boost::regex_search(ip,expr_local_unicast_ipv6) ) + { + Log->print_regex_match(expr_local_unicast_ipv6.str(),ip); + return true; + } + else if ( boost::regex_search(ip,expr_link_local_ipv6) ) + { + Log->print_regex_match(expr_link_local_ipv6.str(),ip); + return true; + } + else if ( boost::regex_search(ip,expr_site_local_ipv6) ) + { + Log->print_regex_match(expr_site_local_ipv6.str(),ip); + return true; + } + + return false; +} + + +/** + * Tests if a given IP is a local IPv4 address + * @param ip The IP to test + * @return true if given IP is local, false if not. + */ +bool IPAddrHelper::is_local_ipv4(const string ip) const { // 127.0.0.1 boost::regex expr_loopback("127\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"); @@ -126,23 +184,98 @@ bool IPAddrHelper::is_local(const string ip) const string IPAddrHelper::get_actual_ip() { string ip; + if ( WebcheckIpUrl.empty() ) - { - ip = dns_query(""); - } + ip = get_local_wan_nic_ip(); else - { ip = webcheck_ip(); - } - // If IP is within a private range then return "" - if ( is_local(ip) ) + return ip; +} + + +/** + * Get the IP address of the local wan interface if there is one. + * @return The IP address of the wan interface or an empty string if something went wrong. + */ +string IPAddrHelper::get_local_wan_nic_ip() +{ + struct ifaddrs *if_addr_struct, *ifa; + int address_family, ret_val; + char ip_addr_buff[NI_MAXHOST]; + list external_ipv4_addresses; + list external_ipv6_addresses; + + // Get linked list of all interface addresses. + if ( getifaddrs(&if_addr_struct) == -1 ) { - Log->print_ip_is_local(ip); + Log->print_error_getting_local_wan_ip("getifaddrs", strerror(errno)); + freeifaddrs(if_addr_struct); return ""; } - return ip; + // Iterate through the linked list. + for ( ifa = if_addr_struct; ifa != NULL; ifa = ifa->ifa_next) + { + // Get the address family of the actual address. + address_family = ifa->ifa_addr->sa_family; + + // If it is an IPv4 then process further. + if ( address_family == AF_INET ) + { + // Translate the address to a protocol independent representation (dottet format). Copy address into ip_addr_buff. + ret_val = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), ip_addr_buff, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if ( ret_val != 0 ) + { + Log->print_error_getting_local_wan_ip("getnameinfo", gai_strerror(ret_val)); + freeifaddrs(if_addr_struct); + return ""; + } + + // Generate IPv4 string out of char array. + string ipv4_addr(ip_addr_buff); + + Log->print_own_ipv4(ipv4_addr, Hostname); + + // Test if it is a local address. + if ( !is_local_ipv4(ipv4_addr) ) + external_ipv4_addresses.push_back(ipv4_addr); + else + Log->print_ip_is_local(ipv4_addr); + } + // If it is an IPv6 address and IPv6 is enabled then process further. + else if ( (address_family == AF_INET6) && (UseIPv6 == true) ) + { + // Translate the address to a protocol independent representation (dottet format). Copy address into ip_addr_buff. + ret_val = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), ip_addr_buff, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if ( ret_val != 0 ) + { + Log->print_error_getting_local_wan_ip("getnameinfo", gai_strerror(ret_val)); + freeifaddrs(if_addr_struct); + return ""; + } + + // Generate IPv6 string out of char array. + string ipv6_addr(ip_addr_buff); + + Log->print_own_ipv6(ipv6_addr, Hostname); + + // Test if it is a local address. + if ( !is_local_ipv6(ipv6_addr) ) + external_ipv6_addresses.push_back(ipv6_addr); + else + Log->print_ip_is_local(ipv6_addr); + } + } + freeifaddrs(if_addr_struct); + + // Return the first element in IPv6 list if IPv6 is enabled, otherwise return first element in IPv4 list. + if ( (UseIPv6 == true) && (!external_ipv6_addresses.empty()) ) + return external_ipv6_addresses.front(); + else if ( !external_ipv4_addresses.empty() ) + return external_ipv4_addresses.front(); + + return ""; } @@ -153,9 +286,10 @@ string IPAddrHelper::get_actual_ip() */ string IPAddrHelper::dns_query(const string& _hostname) const { - string ip_addr_v4; - string ip_addr_v6; + list external_ipv4_addresses; + list external_ipv6_addresses; + // Init the hostname with the given _hostname or with local Hostname if empty. string hostname = Hostname; if ( !_hostname.empty() ) hostname = _hostname; @@ -165,20 +299,49 @@ string IPAddrHelper::dns_query(const string& _hostname) const // BOOST asio isn't the simplest way to perform a DNS lookup, but it works just fine. net::io_service io_service; net::ip::tcp::resolver resolver(io_service); - net::ip::tcp::resolver::query query(hostname, "0"); + + // Define the DNS query. + net::ip::tcp::resolver::query query(hostname, "0", net::ip::resolver_query_base::address_configured | net::ip::resolver_query_base::all_matching); + + // Perform the DNS query. net::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); net::ip::tcp::resolver::iterator end; + + // Iterate through the returned IP addresses. while(endpoint_iterator != end) { + // Get the IP address out of the endpoint iterator. net::ip::address ip; ip = endpoint_iterator->endpoint().address(); + + // Test if it is a IPv4 address. if ( ip.is_v4() ) - ip_addr_v4 = ip.to_string(); - else if ( ip.is_v6() ) - ip_addr_v6 = ip.to_string(); - Log->print_own_ipv4(ip_addr_v4, hostname); - if ( UseIPv6 == true ) - Log->print_own_ipv6(ip_addr_v6, hostname); + { + // Get the string representation. + string ipv4_addr = ip.to_string(); + + Log->print_own_ipv4(ipv4_addr, hostname); + + // If it is not a local address then push it in the external ipv4 address list. + if ( !is_local_ipv4(ipv4_addr) ) + external_ipv4_addresses.push_back(ipv4_addr); + else + Log->print_ip_is_local(ipv4_addr); + } + // Test if it is a IPv6 address and if IPv6 is enabled. + else if ( (ip.is_v6()) && (UseIPv6 == true) ) + { + // Get the string representation. + string ipv6_addr = ip.to_string(); + + Log->print_own_ipv6(ipv6_addr, hostname); + + // If it is not a local address then push it in the external ipv6 address list. + if ( !is_local_ipv6(ipv6_addr) ) + external_ipv6_addresses.push_back(ipv6_addr); + else + Log->print_ip_is_local(ipv6_addr); + } endpoint_iterator++; } io_service.reset(); @@ -189,10 +352,14 @@ string IPAddrHelper::dns_query(const string& _hostname) const return ""; } - if ( (UseIPv6 == true) && (ip_addr_v6 != "") ) - return ip_addr_v6; + // Return the first element in IPv6 list if IPv6 is enabled, otherwise return first element in IPv4 list. + if ( (UseIPv6 == true) && (!external_ipv6_addresses.empty()) ) + return external_ipv6_addresses.front(); + else if ( !external_ipv4_addresses.empty() ) + return external_ipv4_addresses.front(); - return ip_addr_v4; + // Could not get a external IP address, so return empty string. + return ""; } @@ -213,7 +380,7 @@ string IPAddrHelper::webcheck_ip() { // Webcheck not allowed, log it and return empty string. Log->print_webcheck_exceed_interval( LastWebcheck, (WebcheckInterval*60), current_time ); - return ip_addr; + return ""; } // Init CURL buffers @@ -250,33 +417,50 @@ string IPAddrHelper::webcheck_ip() { // Log it and return the empty string. Log->print_webcheck_no_ip(); - return ip_addr; + return ""; } // Log the received curl data. Log->print_received_curl_data(curl_writedata_buff); - // Try to parse a IPAddress out of the received data. - ip_addr = parse_ip(curl_writedata_buff); + // Try to parse a IPv4 address out of the received data. + ip_addr = parse_ipv4(curl_writedata_buff); + + if ( !ip_addr.empty() ) + { + // Got a IPv4 address out of the received data. + Log->print_own_ipv4(ip_addr, Hostname); + } + else + { + return ""; + } // Set the LastWebcheck time to current time. LastWebcheck = current_time; + // If IP is within a private range then return "" + if ( is_local_ipv4(ip_addr) ) + { + Log->print_ip_is_local(ip_addr); + return ""; + } + // Return the parsed IPAddress. return ip_addr; } /** - * Tries to find a valid IPv4 Address in dottet format in a given string and returns the IP-Address found. + * Tries to find a IPv4 Address in dottet format in a given string and returns the IP-Address found. * @param data The string data to search in for a valid IPv4-Address. * @return The IP Address found or an empty string. */ -string IPAddrHelper::parse_ip(const string& data) const +string IPAddrHelper::parse_ipv4(const string& data) const { string ip = ""; - // Regex for valid ip address + // Regex for ipv4 address in dottet format boost::regex expr("([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})"); boost::smatch matches; @@ -284,7 +468,7 @@ string IPAddrHelper::parse_ip(const string& data) const if ( boost::regex_search(data,matches,expr) ) { ip = matches[1]; - Log->print_regex_found_ip(ip); + Log->print_regex_match(expr.str(),ip); } else { diff --git a/src/ip_addr_helper.h b/src/ip_addr_helper.h index ebd855f..45d970a 100644 --- a/src/ip_addr_helper.h +++ b/src/ip_addr_helper.h @@ -41,11 +41,12 @@ private: bool UseIPv6; std::string Hostname; - bool is_local(const std::string ip) const; + bool is_local_ipv4(const std::string ip) const; + bool is_local_ipv6(const std::string ip) const; std::string webcheck_ip(); CURL * init_curl(std::string& curl_writedata_buff, char* curl_err_buff) const; int perform_curl_operation(CURL * curl_easy_handle, char* curl_err_buff, const std::string& actual_url) const; - std::string parse_ip(const std::string& data) const; + std::string parse_ipv4(const std::string& data) const; public: @@ -61,6 +62,8 @@ public: std::string get_actual_ip(); + std::string get_local_wan_nic_ip(); + // libcurl is a C library, so we have to make the callback member function static :-( static int http_receive(char *inBuffer, size_t size, size_t nmemb, std::string *outBuffer); diff --git a/src/logger.cpp b/src/logger.cpp index 91d040d..1bc189e 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -1641,3 +1641,35 @@ void Logger::print_no_update_needed(const string& hostname, const string& ip_dns log_notice(msg.str(),level); } } + + +/** + * Error while trying to get local wan interface IP address through getifaddrs. + * @param error The system call which raised the error. + * @param error The error set by getifaddrs. + */ +void Logger::print_error_getting_local_wan_ip(const std::string& system_call, const std::string& error) const +{ + int level = 0; + if ( level <= Loglevel ) + { + ostringstream msg; + msg << "Error while trying to get local wan interface IP address through '" << system_call << "': " << error << endl; + log_error(msg.str(),level); + } +} + + +/** + * Could not get IP address of local wan interface. + */ +void Logger::print_no_wan_ip() const +{ + int level = 0; + if ( level <= Loglevel ) + { + ostringstream msg; + msg << "Could not get IP address of local wan interface." << endl; + log_error(msg.str(),level); + } +} diff --git a/src/logger.h b/src/logger.h index a5e5d73..85f6d99 100644 --- a/src/logger.h +++ b/src/logger.h @@ -140,6 +140,8 @@ public: void print_webcheck_no_ip() const; + void print_no_wan_ip() const; + void print_webcheck_url_connection_problem(const char * curl_err_buff, const std::string& url) const; void print_webcheck_error(const char * curl_err_buff, const std::string& url) const; @@ -231,6 +233,8 @@ public: void print_update_service_ttl_not_expired(const std::string& hostname, const std::string& ip_dns_recheck, const std::string& ip_last_update, const std::string& ip_host, const int lastupdated, const int dns_cache_ttl, const int current_time) const; void print_no_update_needed(const std::string& hostname, const std::string& ip_dns_recheck, const std::string& ip_last_update, const std::string& ip_host, const int lastupdated) const; + + void print_error_getting_local_wan_ip(const std::string& system_call, const std::string& error) const; }; #endif diff --git a/src/updater.cpp b/src/updater.cpp index 54306b4..3d18418 100644 --- a/src/updater.cpp +++ b/src/updater.cpp @@ -294,6 +294,10 @@ void Updater::update_services() } } } + else + { + Log->print_no_wan_ip(); + } } -- 1.7.1