/* * Copyright (c) 2017, Matias Fontanini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #ifndef _WIN32 #if defined(BSD) || defined(__FreeBSD_kernel__) #include #include #include #include #include #include #include #else #include #endif #include #include #include #ifdef __ANDROID_API__ #include #include #endif #else #include #include #include #undef interface #endif #include #include #include #include using std::vector; using std::string; using std::set; using std::ifstream; using std::istream; namespace Tins { namespace Utils { struct InterfaceCollector { set ifaces; #ifdef _WIN32 bool operator() (PIP_ADAPTER_ADDRESSES addr) { ifaces.insert(addr->AdapterName); return false; } #else bool operator() (struct ifaddrs* addr) { ifaces.insert(addr->ifa_name); return false; } #endif }; bool from_hex(const string& str, uint32_t& result) { size_t i = 0; result = 0; while (i < str.size()) { uint8_t tmp; if (str[i] >= 'A' && str[i] <= 'F') { tmp = (str[i] - 'A' + 10); } else if (str[i] >= '0' && str[i] <= '9') { tmp = (str[i] - '0'); } else { return false; } result = (result << 4) | tmp; i++; } return true; } bool from_hex(const string& str, string& result) { result.clear(); for (size_t i = 0; i < str.size(); i+= 2) { uint8_t value = 0; for (size_t j = i; j < i + 2 && j < str.size(); ++j) { if (str[j] >= 'A' && str[j] <= 'F') { value = (value << 4) | (str[j] - 'A' + 10); } else if (str[j] >= 'a' && str[j] <= 'f') { value = (value << 4) | (str[j] - 'a' + 10); } else if (str[j] >= '0' && str[j] <= '9') { value = (value << 4) | (str[j] - '0'); } else { return false; } } result.push_back(value); } return true; } void skip_line(istream& input) { int c = 0; while (c != '\n' && input) { c = input.get(); } } #if defined(BSD) || defined(__FreeBSD_kernel__) vector query_route_table(int family) { int mib[6]; vector buf; size_t len; mib[0] = CTL_NET; mib[1] = AF_ROUTE; mib[2] = 0; mib[3] = family; mib[4] = NET_RT_DUMP; mib[5] = 0; if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { throw exception_base("sysctl failed"); } buf.resize(len); if (sysctl(mib, 6, &buf[0], &len, NULL, 0) < 0) { throw exception_base("sysctl failed"); } buf.resize(len); return buf; } void parse_header(struct rt_msghdr* rtm, vector& addrs) { char* ptr = (char *)(rtm + 1); // Iterate from RTA_DST (0) to RTA_NETMASK (2) for (int i = 0; i < 3; ++i) { sockaddr* sa = 0; if ((rtm->rtm_addrs & (1 << i)) != 0) { sa = (struct sockaddr *)ptr; ptr += sa->sa_len; if (sa->sa_family == 0) { sa = 0; } } addrs[i] = sa; } } vector route_entries() { vector output; vector buffer = query_route_table(AF_INET); char* next = &buffer[0], *end = &buffer[buffer.size()]; rt_msghdr* rtm; vector sa(32); char iface_name[IF_NAMESIZE]; while (next < end) { rtm = (rt_msghdr*)next; // Filter: // * RTF_STATIC (only manually added routes) if ((rtm->rtm_flags & (RTF_STATIC)) != 0) { parse_header(rtm, sa); if (sa[RTAX_DST] && sa[RTAX_GATEWAY] && if_indextoname(rtm->rtm_index, iface_name)) { RouteEntry entry; entry.destination = IPv4Address(((struct sockaddr_in *)sa[RTAX_DST])->sin_addr.s_addr); entry.gateway = IPv4Address(((struct sockaddr_in *)sa[RTAX_GATEWAY])->sin_addr.s_addr); if (sa[RTAX_NETMASK]) { entry.mask = IPv4Address(((struct sockaddr_in *)sa[RTAX_NETMASK])->sin_addr.s_addr); } entry.interface = iface_name; entry.metric = 0; output.push_back(entry); } } next += rtm->rtm_msglen; } return output; } vector route6_entries() { vector output; vector buffer = query_route_table(AF_INET6); char* next = &buffer[0], *end = &buffer[buffer.size()]; rt_msghdr* rtm; vector sa(9); char iface_name[IF_NAMESIZE]; while (next < end) { rtm = (rt_msghdr*)next; // Filter protocol-cloned entries bool process_entry = true; // These were removed in recent versions of FreeBSD #if defined(RTF_WASCLONED) && defined(RTF_PRCLONING) process_entry = (rtm->rtm_flags & RTF_WASCLONED) == 0 || (rtm->rtm_flags & RTF_PRCLONING) == 0; #endif if (process_entry) { parse_header(rtm, sa); if (sa[RTAX_DST] && sa[RTAX_GATEWAY] && if_indextoname(rtm->rtm_index, iface_name)) { Route6Entry entry; entry.destination = IPv6Address(((struct sockaddr_in6 *)sa[RTAX_DST])->sin6_addr.s6_addr); entry.gateway = IPv6Address(((struct sockaddr_in6 *)sa[RTAX_GATEWAY])->sin6_addr.s6_addr); int prefix_length = 0; if (sa[RTAX_NETMASK]) { struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa[RTAX_NETMASK]; for (size_t i = 0; i < 16; ++i) { uint8_t this_byte = sin->sin6_addr.s6_addr[i]; // Stop when we find a zero byte if (this_byte == 0) { break; } switch (this_byte) { case 0xff: prefix_length += 8; break; case 0xfe: prefix_length += 7; break; case 0xfc: prefix_length += 6; break; case 0xf8: prefix_length += 5; break; case 0xf0: prefix_length += 4; break; case 0xe0: prefix_length += 3; break; case 0xc0: prefix_length += 2; break; case 0x80: prefix_length += 1; break; default: break; } } } entry.mask = IPv6Address::from_prefix_length(prefix_length); entry.interface = iface_name; entry.metric = 0; output.push_back(entry); } } next += rtm->rtm_msglen; } return output; } #elif defined(_WIN32) vector route_entries() { vector output; MIB_IPFORWARDTABLE* table; ULONG size = 0; GetIpForwardTable(0, &size, 0); vector buffer(size); table = (MIB_IPFORWARDTABLE*)&buffer[0]; GetIpForwardTable(table, &size, 0); for (DWORD i = 0; i < table->dwNumEntries; i++) { MIB_IPFORWARDROW* row = &table->table[i]; if (row->dwForwardType == MIB_IPROUTE_TYPE_INDIRECT || row->dwForwardType == MIB_IPROUTE_TYPE_DIRECT) { RouteEntry entry; entry.interface = NetworkInterface::from_index(row->dwForwardIfIndex).name(); entry.destination = IPv4Address(row->dwForwardDest); entry.mask = IPv4Address(row->dwForwardMask); entry.gateway = IPv4Address(row->dwForwardNextHop); entry.metric = row->dwForwardMetric1; output.push_back(entry); } } return output; } vector route6_entries() { vector output; MIB_IPFORWARD_TABLE2* table; GetIpForwardTable2(AF_INET6, &table); for (ULONG i = 0; i < table->NumEntries; i++) { MIB_IPFORWARD_ROW2* row = &table->Table[i]; if (true) { try { Route6Entry entry; entry.interface = NetworkInterface::from_index(row->InterfaceIndex).name(); entry.destination = IPv6Address(row->DestinationPrefix.Prefix.Ipv6.sin6_addr.s6_addr); entry.mask = IPv6Address::from_prefix_length(row->DestinationPrefix.PrefixLength); entry.gateway = IPv6Address(row->NextHop.Ipv6.sin6_addr.s6_addr); entry.metric = row->Metric; output.push_back(entry); } catch (const invalid_interface&) { } } } FreeMibTable(table); return output; } #else // GNU/LINUX vector route_entries() { using namespace Tins::Internals; vector output; ifstream input("/proc/net/route"); string destination, mask, metric, gw; uint32_t dummy; skip_line(input); RouteEntry entry; while (input >> entry.interface >> destination >> gw) { for (unsigned i(0); i < 4; ++i) { input >> metric; } input >> mask; from_hex(destination, dummy); entry.destination = IPv4Address(dummy); from_hex(mask, dummy); entry.mask = IPv4Address(dummy); from_hex(gw, dummy); entry.gateway = IPv4Address(dummy); from_hex(metric, dummy); entry.metric = dummy; skip_line(input); output.push_back(entry); } return output; } vector route6_entries() { using namespace Tins::Internals; vector output; ifstream input("/proc/net/ipv6_route"); string destination, mask_length, metric, next_hop, dummy, flags; Route6Entry entry; while (input >> destination >> mask_length) { string temporary; uint32_t temporary_int; for (unsigned i(0); i < 2; ++i) { input >> dummy; } input >> next_hop; input >> metric; for (unsigned i(0); i < 2; ++i) { input >> dummy; } input >> flags >> entry.interface; from_hex(destination, temporary); entry.destination = IPv6Address((const uint8_t*)&temporary[0]); from_hex(mask_length, temporary_int); entry.mask = IPv6Address::from_prefix_length(temporary_int); from_hex(next_hop, temporary); entry.gateway = IPv6Address((const uint8_t*)&temporary[0]); from_hex(metric, temporary_int); entry.metric = temporary_int; // Process flags from_hex(flags, temporary_int); // Skip: // * 0x01000000 -> cache entries if ((temporary_int & 0x01000000) == 0) { output.push_back(entry); } } return output; } #endif #ifdef _WIN32 set network_interfaces() { set output; ULONG size; ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size); vector buffer(size); if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) { PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0]; while (iface) { output.insert(iface->AdapterName); iface = iface->Next; } } return output; } #else set network_interfaces() { #if !defined(ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 24) set output; struct ifaddrs* ifaddrs = 0; struct ifaddrs* if_it = 0; getifaddrs(&ifaddrs); for (if_it = ifaddrs; if_it; if_it = if_it->ifa_next) { output.insert(if_it->ifa_name); } if (ifaddrs) { freeifaddrs(ifaddrs); } return output; #else throw std::runtime_error("android ifaddr not supported"); #endif } #endif // _WIN32 bool gateway_from_ip(IPv4Address ip, IPv4Address& gw_addr) { typedef vector entries_type; entries_type entries = route_entries(); uint32_t ip_int = ip; for (entries_type::const_iterator it(entries.begin()); it != entries.end(); ++it) { if ((ip_int & it->mask) == it->destination) { gw_addr = it->gateway; return true; } } return false; } bool gateway_from_ip(IPv6Address ip, IPv6Address& gw_addr) { typedef vector entries_type; entries_type entries =route6_entries(); for (entries_type::const_iterator it(entries.begin()); it != entries.end(); ++it) { if ((ip & it->mask) == it->destination) { gw_addr = it->gateway; return true; } } return false; } } // Utils } // Tins