Split out ipaddr stuff into a separate file
This commit is contained in:
parent
f2c1643635
commit
3c1647f72f
10 changed files with 476 additions and 378 deletions
|
@ -202,6 +202,7 @@
|
||||||
<ClInclude Include="network_win32_dnsblock.h" />
|
<ClInclude Include="network_win32_dnsblock.h" />
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
<ClInclude Include="stdafx.h" />
|
<ClInclude Include="stdafx.h" />
|
||||||
|
<ClInclude Include="tunsafe_ipaddr.h" />
|
||||||
<ClInclude Include="tunsafe_threading.h" />
|
<ClInclude Include="tunsafe_threading.h" />
|
||||||
<ClInclude Include="tunsafe_types.h" />
|
<ClInclude Include="tunsafe_types.h" />
|
||||||
<ClInclude Include="util_win32.h" />
|
<ClInclude Include="util_win32.h" />
|
||||||
|
@ -221,6 +222,7 @@
|
||||||
<ClCompile Include="crypto\siphash\siphash.cpp" />
|
<ClCompile Include="crypto\siphash\siphash.cpp" />
|
||||||
<ClCompile Include="ipzip2\ipzip2.cpp" />
|
<ClCompile Include="ipzip2\ipzip2.cpp" />
|
||||||
<ClCompile Include="network_win32_dnsblock.cpp" />
|
<ClCompile Include="network_win32_dnsblock.cpp" />
|
||||||
|
<ClCompile Include="tunsafe_ipaddr.cpp" />
|
||||||
<ClCompile Include="tunsafe_threading.cpp" />
|
<ClCompile Include="tunsafe_threading.cpp" />
|
||||||
<ClCompile Include="util.cpp" />
|
<ClCompile Include="util.cpp" />
|
||||||
<ClCompile Include="network_win32.cpp" />
|
<ClCompile Include="network_win32.cpp" />
|
||||||
|
|
|
@ -120,6 +120,9 @@
|
||||||
<ClInclude Include="crypto\chacha20poly1305.h">
|
<ClInclude Include="crypto\chacha20poly1305.h">
|
||||||
<Filter>crypto\chacha20poly1305</Filter>
|
<Filter>crypto\chacha20poly1305</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="tunsafe_ipaddr.h">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
|
@ -185,6 +188,9 @@
|
||||||
<ClCompile Include="crypto\chacha20poly1305.cpp">
|
<ClCompile Include="crypto\chacha20poly1305.cpp">
|
||||||
<Filter>crypto\chacha20poly1305</Filter>
|
<Filter>crypto\chacha20poly1305</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="tunsafe_ipaddr.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="TunSafe.rc" />
|
<ResourceCompile Include="TunSafe.rc" />
|
||||||
|
|
14
netapi.h
14
netapi.h
|
@ -3,9 +3,8 @@
|
||||||
#ifndef TINYVPN_NETAPI_H_
|
#ifndef TINYVPN_NETAPI_H_
|
||||||
#define TINYVPN_NETAPI_H_
|
#define TINYVPN_NETAPI_H_
|
||||||
|
|
||||||
#include "stdafx.h"
|
|
||||||
#include "tunsafe_types.h"
|
#include "tunsafe_types.h"
|
||||||
|
#include "tunsafe_ipaddr.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -18,17 +17,6 @@
|
||||||
|
|
||||||
#pragma warning (disable: 4200)
|
#pragma warning (disable: 4200)
|
||||||
|
|
||||||
union IpAddr {
|
|
||||||
sockaddr_in sin;
|
|
||||||
sockaddr_in6 sin6;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WgCidrAddr {
|
|
||||||
uint8 addr[16];
|
|
||||||
uint8 size;
|
|
||||||
uint8 cidr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Packet {
|
struct Packet {
|
||||||
union {
|
union {
|
||||||
Packet *next;
|
Packet *next;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "network_win32_dnsblock.h"
|
#include "network_win32_dnsblock.h"
|
||||||
#include "wireguard_config.h"
|
#include "wireguard_config.h"
|
||||||
#include "tunsafe_threading.h"
|
#include "tunsafe_threading.h"
|
||||||
|
#include "tunsafe_dnsresolve.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "tunsafe_threading.cpp"
|
#include "tunsafe_threading.cpp"
|
||||||
#include "tunsafe_cpu.cpp"
|
#include "tunsafe_cpu.cpp"
|
||||||
#include "ip_to_peer_map.cpp"
|
#include "ip_to_peer_map.cpp"
|
||||||
|
#include "tunsafe_ipaddr.cpp"
|
||||||
#include "crypto/curve25519/curve25519-donna.cpp"
|
#include "crypto/curve25519/curve25519-donna.cpp"
|
||||||
#include "crypto/chacha20poly1305.cpp"
|
#include "crypto/chacha20poly1305.cpp"
|
||||||
#include "crypto/blake2s/blake2s.cpp"
|
#include "crypto/blake2s/blake2s.cpp"
|
||||||
|
|
41
tunsafe_dnsresolve.h
Normal file
41
tunsafe_dnsresolve.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef TUNSAFE_DNSRESOLVE_H_
|
||||||
|
#define TUNSAFE_DNSRESOLVE_H_
|
||||||
|
|
||||||
|
#include "tunsafe_threading.h"
|
||||||
|
#include "tunsafe_ipaddr.h"
|
||||||
|
|
||||||
|
class DnsBlocker;
|
||||||
|
|
||||||
|
class DnsResolverCanceller {
|
||||||
|
public:
|
||||||
|
DnsResolverCanceller() : cancel_(false) {}
|
||||||
|
void Cancel();
|
||||||
|
void Reset() { cancel_ = false; }
|
||||||
|
bool is_cancelled() { return cancel_; }
|
||||||
|
public:
|
||||||
|
bool cancel_;
|
||||||
|
ConditionVariable condvar_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DnsResolver {
|
||||||
|
public:
|
||||||
|
explicit DnsResolver(DnsBlocker *dns_blocker);
|
||||||
|
~DnsResolver();
|
||||||
|
|
||||||
|
bool Resolve(const char *hostname, IpAddr *result);
|
||||||
|
void ClearCache();
|
||||||
|
|
||||||
|
void Cancel() { token_.Cancel(); }
|
||||||
|
void ResetCancel() { token_.Reset(); }
|
||||||
|
private:
|
||||||
|
struct Entry {
|
||||||
|
std::string name;
|
||||||
|
IpAddr ip;
|
||||||
|
Entry(const std::string &name, const IpAddr &ip) : name(name), ip(ip) {}
|
||||||
|
};
|
||||||
|
std::vector<Entry> cache_;
|
||||||
|
DnsBlocker *dns_blocker_;
|
||||||
|
DnsResolverCanceller token_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TUNSAFE_DNSRESOLVE_H_
|
380
tunsafe_ipaddr.cpp
Normal file
380
tunsafe_ipaddr.cpp
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "tunsafe_ipaddr.h"
|
||||||
|
#include "tunsafe_dnsresolve.h"
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
#include "network_win32_dnsblock.h"
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OS_POSIX)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#endif // defined(OS_POSIX)
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
const char *print_ip_prefix(char buf[kSizeOfAddress], int family, const void *ip, int prefixlen) {
|
||||||
|
// cast to void* to work on VS2015
|
||||||
|
if (!inet_ntop(family, (void*)ip, buf, kSizeOfAddress - 8)) {
|
||||||
|
memcpy(buf, "unknown", 8);
|
||||||
|
}
|
||||||
|
if (prefixlen >= 0)
|
||||||
|
snprintf(buf + strlen(buf), 8, "/%d", prefixlen);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *PrintIpAddr(const IpAddr &addr, char buf[kSizeOfAddress]) {
|
||||||
|
if (addr.sin.sin_family == AF_INET) {
|
||||||
|
print_ip_prefix(buf, addr.sin.sin_family, &addr.sin.sin_addr, -1);
|
||||||
|
} else if (addr.sin.sin_family == AF_INET6) {
|
||||||
|
print_ip_prefix(buf, addr.sin.sin_family, &addr.sin6.sin6_addr, -1);
|
||||||
|
} else {
|
||||||
|
buf[0] = 0;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *PrintWgCidrAddr(const WgCidrAddr &addr, char buf[kSizeOfAddress]) {
|
||||||
|
if (addr.size == 32) {
|
||||||
|
print_ip_prefix(buf, AF_INET, addr.addr, addr.cidr);
|
||||||
|
} else if (addr.size == 128) {
|
||||||
|
print_ip_prefix(buf, AF_INET6, addr.addr, addr.cidr);
|
||||||
|
} else {
|
||||||
|
buf[0] = 0;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Addr {
|
||||||
|
byte addr[4];
|
||||||
|
uint8 cidr;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ParseCidrAddr(const char *s, WgCidrAddr *out) {
|
||||||
|
const char *slash = strchr(s, '/');
|
||||||
|
if (!slash)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t len = slash - s;
|
||||||
|
char *tmp = (char*)alloca(len + 1);
|
||||||
|
tmp[len] = 0;
|
||||||
|
memcpy(tmp, s, len);
|
||||||
|
|
||||||
|
int e = atoi(slash + 1);
|
||||||
|
if (e < 0) return false;
|
||||||
|
|
||||||
|
if (inet_pton(AF_INET, tmp, out->addr) == 1) {
|
||||||
|
if (e > 32) return false;
|
||||||
|
out->cidr = e;
|
||||||
|
out->size = 32;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (inet_pton(AF_INET6, tmp, out->addr) == 1) {
|
||||||
|
if (e > 128) return false;
|
||||||
|
out->cidr = e;
|
||||||
|
out->size = 128;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Mutex g_dns_mutex;
|
||||||
|
|
||||||
|
// This starts a background thread for running DNS resolving.
|
||||||
|
class DnsResolverThread : private Thread::Runner {
|
||||||
|
public:
|
||||||
|
DnsResolverThread();
|
||||||
|
~DnsResolverThread();
|
||||||
|
|
||||||
|
// Resolve the hostname and store the result in |result|.
|
||||||
|
// The function will block until it's resolved. If the cancellation
|
||||||
|
// token or becomes signalled, the call will fail.
|
||||||
|
bool Resolve(const char *hostname, IpAddr *result, DnsResolverCanceller *token);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void ThreadMain();
|
||||||
|
void StartThread();
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
enum {
|
||||||
|
// Set when it's been posted to the job queue
|
||||||
|
POSTED = 0,
|
||||||
|
// Set when the thread has finished and original thread should delete
|
||||||
|
COMPLETE = 1,
|
||||||
|
// Set when the original thread has cancelled and worker thread should delete
|
||||||
|
CANCELLED = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
Entry() : hostname(NULL) {}
|
||||||
|
~Entry() { free(hostname); }
|
||||||
|
|
||||||
|
char *hostname;
|
||||||
|
IpAddr *result;
|
||||||
|
Entry *next;
|
||||||
|
uint32 state;
|
||||||
|
ConditionVariable *condvar;
|
||||||
|
};
|
||||||
|
Entry *entry_;
|
||||||
|
Thread thread_;
|
||||||
|
bool thread_active_;
|
||||||
|
};
|
||||||
|
|
||||||
|
DnsResolverThread::DnsResolverThread() {
|
||||||
|
thread_active_ = false;
|
||||||
|
entry_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsResolverThread::~DnsResolverThread() {
|
||||||
|
assert(entry_ == NULL);
|
||||||
|
thread_.StopThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DnsResolverCanceller::Cancel() {
|
||||||
|
g_dns_mutex.Acquire();
|
||||||
|
cancel_ = true;
|
||||||
|
condvar_.Wake();
|
||||||
|
g_dns_mutex.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DnsResolverThread::Resolve(const char *hostname, IpAddr *result, DnsResolverCanceller *token) {
|
||||||
|
if (token->cancel_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Entry *e = new Entry;
|
||||||
|
e->hostname = _strdup(hostname);
|
||||||
|
e->result = result;
|
||||||
|
e->next = NULL;
|
||||||
|
e->state = Entry::POSTED;
|
||||||
|
e->condvar = &token->condvar_;
|
||||||
|
result->sin.sin_family = 0;
|
||||||
|
|
||||||
|
// Push it to the queue and start thread
|
||||||
|
g_dns_mutex.Acquire();
|
||||||
|
Entry **p = &entry_;
|
||||||
|
while (*p) p = &(*p)->next;
|
||||||
|
*p = e;
|
||||||
|
if (!thread_active_)
|
||||||
|
StartThread();
|
||||||
|
// Wait for something to happen with it.
|
||||||
|
while (!token->cancel_ && e->state == Entry::POSTED)
|
||||||
|
token->condvar_.Wait(&g_dns_mutex);
|
||||||
|
if (e->state == Entry::COMPLETE) {
|
||||||
|
delete e;
|
||||||
|
} else {
|
||||||
|
e->state = Entry::CANCELLED;
|
||||||
|
}
|
||||||
|
g_dns_mutex.Release();
|
||||||
|
return result->sin.sin_family != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DnsResolverThread::StartThread() {
|
||||||
|
thread_.StopThread();
|
||||||
|
thread_active_ = true;
|
||||||
|
thread_.StartThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DnsResolverThread::ThreadMain() {
|
||||||
|
Entry *e;
|
||||||
|
struct addrinfo *ai;
|
||||||
|
g_dns_mutex.Acquire();
|
||||||
|
while ((e = entry_) != NULL) {
|
||||||
|
entry_ = e->next;
|
||||||
|
g_dns_mutex.Release();
|
||||||
|
|
||||||
|
struct addrinfo hints = {0};
|
||||||
|
hints.ai_family = PF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_flags = AI_DEFAULT;
|
||||||
|
ai = NULL;
|
||||||
|
if (getaddrinfo(e->hostname, NULL, &hints, &ai) != 0)
|
||||||
|
ai = NULL;
|
||||||
|
// he = gethostbyname(e->hostname);
|
||||||
|
g_dns_mutex.Acquire();
|
||||||
|
if (e->state == Entry::CANCELLED) {
|
||||||
|
delete e;
|
||||||
|
} else {
|
||||||
|
if (ai) {
|
||||||
|
e->result->sin.sin_family = ai->ai_family;
|
||||||
|
e->result->sin.sin_port = 0;
|
||||||
|
if (ai->ai_family == AF_INET)
|
||||||
|
memcpy(&e->result->sin.sin_addr, &((sockaddr_in*)ai->ai_addr)->sin_addr, 4);
|
||||||
|
else
|
||||||
|
memcpy(&e->result->sin6.sin6_addr, &((sockaddr_in6*)ai->ai_addr)->sin6_addr, 16);
|
||||||
|
}
|
||||||
|
/* if (he) {
|
||||||
|
e->result->sin.sin_family = AF_INET;
|
||||||
|
e->result->sin.sin_port = 0;
|
||||||
|
memcpy(&e->result->sin.sin_addr, he->h_addr_list[0], 4);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
e->state = Entry::COMPLETE;
|
||||||
|
e->condvar->Wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ai)
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
}
|
||||||
|
thread_active_ = false;
|
||||||
|
g_dns_mutex.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
static DnsResolverThread g_dnsresolver_thread;
|
||||||
|
|
||||||
|
static bool InterruptibleSleep(int delay, DnsResolverCanceller *token) {
|
||||||
|
g_dns_mutex.Acquire();
|
||||||
|
uint32 time_at_start = (uint32)OsGetMilliseconds();
|
||||||
|
while (delay > 0 && !token->cancel_) {
|
||||||
|
token->condvar_.WaitTimed(&g_dns_mutex, delay);
|
||||||
|
uint32 now = (uint32)OsGetMilliseconds();
|
||||||
|
delay -= (now - time_at_start);
|
||||||
|
time_at_start = now;
|
||||||
|
}
|
||||||
|
g_dns_mutex.Release();
|
||||||
|
return (delay <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsResolver::DnsResolver(DnsBlocker *dns_blocker) {
|
||||||
|
dns_blocker_ = dns_blocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsResolver::~DnsResolver() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DnsResolver::ClearCache() {
|
||||||
|
cache_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DnsResolver::Resolve(const char *hostname, IpAddr *result) {
|
||||||
|
int attempt = 0;
|
||||||
|
static const uint8 retry_delays[] = {1, 2, 3, 5, 10};
|
||||||
|
char buf[kSizeOfAddress];
|
||||||
|
|
||||||
|
memset(result, 0, sizeof(IpAddr));
|
||||||
|
|
||||||
|
// First check cache
|
||||||
|
for (auto it = cache_.begin(); it != cache_.end(); ++it) {
|
||||||
|
if (it->name == hostname) {
|
||||||
|
|
||||||
|
*result = it->ip;
|
||||||
|
RINFO("Resolved %s to %s%s", hostname, PrintIpAddr(*result, buf), " (cached)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
// Then disable dns blocker (otherwise the windows dns client service can't resolve)
|
||||||
|
if (dns_blocker_ && dns_blocker_->IsActive()) {
|
||||||
|
RINFO("Disabling DNS blocker to resolve %s", hostname);
|
||||||
|
dns_blocker_->RestoreDns();
|
||||||
|
}
|
||||||
|
#endif // defined(OS_WIN)
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (g_dnsresolver_thread.Resolve(hostname, result, &token_)) {
|
||||||
|
// add to cache
|
||||||
|
cache_.emplace_back(hostname, *result);
|
||||||
|
RINFO("Resolved %s to %s%s", hostname, PrintIpAddr(*result, buf), "");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (token_.is_cancelled())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RINFO("Unable to resolve %s. Trying again in %d second(s)", hostname, retry_delays[attempt]);
|
||||||
|
if (!InterruptibleSleep(retry_delays[attempt] * 1000, &token_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (attempt != ARRAY_SIZE(retry_delays) - 1)
|
||||||
|
attempt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse an IPV4 address into sin, doing NAT64 translation if applicable (on IOS)
|
||||||
|
static bool ParseIpv4WithNAT64Translation(const char *s, IpAddr *sin, int flags) {
|
||||||
|
// First verify it's actually a valid ipv4 address to prevent getaddrinfo from doing a slow resolve
|
||||||
|
if (inet_pton(AF_INET, s, &sin->sin.sin_addr) != 1)
|
||||||
|
return false;
|
||||||
|
sin->sin.sin_family = AF_INET;
|
||||||
|
|
||||||
|
#if defined(OS_IOS)
|
||||||
|
if (!(flags & kParseSockaddrDontDoNAT64)) {
|
||||||
|
struct addrinfo hints = {0};
|
||||||
|
hints.ai_family = PF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_flags = AI_DEFAULT;
|
||||||
|
struct addrinfo* ai = NULL;
|
||||||
|
// When NAT64 is enabled, I don't get an IPv4 address back.
|
||||||
|
if (getaddrinfo(s, NULL, &hints, &ai) == 0) {
|
||||||
|
// check so we have an AF_INET6 and no AF_INET
|
||||||
|
struct sockaddr_in6 *found = NULL;
|
||||||
|
for(struct addrinfo *t = ai; t; t = t->ai_next) {
|
||||||
|
if (t->ai_family == AF_INET) { found = NULL; break; }
|
||||||
|
if (t->ai_family == AF_INET6) found = (sockaddr_in6*)t->ai_addr;
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
memset(&sin->sin.sin_addr, 0, 4); // clear out ipv4 address already written
|
||||||
|
memcpy(&sin->sin6.sin6_addr, &found->sin6_addr, 16);
|
||||||
|
sin->sin.sin_family = AF_INET6;
|
||||||
|
}
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // defined(OS_IOS)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseSockaddrInWithPort(const char *si, IpAddr *sin, DnsResolver *resolver, int flags) {
|
||||||
|
size_t len = strlen(si) + 1;
|
||||||
|
char *s = (char*)alloca(len);
|
||||||
|
memcpy(s, si, len);
|
||||||
|
|
||||||
|
memset(sin, 0, sizeof(IpAddr));
|
||||||
|
if (*s == '[') {
|
||||||
|
char *end = strchr(s, ']');
|
||||||
|
if (end == NULL)
|
||||||
|
return false;
|
||||||
|
*end = 0;
|
||||||
|
if (inet_pton(AF_INET6, s + 1, &sin->sin6.sin6_addr) != 1)
|
||||||
|
return false;
|
||||||
|
char *x = strchr(end + 1, ':');
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
sin->sin6.sin6_family = AF_INET6;
|
||||||
|
sin->sin6.sin6_port = htons(atoi(x + 1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
char *x = strchr(s, ':');
|
||||||
|
if (!x) return false;
|
||||||
|
*x = 0;
|
||||||
|
|
||||||
|
if (!ParseIpv4WithNAT64Translation(s, sin, flags)) {
|
||||||
|
if (!resolver) {
|
||||||
|
return false;
|
||||||
|
} else if (!resolver->Resolve(s, sin)) {
|
||||||
|
RERROR("Unable to resolve %s", s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sin->sin.sin_port = htons(atoi(x + 1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseSockaddrInWithoutPort(char *s, IpAddr *sin, DnsResolver *resolver, int flags) {
|
||||||
|
memset(sin, 0, sizeof(IpAddr));
|
||||||
|
|
||||||
|
if (inet_pton(AF_INET6, s, &sin->sin6.sin6_addr) == 1) {
|
||||||
|
sin->sin.sin_family = AF_INET6;
|
||||||
|
return true;
|
||||||
|
} else if (ParseIpv4WithNAT64Translation(s, sin, flags)) {
|
||||||
|
return true;
|
||||||
|
} else if (!resolver) {
|
||||||
|
return false;
|
||||||
|
} else if (!resolver->Resolve(s, sin)) {
|
||||||
|
RERROR("Unable to resolve %s", s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
40
tunsafe_ipaddr.h
Normal file
40
tunsafe_ipaddr.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef TUNSAFE_IPADDR_H_
|
||||||
|
#define TUNSAFE_IPADDR_H_
|
||||||
|
|
||||||
|
#include "tunsafe_types.h"
|
||||||
|
|
||||||
|
#if !defined(OS_WIN)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
union IpAddr {
|
||||||
|
sockaddr_in sin;
|
||||||
|
sockaddr_in6 sin6;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WgCidrAddr {
|
||||||
|
uint8 addr[16];
|
||||||
|
uint8 size;
|
||||||
|
uint8 cidr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DnsResolver;
|
||||||
|
|
||||||
|
#define kSizeOfAddress 64
|
||||||
|
const char *print_ip_prefix(char buf[kSizeOfAddress], int family, const void *ip, int prefixlen);
|
||||||
|
char *PrintIpAddr(const IpAddr &addr, char buf[kSizeOfAddress]);
|
||||||
|
char *PrintWgCidrAddr(const WgCidrAddr &addr, char buf[kSizeOfAddress]);
|
||||||
|
|
||||||
|
bool ParseCidrAddr(const char *s, WgCidrAddr *out);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kParseSockaddrDontDoNAT64 = 1,
|
||||||
|
};
|
||||||
|
bool ParseSockaddrInWithPort(const char *s, IpAddr *sin, DnsResolver *resolver, int flags = 0);
|
||||||
|
bool ParseSockaddrInWithoutPort(char *s, IpAddr *sin, DnsResolver *resolver, int flags = 0);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // TUNSAFE_IPADDR_H_
|
|
@ -2,333 +2,13 @@
|
||||||
// Copyright (C) 2018 Ludvig Strigeus <info@tunsafe.com>. All Rights Reserved.
|
// Copyright (C) 2018 Ludvig Strigeus <info@tunsafe.com>. All Rights Reserved.
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "wireguard_config.h"
|
#include "wireguard_config.h"
|
||||||
#include "netapi.h"
|
|
||||||
#include "tunsafe_endian.h"
|
|
||||||
#include "wireguard.h"
|
#include "wireguard.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <stdarg.h>
|
||||||
#if defined(OS_POSIX)
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
|
||||||
#include "network_win32_dnsblock.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *print_ip_prefix(char buf[kSizeOfAddress], int family, const void *ip, int prefixlen) {
|
|
||||||
// cast to void* to work on VS2015
|
|
||||||
if (!inet_ntop(family, (void*)ip, buf, kSizeOfAddress - 8)) {
|
|
||||||
memcpy(buf, "unknown", 8);
|
|
||||||
}
|
|
||||||
if (prefixlen >= 0)
|
|
||||||
snprintf(buf + strlen(buf), 8, "/%d", prefixlen);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *PrintIpAddr(const IpAddr &addr, char buf[kSizeOfAddress]) {
|
|
||||||
if (addr.sin.sin_family == AF_INET) {
|
|
||||||
print_ip_prefix(buf, addr.sin.sin_family, &addr.sin.sin_addr, -1);
|
|
||||||
} else if (addr.sin.sin_family == AF_INET) {
|
|
||||||
print_ip_prefix(buf, addr.sin.sin_family, &addr.sin6.sin6_addr, -1);
|
|
||||||
} else {
|
|
||||||
buf[0] = 0;
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char *PrintWgCidrAddr(const WgCidrAddr &addr, char buf[kSizeOfAddress]) {
|
|
||||||
if (addr.size == 32) {
|
|
||||||
print_ip_prefix(buf, AF_INET, addr.addr, addr.cidr);
|
|
||||||
} else if (addr.size == 128) {
|
|
||||||
print_ip_prefix(buf, AF_INET6, addr.addr, addr.cidr);
|
|
||||||
} else {
|
|
||||||
buf[0] = 0;
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Addr {
|
|
||||||
byte addr[4];
|
|
||||||
uint8 cidr;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ParseCidrAddr(char *s, WgCidrAddr *out) {
|
|
||||||
char *slash = strchr(s, '/');
|
|
||||||
if (!slash)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*slash = 0;
|
|
||||||
int e = atoi(slash + 1);
|
|
||||||
if (e < 0) return false;
|
|
||||||
|
|
||||||
if (inet_pton(AF_INET, s, out->addr) == 1) {
|
|
||||||
if (e > 32) return false;
|
|
||||||
out->cidr = e;
|
|
||||||
out->size = 32;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (inet_pton(AF_INET6, s, out->addr) == 1) {
|
|
||||||
if (e > 128) return false;
|
|
||||||
out->cidr = e;
|
|
||||||
out->size = 128;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Mutex g_dns_mutex;
|
|
||||||
|
|
||||||
// This starts a background thread for running DNS resolving.
|
|
||||||
class DnsResolverThread : private Thread::Runner {
|
|
||||||
public:
|
|
||||||
DnsResolverThread();
|
|
||||||
~DnsResolverThread();
|
|
||||||
|
|
||||||
// Resolve the hostname and store the result in |result|.
|
|
||||||
// The function will block until it's resolved. If the cancellation
|
|
||||||
// token or becomes signalled, the call will fail.
|
|
||||||
bool Resolve(const char *hostname, IpAddr *result, DnsResolverCanceller *token);
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual void ThreadMain();
|
|
||||||
void StartThread();
|
|
||||||
|
|
||||||
struct Entry {
|
|
||||||
enum {
|
|
||||||
// Set when it's been posted to the job queue
|
|
||||||
POSTED = 0,
|
|
||||||
// Set when the thread has finished and original thread should delete
|
|
||||||
COMPLETE = 1,
|
|
||||||
// Set when the original thread has cancelled and worker thread should delete
|
|
||||||
CANCELLED = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
Entry() : hostname(NULL) {}
|
|
||||||
~Entry() { free(hostname); }
|
|
||||||
|
|
||||||
char *hostname;
|
|
||||||
IpAddr *result;
|
|
||||||
Entry *next;
|
|
||||||
uint32 state;
|
|
||||||
ConditionVariable *condvar;
|
|
||||||
};
|
|
||||||
Entry *entry_;
|
|
||||||
Thread thread_;
|
|
||||||
bool thread_active_;
|
|
||||||
};
|
|
||||||
|
|
||||||
DnsResolverThread::DnsResolverThread() {
|
|
||||||
thread_active_ = false;
|
|
||||||
entry_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DnsResolverThread::~DnsResolverThread() {
|
|
||||||
assert(entry_ == NULL);
|
|
||||||
thread_.StopThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DnsResolverCanceller::Cancel() {
|
|
||||||
g_dns_mutex.Acquire();
|
|
||||||
cancel_ = true;
|
|
||||||
condvar_.Wake();
|
|
||||||
g_dns_mutex.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DnsResolverThread::Resolve(const char *hostname, IpAddr *result, DnsResolverCanceller *token) {
|
|
||||||
if (token->cancel_)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Entry *e = new Entry;
|
|
||||||
e->hostname = _strdup(hostname);
|
|
||||||
e->result = result;
|
|
||||||
e->next = NULL;
|
|
||||||
e->state = Entry::POSTED;
|
|
||||||
e->condvar = &token->condvar_;
|
|
||||||
result->sin.sin_family = 0;
|
|
||||||
|
|
||||||
// Push it to the queue and start thread
|
|
||||||
g_dns_mutex.Acquire();
|
|
||||||
Entry **p = &entry_;
|
|
||||||
while (*p) p = &(*p)->next;
|
|
||||||
*p = e;
|
|
||||||
if (!thread_active_)
|
|
||||||
StartThread();
|
|
||||||
// Wait for something to happen with it.
|
|
||||||
while (!token->cancel_ && e->state == Entry::POSTED)
|
|
||||||
token->condvar_.Wait(&g_dns_mutex);
|
|
||||||
if (e->state == Entry::COMPLETE) {
|
|
||||||
delete e;
|
|
||||||
} else {
|
|
||||||
e->state = Entry::CANCELLED;
|
|
||||||
}
|
|
||||||
g_dns_mutex.Release();
|
|
||||||
return result->sin.sin_family != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DnsResolverThread::StartThread() {
|
|
||||||
thread_.StopThread();
|
|
||||||
thread_active_ = true;
|
|
||||||
thread_.StartThread(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DnsResolverThread::ThreadMain() {
|
|
||||||
Entry *e = NULL;
|
|
||||||
struct hostent *he = NULL;
|
|
||||||
for (;;) {
|
|
||||||
g_dns_mutex.Acquire();
|
|
||||||
if (e) {
|
|
||||||
if (e->state == Entry::CANCELLED) {
|
|
||||||
delete e;
|
|
||||||
} else {
|
|
||||||
if (he) {
|
|
||||||
e->result->sin.sin_family = AF_INET;
|
|
||||||
e->result->sin.sin_port = 0;
|
|
||||||
memcpy(&e->result->sin.sin_addr, he->h_addr_list[0], 4);
|
|
||||||
}
|
|
||||||
e->state = Entry::COMPLETE;
|
|
||||||
e->condvar->Wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!(e = entry_)) {
|
|
||||||
thread_active_ = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
entry_ = e->next;
|
|
||||||
g_dns_mutex.Release();
|
|
||||||
he = gethostbyname(e->hostname);
|
|
||||||
}
|
|
||||||
g_dns_mutex.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
static DnsResolverThread g_dnsresolver_thread;
|
|
||||||
|
|
||||||
bool InterruptibleSleep(int delay, DnsResolverCanceller *token) {
|
|
||||||
g_dns_mutex.Acquire();
|
|
||||||
uint32 time_at_start = (uint32)OsGetMilliseconds();
|
|
||||||
while (delay > 0 && !token->cancel_) {
|
|
||||||
token->condvar_.WaitTimed(&g_dns_mutex, delay);
|
|
||||||
uint32 now = (uint32)OsGetMilliseconds();
|
|
||||||
delay -= (now - time_at_start);
|
|
||||||
time_at_start = now;
|
|
||||||
}
|
|
||||||
g_dns_mutex.Release();
|
|
||||||
return (delay <= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
DnsResolver::DnsResolver(DnsBlocker *dns_blocker) {
|
|
||||||
dns_blocker_ = dns_blocker;
|
|
||||||
}
|
|
||||||
|
|
||||||
DnsResolver::~DnsResolver() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void DnsResolver::ClearCache() {
|
|
||||||
cache_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DnsResolver::Resolve(const char *hostname, IpAddr *result) {
|
|
||||||
int attempt = 0;
|
|
||||||
static const uint8 retry_delays[] = {1, 2, 3, 5, 10};
|
|
||||||
char buf[kSizeOfAddress];
|
|
||||||
|
|
||||||
memset(result, 0, sizeof(IpAddr));
|
|
||||||
|
|
||||||
// First check cache
|
|
||||||
for (auto it = cache_.begin(); it != cache_.end(); ++it) {
|
|
||||||
if (it->name == hostname) {
|
|
||||||
|
|
||||||
*result = it->ip;
|
|
||||||
RINFO("Resolved %s to %s%s", hostname, PrintIpAddr(*result, buf), " (cached)");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
|
||||||
// Then disable dns blocker (otherwise the windows dns client service can't resolve)
|
|
||||||
if (dns_blocker_ && dns_blocker_->IsActive()) {
|
|
||||||
RINFO("Disabling DNS blocker to resolve %s", hostname);
|
|
||||||
dns_blocker_->RestoreDns();
|
|
||||||
}
|
|
||||||
#endif // defined(OS_WIN)
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (g_dnsresolver_thread.Resolve(hostname, result, &token_)) {
|
|
||||||
// add to cache
|
|
||||||
cache_.emplace_back(hostname, *result);
|
|
||||||
RINFO("Resolved %s to %s%s", hostname, PrintIpAddr(*result, buf), "");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (token_.is_cancelled())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RINFO("Unable to resolve %s. Trying again in %d second(s)", hostname, retry_delays[attempt]);
|
|
||||||
if (!InterruptibleSleep(retry_delays[attempt] * 1000, &token_))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (attempt != ARRAY_SIZE(retry_delays) - 1)
|
|
||||||
attempt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParseSockaddrInWithPort(const char *si, IpAddr *sin, DnsResolver *resolver) {
|
|
||||||
size_t len = strlen(si) + 1;
|
|
||||||
char *s = (char*)alloca(len);
|
|
||||||
memcpy(s, si, len);
|
|
||||||
|
|
||||||
memset(sin, 0, sizeof(IpAddr));
|
|
||||||
if (*s == '[') {
|
|
||||||
char *end = strchr(s, ']');
|
|
||||||
if (end == NULL)
|
|
||||||
return false;
|
|
||||||
*end = 0;
|
|
||||||
if (inet_pton(AF_INET6, s + 1, &sin->sin6.sin6_addr) != 1)
|
|
||||||
return false;
|
|
||||||
char *x = strchr(end + 1, ':');
|
|
||||||
if (!x)
|
|
||||||
return false;
|
|
||||||
sin->sin.sin_family = AF_INET6;
|
|
||||||
sin->sin.sin_port = htons(atoi(x + 1));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
char *x = strchr(s, ':');
|
|
||||||
if (!x) return false;
|
|
||||||
*x = 0;
|
|
||||||
|
|
||||||
if (inet_pton(AF_INET, s, &sin->sin.sin_addr) == 1) {
|
|
||||||
sin->sin.sin_family = AF_INET;
|
|
||||||
} else if (!resolver) {
|
|
||||||
return false;
|
|
||||||
} else if (!resolver->Resolve(s, sin)) {
|
|
||||||
RERROR("Unable to resolve %s", s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sin->sin.sin_port = htons(atoi(x + 1));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ParseSockaddrInWithoutPort(char *s, IpAddr *sin, DnsResolver *resolver) {
|
|
||||||
if (inet_pton(AF_INET6, s, &sin->sin6.sin6_addr) == 1) {
|
|
||||||
sin->sin.sin_family = AF_INET6;
|
|
||||||
return true;
|
|
||||||
} else if (inet_pton(AF_INET, s, &sin->sin.sin_addr) == 1) {
|
|
||||||
sin->sin.sin_family = AF_INET;
|
|
||||||
return true;
|
|
||||||
} else if (!resolver->Resolve(s, sin)) {
|
|
||||||
RERROR("Unable to resolve %s", s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class WgFileParser {
|
class WgFileParser {
|
||||||
public:
|
public:
|
||||||
|
@ -449,7 +129,7 @@ bool WgFileParser::ParseFlag(const char *group, const char *key, char *value) {
|
||||||
} else if (strcmp(key, "DNS") == 0) {
|
} else if (strcmp(key, "DNS") == 0) {
|
||||||
SplitString(value, ',', &ss);
|
SplitString(value, ',', &ss);
|
||||||
for (size_t i = 0; i < ss.size(); i++) {
|
for (size_t i = 0; i < ss.size(); i++) {
|
||||||
if (!ParseSockaddrInWithoutPort(ss[i], &sin, dns_resolver_))
|
if (!ParseSockaddrInWithoutPort(ss[i], &sin, dns_resolver_, kParseSockaddrDontDoNAT64))
|
||||||
return false;
|
return false;
|
||||||
wg_->AddDnsServer(sin);
|
wg_->AddDnsServer(sin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,44 +3,10 @@
|
||||||
#ifndef TINYVPN_TINYVPN_H_
|
#ifndef TINYVPN_TINYVPN_H_
|
||||||
#define TINYVPN_TINYVPN_H_
|
#define TINYVPN_TINYVPN_H_
|
||||||
|
|
||||||
#include "netapi.h"
|
#include <string>
|
||||||
#include "tunsafe_threading.h"
|
|
||||||
|
|
||||||
|
class DnsResolver;
|
||||||
class WireguardProcessor;
|
class WireguardProcessor;
|
||||||
class DnsBlocker;
|
|
||||||
|
|
||||||
class DnsResolverCanceller {
|
|
||||||
public:
|
|
||||||
DnsResolverCanceller() : cancel_(false) {}
|
|
||||||
void Cancel();
|
|
||||||
void Reset() { cancel_ = false; }
|
|
||||||
bool is_cancelled() { return cancel_; }
|
|
||||||
public:
|
|
||||||
bool cancel_;
|
|
||||||
ConditionVariable condvar_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DnsResolver {
|
|
||||||
public:
|
|
||||||
explicit DnsResolver(DnsBlocker *dns_blocker);
|
|
||||||
~DnsResolver();
|
|
||||||
|
|
||||||
bool Resolve(const char *hostname, IpAddr *result);
|
|
||||||
void ClearCache();
|
|
||||||
|
|
||||||
void Cancel() { token_.Cancel(); }
|
|
||||||
void ResetCancel() { token_.Reset(); }
|
|
||||||
private:
|
|
||||||
struct Entry {
|
|
||||||
std::string name;
|
|
||||||
IpAddr ip;
|
|
||||||
Entry(const std::string &name, const IpAddr &ip) : name(name), ip(ip) {}
|
|
||||||
};
|
|
||||||
std::vector<Entry> cache_;
|
|
||||||
DnsBlocker *dns_blocker_;
|
|
||||||
DnsResolverCanceller token_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class WgConfig {
|
class WgConfig {
|
||||||
public:
|
public:
|
||||||
|
@ -52,12 +18,5 @@ private:
|
||||||
bool ParseWireGuardConfigString(WireguardProcessor *wg, char *buf, size_t buf_size, DnsResolver *dns_resolver);
|
bool ParseWireGuardConfigString(WireguardProcessor *wg, char *buf, size_t buf_size, DnsResolver *dns_resolver);
|
||||||
bool ParseWireGuardConfigFile(WireguardProcessor *wg, const char *filename, DnsResolver *dns_resolver);
|
bool ParseWireGuardConfigFile(WireguardProcessor *wg, const char *filename, DnsResolver *dns_resolver);
|
||||||
|
|
||||||
#define kSizeOfAddress 64
|
|
||||||
const char *print_ip_prefix(char buf[kSizeOfAddress], int family, const void *ip, int prefixlen);
|
|
||||||
char *PrintIpAddr(const IpAddr &addr, char buf[kSizeOfAddress]);
|
|
||||||
char *PrintWgCidrAddr(const WgCidrAddr &addr, char buf[kSizeOfAddress]);
|
|
||||||
|
|
||||||
bool ParseCidrAddr(char *s, WgCidrAddr *out);
|
|
||||||
bool ParseSockaddrInWithPort(const char *s, IpAddr *sin, DnsResolver *resolver);
|
|
||||||
|
|
||||||
#endif // TINYVPN_TINYVPN_H_
|
#endif // TINYVPN_TINYVPN_H_
|
||||||
|
|
Loading…
Reference in a new issue