Redirect Ipv6 link-local connections to non-link-local address.
This commit is contained in:
+291
-30
@@ -27,10 +27,12 @@
|
|||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <regex>
|
||||||
|
#include "ss.hpp"
|
||||||
|
#include "Finally.hpp"
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
std::string pipedal::GetNonLinkLocalAddress(const std::string fromAddress);
|
|
||||||
|
|
||||||
static bool IsIpv4MappedAddress(const struct in6_addr &inetAddr6)
|
static bool IsIpv4MappedAddress(const struct in6_addr &inetAddr6)
|
||||||
{
|
{
|
||||||
@@ -188,7 +190,6 @@ bool pipedal::IsOnLocalSubnet(const std::string &fromAddress)
|
|||||||
// ipv4
|
// ipv4
|
||||||
struct in_addr inetAddr;
|
struct in_addr inetAddr;
|
||||||
memset(&inetAddr, 0, sizeof(inetAddr));
|
memset(&inetAddr, 0, sizeof(inetAddr));
|
||||||
|
|
||||||
if (inet_pton(AF_INET, address.c_str(), &inetAddr) == 1)
|
if (inet_pton(AF_INET, address.c_str(), &inetAddr) == 1)
|
||||||
{
|
{
|
||||||
uint32_t remoteAddress = htonl(inetAddr.s_addr);
|
uint32_t remoteAddress = htonl(inetAddr.s_addr);
|
||||||
@@ -314,12 +315,89 @@ static std::string GetInterfaceForIp6Address(const in6_addr inetAddr6)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string GetNonLinkLocalAddress(const boost::asio::ip::address_v6 &ipV6Address)
|
||||||
|
{
|
||||||
|
auto targetInterfaceId = ipV6Address.scope_id();
|
||||||
|
boost::asio::ip::address_v6::bytes_type bytes = ipV6Address.to_bytes();
|
||||||
|
auto scopeId = ipV6Address.scope_id();
|
||||||
|
|
||||||
|
struct ifaddrs *ifap = nullptr;
|
||||||
|
if (getifaddrs(&ifap) != 0)
|
||||||
|
throw std::runtime_error("No networks available.");
|
||||||
|
|
||||||
|
Finally f{[ifap]() {
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
const char *targetInterfaceName = nullptr;
|
||||||
|
const ifaddrs *targetInterface = nullptr;
|
||||||
|
|
||||||
|
// get the nameof the interface for this scopeId.
|
||||||
|
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
|
||||||
|
{
|
||||||
|
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr)
|
||||||
|
{ // TODO: Add support for AF_INET6
|
||||||
|
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
|
||||||
|
if (pAddr->sin6_scope_id == scopeId)
|
||||||
|
{
|
||||||
|
targetInterface = p;
|
||||||
|
targetInterfaceName = p->ifa_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!targetInterface)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unknown address.");
|
||||||
|
}
|
||||||
|
// an IPV4 address would be the ideal result.
|
||||||
|
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
|
||||||
|
{
|
||||||
|
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
|
||||||
|
{
|
||||||
|
if (strcmp(p->ifa_name,targetInterfaceName) == 0)
|
||||||
|
{
|
||||||
|
constexpr int BUFSIZE = 128;
|
||||||
|
char host[BUFSIZE];
|
||||||
|
if (getnameinfo(p->ifa_addr, sizeof(struct sockaddr_in), host, BUFSIZE, NULL, 0, NI_NUMERICHOST) == 0)
|
||||||
|
{
|
||||||
|
host[BUFSIZE - 1] = '\0';
|
||||||
|
|
||||||
|
result = host;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// failing that, a global unique ip address.
|
||||||
|
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
|
||||||
|
{
|
||||||
|
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
|
||||||
|
{ // TODO: Add support for AF_INET6
|
||||||
|
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
|
||||||
|
if ((!IN6_IS_ADDR_LINKLOCAL(pAddr)) && (!IN6_IS_ADDR_SITELOCAL(pAddr)))
|
||||||
|
{
|
||||||
|
char buffer[128];
|
||||||
|
inet_ntop(AF_INET6, &(pAddr->sin6_addr),
|
||||||
|
buffer,sizeof(buffer));
|
||||||
|
return SS('[' << buffer << ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// probably network address settling after the hotspot comes up. :-/
|
||||||
|
throw std::runtime_error("Not ready. Try again later.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static std::string GetNonLinkLocalAddressForInterface(const std::string &name)
|
static std::string GetNonLinkLocalAddressForInterface(const std::string &name)
|
||||||
{
|
{
|
||||||
struct ifaddrs *ifap = nullptr;
|
struct ifaddrs *ifap = nullptr;
|
||||||
if (getifaddrs(&ifap) != 0)
|
if (getifaddrs(&ifap) != 0)
|
||||||
return "";
|
return "";
|
||||||
std::string result = "notlinklocal.error";
|
std::string result;
|
||||||
|
|
||||||
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
|
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
|
||||||
{
|
{
|
||||||
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
|
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
|
||||||
@@ -352,6 +430,10 @@ static std::string GetNonLinkLocalAddressForInterface(const std::string &name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
freeifaddrs(ifap);
|
freeifaddrs(ifap);
|
||||||
|
if (result.empty())
|
||||||
|
{
|
||||||
|
throw std::logic_error("Address not supported.");
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
static std::string GetNonLinkLocalAddressForIp4Interface(const std::string &name)
|
static std::string GetNonLinkLocalAddressForIp4Interface(const std::string &name)
|
||||||
@@ -385,35 +467,97 @@ static std::string GetNonLinkLocalAddressForIp4Interface(const std::string &name
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pipedal::IsLinkLocalAddress(const std::string fromAddress)
|
// bool pipedal::IsLinkLocalAddress(const std::string & fromAddress)
|
||||||
{
|
// {
|
||||||
std::string address = fromAddress;
|
// std::string address = fromAddress;
|
||||||
std::string result;
|
// std::string result;
|
||||||
if (address[0] != '[')
|
// if (address[0] != '[')
|
||||||
{
|
// {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
// ipv6
|
// // ipv6
|
||||||
if (address[0] != '[' || address[address.length() - 1] != ']')
|
// if (address[0] != '[' || address[address.length() - 1] != ']')
|
||||||
throw std::invalid_argument("Bad address.");
|
// throw std::invalid_argument("Bad address.");
|
||||||
address = address.substr(1, address.length() - 2);
|
// address = address.substr(1, address.length() - 2);
|
||||||
|
|
||||||
struct in6_addr inetAddr6;
|
// // strip the interface suffice if present.
|
||||||
memset(&inetAddr6, 0, sizeof(inetAddr6));
|
// auto pos = address.find_last_of('%'); //
|
||||||
if (inet_pton(AF_INET6, address.c_str(), &inetAddr6) == 1)
|
// if (pos != std::string::npos)
|
||||||
{
|
// {
|
||||||
if (IN6_IS_ADDR_LINKLOCAL(&inetAddr6))
|
// address = address.substr(0,pos);
|
||||||
{
|
// }
|
||||||
return true;
|
// struct in6_addr inetAddr6;
|
||||||
}
|
// memset(&inetAddr6, 0, sizeof(inetAddr6));
|
||||||
}
|
// xx trashes memory. if (inet_pton(AF_INET6, address.c_str(), &address) == 1)
|
||||||
}
|
// {
|
||||||
return false;
|
// if (IN6_IS_ADDR_LINKLOCAL(&inetAddr6))
|
||||||
}
|
// {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
std::string pipedal::GetNonLinkLocalAddress(const std::string fromAddress)
|
// static std::string GetNonLinkLocalAddress(const std::string &address)
|
||||||
|
// {
|
||||||
|
// std::string address = fromAddress;
|
||||||
|
// std::string result;
|
||||||
|
// if (address[0] != '[')
|
||||||
|
// {
|
||||||
|
// return address;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // ipv6
|
||||||
|
// if (address[0] != '[' || address[address.length() - 1] != ']')
|
||||||
|
// throw std::invalid_argument("Bad address.");
|
||||||
|
// address = address.substr(1, address.length() - 2);
|
||||||
|
|
||||||
|
// auto nPos = address.find('%') ;
|
||||||
|
// if (nPos != std::string::npos)
|
||||||
|
// {
|
||||||
|
// std::string ifName = address.substr(nPos+1);
|
||||||
|
// return GetNonLinkLocalAddressForInterface(ifName);
|
||||||
|
|
||||||
|
// }
|
||||||
|
// struct sockaddr_in6 sa6;
|
||||||
|
// auto&inetAddr6 = sa6.sin6_addr;
|
||||||
|
// if (inet_pton(AF_INET6, address.c_str(), &inetAddr6) == 1)
|
||||||
|
// {
|
||||||
|
// int32_t remoteAddress = -1;
|
||||||
|
// // cases:
|
||||||
|
// // [::FFFF:ipv4 address]
|
||||||
|
// // [FE80:: ] link local.
|
||||||
|
// // [FEC0:: ] site local.
|
||||||
|
// // others?
|
||||||
|
// if (IN6_IS_ADDR_V4MAPPED(&inetAddr6))
|
||||||
|
// {
|
||||||
|
// int8_t *pAddr = (int8_t *)&inetAddr6;
|
||||||
|
// uint32_t remoteAddress = htonl(*(int32_t *)(pAddr + 12));
|
||||||
|
// std::string interfaceName = GetInterfaceForIp4Address(remoteAddress);
|
||||||
|
// result = GetNonLinkLocalAddressForIp4Interface(interfaceName);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
|
||||||
|
|
||||||
|
// std::string interfaceName = GetInterfaceForIp6Address(inetAddr6);
|
||||||
|
// result = GetNonLinkLocalAddressForInterface(interfaceName);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (result == "")
|
||||||
|
// {
|
||||||
|
// result = GetNonLinkLocalAddressForInterface("");
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
std::string pipedal::GetNonLinkLocalAddress(const std::string &fromAddress)
|
||||||
{
|
{
|
||||||
std::string address = fromAddress;
|
std::string address = fromAddress;
|
||||||
std::string result;
|
std::string result;
|
||||||
@@ -472,3 +616,120 @@ std::string pipedal::GetInterfaceIpv4Address(const std::string& interfaceName)
|
|||||||
{
|
{
|
||||||
return GetNonLinkLocalAddressForIp4Interface(interfaceName);
|
return GetNonLinkLocalAddressForIp4Interface(interfaceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// std::string getNonLinkLocalAddress(const std::string&address, const std::string&interface)
|
||||||
|
// {
|
||||||
|
// if (!interface.empty())
|
||||||
|
// {
|
||||||
|
// return GetNonLinkLocalAddressForInterface(interface);
|
||||||
|
// }
|
||||||
|
// return GetNonLinkLocalAddress(address);
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
static bool parseForLinkLocalUrl(const std::string&url,std::string*prefix, std::string *suffix)
|
||||||
|
{
|
||||||
|
auto iter = url.begin();
|
||||||
|
auto end = url.end();
|
||||||
|
|
||||||
|
// proto:
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (iter == end) return false;
|
||||||
|
if (*iter == ':')
|
||||||
|
{
|
||||||
|
++iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// //
|
||||||
|
if (iter == end || *iter != '/') return false;
|
||||||
|
++iter;
|
||||||
|
if (iter == end || *iter != '/') return false;
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
|
||||||
|
// (user@)? of id: http://user@[::1]/
|
||||||
|
if (*iter != '[') {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (iter == end || *iter == '/') return false;
|
||||||
|
if (*iter == '@')
|
||||||
|
{
|
||||||
|
++iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// [fe80:*]
|
||||||
|
if (iter == end || *iter != '[')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto endOfPrefix = iter;
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
// fe80: ...
|
||||||
|
if (iter == end || !(*iter == 'f' || *iter == 'F')) return false;
|
||||||
|
++iter;
|
||||||
|
if (iter == end || !(*iter == 'e' || *iter == 'E')) return false;
|
||||||
|
++iter;
|
||||||
|
if (iter == end || *iter != '8' ) return false;
|
||||||
|
++iter;
|
||||||
|
if (iter == end || *iter != '0' ) return false;
|
||||||
|
++iter;
|
||||||
|
if (iter == end || *iter != ':' ) return false;
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (iter == end) break;
|
||||||
|
char c = *iter;
|
||||||
|
bool valid = (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c >= '0' && c <= '9' || c == ':';
|
||||||
|
if (!valid) break;
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
// potentially a scope ID
|
||||||
|
// [::1%wlan0]
|
||||||
|
auto endOfAddress = iter;
|
||||||
|
if (iter != end && *iter == '%')
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
if (iter == end) return false;
|
||||||
|
if (*iter == ']') break;
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// made it!
|
||||||
|
if (iter == end || *iter != ']') return false;
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
auto startOfRest = iter;
|
||||||
|
|
||||||
|
*prefix = std::string(url.begin(),endOfPrefix);
|
||||||
|
*suffix = std::string(startOfRest,url.end());
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
bool pipedal::RemapLinkLocalUrl(
|
||||||
|
const boost::asio::ip::address &boostAddress,
|
||||||
|
const std::string&url,std::string*outputUrl)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string prefix, suffix;
|
||||||
|
if (!parseForLinkLocalUrl(url,&prefix,&suffix)) return false;
|
||||||
|
|
||||||
|
boost::asio::ip::address_v6 addressV6 = boostAddress.to_v6();
|
||||||
|
|
||||||
|
std::string directAddress;
|
||||||
|
directAddress = ::GetNonLinkLocalAddress(addressV6);
|
||||||
|
|
||||||
|
*outputUrl = SS(prefix << directAddress << suffix);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+9
-3
@@ -20,19 +20,25 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
namespace pipedal {
|
namespace pipedal {
|
||||||
|
|
||||||
|
|
||||||
std::string GetInterfaceIpv4Address(const std::string& interfaceName);
|
std::string GetInterfaceIpv4Address(const std::string& interfaceName);
|
||||||
|
|
||||||
|
|
||||||
bool IsLinkLocalAddress(const std::string fromAddress);
|
// bool IsLinkLocalAddress(const std::string &fromAddress);
|
||||||
|
|
||||||
std::string GetNonLinkLocalAddress(const std::string fromAddress);
|
std::string GetNonLinkLocalAddress(const std::string& fromAddress);
|
||||||
|
|
||||||
bool IsOnLocalSubnet(const std::string&fromAddress);
|
bool IsOnLocalSubnet(const std::string&fromAddress);
|
||||||
|
|
||||||
|
|
||||||
|
bool RemapLinkLocalUrl(
|
||||||
|
const boost::asio::ip::address &address,
|
||||||
|
const std::string&url,std::string*outputUrl);
|
||||||
|
|
||||||
bool ParseHttpAddress(const std::string address,
|
bool ParseHttpAddress(const std::string address,
|
||||||
std::string *pUser,
|
std::string *pUser,
|
||||||
std::string *pServer,
|
std::string *pServer,
|
||||||
|
|||||||
Reference in New Issue
Block a user