#include "ip_addr_helper.h"
#include <boost/asio.hpp>
#include <boost/regex.hpp>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+
using namespace std;
/**
- * 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}");
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<string> external_ipv4_addresses;
+ list<string> 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 "";
}
*/
string IPAddrHelper::dns_query(const string& _hostname) const
{
- string ip_addr_v4;
- string ip_addr_v6;
+ list<string> external_ipv4_addresses;
+ list<string> external_ipv6_addresses;
+ // Init the hostname with the given _hostname or with local Hostname if empty.
string hostname = Hostname;
if ( !_hostname.empty() )
hostname = _hostname;
// 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();
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 "";
}
{
// 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
{
// 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;
if ( boost::regex_search(data,matches,expr) )
{
ip = matches[1];
- Log->print_regex_found_ip(ip);
+ Log->print_regex_match(expr.str(),ip);
}
else
{