1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-06 17:50:40 +03:00
SoftEtherVPN/src/Mayaqua/Network.c
2020-10-31 20:11:11 +01:00

20862 lines
400 KiB
C

// SoftEther VPN Source Code - Developer Edition Master Branch
// Mayaqua Kernel
// Network.c
// Network communication module
#include <GlobalConst.h>
#define ENCRYPT_C
#define NETWORK_C
#define __WINCRYPT_H__
#ifdef WIN32
// Include windows.h for Socket API
#define _WIN32_WINNT 0x0502
#define WINVER 0x0502
#include <Ws2tcpip.h>
#include <Wspiapi.h>
#include <winsock2.h>
#include <windows.h>
#include <Iphlpapi.h>
#include <ws2ipdef.h>
#include <netioapi.h>
#include <Icmpapi.h>
#endif // WIN32
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <stdarg.h>
#include <time.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/engine.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/pkcs7.h>
#include <openssl/pkcs12.h>
#include <openssl/rc4.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <Mayaqua/Mayaqua.h>
#ifdef UNIX_MACOS
#include <sys/event.h>
#endif // UNIX_MACOS
#ifdef OS_WIN32
NETWORK_WIN32_FUNCTIONS *w32net;
struct ROUTE_CHANGE_DATA
{
OVERLAPPED Overlapped;
HANDLE Handle;
UINT NumCalled;
};
#endif // OS_WIN32
// Whether the blocking occurs in SSL
#if defined(UNIX_BSD) || defined(UNIX_MACOS)
#define FIX_SSL_BLOCKING
#endif
// IPV6_V6ONLY constant
#ifdef UNIX_LINUX
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 26
#endif // IPV6_V6ONLY
#endif // UNIX_LINUX
#ifdef UNIX_SOLARIS
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 0x27
#endif // IPV6_V6ONLY
#endif // UNIX_SOLARIS
// HTTP constant
static char http_detect_server_startwith[] = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<HTML><HEAD>\r\n<TITLE>403 Forbidden</TITLE>\r\n</HEAD><BODY>\r\n<H1>Forbidden</H1>\r\nYou don't have permission to access ";
static char http_detect_server_tag_future[] = "9C37197CA7C2428388C2E6E59B829B30";
// DNS cache list
static LIST *DnsCache;
// Lock related
static LOCK *machine_name_lock = NULL;
static LOCK *disconnect_function_lock = NULL;
static LOCK *aho = NULL;
static LOCK *socket_library_lock = NULL;
extern LOCK *openssl_lock;
static LOCK *ssl_accept_lock = NULL;
static LOCK *ssl_connect_lock = NULL;
static COUNTER *num_tcp_connections = NULL;
static LOCK *dns_lock = NULL;
static LOCK *unix_dns_server_addr_lock = NULL;
static IP unix_dns_server;
static LIST *HostCacheList = NULL;
static LIST *WaitThreadList = NULL;
static bool disable_cache = false;
static bool NetworkReleaseMode = false; // Network release mode
static UCHAR machine_ip_process_hash[SHA1_SIZE];
static LOCK *machine_ip_process_hash_lock = NULL;
static LOCK *current_global_ip_lock = NULL;
static LOCK *current_fqdn_lock = NULL;
static bool current_global_ip_set = false;
static IP current_glocal_ipv4 = {0};
static IP current_glocal_ipv6 = {0};
static char current_fqdn[MAX_SIZE];
static bool g_no_rudp_server = false;
static bool g_no_rudp_register = false;
static bool g_natt_low_priority = false;
static LOCK *host_ip_address_list_cache_lock = NULL;
static UINT64 host_ip_address_list_cache_last = 0;
static LIST *host_ip_address_cache = NULL;
static bool disable_gethostname_by_accept = false;
static COUNTER *getip_thread_counter = NULL;
static UINT max_getip_thread = 0;
static LIST *ip_clients = NULL;
static LIST *local_mac_list = NULL;
static LOCK *local_mac_list_lock = NULL;
static UINT rand_port_numbers[256] = {0};
static bool g_use_privateip_file = false;
static bool g_source_ip_validation_force_disable = false;
static DH_CTX *dh_param = NULL;
typedef struct PRIVATE_IP_SUBNET
{
UINT Ip, Mask, Ip2;
} PRIVATE_IP_SUBNET;
static LIST *g_private_ip_list = NULL;
static LIST *g_dyn_value_list = NULL;
//#define RUDP_DETAIL_LOG
// Get a value from a dynamic value list (Returns a default value if the value is not found)
UINT64 GetDynValueOrDefault(char *name, UINT64 default_value, UINT64 min_value, UINT64 max_value)
{
UINT64 ret = GetDynValue(name);
if (ret == 0)
{
return default_value;
}
if (ret < min_value)
{
ret = min_value;
}
if (ret > max_value)
{
ret = max_value;
}
return ret;
}
// Get a value from a dynamic value list (Returns a default value if the value is not found)
// The value is limited to 1/5 to 50 times of the default value for safety
UINT64 GetDynValueOrDefaultSafe(char *name, UINT64 default_value)
{
return GetDynValueOrDefault(name, default_value, default_value / (UINT64)5, default_value * (UINT64)50);
}
// Get a value from a dynamic value list
UINT64 GetDynValue(char *name)
{
UINT64 ret = 0;
// Validate arguments
if (name == NULL)
{
return 0;
}
if (g_dyn_value_list == NULL)
{
return 0;
}
LockList(g_dyn_value_list);
{
UINT i;
for (i = 0; i < LIST_NUM(g_dyn_value_list); i++)
{
DYN_VALUE *vv = LIST_DATA(g_dyn_value_list, i);
if (StrCmpi(vv->Name, name) == 0)
{
ret = vv->Value;
break;
}
}
}
UnlockList(g_dyn_value_list);
return ret;
}
// Set the value to the dynamic value list
void SetDynListValue(char *name, UINT64 value)
{
// Validate arguments
if (name == NULL)
{
return;
}
if (g_dyn_value_list == NULL)
{
return;
}
LockList(g_dyn_value_list);
{
UINT i;
DYN_VALUE *v = NULL;
for (i = 0; i < LIST_NUM(g_dyn_value_list); i++)
{
DYN_VALUE *vv = LIST_DATA(g_dyn_value_list, i);
if (StrCmpi(vv->Name, name) == 0)
{
v = vv;
break;
}
}
if (v == NULL)
{
v = ZeroMalloc(sizeof(DYN_VALUE));
StrCpy(v->Name, sizeof(v->Name), name);
Add(g_dyn_value_list, v);
}
v->Value = value;
}
UnlockList(g_dyn_value_list);
}
// Apply by extracting dynamic value list from the specified PACK
void ExtractAndApplyDynList(PACK *p)
{
BUF *b;
// Validate arguments
if (p == NULL)
{
return;
}
b = PackGetBuf(p, "DynList");
if (b == NULL)
{
return;
}
AddDynList(b);
FreeBuf(b);
}
// Insert the data to the dynamic value list
void AddDynList(BUF *b)
{
PACK *p;
TOKEN_LIST *t;
// Validate arguments
if (b == NULL)
{
return;
}
SeekBufToBegin(b);
p = BufToPack(b);
if (p == NULL)
{
return;
}
t = GetPackElementNames(p);
if (t != NULL)
{
UINT i;
for (i = 0; i < t->NumTokens; i++)
{
char *name = t->Token[i];
UINT64 v = PackGetInt64(p, name);
SetDynListValue(name, v);
}
FreeToken(t);
}
FreePack(p);
}
// Initialization of the dynamic value list
void InitDynList()
{
g_dyn_value_list = NewList(NULL);
}
// Solution of dynamic value list
void FreeDynList()
{
UINT i;
if (g_dyn_value_list == NULL)
{
return;
}
for (i = 0; i < LIST_NUM(g_dyn_value_list); i++)
{
DYN_VALUE *d = LIST_DATA(g_dyn_value_list, i);
Free(d);
}
ReleaseList(g_dyn_value_list);
g_dyn_value_list = NULL;
}
// Disable NAT-T function globally
void DisableRDUPServerGlobally()
{
g_no_rudp_server = true;
}
// Get the current time zone
int GetCurrentTimezone()
{
int ret = 0;
#ifdef OS_WIN32
ret = GetCurrentTimezoneWin32();
#else // OS_WIN32
{
#if defined(UNIX_MACOS) || defined(UNIX_BSD)
struct timeval tv;
struct timezone tz;
Zero(&tv, sizeof(tv));
Zero(&tz, sizeof(tz));
gettimeofday(&tv, &tz);
ret = tz.tz_minuteswest;
#else // defined(UNIX_MACOS) || defined(UNIX_BSD)
tzset();
ret = timezone / 60;
#endif // defined(UNIX_MACOS) || defined(UNIX_BSD)
}
#endif // OS_WIN32
return ret;
}
// Flag of whether to use the DNS proxy
bool IsUseDnsProxy()
{
return false;
}
// Flag of whether to use an alternate host name
bool IsUseAlternativeHostname()
{
return false;
}
#ifdef OS_WIN32
// Get the current time zone (Win32)
int GetCurrentTimezoneWin32()
{
TIME_ZONE_INFORMATION info;
Zero(&info, sizeof(info));
if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_INVALID)
{
return 0;
}
return info.Bias;
}
#endif // OS_WIN32
// Set the current FQDN of the DDNS
void SetCurrentDDnsFqdn(char *name)
{
// Validate arguments
if (name == NULL)
{
return;
}
Lock(current_fqdn_lock);
{
StrCpy(current_fqdn, sizeof(current_fqdn), name);
}
Unlock(current_fqdn_lock);
}
// Get the current DDNS FQDN hash
UINT GetCurrentDDnsFqdnHash()
{
UINT ret;
UCHAR hash[SHA1_SIZE];
char name[MAX_SIZE];
ClearStr(name, sizeof(name));
GetCurrentDDnsFqdn(name, sizeof(name));
Trim(name);
StrUpper(name);
Sha1(hash, name, StrLen(name));
Copy(&ret, hash, sizeof(UINT));
return ret;
}
// Get the current DDNS FQDN
void GetCurrentDDnsFqdn(char *name, UINT size)
{
ClearStr(name, size);
// Validate arguments
if (name == NULL || size == 0)
{
return;
}
Lock(current_fqdn_lock);
{
StrCpy(name, size, current_fqdn);
}
Unlock(current_fqdn_lock);
Trim(name);
}
// Check whether the specified MAC address exists on the local host (high speed)
bool IsMacAddressLocalFast(void *addr)
{
bool ret = false;
// Validate arguments
if (addr == NULL)
{
return false;
}
Lock(local_mac_list_lock);
{
if (local_mac_list == NULL)
{
// First enumeration
RefreshLocalMacAddressList();
}
ret = IsMacAddressLocalInner(local_mac_list, addr);
}
Unlock(local_mac_list_lock);
return ret;
}
// Update the local MAC address list
void RefreshLocalMacAddressList()
{
Lock(local_mac_list_lock);
{
if (local_mac_list != NULL)
{
FreeNicList(local_mac_list);
}
local_mac_list = GetNicList();
}
Unlock(local_mac_list_lock);
}
// Check whether the specified MAC address exists on the local host
bool IsMacAddressLocalInner(LIST *o, void *addr)
{
bool ret = false;
UINT i;
// Validate arguments
if (o == NULL || addr == NULL)
{
return false;
}
for (i = 0; i < LIST_NUM(o); i++)
{
NIC_ENTRY *e = LIST_DATA(o, i);
if (Cmp(e->MacAddress, addr, 6) == 0)
{
ret = true;
break;
}
}
return ret;
}
// Get a list of the NICs on the computer
LIST *GetNicList()
{
LIST *o = NULL;
#ifdef OS_WIN32
o = Win32GetNicList();
if (o != NULL)
{
return o;
}
#endif // OS_WIN32
return NewListFast(NULL);
}
#ifdef OS_WIN32
LIST *Win32GetNicList()
{
UINT i;
LIST *o = NewListFast(NULL);
MS_ADAPTER_LIST *al = MsCreateAdapterList();
if (al == NULL)
{
return NULL;
}
for (i = 0; i < al->Num; i++)
{
MS_ADAPTER *a = al->Adapters[i];
if (a->Type == 6 && a->AddressSize == 6)
{
NIC_ENTRY *e = ZeroMalloc(sizeof(NIC_ENTRY));
StrCpy(e->IfName, sizeof(e->IfName), a->Title);
Copy(e->MacAddress, a->Address, 6);
Add(o, e);
}
}
MsFreeAdapterList(al);
return o;
}
#endif // OS_WIN32
// Release the NIC list
void FreeNicList(LIST *o)
{
UINT i;
// Validate arguments
if (o == NULL)
{
return;
}
for (i = 0; i < LIST_NUM(o); i++)
{
NIC_ENTRY *e = LIST_DATA(o, i);
Free(e);
}
ReleaseList(o);
}
// If the computer is connected to the FLET'S line currently, detect the type of the line (obsolete)
UINT DetectFletsType()
{
UINT ret = 0;
//LIST *o = GetHostIPAddressList();
// UINT i;
/*
for (i = 0;i < LIST_NUM(o);i++)
{
IP *ip = LIST_DATA(o, i);
if (IsIP6(ip))
{
char ip_str[MAX_SIZE];
IPToStr(ip_str, sizeof(ip_str), ip);
if (IsInSameNetwork6ByStr(ip_str, "2001:c90::", "/32"))
{
// NTT East B-FLETs
ret |= FLETS_DETECT_TYPE_EAST_BFLETS_PRIVATE;
}
if (IsInSameNetwork6ByStr(ip_str, "2408:200::", "/23"))
{
// Wrapping in network of NTT East NGN
ret |= FLETS_DETECT_TYPE_EAST_NGN_PRIVATE;
}
if (IsInSameNetwork6ByStr(ip_str, "2001:a200::", "/23"))
{
// Wrapping in network of NTT West NGN
ret |= FLETS_DETECT_TYPE_WEST_NGN_PRIVATE;
}
}
}
FreeHostIPAddressList(o);
*/
return ret;
}
// Query for the IP address using the DNS proxy for the B FLETs
bool GetIPViaDnsProxyForJapanFlets(IP *ip_ret, char *hostname, bool ipv6, UINT timeout, bool *cancel, char *dns_proxy_hostname)
{
SOCK *s;
char connect_hostname[MAX_SIZE];
char connect_hostname2[MAX_SIZE];
IP dns_proxy_ip;
bool ret = false;
bool dummy_flag = false;
char request_str[512];
// Validate arguments
if (ip_ret == NULL || hostname == NULL)
{
return false;
}
if (timeout == 0)
{
timeout = BFLETS_DNS_PROXY_TIMEOUT_FOR_QUERY;
}
if (cancel == NULL)
{
cancel = &dummy_flag;
}
// Get the IP address of the DNS proxy server
if (IsEmptyStr(dns_proxy_hostname))
{
// B FLETs
if (GetDnsProxyIPAddressForJapanBFlets(&dns_proxy_ip, BFLETS_DNS_PROXY_TIMEOUT_FOR_GET_F, cancel) == false)
{
return false;
}
}
else
{
// FLET'S NEXT
if (GetIP4Ex6Ex2(&dns_proxy_ip, dns_proxy_hostname, FLETS_NGN_DNS_QUERY_TIMEOUT, true, cancel, true) == false)
{
return false;
}
}
if (*cancel)
{
return false;
}
IPToStr(connect_hostname, sizeof(connect_hostname), &dns_proxy_ip);
/*{
StrCpy(connect_hostname, sizeof(connect_hostname), "2409:250:62c0:100:6a05:caff:fe09:5158");
}*/
StrCpy(connect_hostname2, sizeof(connect_hostname2), connect_hostname);
if (IsIP6(&dns_proxy_ip))
{
Format(connect_hostname2, sizeof(connect_hostname2), "[%s]", connect_hostname);
}
s = ConnectEx3(connect_hostname, BFLETS_DNS_PROXY_PORT, timeout, cancel, NULL, NULL, false, false);
if (s == NULL)
{
return false;
}
if (*cancel)
{
Disconnect(s);
ReleaseSock(s);
return false;
}
SetTimeout(s, timeout);
// Start the SSL
if (StartSSLEx(s, NULL, NULL, 0, NULL) && (*cancel == false))
{
UCHAR hash[SHA1_SIZE];
BUF *hash2 = StrToBin(BFLETS_DNS_PROXY_CERT_HASH);
Zero(hash, sizeof(hash));
GetXDigest(s->RemoteX, hash, true);
if (Cmp(hash, hash2->Buf, SHA1_SIZE) == 0)
{
// Send the HTTP Request
Format(request_str, sizeof(request_str),
"GET " BFLETS_DNS_PROXY_PATH "?q=%s&ipv6=%u\r\n"
"\r\n",
hostname, ipv6, connect_hostname2);
if (SendAll(s, request_str, StrLen(request_str), true))
{
if (*cancel == false)
{
BUF *recv_buf = NewBuf();
UINT port_ret;
while (true)
{
UCHAR tmp[MAX_SIZE];
UINT r;
r = Recv(s, tmp, sizeof(tmp), true);
if (r == 0 || (recv_buf->Size > 65536))
{
break;
}
else
{
WriteBuf(recv_buf, tmp, r);
}
}
ret = RUDPParseIPAndPortStr(recv_buf->Buf, recv_buf->Size, ip_ret, &port_ret);
FreeBuf(recv_buf);
}
}
}
FreeBuf(hash2);
}
Disconnect(s);
ReleaseSock(s);
if (ret)
{
NewDnsCache(hostname, ip_ret);
}
return ret;
}
// Get the IP address of the available DNS proxy in B-FLET'S service that is provided by NTT East of Japan
bool GetDnsProxyIPAddressForJapanBFlets(IP *ip_ret, UINT timeout, bool *cancel)
{
BUF *b;
LIST *o;
bool ret = false;
// Validate arguments
if (ip_ret == NULL)
{
return false;
}
if (timeout == 0)
{
timeout = BFLETS_DNS_PROXY_TIMEOUT_FOR_GET_F;
}
b = QueryFileByUdpForJapanBFlets(timeout, cancel);
if (b == NULL)
{
return false;
}
o = ReadIni(b);
if (o != NULL)
{
INI_ENTRY *e = GetIniEntry(o, "DDnsServerForBFlets");
if (e != NULL)
{
char *s = e->Value;
if (IsEmptyStr(s) == false)
{
IP ip;
if (StrToIP(&ip, s))
{
if (IsZeroIp(&ip) == false)
{
Copy(ip_ret, &ip, sizeof(IP));
ret = true;
}
}
}
}
}
FreeIni(o);
FreeBuf(b);
return ret;
}
// Get a valid F.txt file in B-FLET'S service that is provided by NTT East of Japan
BUF *QueryFileByUdpForJapanBFlets(UINT timeout, bool *cancel)
{
bool dummy_flag = false;
BUF *txt_buf = NULL;
BUF *ret = NULL;
LIST *ip_list = NULL;
UINT i;
// Validate arguments
if (cancel == NULL)
{
cancel = &dummy_flag;
}
if (timeout == 0)
{
timeout = BFLETS_DNS_PROXY_TIMEOUT_FOR_GET_F;
}
txt_buf = ReadDump(UDP_FILE_QUERY_BFLETS_TXT_FILENAME);
if (txt_buf == NULL)
{
return NULL;
}
ip_list = NewListFast(NULL);
while (true)
{
char *line = CfgReadNextLine(txt_buf);
if (line == NULL)
{
break;
}
Trim(line);
if (IsEmptyStr(line) == false && StartWith(line, "#") == false)
{
IP ip;
if (StrToIP6(&ip, line))
{
if (IsZeroIp(&ip) == false)
{
if (IsIPv6LocalNetworkAddress(&ip) == false)
{
Add(ip_list, Clone(&ip, sizeof(IP)));
}
}
}
}
Free(line);
}
FreeBuf(txt_buf);
ret = QueryFileByIPv6Udp(ip_list, timeout, cancel);
for (i = 0; i < LIST_NUM(ip_list); i++)
{
IP *ip = LIST_DATA(ip_list, i);
Free(ip);
}
ReleaseList(ip_list);
return ret;
}
// Request a file by UDP (send the requests to the multiple IP addresses at the same time)
BUF *QueryFileByIPv6Udp(LIST *ip_list, UINT timeout, bool *cancel)
{
bool dummy_flag = false;
UINT64 start_tick, giveup_tick;
UINT64 next_send_tick;
SOCK *s;
INTERRUPT_MANAGER *interrupt;
BUF *buf = NULL;
SOCK_EVENT *se;
UCHAR *tmp_buf;
UINT tmp_buf_size = 65535;
// Validate arguments
if (cancel == NULL)
{
cancel = &dummy_flag;
}
if (ip_list == NULL)
{
return NULL;
}
s = NewUDP6(0, NULL);
if (s == NULL)
{
return NULL;
}
tmp_buf = Malloc(tmp_buf_size);
start_tick = Tick64();
giveup_tick = start_tick + (UINT64)timeout;
next_send_tick = 0;
interrupt = NewInterruptManager();
AddInterrupt(interrupt, giveup_tick);
se = NewSockEvent();
JoinSockToSockEvent(s, se);
while (true)
{
UINT64 now = Tick64();
if (now >= giveup_tick)
{
// Time-out
break;
}
if (*cancel)
{
// User canceled
break;
}
// Receive
while (true)
{
IP src_ip;
UINT src_port;
UINT r;
r = RecvFrom(s, &src_ip, &src_port, tmp_buf, tmp_buf_size);
if (r == SOCK_LATER || r == 0)
{
break;
}
if (src_port == UDP_FILE_QUERY_DST_PORT)
{
if (r >= 40)
{
if (Cmp(tmp_buf, UDP_FILE_QUERY_MAGIC_NUMBER, StrLen(UDP_FILE_QUERY_MAGIC_NUMBER)) == 0)
{
// Successful reception
buf = NewBuf();
WriteBuf(buf, tmp_buf, r);
SeekBuf(buf, 0, 0);
break;
}
}
}
}
if (buf != NULL)
{
// Successful reception
break;
}
if (next_send_tick == 0 || (now >= next_send_tick))
{
// Transmission
UINT i;
for (i = 0; i < LIST_NUM(ip_list); i++)
{
IP *ip = LIST_DATA(ip_list, i);
UCHAR c = 'F';
SendTo(s, ip, UDP_FILE_QUERY_DST_PORT, &c, 1);
}
next_send_tick = now + (UINT64)UDP_FILE_QUERY_RETRY_INTERVAL;
AddInterrupt(interrupt, next_send_tick);
}
WaitSockEvent(se, GetNextIntervalForInterrupt(interrupt));
}
FreeInterruptManager(interrupt);
Disconnect(s);
ReleaseSock(s);
ReleaseSockEvent(se);
Free(tmp_buf);
return buf;
}
// Parse the user name of the NT
void ParseNtUsername(char *src_username, char *dst_username, UINT dst_username_size, char *dst_domain, UINT dst_domain_size, bool do_not_parse_atmark)
{
char tmp_username[MAX_SIZE];
char tmp_domain[MAX_SIZE];
TOKEN_LIST *t;
if (src_username != dst_username)
{
ClearStr(dst_username, dst_username_size);
}
ClearStr(dst_domain, dst_domain_size);
// Validate arguments
if (src_username == NULL || dst_username == NULL || dst_domain == NULL)
{
return;
}
StrCpy(tmp_username, sizeof(tmp_username), src_username);
ClearStr(tmp_domain, sizeof(tmp_domain));
// Analysis of username@domain.name format
if (do_not_parse_atmark == false)
{
t = ParseTokenWithNullStr(tmp_username, "@");
if (t->NumTokens >= 1)
{
StrCpy(tmp_username, sizeof(tmp_username), t->Token[0]);
}
if (t->NumTokens >= 2)
{
StrCpy(tmp_domain, sizeof(tmp_domain), t->Token[1]);
}
FreeToken(t);
}
// If the username part is in "domain\username" format, split it
t = ParseTokenWithNullStr(tmp_username, "\\");
if (t->NumTokens >= 2)
{
if (IsEmptyStr(tmp_domain))
{
StrCpy(tmp_domain, sizeof(tmp_domain), t->Token[0]);
}
StrCpy(tmp_username, sizeof(tmp_username), t->Token[1]);
}
FreeToken(t);
StrCpy(dst_username, dst_username_size, tmp_username);
StrCpy(dst_domain, dst_domain_size, tmp_domain);
}
// The calculation of the optimum MSS value for use in TCP/IP packet in the payload of bulk transfer in R-UDP session
UINT RUDPCalcBestMssForBulk(RUDP_STACK *r, RUDP_SESSION *se)
{
UINT ret;
// Validate arguments
if (r == NULL || se == NULL)
{
return 0;
}
ret = MTU_FOR_PPPOE;
// IPv4
if (IsIP6(&se->YourIp) == false)
{
ret -= 20;
}
else
{
ret -= 40;
}
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
// ICMP
ret -= 8;
ret -= SHA1_SIZE;
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
// UDP
ret -= 8;
// DNS
ret -= 42;
}
// IV
ret -= SHA1_SIZE;
// Sign
ret -= SHA1_SIZE;
// SEQ_NO
ret -= sizeof(UINT64);
// Padding Max
ret -= 31;
// Ethernet header (target packets of communication)
ret -= 14;
// IPv4 Header (target packet of communication)
ret -= 20;
// TCP header (target packet of communication)
ret -= 20;
// I don't know well, but subtract 24 bytes
ret -= 24;
return ret;
}
// Processing of the reply packet from the NAT-T server
void RUDPProcess_NatT_Recv(RUDP_STACK *r, UDPPACKET *udp)
{
BUF *b;
PACK *p;
// Validate arguments
if (r == NULL || udp == NULL)
{
return;
}
if (udp->Size >= 8)
{
char tmp[128];
Zero(tmp, sizeof(tmp));
Copy(tmp, udp->Data, MIN(udp->Size, sizeof(tmp) - 1));
if (StartWith(tmp, "IP="))
{
IP my_ip;
UINT my_port;
// There was a response to the packet to determine the NAT state
if (IsEmptyStr(r->NatT_Registered_IPAndPort) == false)
{
if (StrCmpi(r->NatT_Registered_IPAndPort, tmp) != 0)
{
// Redo getting the token and registration because the NAT state is changed
ClearStr(r->NatT_Registered_IPAndPort, sizeof(r->NatT_Registered_IPAndPort));
r->NatT_GetTokenNextTick = 0;
r->NatT_GetTokenFailNum = 0;
r->NatT_Token_Ok = false;
Zero(r->NatT_Token, sizeof(r->NatT_Token));
r->NatT_RegisterNextTick = 0;
r->NatT_RegisterFailNum = 0;
r->NatT_Register_Ok = false;
}
}
if (RUDPParseIPAndPortStr(udp->Data, udp->Size, &my_ip, &my_port))
{
if (r->NatTGlobalUdpPort != NULL)
{
*r->NatTGlobalUdpPort = my_port;
}
}
return;
}
}
// Interpret the UDP packet
b = NewBuf();
WriteBuf(b, udp->Data, udp->Size);
SeekBuf(b, 0, 0);
p = BufToPack(b);
if (p != NULL)
{
bool is_ok = PackGetBool(p, "ok");
UINT64 tran_id = PackGetInt64(p, "tran_id");
ExtractAndApplyDynList(p);
if (r->ServerMode)
{
if (PackCmpStr(p, "opcode", "get_token"))
{
// Get the Token
if (is_ok && (tran_id == r->NatT_TranId))
{
char tmp[MAX_SIZE];
if (PackGetStr(p, "token", tmp, sizeof(tmp)) && IsEmptyStr(tmp) == false)
{
char myip[MAX_SIZE];
// Acquisition success
StrCpy(r->NatT_Token, sizeof(r->NatT_Token), tmp);
r->NatT_Token_Ok = true;
r->NatT_GetTokenNextTick = r->Now + (UINT64)GenRandInterval(UDP_NAT_T_GET_TOKEN_INTERVAL_2_MIN, UDP_NAT_T_GET_TOKEN_INTERVAL_2_MAX);
r->NatT_GetTokenFailNum = 0;
// Since success to obtain the self global IPv4 address,
// re-obtain the destination NAT-T host from this IPv4 address
if (PackGetStr(p, "your_ip", myip, sizeof(myip)))
{
IP ip;
char new_hostname[MAX_SIZE];
StrToIP(&ip, myip);
SetCurrentGlobalIP(&ip, false);
RUDPGetRegisterHostNameByIP(new_hostname,
sizeof(new_hostname), &ip);
Lock(r->Lock);
{
if (StrCmpi(r->CurrentRegisterHostname, new_hostname) != 0)
{
r->NumChangedHostname++;
if (r->NumChangedHostname <= RUDP_NATT_MAX_CONT_CHANGE_HOSTNAME)
{
if (r->NumChangedHostnameValueResetTick == 0)
{
r->NumChangedHostnameValueResetTick = r->Now + (UINT64)RUDP_NATT_CONT_CHANGE_HOSTNAME_RESET_INTERVAL;
}
// Change the host name
Debug("CurrentRegisterHostname Changed: New=%s\n", new_hostname);
StrCpy(r->CurrentRegisterHostname, sizeof(r->CurrentRegisterHostname), new_hostname);
Zero(&r->NatT_IP, sizeof(r->NatT_IP));
//Zero(&r->NatT_IP_Safe, sizeof(r->NatT_IP_Safe));
Set(r->HaltEvent);
}
else
{
if (r->NumChangedHostnameValueResetTick == 0)
{
r->NumChangedHostnameValueResetTick = r->Now + (UINT64)RUDP_NATT_CONT_CHANGE_HOSTNAME_RESET_INTERVAL;
}
if (r->Now >= r->NumChangedHostnameValueResetTick)
{
r->NumChangedHostname = 0;
r->NumChangedHostnameValueResetTick = 0;
}
}
}
else
{
r->NumChangedHostname = 0;
r->NumChangedHostnameValueResetTick = 0;
}
}
Unlock(r->Lock);
}
AddInterrupt(r->Interrupt, r->NatT_GetTokenNextTick);
}
}
}
else if (PackCmpStr(p, "opcode", "nat_t_register"))
{
// NAT-T server registration result
if (is_ok && (tran_id == r->NatT_TranId))
{
UINT my_global_port;
// Successful registration
r->NatT_Register_Ok = true;
r->NatT_RegisterNextTick = r->Now + (UINT64)GenRandInterval(UDP_NAT_T_REGISTER_INTERVAL_MIN, UDP_NAT_T_REGISTER_INTERVAL_MAX);
r->NatT_RegisterFailNum = 0;
Debug("NAT-T Registered.\n");
// Save the IP address and port number at the time of registration
PackGetStr(p, "your_ip_and_port", r->NatT_Registered_IPAndPort, sizeof(r->NatT_Registered_IPAndPort));
if (g_source_ip_validation_force_disable == false)
{
// Enable the source IP address validation mechanism
r->NatT_EnableSourceIpValidation = PackGetBool(p, "enable_source_ip_validation");
}
else
{
// Force disable the source IP address validation mechanism
r->NatT_EnableSourceIpValidation = false;
}
// Global port of itself
my_global_port = PackGetInt(p, "your_port");
if (my_global_port != 0)
{
if (r->NatTGlobalUdpPort != NULL)
{
*r->NatTGlobalUdpPort = my_global_port;
}
}
AddInterrupt(r->Interrupt, r->NatT_RegisterNextTick);
}
}
else if (PackCmpStr(p, "opcode", "nat_t_connect_relay"))
{
// Connection request from the client via the NAT-T server
if (is_ok && (PackGetInt64(p, "session_key") == r->NatT_SessionKey))
{
char client_ip_str[MAX_SIZE];
UINT client_port;
IP client_ip;
PackGetStr(p, "client_ip", client_ip_str, sizeof(client_ip_str));
client_port = PackGetInt(p, "client_port");
StrToIP(&client_ip, client_ip_str);
if (IsZeroIp(&client_ip) == false && client_port != 0)
{
UCHAR *rand_data;
UINT rand_size;
if (r->NatT_EnableSourceIpValidation)
{
RUDPAddIpToValidateList(r, &client_ip);
}
rand_size = Rand32() % 19;
rand_data = Malloc(rand_size);
Rand(rand_data, rand_size);
RUDPSendPacket(r, &client_ip, client_port, rand_data, rand_size, 0);
Free(rand_data);
}
}
}
}
FreePack(p);
}
FreeBuf(b);
}
// Process such as packet transmission for NAT-T server
void RUDPDo_NatT_Interrupt(RUDP_STACK *r)
{
// Validate arguments
if (r == NULL)
{
return;
}
if (r->ServerMode)
{
if (g_no_rudp_register == false && IsZeroIp(&r->NatT_IP_Safe) == false)
{
if (r->NatT_GetTokenNextTick == 0 || r->Now >= r->NatT_GetTokenNextTick)
{
// Try to get a token from the NAT-T server periodically
PACK *p = NewPack();
BUF *b;
PackAddStr(p, "opcode", "get_token");
PackAddInt64(p, "tran_id", r->NatT_TranId);
PackAddInt(p, "nat_traversal_version", UDP_NAT_TRAVERSAL_VERSION);
b = PackToBuf(p);
FreePack(p);
RUDPSendPacket(r, &r->NatT_IP_Safe, UDP_NAT_T_PORT, b->Buf, b->Size, 0);
FreeBuf(b);
// Determine the next acquisition time
r->NatT_GetTokenFailNum++;
r->NatT_GetTokenNextTick = r->Now + (UINT64)(UDP_NAT_T_GET_TOKEN_INTERVAL_1 * (UINT64)MIN(r->NatT_GetTokenFailNum, UDP_NAT_T_GET_TOKEN_INTERVAL_FAIL_MAX));
AddInterrupt(r->Interrupt, r->NatT_GetTokenNextTick);
r->NatT_Token_Ok = false;
}
}
{
if (IsZeroIp(&r->NatT_IP_Safe) == false)
{
// Normal servers: Send request packets to the NAT-T server
if (r->NatT_NextNatStatusCheckTick == 0 || r->Now >= r->NatT_NextNatStatusCheckTick)
{
UCHAR a = 'A';
UINT ddns_hash;
// Check of the NAT state
RUDPSendPacket(r, &r->NatT_IP_Safe, UDP_NAT_T_PORT, &a, 1, 0);
// Execution time of the next
r->NatT_NextNatStatusCheckTick = r->Now + (UINT64)GenRandInterval(UDP_NAT_T_NAT_STATUS_CHECK_INTERVAL_MIN, UDP_NAT_T_NAT_STATUS_CHECK_INTERVAL_MAX);
AddInterrupt(r->Interrupt, r->NatT_NextNatStatusCheckTick);
// Check whether the DDNS host name changing have not occurred
ddns_hash = GetCurrentDDnsFqdnHash();
if (r->LastDDnsFqdnHash != ddns_hash)
{
r->LastDDnsFqdnHash = ddns_hash;
// Do the Register immediately if there is a change in the DDNS host name
r->NatT_RegisterNextTick = 0;
}
}
}
}
if (r->NatT_Token_Ok && g_no_rudp_register == false && IsZeroIp(&r->NatT_IP_Safe) == false)
{
if (r->NatT_RegisterNextTick == 0 || r->Now >= r->NatT_RegisterNextTick)
{
// Try to register itself periodically for NAT-T server
PACK *p = NewPack();
BUF *b;
char private_ip_str[MAX_SIZE];
char machine_key[MAX_SIZE];
char machine_name[MAX_SIZE];
UCHAR hash[SHA1_SIZE];
char ddns_fqdn[MAX_SIZE];
Debug("NAT-T Registering...\n");
GetCurrentDDnsFqdn(ddns_fqdn, sizeof(ddns_fqdn));
PackAddStr(p, "opcode", "nat_t_register");
PackAddInt64(p, "tran_id", r->NatT_TranId);
PackAddStr(p, "token", r->NatT_Token);
PackAddStr(p, "svc_name", r->SvcName);
PackAddStr(p, "product_str", "SoftEther OSS");
PackAddInt64(p, "session_key", r->NatT_SessionKey);
PackAddInt(p, "nat_traversal_version", UDP_NAT_TRAVERSAL_VERSION);
if (g_natt_low_priority)
{
PackAddBool(p, "low_priority", g_natt_low_priority);
}
Zero(private_ip_str, sizeof(private_ip_str));
if (IsZeroIp(&r->My_Private_IP_Safe) == false)
{
IPToStr(private_ip_str, sizeof(private_ip_str), &r->My_Private_IP_Safe);
PackAddStr(p, "private_ip", private_ip_str);
}
PackAddInt(p, "private_port", r->UdpSock->LocalPort);
Zero(hash, sizeof(hash));
GetCurrentMachineIpProcessHash(hash);
BinToStr(machine_key, sizeof(machine_key), hash, sizeof(hash));
PackAddStr(p, "machine_key", machine_key);
Zero(machine_name, sizeof(machine_name));
GetMachineName(machine_name, sizeof(machine_name));
PackAddStr(p, "host_name", machine_name);
PackAddStr(p, "ddns_fqdn", ddns_fqdn);
b = PackToBuf(p);
FreePack(p);
RUDPSendPacket(r, &r->NatT_IP_Safe, UDP_NAT_T_PORT, b->Buf, b->Size, 0);
//RUDPSendPacket(r, &r->NatT_IP_Safe, UDP_NAT_T_PORT, "a", 1);
FreeBuf(b);
// Determine the next acquisition time
r->NatT_RegisterFailNum++;
r->NatT_RegisterNextTick = r->Now + (UINT64)UDP_NAT_T_REGISTER_INTERVAL_INITIAL * (UINT64)MIN(r->NatT_RegisterFailNum, UDP_NAT_T_REGISTER_INTERVAL_FAIL_MAX);
AddInterrupt(r->Interrupt, r->NatT_RegisterNextTick);
r->NatT_Register_Ok = false;
}
}
}
}
// R-UDP packet reception procedure
void RUDPRecvProc(RUDP_STACK *r, UDPPACKET *p)
{
RUDP_SESSION *se = NULL;
// Validate arguments
if (r == NULL || p == NULL)
{
return;
}
if (r->ServerMode)
{
if (g_no_rudp_server)
{
return;
}
}
if (r->ServerMode && r->NoNatTRegister == false)
{
if (p->SrcPort == UDP_NAT_T_PORT && CmpIpAddr(&p->SrcIP, &r->NatT_IP_Safe) == 0)
{
// There was a response from the NAT-T server
RUDPProcess_NatT_Recv(r, p);
return;
}
}
if (r->ServerMode)
{
if (r->ProcRpcRecv != NULL)
{
if (r->ProcRpcRecv(r, p))
{
return;
}
}
}
if (r->ServerMode)
{
// Search the session by the end-point information if in the server mode
se = RUDPSearchSession(r, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort);
}
else
{
// Session should exist only one in the case of client mode
if (LIST_NUM(r->SessionList) >= 1)
{
se = LIST_DATA(r->SessionList, 0);
}
else
{
se = NULL;
}
}
if (p->Size < 20)
{
// The received packet is too small
if (r->ServerMode == false)
{
if (se != NULL && se->Status == RUDP_SESSION_STATUS_CONNECT_SENT)
{
if (CmpIpAddr(&se->YourIp, &p->SrcIP) == 0)
{
// If the connection initialization packet which is shorter than 20 bytes
// has been received from the server side, overwrite the source port number
// of the packet to the client-side session information (for some NAT)
se->YourPort = p->SrcPort;
}
}
}
return;
}
if (se == NULL && r->ServerMode && p->Size >= 40)
{
// Corresponding to a sudden change of port number on the client side.
// The target session is a session which matches the client side IP address
// and the key and the signature is verified
UINT i;
for (i = 0; i < LIST_NUM(r->SessionList); i++)
{
RUDP_SESSION *s = LIST_DATA(r->SessionList, i);
if (CmpIpAddr(&s->YourIp, &p->SrcIP) == 0)
{
if (RUDPCheckSignOfRecvPacket(r, s, p->Data, p->Size))
{
// Signature matched
se = s;
break;
}
}
}
}
if (se == NULL)
{
// There is no session
if (r->ServerMode)
{
if (p->Size < 40)
{
bool ok = true;
UCHAR ctoken_hash[SHA1_SIZE];
Zero(ctoken_hash, sizeof(ctoken_hash));
// Examine the quota of new session creation
if (LIST_NUM(r->SessionList) >= RUDP_QUOTA_MAX_NUM_SESSIONS)
{
// Entire number of sessions exceeds the limit
ok = false;
}
else if (r->NatT_EnableSourceIpValidation && RUDPIsIpInValidateList(r, &p->SrcIP) == false)
{
// Invalid source IP address, which is not registered on the validated source IP address list
ok = false;
}
else
{
UINT i;
// Check the number of sessions per IP address
UINT num = 0;
for (i = 0; i < LIST_NUM(r->SessionList); i++)
{
RUDP_SESSION *se = LIST_DATA(r->SessionList, i);
if (CmpIpAddr(&se->YourIp, &p->SrcIP) == 0)
{
num++;
}
}
if (num >= RUDP_QUOTA_MAX_NUM_SESSIONS_PER_IP)
{
// Limit exceeded the number of sessions per IP address
ok = false;
}
}
if (ok)
{
char ip_str[64];
// Create a session since a new session creation request packet was received
se = RUDPNewSession(true, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, p->Data);
se->Status = RUDP_SESSION_STATUS_ESTABLISHED;
Insert(r->SessionList, se);
IPToStr(ip_str, sizeof(ip_str), &p->SrcIP);
Debug("RUDPNewSession %X %s:%u\n", se, ip_str, p->SrcPort);
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
// In case of ICMP, save the ICMP TYPE number to use
se->Icmp_Type = (p->Type == ICMP_TYPE_INFORMATION_REQUEST ? ICMP_TYPE_INFORMATION_REPLY : p->Type);
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
// Save the Tran ID to be used if it's a DNS
se->Dns_TranId = (USHORT)p->Type;
}
}
}
}
}
else
{
if (p->Size < 40)
{
if (r->ServerMode)
{
if (Cmp(se->Key_Init, p->Data, SHA1_SIZE) == 0)
{
// New session creation request packet have received more than once. reply an ACK immediately for second and subsequent
se->LastSentTick = 0;
// Update the endpoint information
Copy(&se->YourIp, &p->SrcIP, sizeof(IP));
se->YourPort = p->SrcPort;
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
// In case of ICMP, save the ICMP TYPE number to use
se->Icmp_Type = (p->Type == ICMP_TYPE_INFORMATION_REQUEST ? ICMP_TYPE_INFORMATION_REPLY : p->Type);
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
// Save the Tran ID to be used if it's a DNS
se->Dns_TranId = (USHORT)p->Type;
}
}
else
{
// Since the different session creation request packet have been received from the same end point, ignore it
}
}
}
else
{
// Process the received packet
if (RUDPProcessRecvPacket(r, se, p->Data, p->Size) || RUDPProcessBulkRecvPacket(r, se, p->Data, p->Size))
{
// Update endpoint information (only the port number)
//Copy(&se->YourIp, &p->SrcIP, sizeof(IP));
se->YourPort = p->SrcPort;
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
// In case of ICMP, save the ICMP TYPE number to use
if (r->ServerMode)
{
se->Icmp_Type = (p->Type == ICMP_TYPE_INFORMATION_REQUEST ? ICMP_TYPE_INFORMATION_REPLY : p->Type);
}
else
{
se->Icmp_Type = (p->Type == ICMP_TYPE_INFORMATION_REPLY ? ICMP_TYPE_INFORMATION_REQUEST : p->Type);
}
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
if (r->ServerMode)
{
// Save the Tran ID to be used if it's a DNS
se->Dns_TranId = (USHORT)p->Type;
}
}
}
}
}
}
// Check whether the specificed IP address is in the validated source IP address list
bool RUDPIsIpInValidateList(RUDP_STACK *r, IP *ip)
{
UINT i;
UINT64 now = Tick64();
LIST *o = NULL;
bool ret = false;
// Validate arguments
if (r == NULL || ip == NULL)
{
return false;
}
// Always allow private IP addresses
if (IsIPPrivate(ip))
{
return true;
}
if (IsIPAddressInSameLocalNetwork(ip))
{
return true;
}
for (i = 0; i < LIST_NUM(r->NatT_SourceIpList); i++)
{
RUDP_SOURCE_IP *s = (RUDP_SOURCE_IP *)LIST_DATA(r->NatT_SourceIpList, i);
if (s->ExpiresTick <= now)
{
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, s);
}
}
if (o != NULL)
{
for (i = 0; i < LIST_NUM(o); i++)
{
RUDP_SOURCE_IP *s = (RUDP_SOURCE_IP *)LIST_DATA(o, i);
Delete(r->NatT_SourceIpList, s);
Free(s);
}
ReleaseList(o);
}
for (i = 0; i < LIST_NUM(r->NatT_SourceIpList); i++)
{
RUDP_SOURCE_IP *s = (RUDP_SOURCE_IP *)LIST_DATA(r->NatT_SourceIpList, i);
if (CmpIpAddr(&s->ClientIP, ip) == 0)
{
ret = true;
break;
}
}
Debug("RUDP: NAT-T: Validate IP: %r, ret=%u (current list len = %u)\n", ip, ret, LIST_NUM(r->NatT_SourceIpList));
return ret;
}
// Add an IP address to the validated source IP address list
void RUDPAddIpToValidateList(RUDP_STACK *r, IP *ip)
{
UINT i;
RUDP_SOURCE_IP *sip;
UINT64 now = Tick64();
LIST *o = NULL;
// Validate arguments
if (r == NULL || ip == NULL)
{
return;
}
if (LIST_NUM(r->NatT_SourceIpList) >= RUDP_MAX_VALIDATED_SOURCE_IP_ADDRESSES)
{
return;
}
for (i = 0; i < LIST_NUM(r->NatT_SourceIpList); i++)
{
RUDP_SOURCE_IP *s = (RUDP_SOURCE_IP *)LIST_DATA(r->NatT_SourceIpList, i);
if (s->ExpiresTick <= now)
{
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, s);
}
}
if (o != NULL)
{
for (i = 0; i < LIST_NUM(o); i++)
{
RUDP_SOURCE_IP *s = (RUDP_SOURCE_IP *)LIST_DATA(o, i);
Delete(r->NatT_SourceIpList, s);
Free(s);
}
ReleaseList(o);
}
sip = NULL;
for (i = 0; i < LIST_NUM(r->NatT_SourceIpList); i++)
{
RUDP_SOURCE_IP *s = (RUDP_SOURCE_IP *)LIST_DATA(r->NatT_SourceIpList, i);
if (CmpIpAddr(&s->ClientIP, ip) == 0)
{
sip = s;
break;
}
}
if (sip == NULL)
{
sip = ZeroMalloc(sizeof(RUDP_SOURCE_IP));
Copy(&sip->ClientIP, ip, sizeof(IP));
Add(r->NatT_SourceIpList, sip);
}
sip->ExpiresTick = now + (UINT64)RUDP_VALIDATED_SOURCE_IP_ADDRESS_EXPIRES;
Debug("RUDP: NAT-T: Src IP added: %r (current list len = %u)\n", ip, LIST_NUM(r->NatT_SourceIpList));
}
// R-UDP interrupt processing procedure
void RUDPInterruptProc(RUDP_STACK *r)
{
UINT i;
LIST *o;
// Validate arguments
if (r == NULL)
{
return;
}
// Packet transmission and other process for NAT-T server
if (r->NoNatTRegister == false)
{
RUDPDo_NatT_Interrupt(r);
}
if (r->ServerMode == false)
{
if (r->ClientInitiated == false)
{
bool client_target_inited = false;
Lock(r->Lock);
{
client_target_inited = r->TargetIpAndPortInited;
}
Unlock(r->Lock);
if (client_target_inited)
{
// Start a connection when there is the end point information of the destination server to connect as a client
RUDP_SESSION *se;
UCHAR init_key[SHA1_SIZE];
char ip_str[128];
UINT64 ui;
Rand(init_key, SHA1_SIZE);
se = RUDPNewSession(false, &r->UdpSock->LocalIP, r->UdpSock->LocalPort,
&r->TargetIp, r->TargetPort, init_key);
IPToStr(ip_str, sizeof(ip_str), &r->TargetIp);
Debug("RUDPNewSession %X %s:%u\n", se, ip_str, r->TargetPort);
Insert(r->SessionList, se);
ui = Endian64(se->Magic_Disconnect);
WriteFifo(se->SendFifo, &ui, sizeof(UINT64));
r->ClientInitiated = true;
}
}
}
// Process for all the sessions
for (i = 0; i < LIST_NUM(r->SessionList); i++)
{
RUDP_SESSION *se = LIST_DATA(r->SessionList, i);
if (r->Halt)
{
// Disconnect all the sessions if the R-UDP stack stopped
RUDPDisconnectSession(r, se, false);
}
if (se->FlushBulkSendTube)
{
if (se->TcpSock != NULL && se->TcpSock->BulkSendTube != NULL)
{
TubeFlush(se->TcpSock->BulkSendTube);
}
se->FlushBulkSendTube = false;
}
if (se->Status == RUDP_SESSION_STATUS_ESTABLISHED)
{
// Process for all of the sessions which is established a connection
UINT j;
if (r->Now >= (se->LatestRecvMyTick + (UINT64)RUDP_TIMEOUT))
{
// Disconnect the session because the fully communication failure is detected for a while
Debug("R-UDP Session %X Timed Out.\n", se);
RUDPDisconnectSession(r, se, false);
}
// If there are received segments, read to the part that has arrived in succession
if (FifoSize(se->RecvFifo) <= RUDP_MAX_FIFO_SIZE)
{
LIST *o;
UINT64 current_seq_no;
o = NULL;
current_seq_no = se->LastRecvCompleteSeqNo;
for (j = 0; j < LIST_NUM(se->RecvSegmentList); j++)
{
RUDP_SEGMENT *s;
current_seq_no++;
s = LIST_DATA(se->RecvSegmentList, j);
if (s->SeqNo == current_seq_no)
{
#ifdef RUDP_DETAIL_LOG
Debug("%X s->SeqNo = %I64u, current_seq_no = %I64u\n", se, s->SeqNo, current_seq_no);
#endif // RUDP_DETAIL_LOG
if (s->Size == sizeof(se->Magic_KeepAliveRequest) && Cmp(s->Data, se->Magic_KeepAliveRequest, sizeof(se->Magic_KeepAliveRequest)) == 0)
{
// Receive the KeepAlive Request
#ifdef RUDP_DETAIL_LOG
Debug("Recv KeepAlive Request\n");
#endif // RUDP_DETAIL_LOG
// Send a KeepAlive Response if the transmisson queue is empty
if (LIST_NUM(se->SendSegmentList) == 0)
{
#ifdef RUDP_DETAIL_LOG
Debug("Send KeepAlive Response\n");
#endif // RUDP_DETAIL_LOG
RUDPSendSegment(r, se, se->Magic_KeepAliveResponse, sizeof(se->Magic_KeepAliveResponse));
}
}
else if (s->Size == sizeof(se->Magic_KeepAliveResponse) && Cmp(s->Data, se->Magic_KeepAliveResponse, sizeof(se->Magic_KeepAliveResponse)) == 0)
{
// Receive the KeepAlive Response
#ifdef RUDP_DETAIL_LOG
Debug("Recv KeepAlive Response\n");
#endif // RUDP_DETAIL_LOG
}
else
{
// Write to the receive FIFO
WriteFifo(se->RecvFifo, s->Data, s->Size);
}
r->TotalLogicalReceived += s->Size;
// Advance the SEQ NO which has been received completely
se->LastRecvCompleteSeqNo = s->SeqNo;
// Add to the Delete list
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, s);
}
else
{
// Continuous reading is interrupted
#ifdef RUDP_DETAIL_LOG
Debug("%X s->SeqNo = %I64u, current_seq_no = %I64u\n", se, s->SeqNo, current_seq_no);
WHERE;
#endif // RUDP_DETAIL_LOG
break;
}
}
// Delete the segment which has been received completely
if (o != NULL)
{
for (j = 0; j < LIST_NUM(o); j++)
{
RUDP_SEGMENT *s = LIST_DATA(o, j);
Delete(se->RecvSegmentList, s);
Free(s);
}
ReleaseList(o);
}
}
if (r->ServerMode && se->Magic_Disconnect == 0)
{
if (FifoSize(se->RecvFifo) >= sizeof(UINT64))
{
UINT64 ui;
if (ReadFifo(se->RecvFifo, &ui, sizeof(UINT64)) == sizeof(UINT64))
{
ui = Endian64(ui);
if ((ui & 0xffffffff00000000ULL) != 0ULL)
{
se->Magic_Disconnect = ui;
}
}
}
}
// If the data remains in FIFO, write it to the TCP socket as possible
if (r->ServerMode == false || se->Magic_Disconnect != 0)
{
while (FifoSize(se->RecvFifo) >= 1)
{
UINT ret;
RUDPInitSock(r, se);
ret = Send(se->TcpSock, FifoPtr(se->RecvFifo), FifoSize(se->RecvFifo), false);
if (ret == SOCK_LATER)
{
// Can not write any more
break;
}
else if (ret == 0)
{
// Disconnected
Disconnect(se->TcpSock);
RUDPDisconnectSession(r, se, false);
break;
}
else
{
// Writing success
ReadFifo(se->RecvFifo, NULL, ret);
}
}
}
// Read the data as much as possible from the TCP socket and store it to FIFO
if (se->TcpSock != NULL)
{
SetNoNeedToRead(se->TcpSock);
while (FifoSize(se->SendFifo) <= RUDP_MAX_FIFO_SIZE)
{
UINT ret = Recv(se->TcpSock, r->TmpBuf, sizeof(r->TmpBuf), false);
if (ret == SOCK_LATER)
{
// Can not read any more
break;
}
else if (ret == 0)
{
// Disconnected
Disconnect(se->TcpSock);
RUDPDisconnectSession(r, se, false);
break;
}
else
{
// Reading success
WriteFifo(se->SendFifo, r->TmpBuf, ret);
}
}
}
// Attempt to send a divided segment
while (true)
{
UINT64 seq_no_min, seq_no_max;
seq_no_min = RUDPGetCurrentSendingMinSeqNo(se);
seq_no_max = RUDPGetCurrentSendingMaxSeqNo(se);
#ifdef RUDP_DETAIL_LOG
Debug("min=%I64u max=%I64u\n", seq_no_min, seq_no_max);
#endif // RUDP_DETAIL_LOG
if (seq_no_min == 0 || ((seq_no_min + RUDP_MAX_NUM_ACK - 1) >= se->NextSendSeqNo))
{
// Because there is a room to send a new segment, send a segment
UINT size = MIN(FifoSize(se->SendFifo), RUDP_MAX_SEGMENT_SIZE);
if (size == 0)
{
// There is no more data to send in FIFO
break;
}
// Transmission
RUDPSendSegment(r, se, FifoPtr(se->SendFifo), size);
r->TotalLogicalSent += size;
// Advance the FIFO
ReadFifo(se->SendFifo, NULL, size);
}
else
{
// There is no room to send a new segment further
break;
}
}
if (se->DisconnectFlag == false)
{
UINT64 seq_no_min;
if (se->LastSentTick == 0 || (r->Now >= (se->LastSentTick + (UINT64)se->NextKeepAliveInterval)))
{
if (LIST_NUM(se->SendSegmentList) == 0)
{
// Send a Keep-Alive if no data was sent for a while and the transmission queue is empty
RUDPSendSegment(r, se, se->Magic_KeepAliveRequest, sizeof(se->Magic_KeepAliveRequest));
#ifdef RUDP_DETAIL_LOG
Debug("Sent KeepAlive Request\n");
#endif // RUDP_DETAIL_LOG
}
se->NextKeepAliveInterval = RUDP_KEEPALIVE_INTERVAL_MIN + (Rand32() % (RUDP_KEEPALIVE_INTERVAL_MAX - RUDP_KEEPALIVE_INTERVAL_MIN));
AddInterrupt(r->Interrupt, r->Now + se->NextKeepAliveInterval);
}
seq_no_min = RUDPGetCurrentSendingMinSeqNo(se);
for (j = 0; j < LIST_NUM(se->SendSegmentList); j++)
{
RUDP_SEGMENT *s = LIST_DATA(se->SendSegmentList, j);
if (s->SeqNo <= (seq_no_min + RUDP_MAX_NUM_ACK - 1))
{
if (s->NextSendTick == 0 || r->Now >= s->NextSendTick)
{
UINT next_interval;
// Transmits a segment which has not been sent even once yet, or whose retransmission time has arrived
RUDPSendSegmentNow(r, se, s->SeqNo, s->Data, s->Size);
if (se->CurrentRtt != 0)
{
next_interval = (se->CurrentRtt * 120 / 100) * Power(2, MIN(s->NumSent, 10));
}
else
{
next_interval = RUDP_RESEND_TIMER * Power(2, MIN(s->NumSent, 10));
}
next_interval = MIN(next_interval, RUDP_RESEND_TIMER_MAX);
s->NumSent++;
s->NextSendTick = r->Now + next_interval;
AddInterrupt(r->Interrupt, s->NextSendTick);
}
}
}
while (LIST_NUM(se->ReplyAckList) >= 1)
{
// If there are ACKs which is not responded yet in the list, send all of them
RUDPSendSegmentNow(r, se, se->NextSendSeqNo, NULL, 0);
}
// Send all if there are bulk transfer data
if (se->TcpSock != NULL)
{
SOCK *s = se->TcpSock;
if (s->BulkRecvTube != NULL)
{
TUBE *t = s->BulkRecvTube;
while (true)
{
TUBEDATA *d = TubeRecvAsync(t);
if (d == NULL)
{
break;
}
if (d->Header != NULL && d->HeaderSize == sizeof(TCP_PAIR_HEADER))
{
TCP_PAIR_HEADER *h = d->Header;
if (h->EnableHMac)
{
se->UseHMac = true;
}
}
RUDPBulkSend(r, se, d->Data, d->DataSize);
FreeTubeData(d);
}
}
}
}
}
if (r->ServerMode == false)
{
if (se->Status == RUDP_SESSION_STATUS_CONNECT_SENT)
{
// Send a connection request periodically from the client side
if (se->LastSentTick == 0 || ((se->LastSentTick + (UINT64)RUDP_RESEND_TIMER) <= r->Now))
{
UCHAR tmp[40];
UINT size_of_padding = 19;
UINT size = size_of_padding + SHA1_SIZE;
se->LastSentTick = r->Now;
Copy(tmp, se->Key_Init, SHA1_SIZE);
Rand(tmp + SHA1_SIZE, size_of_padding);
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
// ICMP packet
UCHAR *rand_data;
UINT rand_size;
rand_size = Rand32() % 64 + 64;
rand_data = Malloc(rand_size);
Rand(rand_data, rand_size);
RUDPSendPacket(r, &se->YourIp, se->YourPort, rand_data, rand_size, ICMP_TYPE_ECHO_REQUEST);
Free(rand_data);
se->Client_Icmp_NextSendEchoRequest = r->Now + GenRandInterval(RUDP_CLIENT_ECHO_REQUEST_SEND_INTERVAL_MIN, RUDP_CLIENT_ECHO_REQUEST_SEND_INTERVAL_MAX);
AddInterrupt(r->Interrupt, se->Client_Icmp_NextSendEchoRequest);
// Try in both INFORMATION_REQUEST and ECHO_RESPONSE from the client side first
RUDPSendPacket(r, &se->YourIp, se->YourPort, tmp, size, ICMP_TYPE_ECHO_RESPONSE);
RUDPSendPacket(r, &se->YourIp, se->YourPort, tmp, size, ICMP_TYPE_INFORMATION_REQUEST);
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
// DNS
RUDPSendPacket(r, &se->YourIp, se->YourPort, tmp, size, se->Dns_TranId);
}
else
{
// Normal UDP
RUDPSendPacket(r, &se->YourIp, se->YourPort, tmp, size, 0);
}
AddInterrupt(r->Interrupt, r->Now + (UINT64)RUDP_RESEND_TIMER);
}
}
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
if (se->Client_Icmp_NextSendEchoRequest == 0 || (r->Now >= se->Client_Icmp_NextSendEchoRequest))
{
// Periodic ICMP Echo transmission from the client side when R-UDP used in ICMP mode
// (To maintain the mapping table of the NAT)
UCHAR *rand_data;
UINT rand_size;
rand_size = Rand32() % 64 + 64;
rand_data = Malloc(rand_size);
Rand(rand_data, rand_size);
RUDPSendPacket(r, &se->YourIp, se->YourPort, rand_data, rand_size, ICMP_TYPE_ECHO_REQUEST);
Free(rand_data);
se->Client_Icmp_NextSendEchoRequest = r->Now + GenRandInterval(RUDP_CLIENT_ECHO_REQUEST_SEND_INTERVAL_MIN, RUDP_CLIENT_ECHO_REQUEST_SEND_INTERVAL_MAX);
AddInterrupt(r->Interrupt, se->Client_Icmp_NextSendEchoRequest);
}
}
}
}
// Release the disconnected sessions
o = NULL;
for (i = 0; i < LIST_NUM(r->SessionList); i++)
{
RUDP_SESSION *se = LIST_DATA(r->SessionList, i);
if (se->DisconnectFlag)
{
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, se);
}
}
if (o != NULL)
{
for (i = 0; i < LIST_NUM(o); i++)
{
RUDP_SESSION *se = LIST_DATA(o, i);
Delete(r->SessionList, se);
RUDPFreeSession(se);
}
ReleaseList(o);
}
}
// Do the bulk send
void RUDPBulkSend(RUDP_STACK *r, RUDP_SESSION *se, void *data, UINT data_size)
{
UCHAR *buf;
UINT i, icmp_type, buf_size, padding_size;
icmp_type = 0;
// Validate arguments
if (r == NULL || se == NULL || (data == NULL && data_size != 0))
{
return;
}
if (se->BulkSendKey->Size == RUDP_BULK_KEY_SIZE_V2)
{
UCHAR *tmp, iv[RUDP_BULK_IV_SIZE_V2];
UINT size;
CIPHER *c;
padding_size = Rand32() % 31 + 1;
size = sizeof(UINT64) + data_size + padding_size;
// Packet: IV + Encrypted(SEQ_NO + Data + padding) + MAC
buf_size = RUDP_BULK_IV_SIZE_V2 + sizeof(UINT64) + data_size + padding_size + RUDP_BULK_MAC_SIZE_V2;
buf = Malloc(buf_size);
// IV
Copy(iv, se->BulkNextIv_V2, RUDP_BULK_IV_SIZE_V2);
Copy(buf, iv, RUDP_BULK_IV_SIZE_V2);
// SEQ NO
WRITE_UINT64(buf + RUDP_BULK_IV_SIZE_V2, se->BulkNextSeqNo);
se->BulkNextSeqNo++;
// Data
Copy(buf + RUDP_BULK_IV_SIZE_V2 + sizeof(UINT64), data, data_size);
// Padding
for (i = 0; i < padding_size; i++)
{
buf[RUDP_BULK_IV_SIZE_V2 + sizeof(UINT64) + data_size + i] = (UCHAR)padding_size;
}
size = sizeof(UINT64) + data_size + padding_size;
tmp = buf + RUDP_BULK_IV_SIZE_V2;
// Encryption
c = NewCipher("ChaCha20-Poly1305");
SetCipherKey(c, se->BulkSendKey->Data, true);
CipherProcessAead(c, iv, tmp + size, RUDP_BULK_MAC_SIZE_V2, tmp, tmp, size - RUDP_BULK_MAC_SIZE_V2, NULL, 0);
FreeCipher(c);
// Next IV
Copy(se->BulkNextIv_V2, buf + sizeof(UINT64) + data_size + padding_size, RUDP_BULK_IV_SIZE_V2);
}
else
{
UCHAR crypt_key_src[SHA1_SIZE * 2];
UCHAR crypt_key[SHA1_SIZE];
UCHAR sign[SHA1_SIZE];
UCHAR iv[SHA1_SIZE];
CRYPT *c;
padding_size = Rand32() % 31 + 1;
buf_size = SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + padding_size;
buf = Malloc(buf_size);
// SEQ NO
WRITE_UINT64(buf + SHA1_SIZE + SHA1_SIZE, se->BulkNextSeqNo);
se->BulkNextSeqNo++;
// Data
Copy(buf + SHA1_SIZE + SHA1_SIZE + sizeof(UINT64), data, data_size);
// Padding
for (i = 0; i < padding_size; i++)
{
buf[SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + i] = (UCHAR)padding_size;
}
// Encryption
Copy(iv, se->BulkNextIv, SHA1_SIZE);
Copy(crypt_key_src + 0, se->BulkSendKey->Data, SHA1_SIZE);
Copy(crypt_key_src + SHA1_SIZE, iv, SHA1_SIZE);
Sha1(crypt_key, crypt_key_src, SHA1_SIZE * 2);
c = NewCrypt(crypt_key, sizeof(crypt_key));
Encrypt(c, buf + SHA1_SIZE + SHA1_SIZE, buf + SHA1_SIZE + SHA1_SIZE, sizeof(UINT64) + data_size + padding_size);
FreeCrypt(c);
// IV
Copy(buf + SHA1_SIZE, iv, SHA1_SIZE);
// Sign
if (se->UseHMac == false)
{
Copy(buf + 0, se->BulkSendKey->Data, SHA1_SIZE);
Sha1(sign, buf, SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + padding_size);
Copy(buf + 0, sign, SHA1_SIZE);
}
else
{
HMacSha1(buf + 0, se->BulkSendKey->Data, SHA1_SIZE, buf + SHA1_SIZE, SHA1_SIZE + sizeof(UINT64) + data_size + padding_size);
}
// Next IV
Copy(se->BulkNextIv, buf + buf_size - SHA1_SIZE, SHA1_SIZE);
}
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
icmp_type = se->Icmp_Type;
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
icmp_type = se->Dns_TranId;
}
RUDPSendPacket(r, &se->YourIp, se->YourPort, buf, buf_size, icmp_type);
Free(buf);
}
// Start a socket for R-UDP Listening
SOCK *ListenRUDP(char *svc_name, RUDP_STACK_INTERRUPTS_PROC *proc_interrupts, RUDP_STACK_RPC_RECV_PROC *proc_rpc_recv, void *param, UINT port, bool no_natt_register, bool over_dns_mode)
{
return ListenRUDPEx(svc_name, proc_interrupts, proc_rpc_recv, param, port, no_natt_register, over_dns_mode, NULL, 0, NULL);
}
SOCK *ListenRUDPEx(char *svc_name, RUDP_STACK_INTERRUPTS_PROC *proc_interrupts, RUDP_STACK_RPC_RECV_PROC *proc_rpc_recv, void *param, UINT port, bool no_natt_register, bool over_dns_mode,
volatile UINT *natt_global_udp_port, UCHAR rand_port_id, IP *listen_ip)
{
SOCK *s;
RUDP_STACK *r;
// Creating a R-UDP stack
r = NewRUDPServer(svc_name, proc_interrupts, proc_rpc_recv, param, port, no_natt_register, over_dns_mode, natt_global_udp_port, rand_port_id, listen_ip);
if (r == NULL)
{
return NULL;
}
s = NewSock();
s->Type = SOCK_RUDP_LISTEN;
s->ListenMode = true;
s->Connected = true;
s->LocalPort = r->UdpSock->LocalPort;
s->R_UDP_Stack = r;
return s;
}
// Accept on the R-UDP socket
SOCK *AcceptRUDP(SOCK *s)
{
// Validate arguments
if (s == NULL || s->Type != SOCK_RUDP_LISTEN || s->ListenMode == false)
{
return NULL;
}
while (true)
{
RUDP_STACK *r = s->R_UDP_Stack;
SOCK *ret;
if (s->Disconnecting || s->CancelAccept)
{
return NULL;
}
ret = GetNextWithLock(r->NewSockQueue);
if (ret != NULL)
{
switch (r->Protocol)
{
case RUDP_PROTOCOL_UDP:
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_NAT_T);
AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "RUDP/UDP");
break;
case RUDP_PROTOCOL_DNS:
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_DNS);
AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "RUDP/DNS");
break;
case RUDP_PROTOCOL_ICMP:
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_ICMP);
AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "RUDP/ICMP");
break;
}
return ret;
}
Wait(r->NewSockConnectEvent, INFINITE);
}
}
// Verify the signature of the received packet
bool RUDPCheckSignOfRecvPacket(RUDP_STACK *r, RUDP_SESSION *se, void *recv_data, UINT recv_size)
{
UCHAR sign[SHA1_SIZE];
UCHAR sign2[SHA1_SIZE];
UCHAR *p;
UINT size;
// Validate arguments
if (r == NULL || se == NULL || recv_data == NULL || recv_size == 0)
{
return false;
}
p = (UCHAR *)recv_data;
size = recv_size;
if (size < SHA1_SIZE)
{
return false;
}
// Verification the signature (segment packet)
Copy(sign, p, SHA1_SIZE);
Copy(p, se->Key_Recv, SHA1_SIZE);
Sha1(sign2, p, recv_size);
if (r->Protocol == RUDP_PROTOCOL_DNS || r->Protocol == RUDP_PROTOCOL_ICMP)
{
XorData(sign2, sign2, r->SvcNameHash, SHA1_SIZE);
}
Copy(p, sign, SHA1_SIZE);
if (Cmp(sign, sign2, SHA1_SIZE) == 0)
{
return true;
}
if (se->BulkRecvKey == NULL)
{
return false;
}
// Verification signature (bulk packet)
if (se->BulkRecvKey->Size == RUDP_BULK_KEY_SIZE_V2)
{
UCHAR *iv = p;
CIPHER *c;
// Packet: IV + Encrypted(SEQ_NO + Data + padding) + MAC
// IV
if (size < RUDP_BULK_IV_SIZE_V2)
{
return false;
}
iv = p;
p += RUDP_BULK_IV_SIZE_V2;
size -= RUDP_BULK_IV_SIZE_V2;
// Decrypt
if (size < (RUDP_BULK_MAC_SIZE_V2 + 1))
{
return false;
}
c = NewCipher("ChaCha20-Poly1305");
SetCipherKey(c, se->BulkRecvKey->Data, false);
size = CipherProcessAead(c, iv, p + size, RUDP_BULK_MAC_SIZE_V2, r->TmpBuf, p, size - RUDP_BULK_MAC_SIZE_V2, NULL, 0);
FreeCipher(c);
if (size == 0)
{
return false;
}
return true;
}
else
{
if (se->UseHMac == false)
{
Copy(sign, p, SHA1_SIZE);
Copy(p, se->BulkRecvKey->Data, SHA1_SIZE);
Sha1(sign2, p, recv_size);
Copy(p, sign, SHA1_SIZE);
if (Cmp(sign, sign2, SHA1_SIZE) == 0)
{
return true;
}
}
HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, size - SHA1_SIZE);
if (Cmp(p, sign2, SHA1_SIZE) == 0)
{
se->UseHMac = true;
return true;
}
}
return false;
}
// Process the received packet (bulk)
bool RUDPProcessBulkRecvPacket(RUDP_STACK *r, RUDP_SESSION *se, void *recv_data, UINT recv_size)
{
UCHAR *p;
UCHAR *iv;
UINT size;
UCHAR padlen;
UINT64 seq_no;
UCHAR *payload;
UINT payload_size;
// Validate arguments
if (r == NULL || se == NULL || recv_data == NULL || recv_size == 0 || se->BulkRecvKey == NULL)
{
return false;
}
p = (UCHAR *)recv_data;
size = recv_size;
if (size < SHA1_SIZE)
{
return false;
}
if (se->BulkRecvKey->Size == RUDP_BULK_KEY_SIZE_V2)
{
UINT ret;
CIPHER *c;
// Packet: IV + Encrypted(SEQ_NO + Data + padding) + MAC
// IV
if (size < RUDP_BULK_IV_SIZE_V2)
{
WHERE;
return false;
}
iv = p;
p += RUDP_BULK_IV_SIZE_V2;
size -= RUDP_BULK_IV_SIZE_V2;
// Decrypt
if (size < (RUDP_BULK_MAC_SIZE_V2 + 1))
{
WHERE;
return false;
}
c = NewCipher("ChaCha20-Poly1305");
SetCipherKey(c, se->BulkRecvKey->Data, false);
ret = CipherProcessAead(c, iv, p + size, RUDP_BULK_MAC_SIZE_V2, p, p, size - RUDP_BULK_MAC_SIZE_V2, NULL, 0);
FreeCipher(c);
if (ret == 0)
{
WHERE;
return false;
}
size -= RUDP_BULK_MAC_SIZE_V2;
// padlen
padlen = p[size - 1];
if (padlen == 0)
{
WHERE;
return false;
}
if (size < padlen)
{
WHERE;
return false;
}
size -= padlen;
}
else
{
CRYPT *c;
UCHAR sign[SHA1_SIZE], sign2[SHA1_SIZE];
UCHAR key[SHA1_SIZE], keygen[SHA1_SIZE * 2];
// Validate the signature
if (se->UseHMac == false)
{
Copy(sign, p, SHA1_SIZE);
Copy(p, se->BulkRecvKey->Data, SHA1_SIZE);
Sha1(sign2, p, recv_size);
Copy(p, sign, SHA1_SIZE);
if (Cmp(sign, sign2, SHA1_SIZE) != 0)
{
HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, recv_size - SHA1_SIZE);
if (Cmp(p, sign2, SHA1_SIZE) != 0)
{
return false;
}
else
{
se->UseHMac = true;
}
}
}
else
{
HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, recv_size - SHA1_SIZE);
if (Cmp(p, sign2, SHA1_SIZE) != 0)
{
return false;
}
}
p += SHA1_SIZE;
size -= SHA1_SIZE;
// IV
if (size < SHA1_SIZE)
{
return false;
}
iv = p;
p += SHA1_SIZE;
size -= SHA1_SIZE;
// Decrypt
if (size < 1)
{
return false;
}
Copy(keygen + 0, se->BulkRecvKey->Data, SHA1_SIZE);
Copy(keygen + SHA1_SIZE, iv, SHA1_SIZE);
Sha1(key, keygen, sizeof(keygen));
c = NewCrypt(key, sizeof(key));
Encrypt(c, p, p, size);
FreeCrypt(c);
// padlen
padlen = p[size - 1];
if (padlen == 0)
{
return false;
}
if (size < padlen)
{
return false;
}
size -= padlen;
}
// SEQ NO
seq_no = READ_UINT64(p);
p += sizeof(UINT64);
size -= sizeof(UINT64);
if (seq_no == 0 || seq_no >= (0xF000000000000000ULL))
{
// Sequence number is invalid
return false;
}
if ((seq_no + RUDP_BULK_SEQ_NO_RANGE) < se->BulkRecvSeqNoMax)
{
// Sequence number is too small
return false;
}
se->LastRecvTick = r->Now;
payload = p;
payload_size = size;
se->BulkRecvSeqNoMax = MAX(seq_no, se->BulkRecvSeqNoMax);
// Send the received bulk packet to the Tube of the socket
RUDPInitSock(r, se);
if (se->TcpSock != NULL)
{
SOCK *s = se->TcpSock;
TUBE *t = s->BulkSendTube;
if (t != NULL)
{
TubeSendEx2(t, payload, payload_size, NULL, true, RUDP_BULK_MAX_RECV_PKTS_IN_QUEUE);
se->FlushBulkSendTube = true;
}
}
return true;
}
// Process the received packet (segment)
bool RUDPProcessRecvPacket(RUDP_STACK *r, RUDP_SESSION *se, void *recv_data, UINT recv_size)
{
UCHAR sign[SHA1_SIZE];
UCHAR sign2[SHA1_SIZE];
UCHAR *p;
UCHAR *iv;
UINT size;
UCHAR keygen[SHA1_SIZE * 2];
UCHAR key[SHA1_SIZE];
CRYPT *c;
UCHAR padlen;
UINT num_ack;
UINT i;
UINT64 seq_no;
UCHAR *payload;
UINT payload_size;
UINT64 max_ack;
UINT64 my_tick, your_tick;
// Validate arguments
if (r == NULL || se == NULL || recv_data == NULL || recv_size == 0)
{
return false;
}
p = (UCHAR *)recv_data;
size = recv_size;
if (size < SHA1_SIZE)
{
return false;
}
// Validate the signature
Copy(sign, p, SHA1_SIZE);
Copy(p, se->Key_Recv, SHA1_SIZE);
Sha1(sign2, p, recv_size);
Copy(p, sign, SHA1_SIZE);
if (r->Protocol == RUDP_PROTOCOL_DNS || r->Protocol == RUDP_PROTOCOL_ICMP)
{
XorData(sign2, sign2, r->SvcNameHash, SHA1_SIZE);
}
if (Cmp(sign, sign2, SHA1_SIZE) != 0)
{
//WHERE;
return false;
}
p += SHA1_SIZE;
size -= SHA1_SIZE;
// IV
if (size < SHA1_SIZE)
{
return false;
}
iv = p;
p += SHA1_SIZE;
size -= SHA1_SIZE;
// Decrypt
if (size < 1)
{
return false;
}
Copy(keygen + 0, iv, SHA1_SIZE);
Copy(keygen + SHA1_SIZE, se->Key_Recv, SHA1_SIZE);
Sha1(key, keygen, sizeof(keygen));
c = NewCrypt(key, sizeof(key));
Encrypt(c, p, p, size);
FreeCrypt(c);
// padlen
padlen = p[size - 1];
if (padlen == 0)
{
return false;
}
if (size < padlen)
{
return false;
}
size -= padlen;
// MyTick
if (size < sizeof(UINT64))
{
return false;
}
my_tick = READ_UINT64(p);
p += sizeof(UINT64);
size -= sizeof(UINT64);
// YourTick
if (size < sizeof(UINT64))
{
return false;
}
your_tick = READ_UINT64(p);
p += sizeof(UINT64);
size -= sizeof(UINT64);
if (your_tick > r->Now)
{
return false;
}
// MAX_ACK
if (size < sizeof(UINT64))
{
return false;
}
max_ack = READ_UINT64(p);
p += sizeof(UINT64);
size -= sizeof(UINT64);
// num_ack
if (size < sizeof(UINT))
{
return false;
}
num_ack = READ_UINT(p);
if (num_ack > RUDP_MAX_NUM_ACK)
{
return false;
}
p += sizeof(UINT);
size -= sizeof(UINT);
// ACKs
if (size < (sizeof(UINT64) * num_ack + sizeof(UINT64)))
{
return false;
}
if (max_ack >= 1)
{
RUDPProcessAck2(r, se, max_ack);
}
for (i = 0; i < num_ack; i++)
{
UINT64 seq = READ_UINT64(p);
RUDPProcessAck(r, se, seq);
p += sizeof(UINT64);
size -= sizeof(UINT64);
}
// Processing of the Tick (Calculation of RTT)
if (my_tick >= 2)
{
my_tick--;
}
se->YourTick = MAX(se->YourTick, my_tick);
se->LatestRecvMyTick = MAX(se->LatestRecvMyTick, your_tick);
if (se->LatestRecvMyTick2 != se->LatestRecvMyTick)
{
se->LatestRecvMyTick2 = se->LatestRecvMyTick;
se->CurrentRtt = (UINT)(r->Now - se->LatestRecvMyTick);
#ifdef RUDP_DETAIL_LOG
Debug("CurrentRTT = %u\n", se->CurrentRtt);
#endif // RUDP_DETAIL_LOG
}
// SEQ NO
seq_no = READ_UINT64(p);
p += sizeof(UINT64);
size -= sizeof(UINT64);
if (seq_no == 0)
{
// Sequence number of 0 is a invalid packet
return true;
}
if (seq_no == se->Magic_Disconnect)
{
// Disconnected from opponent
RUDPDisconnectSession(r, se, true);
return true;
}
// Update the last reception date and time
se->LastRecvTick = r->Now;
payload = p;
payload_size = size;
#ifdef RUDP_DETAIL_LOG
Debug("RUDP %X Segment Recv: %I64u (num_ack=%u, size=%u)\n", se, seq_no, num_ack, size);
#endif // RUDP_DETAIL_LOG
if (payload_size >= 1 && payload_size <= RUDP_MAX_SEGMENT_SIZE)
{
// Received one or more bytes of data
#ifdef RUDP_DETAIL_LOG
Debug("Recv Size: %X %I64u %u %u\n", se, seq_no, payload_size, recv_size);
#endif // RUDP_DETAIL_LOG
RUDPProcessRecvPayload(r, se, seq_no, payload, payload_size);
}
if (r->ServerMode == false)
{
if (se->Status == RUDP_SESSION_STATUS_CONNECT_SENT)
{
// Shift to the established state if the connection is not yet in established state
se->Status = RUDP_SESSION_STATUS_ESTABLISHED;
RUDPInitSock(r, se);
}
}
return true;
}
// Disconnect the session
void RUDPDisconnectSession(RUDP_STACK *r, RUDP_SESSION *se, bool disconnected_by_you)
{
// Validate arguments
if (r == NULL || se == NULL)
{
return;
}
if (se->DisconnectFlag == false)
{
UINT i;
se->DisconnectFlag = true;
se->DisconnectedByYou = disconnected_by_you;
Debug("R-UDP Session %X Disconnected. by you flag: %u\n", se, disconnected_by_you);
if (se->TcpSock != NULL)
{
// Disconnect a TCP socket
Disconnect(se->TcpSock);
ReleaseSock(se->TcpSock);
se->TcpSock = NULL;
}
// Send 5 disconnect signals serially if to disconnect from here
if (disconnected_by_you == false)
{
for (i = 0; i < 5; i++)
{
RUDPSendSegmentNow(r, se, se->Magic_Disconnect, NULL, 0);
}
}
}
}
// Initialize the TCP socket for the session
void RUDPInitSock(RUDP_STACK *r, RUDP_SESSION *se)
{
SOCK *s1, *s2;
UINT mss;
// Validate arguments
if (r == NULL || se == NULL || se->DisconnectFlag)
{
return;
}
if (se->TcpSock != NULL)
{
// It has already been created
return;
}
// Creating a TCP socket pair
if (NewTcpPair(&s1, &s2) == false)
{
// Failed to create. Disconnect the session
RUDPDisconnectSession(r, se, false);
return;
}
// Calculate the optimal MSS
mss = RUDPCalcBestMssForBulk(r, se);
if (r->ServerMode)
{
// Server mode
se->TcpSock = s2;
JoinSockToSockEvent(s2, r->SockEvent);
// Update the end point information of the socket s1
ZeroIP4(&s1->LocalIP);
s1->LocalPort = se->MyPort;
Copy(&s1->RemoteIP, &se->YourIp, sizeof(IP));
s1->RemotePort = se->YourPort;
if (IsLocalHostIP(&s1->RemoteIP) == false)
{
AddIpClient(&s1->RemoteIP);
s1->IpClientAdded = true;
}
s1->IsRUDPSocket = true;
s1->BulkSendKey = se->BulkSendKey;
s1->BulkRecvKey = se->BulkRecvKey;
AddRef(s1->BulkSendKey->Ref);
AddRef(s1->BulkRecvKey->Ref);
s1->RUDP_OptimizedMss = mss;
// Enqueue the newly created socket, and set the event
InsertQueueWithLock(r->NewSockQueue, s1);
Set(r->NewSockConnectEvent);
}
else
{
// Client mode
Lock(r->Lock);
{
if (r->TargetConnectedSock == NULL && r->DoNotSetTargetConnectedSock == false)
{
// Update the end point information of the socket s2
Copy(&s2->LocalIP, &r->UdpSock->LocalIP, sizeof(IP));
s2->LocalPort = se->MyPort;
Copy(&s2->RemoteIP, &se->YourIp, sizeof(IP));
s2->RemotePort = se->YourPort;
if (IsLocalHostIP(&s2->RemoteIP) == false)
{
AddIpClient(&s2->RemoteIP);
s2->IpClientAdded = true;
}
s2->IsRUDPSocket = true;
s2->BulkSendKey = se->BulkSendKey;
s2->BulkRecvKey = se->BulkRecvKey;
AddRef(s2->BulkSendKey->Ref);
AddRef(s2->BulkRecvKey->Ref);
s2->RUDP_OptimizedMss = mss;
// Register the socket to the RUDP stack
r->TargetConnectedSock = s2;
s2->R_UDP_Stack = r;
se->TcpSock = s1;
JoinSockToSockEvent(s1, r->SockEvent);
// Set the event to be set when the connection is successful
Set(r->TargetConnectedEvent);
}
else
{
Disconnect(s1);
Disconnect(s2);
ReleaseSock(s1);
ReleaseSock(s2);
}
}
Unlock(r->Lock);
}
}
// Process the received payload
void RUDPProcessRecvPayload(RUDP_STACK *r, RUDP_SESSION *se, UINT64 seq, void *payload_data, UINT payload_size)
{
RUDP_SEGMENT t;
RUDP_SEGMENT *s;
// Validate arguments
if (r == NULL || se == NULL || seq == 0 || payload_data == NULL || payload_size == 0 || payload_size > RUDP_MAX_SEGMENT_SIZE)
{
return;
}
if (seq > (se->LastRecvCompleteSeqNo + RUDP_MAX_NUM_ACK))
{
// Ignore the segment which have sequence number beyond the window size, and also not to reply an ACK
return;
}
if (seq <= se->LastRecvCompleteSeqNo)
{
// Do not receive the segment which have the sequence number that has been already received. However, reply an ACK for it
AddInt64Distinct(se->ReplyAckList, seq);
return;
}
Zero(&t, sizeof(t));
t.SeqNo = seq;
s = Search(se->RecvSegmentList, &t);
if (s != NULL)
{
// Do not receive the segment which have the sequence number that has been already received. However, reply an ACK for it
AddInt64Distinct(se->ReplyAckList, seq);
return;
}
// Received a segment of the new sequence number
s = ZeroMalloc(sizeof(RUDP_SEGMENT));
s->SeqNo = seq;
Copy(s->Data, payload_data, payload_size);
s->Size = payload_size;
Insert(se->RecvSegmentList, s);
// Reply an ACK
AddInt64Distinct(se->ReplyAckList, seq);
// Create a socket for session if it have not been created yet
//RUDPInitSock(r, se);
}
// Process the incoming ACK
void RUDPProcessAck(RUDP_STACK *r, RUDP_SESSION *se, UINT64 seq)
{
RUDP_SEGMENT t;
RUDP_SEGMENT *s;
// Validate arguments
if (r == NULL || se == NULL || seq == 0)
{
return;
}
Zero(&t, sizeof(t));
t.SeqNo = seq;
s = Search(se->SendSegmentList, &t);
if (s == NULL)
{
return;
}
Delete(se->SendSegmentList, s);
Free(s);
}
// Remove all segments which are preceding max_seq as already delivered
void RUDPProcessAck2(RUDP_STACK *r, RUDP_SESSION *se, UINT64 max_seq)
{
LIST *o;
UINT i;
// Validate arguments
if (r == NULL || se == NULL || max_seq == 0)
{
return;
}
o = NULL;
for (i = 0; i < LIST_NUM(se->SendSegmentList); i++)
{
RUDP_SEGMENT *s = LIST_DATA(se->SendSegmentList, i);
if (s->SeqNo <= max_seq)
{
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, s);
}
}
if (o != NULL)
{
for (i = 0; i < LIST_NUM(o); i++)
{
RUDP_SEGMENT *s = LIST_DATA(o, i);
Delete(se->SendSegmentList, s);
Free(s);
}
ReleaseList(o);
}
}
// Get the minimum sequence number which is trying to send
UINT64 RUDPGetCurrentSendingMinSeqNo(RUDP_SESSION *se)
{
RUDP_SEGMENT *s;
// Validate arguments
if (se == NULL)
{
return 0;
}
if (LIST_NUM(se->SendSegmentList) == 0)
{
return 0;
}
s = LIST_DATA(se->SendSegmentList, 0);
return s->SeqNo;
}
// Get the maximum sequence number which is trying to send
UINT64 RUDPGetCurrentSendingMaxSeqNo(RUDP_SESSION *se)
{
RUDP_SEGMENT *s;
// Validate arguments
if (se == NULL)
{
return 0;
}
if (LIST_NUM(se->SendSegmentList) == 0)
{
return 0;
}
s = LIST_DATA(se->SendSegmentList, (LIST_NUM(se->SendSegmentList) - 1));
return s->SeqNo;
}
// R-UDP segment transmission
void RUDPSendSegmentNow(RUDP_STACK *r, RUDP_SESSION *se, UINT64 seq_no, void *data, UINT size)
{
UCHAR dst[RUDP_MAX_PACKET_SIZE];
UCHAR *p;
UCHAR *iv;
LIST *o = NULL;
UINT i;
UCHAR padlen;
UINT current_size;
UCHAR sign[SHA1_SIZE];
UCHAR key[SHA1_SIZE];
UCHAR keygen[SHA1_SIZE * 2];
CRYPT *c;
UINT next_iv_pos;
UINT num_ack;
UINT icmp_type = 0;
// Validate arguments
if (r == NULL || se == NULL || (size != 0 && data == NULL) || (size > RUDP_MAX_SEGMENT_SIZE))
{
return;
}
Zero(dst, sizeof(dst));
p = dst;
// SIGN
Copy(p, se->Key_Send, SHA1_SIZE);
p += SHA1_SIZE;
// IV
iv = p;
Copy(iv, se->NextIv, SHA1_SIZE);
p += SHA1_SIZE;
for (i = 0; i < MIN(LIST_NUM(se->ReplyAckList), RUDP_MAX_NUM_ACK); i++)
{
UINT64 *seq = LIST_DATA(se->ReplyAckList, i);
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, seq);
}
// MyTick
WRITE_UINT64(p, r->Now);
p += sizeof(UINT64);
// YourTick
WRITE_UINT64(p, se->YourTick);
p += sizeof(UINT64);
// MAX_ACK
WRITE_UINT64(p, se->LastRecvCompleteSeqNo);
p += sizeof(UINT64);
// NUM_ACK
num_ack = LIST_NUM(o);
WRITE_UINT(p, num_ack);
p += sizeof(UINT);
if (o != NULL)
{
// ACK body
for (i = 0; i < LIST_NUM(o); i++)
{
UINT64 *seq = LIST_DATA(o, i);
WRITE_UINT64(p, *seq);
p += sizeof(UINT64);
Delete(se->ReplyAckList, seq);
Free(seq);
}
ReleaseList(o);
}
// SEQ
WRITE_UINT64(p, seq_no);
p += sizeof(UINT64);
// data
Copy(p, data, size);
p += size;
// padding
padlen = Rand8();
padlen = MAX(padlen, 1);
for (i = 0; i < padlen; i++)
{
*p = padlen;
p++;
}
current_size = (UINT)(p - dst);
// Encrypt
Copy(keygen + 0, iv, SHA1_SIZE);
Copy(keygen + SHA1_SIZE, se->Key_Send, SHA1_SIZE);
Sha1(key, keygen, sizeof(keygen));
c = NewCrypt(key, sizeof(key));
Encrypt(c, dst + SHA1_SIZE * 2, dst + SHA1_SIZE * 2, current_size - (SHA1_SIZE * 2));
FreeCrypt(c);
// Sign
Sha1(sign, dst, current_size);
if (r->Protocol == RUDP_PROTOCOL_DNS || r->Protocol == RUDP_PROTOCOL_ICMP)
{
XorData(sign, sign, r->SvcNameHash, SHA1_SIZE);
}
Copy(dst, sign, SHA1_SIZE);
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
icmp_type = se->Icmp_Type;
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
icmp_type = se->Dns_TranId;
}
RUDPSendPacket(r, &se->YourIp, se->YourPort, dst, current_size, icmp_type);
if (size >= 1)
{
se->LastSentTick = r->Now;
}
// Next IV
next_iv_pos = Rand32() % (current_size - SHA1_SIZE);
Copy(se->NextIv, dst + next_iv_pos, SHA1_SIZE);
#ifdef RUDP_DETAIL_LOG
Debug("RUDP %X Segment Sent: %I64u (num_ack=%u, size=%u)\n", se, seq_no, num_ack, size);
#endif // RUDP_DETAIL_LOG
if (size >= 1)
{
#ifdef RUDP_DETAIL_LOG
Debug("Send Size: %X %I64u %u %u\n", se, seq_no, size, current_size);
#endif // RUDP_DETAIL_LOG
}
}
// R-UDP segment transmission (only put into the queue)
void RUDPSendSegment(RUDP_STACK *r, RUDP_SESSION *se, void *data, UINT size)
{
RUDP_SEGMENT *s;
// Validate arguments
if (r == NULL || se == NULL || (size != 0 && data == NULL) || (size > RUDP_MAX_SEGMENT_SIZE))
{
return;
}
s = ZeroMalloc(sizeof(RUDP_SEGMENT));
Copy(s->Data, data, size);
s->Size = size;
s->SeqNo = se->NextSendSeqNo++;
Insert(se->SendSegmentList, s);
}
// Search for a session
RUDP_SESSION *RUDPSearchSession(RUDP_STACK *r, IP *my_ip, UINT my_port, IP *your_ip, UINT your_port)
{
RUDP_SESSION t;
RUDP_SESSION *se;
// Validate arguments
if (r == NULL || my_ip == NULL || your_ip == NULL)
{
return NULL;
}
Copy(&t.MyIp, my_ip, sizeof(IP));
t.MyPort = my_port;
Copy(&t.YourIp, your_ip, sizeof(IP));
t.YourPort = your_port;
se = Search(r->SessionList, &t);
return se;
}
// Release of the session
void RUDPFreeSession(RUDP_SESSION *se)
{
UINT i;
// Validate arguments
if (se == NULL)
{
return;
}
Debug("RUDPFreeSession %X\n", se);
for (i = 0; i < LIST_NUM(se->SendSegmentList); i++)
{
RUDP_SEGMENT *s = LIST_DATA(se->SendSegmentList, i);
Free(s);
}
ReleaseList(se->SendSegmentList);
for (i = 0; i < LIST_NUM(se->RecvSegmentList); i++)
{
RUDP_SEGMENT *s = LIST_DATA(se->RecvSegmentList, i);
Free(s);
}
ReleaseList(se->RecvSegmentList);
if (se->TcpSock != NULL)
{
Disconnect(se->TcpSock);
ReleaseSock(se->TcpSock);
}
ReleaseInt64List(se->ReplyAckList);
ReleaseFifo(se->RecvFifo);
ReleaseFifo(se->SendFifo);
ReleaseSharedBuffer(se->BulkSendKey);
ReleaseSharedBuffer(se->BulkRecvKey);
Free(se);
}
// Create a new session
RUDP_SESSION *RUDPNewSession(bool server_mode, IP *my_ip, UINT my_port, IP *your_ip, UINT your_port, UCHAR *init_key)
{
RUDP_SESSION *se;
UCHAR key1[SHA1_SIZE];
UCHAR key2[SHA1_SIZE];
UCHAR bulk_send_key[RUDP_BULK_KEY_SIZE_MAX];
UCHAR bulk_recv_key[RUDP_BULK_KEY_SIZE_MAX];
BUF *b;
se = ZeroMalloc(sizeof(RUDP_SESSION));
Copy(&se->MyIp, my_ip, sizeof(IP));
se->MyPort = my_port;
Copy(&se->YourIp, your_ip, sizeof(IP));
se->YourPort = your_port;
Copy(se->Key_Init, init_key, SHA1_SIZE);
se->LastSentTick = 0;
se->LastRecvTick = Tick64();
se->LatestRecvMyTick = Tick64();
se->NextSendSeqNo = 1;
se->ServerMode = server_mode;
se->SendSegmentList = NewList(RUDPCompareSegmentList);
se->RecvSegmentList = NewList(RUDPCompareSegmentList);
// Generate the two keys
b = NewBuf();
WriteBuf(b, init_key, SHA1_SIZE);
WriteBufStr(b, "zurukko");
Sha1(key1, b->Buf, b->Size);
FreeBuf(b);
b = NewBuf();
WriteBuf(b, init_key, SHA1_SIZE);
WriteBuf(b, key1, SHA1_SIZE);
WriteBufStr(b, "yasushineko");
Sha1(key2, b->Buf, b->Size);
FreeBuf(b);
// Generate the magic number for the KeepAlive
b = NewBuf();
WriteBuf(b, init_key, SHA1_SIZE);
WriteBufStr(b, "Magic_KeepAliveRequest");
Sha1(se->Magic_KeepAliveRequest, b->Buf, b->Size);
FreeBuf(b);
b = NewBuf();
WriteBuf(b, init_key, SHA1_SIZE);
WriteBufStr(b, "Magic_KeepAliveResponse");
Sha1(se->Magic_KeepAliveResponse, b->Buf, b->Size);
FreeBuf(b);
if (server_mode == false)
{
se->Magic_Disconnect = 0xffffffff00000000ULL | (UINT64)(Rand32());
}
Copy(se->Key_Init, init_key, SHA1_SIZE);
if (se->ServerMode)
{
Copy(se->Key_Send, key1, SHA1_SIZE);
Copy(se->Key_Recv, key2, SHA1_SIZE);
}
else
{
Copy(se->Key_Send, key2, SHA1_SIZE);
Copy(se->Key_Recv, key1, SHA1_SIZE);
}
Rand(se->NextIv, sizeof(se->NextIv));
se->ReplyAckList = NewInt64List(true);
se->NextKeepAliveInterval = RUDP_KEEPALIVE_INTERVAL_MIN + (Rand32() % (RUDP_KEEPALIVE_INTERVAL_MAX - RUDP_KEEPALIVE_INTERVAL_MIN));
se->RecvFifo = NewFifo();
se->SendFifo = NewFifo();
se->Dns_TranId = Rand16() % 65535 + 1;
// Generate the bulk transfer key
Rand(bulk_send_key, sizeof(bulk_send_key));
Rand(bulk_recv_key, sizeof(bulk_recv_key));
se->BulkSendKey = NewSharedBuffer(bulk_send_key, sizeof(bulk_send_key));
se->BulkRecvKey = NewSharedBuffer(bulk_recv_key, sizeof(bulk_recv_key));
Rand(se->BulkNextIv, sizeof(se->BulkNextIv));
Rand(se->BulkNextIv_V2, sizeof(se->BulkNextIv_V2));
se->BulkNextSeqNo = 1;
return se;
}
// Comparison function of the segment list items
int RUDPCompareSegmentList(void *p1, void *p2)
{
RUDP_SEGMENT *s1, *s2;
UINT r;
// Validate arguments
if (p1 == NULL || p2 == NULL)
{
return 0;
}
s1 = *((RUDP_SEGMENT **)p1);
s2 = *((RUDP_SEGMENT **)p2);
if (s1 == NULL || s2 == NULL)
{
return 0;
}
r = COMPARE_RET(s1->SeqNo, s2->SeqNo);
return r;
}
// Send a UDP packet
void RUDPSendPacket(RUDP_STACK *r, IP *dest_ip, UINT dest_port, void *data, UINT size, UINT icmp_type)
{
UDPPACKET *p;
// Validate arguments
if (r == NULL || dest_ip == NULL || dest_port == 0 || data == NULL || size == 0)
{
return;
}
p = NewUdpPacket(&r->UdpSock->LocalIP, r->UdpSock->LocalPort,
dest_ip, dest_port,
Clone(data, size), size);
if (r->Protocol == RUDP_PROTOCOL_ICMP || r->Protocol == RUDP_PROTOCOL_DNS)
{
// ICMP Type / DNS Tran ID
p->Type = icmp_type;
}
Add(r->SendPacketList, p);
}
// R-UDP main thread
void RUDPMainThread(THREAD *thread, void *param)
{
RUDP_STACK *r;
bool halt_flag = false;
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
r = (RUDP_STACK *)param;
AddWaitThread(thread);
NoticeThreadInit(thread);
while (true)
{
UINT wait_interval;
UINT i;
UINT min_wait_interval;
UINT num_ignore_errors = 0;
r->Now = Tick64();
Lock(r->Lock);
{
Copy(&r->NatT_IP_Safe, &r->NatT_IP, sizeof(IP));
Copy(&r->My_Private_IP_Safe, &r->My_Private_IP, sizeof(IP));
}
Unlock(r->Lock);
// Receive the data from the UDP socket
while (true)
{
UINT ret;
IP ip_src;
UINT port_src;
ret = RecvFrom(r->UdpSock, &ip_src, &port_src, r->TmpBuf, sizeof(r->TmpBuf));
if (ret == SOCK_LATER)
{
// There is no packet more
break;
}
else if (ret != 0)
{
// Receive a Packet
bool ok = false;
UDPPACKET *p = NewUdpPacket(&ip_src, port_src,
&r->UdpSock->LocalIP, r->UdpSock->LocalPort,
Clone(r->TmpBuf, ret), ret);
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
// Analyse the incoming ICMP packet
UINT ip_header_size = GetIpHeaderSize(p->Data, p->Size);
if (ip_header_size >= sizeof(IPV4_HEADER))
{
if (p->Size >= (ip_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + SHA1_SIZE))
{
IPV4_HEADER *ip_header = (IPV4_HEADER *)(((UCHAR *)p->Data) + 0);
ICMP_HEADER *icmp_header = (ICMP_HEADER *)(((UCHAR *)p->Data) + ip_header_size);
ICMP_ECHO *echo_header = (ICMP_ECHO *)(((UCHAR *)p->Data) + ip_header_size + sizeof(ICMP_HEADER));
if (icmp_header->Type == ICMP_TYPE_ECHO_RESPONSE ||
icmp_header->Type == (r->ServerMode ? ICMP_TYPE_INFORMATION_REQUEST : ICMP_TYPE_INFORMATION_REPLY))
{
UCHAR hash[SHA1_SIZE];
Sha1(hash, ((UCHAR *)p->Data) + ip_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + SHA1_SIZE,
p->Size - (ip_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + SHA1_SIZE));
if (Cmp(hash, ((UCHAR *)p->Data) + ip_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO), SHA1_SIZE) == 0)
{
UCHAR *new_data;
UINT new_data_size;
if (r->ServerMode)
{
// On the server side, the ICMP ID and the SEQ NO of received messages are treated as a source port number
Copy(&p->SrcPort, echo_header, sizeof(UINT));
}
// Record the Type
p->Type = icmp_header->Type;
// Erase the header part
new_data_size = p->Size - (ip_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + SHA1_SIZE);
new_data = Clone(((UCHAR *)p->Data) + ip_header_size + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + SHA1_SIZE, new_data_size);
Free(p->Data);
p->Data = new_data;
p->Size = new_data_size;
ok = true;
}
}
}
}
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
// Analyse the incoming DNS packet
UINT offset;
if (r->ServerMode == false)
{
offset = 42;
}
else
{
offset = 37;
}
if (p->Size > offset)
{
UCHAR *new_data;
UINT new_size = p->Size - offset;
p->Type = *((USHORT *)p->Data);
new_data = Clone(((UCHAR *)p->Data) + offset, new_size);
Free(p->Data);
p->Data = new_data;
p->Size = new_size;
ok = true;
}
}
else
{
// Don't do anything for ordinary UDP packet
ok = true;
}
if (ok)
{
// Process the received packet
RUDPRecvProc(r, p);
r->TotalPhysicalReceived += ret;
}
FreeUdpPacket(p);
}
else
{
if (r->UdpSock->IgnoreRecvErr)
{
// An ignorable reception error occurs
if ((num_ignore_errors++) >= MAX_NUM_IGNORE_ERRORS)
{
break;
}
}
else
{
// A non-ignorable reception error occurs
break;
}
}
}
// Call the interrupt notification callback function
if (r->ProcInterrupts != NULL)
{
r->ProcInterrupts(r);
}
RUDPInterruptProc(r);
// Send all packets in the transmission packet list
for (i = 0; i < LIST_NUM(r->SendPacketList); i++)
{
UDPPACKET *p = LIST_DATA(r->SendPacketList, i);
if (r->Protocol == RUDP_PROTOCOL_ICMP)
{
// In case of the ICMP protocol, assemble an ICMP header
UINT dst_size = sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + SHA1_SIZE + p->Size;
UCHAR *dst_data = ZeroMalloc(dst_size);
ICMP_HEADER *icmp_header = (ICMP_HEADER *)dst_data;
ICMP_ECHO *icmp_echo = (ICMP_ECHO *)(dst_data + sizeof(ICMP_HEADER));
UCHAR *hash = dst_data + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO);
UCHAR *icmp_data = dst_data + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + SHA1_SIZE;
// Header
icmp_header->Type = (UCHAR)p->Type;
icmp_header->Code = 0;
icmp_header->Checksum = 0;
if (r->ServerMode)
{
// On the server side, use the port number in the opponent internal data as ICMP ID and SEQ NO
Copy(icmp_echo, &p->DestPort, 4);
}
else
{
// Use the fixed ICMP ID and SEQ NO on the client side
icmp_echo->Identifier = Endian16(r->Client_IcmpId);
icmp_echo->SeqNo = Endian16(r->Client_IcmpSeqNo);
}
// Data body
Copy(icmp_data, p->Data, p->Size);
// Hash
Sha1(hash, icmp_data, p->Size);
// Checksum calculation
icmp_header->Checksum = IpChecksum(dst_data, dst_size);
// Replacement
Free(p->Data);
p->Data = dst_data;
p->Size = dst_size;
}
else if (r->Protocol == RUDP_PROTOCOL_DNS)
{
BUF *b = NewBuf();
// In case of over DNS protocol, assemble a header that conforms to the DNS protocol
if (r->ServerMode == false)
{
// DNS query header
USHORT us = Rand16() % 65535 + 1;
static UCHAR dns_query_header_1[] =
{
0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08,
};
static UCHAR dns_query_header_2[] =
{
0x00, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10,
0x00, 0x00, 0x00, 0x80, 0x00,
};
UCHAR rand_data[4];
char rand_str[MAX_SIZE];
Rand(rand_data, sizeof(rand_data));
BinToStr(rand_str, sizeof(rand_str), rand_data, sizeof(rand_data));
StrLower(rand_str);
WriteBuf(b, &us, sizeof(USHORT));
WriteBuf(b, dns_query_header_1, sizeof(dns_query_header_1));
WriteBuf(b, rand_str, 8);
WriteBuf(b, dns_query_header_2, sizeof(dns_query_header_2));
us = Endian16((USHORT)p->Size);
WriteBuf(b, &us, sizeof(USHORT));
WriteBuf(b, p->Data, p->Size);
}
else
{
// DNS response header
USHORT us = p->Type;
UINT ui;
static UCHAR dns_response_header_1[] =
{
0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x08,
};
static UCHAR dns_response_header_2[] =
{
0x00, 0x00, 0x30, 0x00, 0x01,
0xc0, 0x0c, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0xa4, 0x5b,
};
static UCHAR dns_response_header_3[] =
{
0x01, 0x00, 0x03, 0x08,
};
UCHAR rand_data[4];
char rand_str[MAX_SIZE];
Rand(rand_data, sizeof(rand_data));
BinToStr(rand_str, sizeof(rand_str), rand_data, sizeof(rand_data));
StrLower(rand_str);
WriteBuf(b, &us, sizeof(USHORT));
WriteBuf(b, dns_response_header_1, sizeof(dns_response_header_1));
WriteBuf(b, rand_str, 8);
WriteBuf(b, dns_response_header_2, sizeof(dns_response_header_2));
us = Endian16((USHORT)(p->Size + 4));
WriteBuf(b, &us, sizeof(USHORT));
WriteBuf(b, dns_response_header_3, sizeof(dns_response_header_3));
WriteBuf(b, p->Data, p->Size);
ui = Rand16() % (60 * 60 * 12) + (60 * 60 * 12);
WRITE_UINT(((UCHAR *)b->Buf) + 0x20, ui);
}
Free(p->Data);
p->Data = b->Buf;
p->Size = b->Size;
Free(b);
}
SendTo(r->UdpSock, &p->DstIP, p->DestPort, p->Data, p->Size);
r->TotalPhysicalSent += p->Size;
FreeUdpPacket(p);
}
DeleteAll(r->SendPacketList);
if (r->Halt)
{
// If it is necessary to stop, stop it after cycling through a loop
if (halt_flag == false)
{
halt_flag = true;
continue;
}
else
{
break;
}
}
// Rest the CPU until the next event
wait_interval = GetNextIntervalForInterrupt(r->Interrupt);
if (r->ServerMode)
{
min_wait_interval = RUDP_LOOP_WAIT_INTERVAL_S;
}
else
{
min_wait_interval = RUDP_LOOP_WAIT_INTERVAL_C;
}
if (wait_interval == INFINITE)
{
wait_interval = min_wait_interval;
}
else
{
wait_interval = MIN(min_wait_interval, wait_interval);
}
#ifdef RUDP_DETAIL_LOG
Debug("wait_interval = %u\n", wait_interval);
#endif // RUDP_DETAIL_LOG
if (wait_interval >= 1)
{
WaitSockEvent(r->SockEvent, wait_interval);
}
#ifdef RUDP_DETAIL_LOG
if (r->ServerMode)
{
char str1[MAX_SIZE];
char str2[MAX_SIZE];
double rate = 0.0;
ToStr64(str1, r->TotalPhysicalReceived);
ToStr64(str2, r->TotalLogicalReceived);
if (r->TotalPhysicalReceived >= 1)
{
rate = (double)r->TotalLogicalReceived / (double)r->TotalPhysicalReceived;
}
Debug("%s / %s %.4f\n", str1, str2, rate);
}
#endif // RUDP_DETAIL_LOG
}
Disconnect(r->UdpSock);
DelWaitThread(thread);
}
// Generate a appropriate register host name from the IP address
void RUDPGetRegisterHostNameByIP(char *dst, UINT size, IP *ip)
{
char tmp[16];
// Validate arguments
if (dst == NULL)
{
return;
}
if (ip != NULL && IsIP4(ip))
{
UCHAR hash[SHA1_SIZE];
Sha1(hash, ip->addr, 4);
BinToStr(tmp, sizeof(tmp), hash, 2);
}
else
{
UCHAR rand[2];
Rand(rand, 2);
BinToStr(tmp, sizeof(tmp), rand, 2);
}
StrLower(tmp);
Format(dst, size,
(IsUseAlternativeHostname() ? UDP_NAT_T_SERVER_TAG_ALT : UDP_NAT_T_SERVER_TAG),
tmp[2], tmp[3]);
if (false)
{
Debug("Hash Src IP: %r\n"
"Hash Dst HN: %s\n",
ip,
dst);
}
}
// Analyze the IP address and port number from the string
bool RUDPParseIPAndPortStr(void *data, UINT data_size, IP *ip, UINT *port)
{
char tmp[MAX_SIZE];
UINT i;
char ipstr[MAX_SIZE];
char *portstr;
// Validate arguments
if (data == NULL || ip == NULL || port == NULL)
{
return false;
}
Zero(tmp, sizeof(tmp));
Copy(tmp, data, MIN(data_size, sizeof(tmp) - 1));
if (StartWith(tmp, "IP=") == false)
{
return false;
}
i = SearchStrEx(tmp, "#", 0, true);
if (i != INFINITE)
{
tmp[i] = 0;
}
StrCpy(ipstr, sizeof(ipstr), tmp + 3);
i = SearchStrEx(ipstr, ",PORT=", 0, true);
if (i == INFINITE)
{
return false;
}
ipstr[i] = 0;
portstr = ipstr + i + 6;
StrToIP(ip, ipstr);
*port = ToInt(portstr);
return true;
}
// R-UDP NAT-T IP address acquisition thread
void RUDPIpQueryThread(THREAD *thread, void *param)
{
RUDP_STACK *r;
UINT64 next_getip_tick = 0;
UINT64 next_getprivate_ip_tick = 0;
UINT last_ip_hash = 0;
void *route_change_poller = NULL;
char current_hostname[MAX_SIZE];
bool last_time_ip_changed = false;
UINT num_retry = 0;
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
r = (RUDP_STACK *)param;
last_ip_hash = GetHostIPAddressHash32();
route_change_poller = NewRouteChange();
IsRouteChanged(route_change_poller);
Zero(current_hostname, sizeof(current_hostname));
while (r->Halt == false)
{
UINT ip_hash = GetHostIPAddressHash32();
UINT64 now = Tick64();
bool ip_changed = false;
if (ip_hash != last_ip_hash)
{
last_time_ip_changed = false;
}
if ((ip_hash != last_ip_hash) || (IsRouteChanged(route_change_poller)))
{
if (last_time_ip_changed == false)
{
// Call all getting functions from the beginning
// if the routing table or the IP address of this host has changed
next_getip_tick = 0;
next_getprivate_ip_tick = 0;
ip_changed = true;
last_ip_hash = ip_hash;
last_time_ip_changed = true;
}
}
else
{
last_time_ip_changed = false;
}
Lock(r->Lock);
{
if (StrCmpi(current_hostname, r->CurrentRegisterHostname) != 0)
{
// The target host name has changed
next_getip_tick = 0;
StrCpy(current_hostname, sizeof(current_hostname), r->CurrentRegisterHostname);
}
}
Unlock(r->Lock);
// Get the IP address of the NAT-T server with DNS
if (next_getip_tick == 0 || now >= next_getip_tick)
{
IP ip;
if (GetIP4(&ip, current_hostname) && IsZeroIp(&ip) == false)
{
Lock(r->Lock);
{
// Debug("%r %r\n",&r->NatT_IP, &ip);
if (CmpIpAddr(&r->NatT_IP, &ip) != 0)
{
// WHERE;
ip_changed = true;
Copy(&r->NatT_IP, &ip, sizeof(IP));
}
}
Unlock(r->Lock);
}
if (IsZeroIp(&r->NatT_IP))
{
num_retry++;
next_getip_tick = now + MIN((UINT64)UDP_NAT_T_GET_IP_INTERVAL * (UINT64)num_retry, (UINT64)UDP_NAT_T_GET_IP_INTERVAL_MAX);
}
else
{
next_getip_tick = now + (UINT64)UDP_NAT_T_GET_IP_INTERVAL_AFTER;
}
if (ip_changed)
{
Debug("NAT-T: NAT-T Server IP (%s): %r\n", current_hostname, &r->NatT_IP);
r->NatT_GetTokenNextTick = 0;
r->NatT_RegisterNextTick = 0;
r->NatT_GetTokenFailNum = 0;
r->NatT_RegisterFailNum = 0;
r->NatT_TranId = Rand64();
SetSockEvent(r->SockEvent);
}
}
// Get a private IP address of this host using TCP
if (next_getprivate_ip_tick == 0 || now >= next_getprivate_ip_tick)
{
IP ip;
if (GetMyPrivateIP(&ip, false))
{
Lock(r->Lock);
{
Copy(&r->My_Private_IP, &ip, sizeof(IP));
}
Unlock(r->Lock);
}
if (IsZeroIp(&r->My_Private_IP))
{
next_getprivate_ip_tick = now + (UINT64)UDP_NAT_T_GET_PRIVATE_IP_INTERVAL;
}
else
{
next_getprivate_ip_tick = now + (UINT64)GenRandInterval(UDP_NAT_T_GET_PRIVATE_IP_INTERVAL_AFTER_MIN, UDP_NAT_T_GET_PRIVATE_IP_INTERVAL_AFTER_MAX);
}
Debug("NAT-T: My Private IP: %r\n", &r->My_Private_IP);
}
if (r->Halt)
{
break;
}
Wait(r->HaltEvent, RUDP_LOOP_WAIT_INTERVAL_S);
}
FreeRouteChange(route_change_poller);
}
// Generate a random intervals
UINT GenRandInterval(UINT min, UINT max)
{
UINT a, b;
a = MIN(min, max);
b = MAX(min, max);
if (a == b)
{
return a;
}
return (Rand32() % (b - a)) + a;
}
// Identify the private IP of the interface which is used to connect to the Internet currently
bool GetMyPrivateIP(IP *ip, bool from_vg)
{
SOCK *s;
IP t;
char *hostname = UDP_NAT_T_GET_PRIVATE_IP_TCP_SERVER;
// Validate arguments
if (ip == NULL)
{
return false;
}
s = ConnectEx(hostname, UDP_NAT_T_PORT_FOR_TCP_1, UDP_NAT_T_GET_PRIVATE_IP_CONNECT_TIMEOUT);
if (s == NULL)
{
s = ConnectEx(hostname, UDP_NAT_T_PORT_FOR_TCP_2, UDP_NAT_T_GET_PRIVATE_IP_CONNECT_TIMEOUT);
if (s == NULL)
{
s = ConnectEx(GetRandHostNameForGetMyPrivateIP(), UDP_NAT_T_PORT_FOR_TCP_1, UDP_NAT_T_GET_PRIVATE_IP_CONNECT_TIMEOUT);
if (s == NULL)
{
return false;
}
}
}
Copy(&t, &s->LocalIP, sizeof(IP));
Disconnect(s);
ReleaseSock(s);
if (IsZeroIp(&t))
{
return false;
}
Copy(ip, &t, sizeof(IP));
return true;
}
char *GetRandHostNameForGetMyPrivateIP()
{
char *hosts[] =
{
"www.microsoft.com",
"www.yahoo.com",
"www.bing.com",
};
UINT num_hosts = 3;
return hosts[Rand32() % num_hosts];
}
// Function to wait until changing any IP address of the host or expiring the specified time or waking the event
void WaitUntilHostIPAddressChanged(void *p, EVENT *event, UINT timeout, UINT ip_check_interval)
{
UINT64 start, end;
UINT last_hash;
// Validate arguments
if (timeout == 0x7FFFFFFF)
{
timeout = 0xFFFFFFFF;
}
if (ip_check_interval == 0)
{
ip_check_interval = 0xFFFFFFFF;
}
if (event == NULL || timeout == 0)
{
return;
}
start = Tick64();
end = start + (UINT64)timeout;
last_hash = GetHostIPAddressHash32();
while (true)
{
UINT64 now = Tick64();
UINT next_interval;
if (now >= end)
{
break;
}
if (p != NULL)
{
if (IsRouteChanged(p))
{
break;
}
}
if (last_hash != GetHostIPAddressHash32())
{
break;
}
next_interval = (UINT)(end - now);
next_interval = MIN(next_interval, ip_check_interval);
if (Wait(event, next_interval))
{
break;
}
}
}
void *InitWaitUntilHostIPAddressChanged()
{
void *p = NewRouteChange();
if (p != NULL)
{
IsRouteChanged(p);
}
return p;
}
void FreeWaitUntilHostIPAddressChanged(void *p)
{
FreeRouteChange(p);
}
// Get whether the specified IPv6 address is on the local network
bool IsIPv6LocalNetworkAddress(IP *ip)
{
UINT type;
LIST *o;
UINT i;
bool ret = false;
IP mask64;
// Validate arguments
if (ip == NULL)
{
return false;
}
if (IsIP6(ip) == false)
{
return false;
}
if (IsZeroIp(ip))
{
return false;
}
type = GetIPAddrType6(ip);
if (type & IPV6_ADDR_LOCAL_UNICAST)
{
return true;
}
if ((type & IPV6_ADDR_GLOBAL_UNICAST) == 0)
{
return false;
}
IntToSubnetMask6(&mask64, 64);
o = GetHostIPAddressList();
ret = false;
for (i = 0; i < LIST_NUM(o); i++)
{
IP *p = LIST_DATA(o, i);
if (IsIP6(p))
{
if (IsZeroIp(p) == false)
{
if (IsLocalHostIP6(p) == false)
{
if (IsInSameNetwork6(p, ip, &mask64))
{
ret = true;
}
}
}
}
}
FreeHostIPAddressList(o);
return ret;
}
// Check whether the specified IP address is localhost or the IP address of the local interface of itself
bool IsIPLocalHostOrMySelf(IP *ip)
{
LIST *o;
bool ret = false;
UINT i;
// Validate arguments
if (ip == NULL)
{
return false;
}
o = GetHostIPAddressList();
if (o == NULL)
{
return false;
}
for (i = 0; i < LIST_NUM(o); i++)
{
IP *p = LIST_DATA(o, i);
if (CmpIpAddr(p, ip) == 0)
{
ret = true;
break;
}
}
FreeHostIPAddressList(o);
if (IsLocalHostIP4(ip) || IsLocalHostIP6(ip))
{
ret = true;
}
return ret;
}
// Obtain the hash value of combining all of the IP address assigned to the host
UINT GetHostIPAddressHash32()
{
BUF *b;
UINT i;
UCHAR hash[SHA1_SIZE];
UINT ret;
LIST *o = GetHostIPAddressList();
if (o == NULL)
{
return 0;
}
b = NewBuf();
for (i = 0; i < LIST_NUM(o); i++)
{
IP *ip = LIST_DATA(o, i);
WriteBuf(b, ip, sizeof(IP));
WriteBufStr(b, ":-) yas (-:");
}
FreeHostIPAddressList(o);
WriteBuf(b, rand_port_numbers, sizeof(rand_port_numbers));
Sha1(hash, b->Buf, b->Size);
FreeBuf(b);
Copy(&ret, hash, sizeof(UINT));
return ret;
}
// Create an IPv4 UDP socket destined for a particular target
SOCK *NewUDP4ForSpecificIp(IP *target_ip, UINT port)
{
SOCK *s;
IP local_ip;
// Validate arguments
if (target_ip == NULL || IsZeroIP(target_ip) || IsIP4(target_ip) == false)
{
target_ip = NULL;
}
Zero(&local_ip, sizeof(local_ip));
GetBestLocalIpForTarget(&local_ip, target_ip);
s = NewUDP4(port, &local_ip);
if (s == NULL)
{
s = NewUDP4(port, NULL);
}
return s;
}
// Get the best self IPv4 address to connect to the target IPv4 address
bool GetBestLocalIpForTarget(IP *local_ip, IP *target_ip)
{
bool ret = false;
ROUTE_ENTRY *e;
IP ip2;
UINT n = 0;
IP zero_ip;
// Validate arguments
Zero(local_ip, sizeof(IP));
ZeroIP4(&zero_ip);
if (target_ip == NULL)
{
target_ip = &zero_ip;
}
if (local_ip == NULL || IsIP4(target_ip) == false)
{
return false;
}
Copy(&ip2, target_ip, sizeof(IP));
while (true)
{
n++;
if (n >= 64)
{
break;
}
e = GetBestRouteEntry(&ip2);
if (e != NULL)
{
if (IsZeroIp(&e->GatewayIP))
{
Free(e);
break;
}
if (e->LocalRouting)
{
ret = true;
Copy(local_ip, &e->GatewayIP, sizeof(IP));
Free(e);
break;
}
else
{
Copy(&ip2, &e->GatewayIP, sizeof(IP));
}
Free(e);
}
}
if (ret == false)
{
if (IsLocalHostIP4(target_ip))
{
GetLocalHostIP4(local_ip);
ret = true;
}
}
return ret;
}
// Create a R-UDP client (Connection via NAT-T gateway)
SOCK *NewRUDPClientNatT(char *svc_name, IP *ip, UINT *error_code, UINT timeout, bool *cancel, char *hint_str, char *target_hostname)
{
IP nat_t_ip;
UINT dummy_int = 0;
UINT64 giveup_tick;
bool dummy_bool = false;
SOCK_EVENT *sock_event;
SOCK *sock;
bool same_lan = false;
char hostname[MAX_SIZE];
if (timeout == 0)
{
timeout = RUDP_TIMEOUT;
}
if (error_code == NULL)
{
error_code = &dummy_int;
}
if (cancel == NULL)
{
cancel = &dummy_bool;
}
*error_code = RUDP_ERROR_UNKNOWN;
if (svc_name == NULL || ip == NULL)
{
return NULL;
}
ListenTcpForPopupFirewallDialog();
giveup_tick = Tick64() + (UINT64)timeout;
// Get the IP address of the NAT-T server
RUDPGetRegisterHostNameByIP(hostname, sizeof(hostname), ip);
if (GetIP4Ex(&nat_t_ip, hostname, 0, cancel) == false)
{
*error_code = RUDP_ERROR_NAT_T_NO_RESPONSE;
return NULL;
}
if (Tick64() >= giveup_tick)
{
*error_code = RUDP_ERROR_TIMEOUT;
return NULL;
}
if (*cancel)
{
*error_code = RUDP_ERROR_USER_CANCELED;
return NULL;
}
sock = NewUDP4ForSpecificIp(&nat_t_ip, 0);
if (sock == NULL)
{
*error_code = RUDP_ERROR_UNKNOWN;
return NULL;
}
else
{
UINT64 next_send_request_tick = 0;
INTERRUPT_MANAGER *interrupt = NewInterruptManager();
UINT64 tran_id = Rand64();
UINT tmp_size = 65536;
UCHAR *tmp = Malloc(tmp_size);
char result_ip_str[MAX_SIZE];
IP result_ip;
UINT result_port;
SOCK *ret = NULL;
UINT num_tries = 0;
UINT64 current_cookie = 0;
AddInterrupt(interrupt, giveup_tick);
sock_event = NewSockEvent();
JoinSockToSockEvent(sock, sock_event);
// Communication with the NAT-T server
while (true)
{
UINT64 now = Tick64();
UINT interval;
UINT r;
IP src_ip;
UINT src_port;
UINT err;
UINT num_ignore_errors = 0;
if (now >= giveup_tick)
{
// Time-out
LABEL_TIMEOUT:
*error_code = RUDP_ERROR_NAT_T_NO_RESPONSE;
break;
}
if (*cancel)
{
// User canceled
*error_code = RUDP_ERROR_USER_CANCELED;
break;
}
err = INFINITE;
// Receive a response packet from the NAT-T server
while (err == INFINITE)
{
r = RecvFrom(sock, &src_ip, &src_port, tmp, tmp_size);
if (r == SOCK_LATER)
{
// No packet
break;
}
else if (r == 0)
{
if (sock->IgnoreRecvErr == false)
{
// Communication error
goto LABEL_TIMEOUT;
}
else
{
if ((num_ignore_errors++) >= MAX_NUM_IGNORE_ERRORS)
{
goto LABEL_TIMEOUT;
}
}
}
else
{
// Check the source IP address and the port number
if (CmpIpAddr(&src_ip, &nat_t_ip) == 0 && src_port == UDP_NAT_T_PORT)
{
BUF *b = NewBuf();
PACK *p;
WriteBuf(b, tmp, r);
SeekBuf(b, 0, 0);
p = BufToPack(b);
if (p != NULL)
{
UINT64 cookie = PackGetInt64(p, "cookie");
if (cookie != 0)
{
current_cookie = cookie;
}
// Compare tran_id
if (PackGetInt64(p, "tran_id") == tran_id)
{
// Compare opcode
if (PackCmpStr(p, "opcode", "nat_t_connect_request"))
{
bool ok = PackGetBool(p, "ok");
bool multi_candidate = PackGetBool(p, "multi_candidates");
if (ok)
{
// Success
PackGetStr(p, "result_ip", result_ip_str, sizeof(result_ip_str));
StrToIP(&result_ip, result_ip_str);
result_port = PackGetInt(p, "result_port");
same_lan = PackGetBool(p, "same_lan");
if (result_port != 0)
{
if (IsZeroIp(&result_ip) == false)
{
if ((sock->IPv6 == false && IsIP4(&result_ip)) ||
(sock->IPv6 && IsIP6(&result_ip)))
{
err = RUDP_ERROR_OK;
}
}
}
}
else if (multi_candidate)
{
// There are two or more computers behind the specified IP address
err = RUDP_ERROR_NAT_T_TWO_OR_MORE;
}
else
{
// Failure
err = RUDP_ERROR_NAT_T_NOT_FOUND;
}
}
}
FreePack(p);
}
FreeBuf(b);
}
}
}
if (err != INFINITE)
{
*error_code = err;
break;
}
if (next_send_request_tick == 0 || now >= next_send_request_tick)
{
// Send a connection request to the NAT-T server
BUF *b;
char ip_str[MAX_SIZE];
PACK *p = NewPack();
PackAddStr(p, "opcode", "nat_t_connect_request");
PackAddInt64(p, "tran_id", tran_id);
IPToStr(ip_str, sizeof(ip_str), ip);
PackAddStr(p, "dest_ip", ip_str);
PackAddInt64(p, "cookie", current_cookie);
if (IsEmptyStr(hint_str) == false)
{
PackAddStr(p, "hint", hint_str);
}
if (IsEmptyStr(target_hostname) == false)
{
PackAddStr(p, "target_hostname", target_hostname);
}
PackAddStr(p, "svc_name", svc_name);
PackAddInt(p, "nat_traversal_version", UDP_NAT_TRAVERSAL_VERSION);
b = PackToBuf(p);
FreePack(p);
SendTo(sock, &nat_t_ip, UDP_NAT_T_PORT, b->Buf, b->Size);
FreeBuf(b);
// Determine the next transmission time
next_send_request_tick = now + (UINT64)UDP_NAT_T_CONNECT_INTERVAL * (UINT64)(Power(2, MAX(num_tries, 6)));
num_tries++;
AddInterrupt(interrupt, next_send_request_tick);
}
interval = GetNextIntervalForInterrupt(interrupt);
interval = MIN(interval, 50);
WaitSockEvent(sock_event, interval);
}
Free(tmp);
FreeInterruptManager(interrupt);
if (*error_code == RUDP_ERROR_OK)
{
UINT remain_timeout;
UINT64 now = Tick64();
// Success to get the IP address and the port number of the target
// Get the rest timeout tolerance
if (now <= giveup_tick)
{
remain_timeout = (UINT)(giveup_tick - now);
}
else
{
remain_timeout = 0;
}
remain_timeout = MAX(remain_timeout, 2000);
if (same_lan)
{
// Discard current UDP socket and create a new UDP socket in NewRUDPClientDirect().
// Because using a UDP socket which used for communication with the NAT-T server
// can cause trouble when the client and the server exists in the same LAN.
ReleaseSockEvent(sock_event);
ReleaseSock(sock);
sock = NULL;
sock_event = NULL;
}
ret = NewRUDPClientDirect(svc_name, &result_ip, result_port, error_code, remain_timeout, cancel,
sock, sock_event, 0, false);
}
if (sock_event != NULL)
{
ReleaseSockEvent(sock_event);
}
if (sock != NULL)
{
if (ret == NULL)
{
Disconnect(sock);
}
ReleaseSock(sock);
}
return ret;
}
}
// Listen to the TCP for a moment to show the firewall dialog
void ListenTcpForPopupFirewallDialog()
{
#ifdef OS_WIN32
static bool tried = false;
if (tried == false)
{
SOCK *s;
tried = true;
s = ListenAnyPortEx2(false, true);
if (s != NULL)
{
Disconnect(s);
ReleaseSock(s);
}
}
#endif // OS_WIN32
}
// Create a R-UDP client (direct connection)
SOCK *NewRUDPClientDirect(char *svc_name, IP *ip, UINT port, UINT *error_code, UINT timeout, bool *cancel, SOCK *sock, SOCK_EVENT *sock_event, UINT local_port, bool over_dns_mode)
{
RUDP_STACK *r;
UINT dummy_int = 0;
SOCK *ret = NULL;
// Validate arguments
if (error_code == NULL)
{
error_code = &dummy_int;
}
if (timeout == 0)
{
timeout = RUDP_TIMEOUT;
}
*error_code = RUDP_ERROR_UNKNOWN;
if (svc_name == NULL || ip == NULL || port == 0)
{
return NULL;
}
r = NewRUDP(false, svc_name, NULL, NULL, NULL, local_port, sock, sock_event, false, over_dns_mode, ip, NULL, 0, NULL);
if (r == NULL)
{
*error_code = RUDP_ERROR_UNKNOWN;
return NULL;
}
// Set the port number and the target IP address
Lock(r->Lock);
{
Copy(&r->TargetIp, ip, sizeof(IP));
r->TargetPort = port;
r->TargetIpAndPortInited = true;
}
Unlock(r->Lock);
SetSockEvent(r->SockEvent);
// Wait for a connection success/failure to the target IP address
WaitEx(r->TargetConnectedEvent, timeout, cancel);
Lock(r->Lock);
{
if (r->TargetConnectedSock != NULL)
{
// The connection succeeded
ret = r->TargetConnectedSock;
r->TargetConnectedSock = NULL;
}
else
{
r->DoNotSetTargetConnectedSock = true;
}
}
Unlock(r->Lock);
if (ret == NULL)
{
// Stop the R-UDP stack if the connection has failed
*error_code = RUDP_ERROR_TIMEOUT;
FreeRUDP(r);
}
else if (cancel != NULL && (*cancel))
{
// User canceled
*error_code = RUDP_ERROR_USER_CANCELED;
Disconnect(ret);
ReleaseSock(ret);
ret = NULL;
}
else
{
*error_code = RUDP_ERROR_OK;
}
return ret;
}
// Creating a R-UDP server
RUDP_STACK *NewRUDPServer(char *svc_name, RUDP_STACK_INTERRUPTS_PROC *proc_interrupts, RUDP_STACK_RPC_RECV_PROC *proc_rpc_recv, void *param, UINT port, bool no_natt_register, bool over_dns_mode, volatile UINT *natt_global_udp_port, UCHAR rand_port_id, IP *listen_ip)
{
RUDP_STACK *r;
// Validate arguments
if (IsEmptyStr(svc_name))
{
return NULL;
}
if (g_no_rudp_server)
{
return NULL;
}
ListenTcpForPopupFirewallDialog();
r = NewRUDP(true, svc_name, proc_interrupts, proc_rpc_recv, param, port, NULL, NULL, no_natt_register, over_dns_mode, NULL, natt_global_udp_port, rand_port_id, listen_ip);
if (r == NULL)
{
return NULL;
}
return r;
}
// Creating a R-UDP
RUDP_STACK *NewRUDP(bool server_mode, char *svc_name, RUDP_STACK_INTERRUPTS_PROC *proc_interrupts, RUDP_STACK_RPC_RECV_PROC *proc_rpc_recv, void *param, UINT port, SOCK *sock, SOCK_EVENT *sock_event, bool server_no_natt_register, bool over_dns_mode, IP *client_target_ip, volatile UINT *natt_global_udp_port, UCHAR rand_port_id, IP *listen_ip)
{
RUDP_STACK *r;
char tmp[MAX_SIZE];
UCHAR pid_hash[SHA1_SIZE];
UINT pid;
USHORT pid_us;
// Validate arguments
if (IsEmptyStr(svc_name))
{
return NULL;
}
ListenTcpForPopupFirewallDialog();
if (sock == NULL)
{
if (server_mode == false && client_target_ip != NULL)
{
sock = NewUDP4ForSpecificIp(client_target_ip, port);
}
else
{
if (rand_port_id == 0)
{
sock = NewUDPEx2(port, false, listen_ip);
}
else
{
sock = NewUDPEx2RandMachineAndExePath(false, listen_ip, 0, rand_port_id);
}
}
if (sock == NULL)
{
return NULL;
}
}
else
{
AddRef(sock->ref);
}
if (port == 0)
{
port = sock->LocalPort;
}
if (rand_port_id != 0)
{
rand_port_numbers[rand_port_id] = port;
}
if (sock_event == NULL)
{
sock_event = NewSockEvent();
}
else
{
AddRef(sock_event->ref);
}
r = ZeroMalloc(sizeof(RUDP_STACK));
r->NatT_SessionKey = Rand64();
StrCpy(r->SvcName, sizeof(r->SvcName), svc_name);
r->RandPortId = rand_port_id;
r->NatTGlobalUdpPort = natt_global_udp_port;
r->ServerMode = server_mode;
r->Interrupt = NewInterruptManager();
r->SessionList = NewList(RUDPCompareSessionList);
r->UdpSock = sock;
r->Port = port;
r->SockEvent = sock_event;
r->HaltEvent = NewEvent();
r->Now = Tick64();
r->Lock = NewLock();
r->Param = param;
r->TargetConnectedEvent = NewEvent();
r->SendPacketList = NewList(NULL);
r->NewSockConnectEvent = NewEvent();
r->NewSockQueue = NewQueue();
r->NatT_TranId = Rand64();
r->NatT_SourceIpList = NewListFast(NULL);
StrCpy(tmp, sizeof(tmp), r->SvcName);
Trim(tmp);
StrLower(tmp);
Sha1(r->SvcNameHash, tmp, StrLen(tmp));
r->Client_IcmpId = (USHORT)(Rand32() % 65534 + 1);
r->Client_IcmpSeqNo = (USHORT)(Rand32() % 65534 + 1);
// Determination of the type of the protocol
r->Protocol = RUDP_PROTOCOL_UDP;
if (r->Port == MAKE_SPECIAL_PORT(IP_PROTO_ICMPV4))
{
r->Protocol = RUDP_PROTOCOL_ICMP;
// Generate the ICMP ID based on the process ID
#ifdef OS_WIN32
pid = (UINT)MsGetProcessId();
#else // OS_WIN32
pid = (UINT)getpid();
#endif // OS_WIN32
pid = Endian32(pid);
Sha1(pid_hash, &pid, sizeof(UINT));
pid_us = READ_USHORT(pid_hash);
if (pid_us == 0 || pid_us == 0xFFFF)
{
pid_us = 1;
}
r->Client_IcmpId = pid_us;
}
else if (over_dns_mode)
{
r->Protocol = RUDP_PROTOCOL_DNS;
}
if (r->ServerMode)
{
r->NoNatTRegister = server_no_natt_register;
if (r->Protocol == RUDP_PROTOCOL_ICMP || r->Protocol == RUDP_PROTOCOL_DNS)
{
// Never register to the NAT-T server in case of using the DNS or the ICMP
r->NoNatTRegister = true;
}
}
if (true
)
{
RUDPGetRegisterHostNameByIP(r->CurrentRegisterHostname, sizeof(r->CurrentRegisterHostname), NULL);
}
if (r->ServerMode)
{
r->ProcInterrupts = proc_interrupts;
r->ProcRpcRecv = proc_rpc_recv;
}
if (r->ServerMode && r->NoNatTRegister == false
)
{
r->IpQueryThread = NewThread(RUDPIpQueryThread, r);
}
JoinSockToSockEvent(r->UdpSock, r->SockEvent);
r->Thread = NewThread(RUDPMainThread, r);
WaitThreadInit(r->Thread);
return r;
}
// R-UDP session comparison function
int RUDPCompareSessionList(void *p1, void *p2)
{
RUDP_SESSION *s1, *s2;
UINT r;
// Validate arguments
if (p1 == NULL || p2 == NULL)
{
return 0;
}
s1 = *((RUDP_SESSION **)p1);
s2 = *((RUDP_SESSION **)p2);
if (s1 == NULL || s2 == NULL)
{
return 0;
}
r = CmpIpAddr(&s1->YourIp, &s2->YourIp);
if (r != 0)
{
return r;
}
r = COMPARE_RET(s1->YourPort, s2->YourPort);
if (r != 0)
{
return r;
}
r = CmpIpAddr(&s1->MyIp, &s2->MyIp);
if (r != 0)
{
return r;
}
r = COMPARE_RET(s1->MyPort, s2->MyPort);
if (r != 0)
{
return r;
}
return 0;
}
// Release of the R-UDP
void FreeRUDP(RUDP_STACK *r)
{
UINT i;
// Validate arguments
if (r == NULL)
{
return;
}
r->Halt = true;
Set(r->HaltEvent);
SetSockEvent(r->SockEvent);
if (r->ServerMode && r->NoNatTRegister == false)
{
if (r->IpQueryThread != NULL)
{
WaitThread(r->IpQueryThread, INFINITE);
ReleaseThread(r->IpQueryThread);
}
}
WaitThread(r->Thread, INFINITE);
ReleaseThread(r->Thread);
for (i = 0; i < LIST_NUM(r->SessionList); i++)
{
RUDP_SESSION *se = LIST_DATA(r->SessionList, i);
RUDPFreeSession(se);
}
ReleaseList(r->SessionList);
for (i = 0; i < LIST_NUM(r->SendPacketList); i++)
{
UDPPACKET *p = LIST_DATA(r->SendPacketList, i);
FreeUdpPacket(p);
}
while (true)
{
SOCK *s = GetNext(r->NewSockQueue);
if (s == NULL)
{
break;
}
Disconnect(s);
ReleaseSock(s);
}
for (i = 0; i < LIST_NUM(r->NatT_SourceIpList); i++)
{
RUDP_SOURCE_IP *sip = (RUDP_SOURCE_IP *)LIST_DATA(r->NatT_SourceIpList, i);
Free(sip);
}
ReleaseList(r->NatT_SourceIpList);
ReleaseQueue(r->NewSockQueue);
ReleaseList(r->SendPacketList);
FreeInterruptManager(r->Interrupt);
Disconnect(r->UdpSock);
ReleaseSock(r->UdpSock);
ReleaseSockEvent(r->SockEvent);
ReleaseEvent(r->HaltEvent);
ReleaseEvent(r->TargetConnectedEvent);
ReleaseEvent(r->NewSockConnectEvent);
Disconnect(r->TargetConnectedSock);
ReleaseSock(r->TargetConnectedSock);
DeleteLock(r->Lock);
if (r->RandPortId != 0)
{
rand_port_numbers[r->RandPortId] = 0;
}
Free(r);
}
// Generate a hash from the current computer name and the process name
void GetCurrentMachineIpProcessHash(void *hash)
{
// Validate arguments
if (hash == NULL)
{
return;
}
Lock(machine_ip_process_hash_lock);
{
if (IsZero(machine_ip_process_hash, SHA1_SIZE))
{
GetCurrentMachineIpProcessHashInternal(machine_ip_process_hash);
}
Copy(hash, machine_ip_process_hash, SHA1_SIZE);
}
Unlock(machine_ip_process_hash_lock);
}
void GetCurrentMachineIpProcessHashInternal(void *hash)
{
BUF *b;
LIST *ip_list;
char machine_name[MAX_SIZE];
wchar_t exe_path[MAX_PATH];
char *product_id = NULL;
// Validate arguments
if (hash == NULL)
{
return;
}
#ifdef OS_WIN32
product_id = MsRegReadStr(REG_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ProductId");
if (product_id == NULL)
{
product_id = MsRegReadStr(REG_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "ProductId");
}
#endif // OS_WIN32
b = NewBuf();
GetMachineHostName(machine_name, sizeof(machine_name));
Trim(machine_name);
StrUpper(machine_name);
GetExeNameW(exe_path, sizeof(exe_path));
UniTrim(exe_path);
UniStrUpper(exe_path);
WriteBuf(b, machine_name, StrSize(machine_name));
WriteBuf(b, exe_path, UniStrSize(exe_path));
WriteBuf(b, product_id, StrSize(product_id));
ip_list = GetHostIPAddressList();
if (ip_list != NULL)
{
UINT i;
for (i = 0; i < LIST_NUM(ip_list); i++)
{
IP *ip = LIST_DATA(ip_list, i);
WriteBuf(b, ip, sizeof(IP));
}
}
FreeHostIPAddressList(ip_list);
Sha1(hash, b->Buf, b->Size);
FreeBuf(b);
Free(product_id);
}
// Create a pair of pre-bound TCP sockets
bool NewTcpPair(SOCK **s1, SOCK **s2)
{
SOCK *a;
SOCK *s, *c;
TUBE *t1, *t2;
SOCK_EVENT *e1, *e2;
// Validate arguments
if (s1 == NULL || s2 == NULL)
{
return false;
}
a = ListenAnyPortEx2(true, true);
if (a == NULL)
{
return false;
}
c = Connect("127.0.0.1", a->LocalPort);
if (c == NULL)
{
ReleaseSock(a);
return false;
}
s = Accept(a);
if (s == NULL)
{
ReleaseSock(c);
ReleaseSock(a);
return false;
}
ReleaseSock(a);
if ((s->LocalPort != c->RemotePort) || (s->RemotePort != c->LocalPort))
{
ReleaseSock(s);
ReleaseSock(c);
return false;
}
NewTubePair(&t1, &t2, sizeof(TCP_PAIR_HEADER));
// Creating a socket event
e1 = NewSockEvent();
e2 = NewSockEvent();
SetTubeSockEvent(t1, e1);
SetTubeSockEvent(t2, e2);
AddRef(t1->Ref);
AddRef(t2->Ref);
s->BulkRecvTube = c->BulkSendTube = t1;
s->BulkSendTube = c->BulkRecvTube = t2;
ReleaseSockEvent(e1);
ReleaseSockEvent(e2);
*s1 = s;
*s2 = c;
return true;
}
// Listen in any available port
SOCK *ListenAnyPortEx2(bool local_only, bool disable_ca)
{
UINT i;
SOCK *s;
for (i = 40000; i < 65536; i++)
{
s = ListenEx(i, local_only);
if (s != NULL)
{
return s;
}
}
return NULL;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define X509_STORE_CTX_get0_cert(o) ((o)->cert)
#endif
// Verify client SSL certificate during TLS handshake.
//
// (actually, only save the certificate for later authentication in Protocol.c)
int SslCertVerifyCallback(int preverify_ok, X509_STORE_CTX *ctx)
{
SSL *ssl;
struct SslClientCertInfo *clientcert;
X509 *cert;
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
clientcert = SSL_get_ex_data(ssl, GetSslClientCertIndex());
if (clientcert != NULL)
{
clientcert->PreverifyErr = X509_STORE_CTX_get_error(ctx);
clientcert->PreverifyErrMessage[0] = '\0';
if (!preverify_ok)
{
const char *msg = X509_verify_cert_error_string(clientcert->PreverifyErr);
StrCpy(clientcert->PreverifyErrMessage, PREVERIFY_ERR_MESSAGE_SIZE, (char *)msg);
Debug("SslCertVerifyCallback preverify error: '%s'\n", msg);
}
else
{
cert = X509_STORE_CTX_get0_cert(ctx);
if (cert != NULL)
{
X *tmpX = X509ToX(cert); // this only wraps cert, but we need to make a copy
if (!CompareX(tmpX, clientcert->X))
{
X *copyX = CloneX(tmpX);
if (clientcert->X != NULL)
{
FreeX(clientcert->X);
}
clientcert->X = copyX;
}
tmpX->do_not_free = true; // do not release inner X509 object
FreeX(tmpX);
}
}
}
return 1; /* allow the verification process to continue */
}
// Create a new SSL pipe
SSL_PIPE *NewSslPipe(bool server_mode, X *x, K *k, DH_CTX *dh)
{
return NewSslPipeEx(server_mode, x, k, dh, false, NULL);
}
// Create a new SSL pipe with extended options
SSL_PIPE *NewSslPipeEx(bool server_mode, X *x, K *k, DH_CTX *dh, bool verify_peer, struct SslClientCertInfo *clientcert)
{
SSL_PIPE *s;
SSL *ssl;
SSL_CTX *ssl_ctx = NewSSLCtx(server_mode);
Lock(openssl_lock);
{
if (server_mode)
{
#ifdef SSL_OP_NO_SSLv3
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3);
#endif // SSL_OP_NO_SSLv3
#ifdef SSL_OP_NO_TLSv1_3
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); // For some reason pppd under linux doesn't like it
#endif
AddChainSslCertOnDirectory(ssl_ctx);
if (dh != NULL)
{
SSL_CTX_set_tmp_dh(ssl_ctx, dh->dh);
}
}
if (verify_peer)
{
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, SslCertVerifyCallback);
}
if (dh != NULL)
{
SSL_CTX_set_options(ssl_ctx, SSL_OP_SINGLE_DH_USE);
}
if (server_mode == false)
{
SSL_CTX_set_options(ssl_ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
}
ssl = SSL_new(ssl_ctx);
SSL_set_ex_data(ssl, GetSslClientCertIndex(), clientcert);
}
Unlock(openssl_lock);
s = ZeroMalloc(sizeof(SSL_PIPE));
s->ssl = ssl;
s->ssl_ctx = ssl_ctx;
s->ServerMode = server_mode;
s->SslInOut = NewSslBioSsl();
s->RawIn = NewSslBioMem();
s->RawOut = NewSslBioMem();
if (x != NULL && k != NULL)
{
Lock(openssl_lock);
{
SSL_use_certificate(s->ssl, x->x509);
SSL_use_PrivateKey(s->ssl, k->pkey);
}
Unlock(openssl_lock);
}
if (s->ServerMode == false)
{
SSL_set_connect_state(s->ssl);
}
else
{
SSL_set_accept_state(s->ssl);
}
SSL_set_bio(s->ssl, s->RawIn->bio, s->RawOut->bio);
BIO_set_ssl(s->SslInOut->bio, s->ssl, BIO_NOCLOSE);
//s->RawIn->NoFree = true;
s->RawOut->NoFree = true;
return s;
}
// Synchronization of the SSL pipe
bool SyncSslPipe(SSL_PIPE *s)
{
UINT i;
// Validate arguments
if (s == NULL || s->IsDisconnected)
{
return false;
}
for (i = 0; i < 2; i++)
{
if (SslBioSync(s->RawIn, true, false) == false)
{
s->IsDisconnected = true;
Debug("SyncSslPipe: s->RawIn error.\n");
return false;
}
if (SslBioSync(s->RawOut, false, true) == false)
{
s->IsDisconnected = true;
Debug("SyncSslPipe: s->RawOut error.\n");
return false;
}
if (SslBioSync(s->SslInOut, true, true) == false)
{
s->IsDisconnected = true;
Debug("SyncSslPipe: s->SslInOut error.\n");
return false;
}
}
return true;
}
// Release of the SSL pipe
void FreeSslPipe(SSL_PIPE *s)
{
// Validate arguments
if (s == NULL)
{
return;
}
FreeSslBio(s->SslInOut);
FreeSslBio(s->RawIn);
FreeSslBio(s->RawOut);
SSL_free(s->ssl);
SSL_CTX_free(s->ssl_ctx);
Free(s);
}
// Release of the SSL BIO
void FreeSslBio(SSL_BIO *b)
{
// Validate arguments
if (b == NULL)
{
return;
}
if (b->NoFree == false)
{
BIO_free(b->bio);
}
ReleaseFifo(b->RecvFifo);
ReleaseFifo(b->SendFifo);
Free(b);
}
// Create a new SSL BIO (SSL)
SSL_BIO *NewSslBioSsl()
{
SSL_BIO *b = ZeroMalloc(sizeof(SSL_BIO));
b->bio = BIO_new(BIO_f_ssl());
b->RecvFifo = NewFifo();
b->SendFifo = NewFifo();
return b;
}
// Create a new SSL BIO (memory)
SSL_BIO *NewSslBioMem()
{
SSL_BIO *b = ZeroMalloc(sizeof(SSL_BIO));
b->bio = BIO_new(BIO_s_mem());
b->RecvFifo = NewFifo();
b->SendFifo = NewFifo();
return b;
}
// Synchronize memory contents of the SSL BIO with the FIFO
bool SslBioSync(SSL_BIO *b, bool sync_send, bool sync_recv)
{
// Validate arguments
if (b == NULL)
{
return false;
}
if (b->IsDisconnected)
{
return false;
}
// Write the contents of the SendFifo to the BIO
if (sync_send)
{
while (b->SendFifo->size >= 1)
{
int r = BIO_write(b->bio, GetFifoPointer(b->SendFifo), FifoSize(b->SendFifo));
if (r == 0)
{
b->IsDisconnected = true;
WHERE;
return false;
}
else
{
if (r < 0)
{
if (BIO_should_retry(b->bio))
{
break;
}
else
{
b->IsDisconnected = true;
WHERE;
return false;
}
}
else
{
ReadFifo(b->SendFifo, NULL, (UINT)r);
}
}
}
}
// Save to the RecvFifo by reading from the BIO
if (sync_recv)
{
while (true)
{
UCHAR tmp[4096];
int r;
r = BIO_read(b->bio, tmp, sizeof(tmp));
if (r == 0)
{
b->IsDisconnected = true;
WHERE;
return false;
}
else
{
if (r < 0)
{
if (BIO_should_retry(b->bio))
{
break;
}
else
{
b->IsDisconnected = true;
WHERE;
Debug("OpenSSL Error: %s\n", ERR_error_string(ERR_peek_last_error(), NULL));
return false;
}
}
else
{
WriteFifo(b->RecvFifo, tmp, (UINT)r);
}
}
}
}
return true;
}
// Release the memory for the return value of the ICMP API
void IcmpApiFreeResult(ICMP_RESULT *ret)
{
// Validate arguments
if (ret == NULL)
{
return;
}
if (ret->Data != NULL)
{
Free(ret->Data);
}
Free(ret);
}
// Send an ICMP Echo using ICMP API
ICMP_RESULT *IcmpApiEchoSend(IP *dest_ip, UCHAR ttl, UCHAR *data, UINT size, UINT timeout)
{
#ifdef OS_WIN32
// Validate arguments
if (dest_ip == NULL || IsIP4(dest_ip) == false || (size != 0 && data == NULL))
{
return NULL;
}
if (ttl == 0)
{
ttl = 127;
}
if (IsIcmpApiSupported())
{
HANDLE h;
DWORD dw;
IPAddr dest_addr;
UINT reply_size;
ICMP_ECHO_REPLY *reply;
ICMP_RESULT *ret = NULL;
IP_OPTION_INFORMATION opt;
h = w32net->IcmpCreateFile();
if (h == INVALID_HANDLE_VALUE)
{
return NULL;
}
Zero(&opt, sizeof(opt));
opt.Ttl = ttl;
IPToInAddr((struct in_addr *)&dest_addr, dest_ip);
reply_size = sizeof(*reply) + size + 64;
reply = ZeroMalloc(reply_size);
dw = w32net->IcmpSendEcho(h, dest_addr, data, size, &opt, reply, reply_size, timeout);
ret = ZeroMalloc(sizeof(ICMP_RESULT));
if (dw >= 1 && reply->Status == IP_SUCCESS)
{
ret->Ok = true;
}
else
{
switch (reply->Status)
{
case IP_DEST_NET_UNREACHABLE:
ret->Type = ICMP_TYPE_DESTINATION_UNREACHABLE;
ret->Code = ICMP_CODE_NET_UNREACHABLE;
break;
case IP_DEST_HOST_UNREACHABLE:
ret->Type = ICMP_TYPE_DESTINATION_UNREACHABLE;
ret->Code = ICMP_CODE_HOST_UNREACHABLE;
break;
case IP_DEST_PROT_UNREACHABLE:
ret->Type = ICMP_TYPE_DESTINATION_UNREACHABLE;
ret->Code = ICMP_CODE_PROTOCOL_UNREACHABLE;
break;
case IP_DEST_PORT_UNREACHABLE:
ret->Type = ICMP_TYPE_DESTINATION_UNREACHABLE;
ret->Code = ICMP_CODE_PORT_UNREACHABLE;
break;
case IP_TTL_EXPIRED_TRANSIT:
ret->Type = ICMP_TYPE_TIME_EXCEEDED;
ret->Code = ICMP_CODE_TTL_EXCEEDED_IN_TRANSIT;
break;
case IP_TTL_EXPIRED_REASSEM:
ret->Type = ICMP_TYPE_TIME_EXCEEDED;
ret->Code = ICMP_CODE_FRAGMENT_REASSEMBLY_TIME_EXCEEDED;
break;
default:
ret->Timeout = true;
break;
}
}
if (ret->Timeout == false)
{
ret->Ttl = reply->Options.Ttl;
ret->Rtt = reply->RoundTripTime;
InAddrToIP(&ret->IpAddress, (struct in_addr *)&reply->Address);
if (reply->DataSize >= 1 && reply->Data != NULL)
{
ret->DataSize = reply->DataSize;
ret->Data = Clone(reply->Data, reply->DataSize);
}
}
Free(reply);
w32net->IcmpCloseHandle(h);
return ret;
}
else
{
return NULL;
}
#else // OS_WIN32
return NULL;
#endif // OS_WIN32
}
// Detect whether the ICMP API is supported
bool IsIcmpApiSupported()
{
#ifdef OS_WIN32
if (w32net->IcmpCloseHandle != NULL &&
w32net->IcmpCreateFile != NULL &&
w32net->IcmpSendEcho != NULL)
{
return true;
}
#endif // OS_WIN32
return false;
}
// Initialize the routing table change detector
ROUTE_CHANGE *NewRouteChange()
{
#ifdef OS_WIN32
return Win32NewRouteChange();
#else // OS_WIN32
return NULL;
#endif // OS_WIN32
}
// Release the routing table change detector
void FreeRouteChange(ROUTE_CHANGE *r)
{
#ifdef OS_WIN32
Win32FreeRouteChange(r);
#endif // OS_WIN32
}
// Get whether the routing table has been changed
bool IsRouteChanged(ROUTE_CHANGE *r)
{
#ifdef OS_WIN32
return Win32IsRouteChanged(r);
#else // OS_WIN32
return false;
#endif // OS_WIN32
}
// Routing table change detector function (Win32)
#ifdef OS_WIN32
ROUTE_CHANGE *Win32NewRouteChange()
{
ROUTE_CHANGE *r;
bool ret;
if (MsIsNt() == false)
{
return NULL;
}
if (w32net->CancelIPChangeNotify == NULL ||
w32net->NotifyRouteChange == NULL)
{
return NULL;
}
r = ZeroMalloc(sizeof(ROUTE_CHANGE));
r->Data = ZeroMalloc(sizeof(ROUTE_CHANGE_DATA));
r->Data->Overlapped.hEvent = CreateEventA(NULL, false, true, NULL);
ret = w32net->NotifyRouteChange(&r->Data->Handle, &r->Data->Overlapped);
if (!(ret == NO_ERROR || ret == WSA_IO_PENDING || WSAGetLastError() == WSA_IO_PENDING))
{
Free(r->Data);
Free(r);
return NULL;
}
return r;
}
void Win32FreeRouteChange(ROUTE_CHANGE *r)
{
// Validate arguments
if (r == NULL)
{
return;
}
w32net->CancelIPChangeNotify(&r->Data->Overlapped);
CloseHandle(r->Data->Overlapped.hEvent);
Free(r->Data);
Free(r);
}
bool Win32IsRouteChanged(ROUTE_CHANGE *r)
{
// Validate arguments
if (r == NULL)
{
return false;
}
if ((r->Data->NumCalled++) == 0)
{
return true;
}
if (WaitForSingleObject(r->Data->Overlapped.hEvent, 0) == WAIT_OBJECT_0)
{
w32net->NotifyRouteChange(&r->Data->Handle, &r->Data->Overlapped);
return true;
}
return false;
}
typedef struct WIN32_ACCEPT_CHECK_DATA
{
bool IsIPv6;
bool Rejected;
} WIN32_ACCEPT_CHECK_DATA;
int CALLBACK Win32AcceptCheckCallback(LPWSABUF lpCallerId, LPWSABUF lpCallerData, LPQOS pQos,
LPQOS lpGQOS, LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,
GROUP FAR *g, DWORD_PTR dwCallbackData)
{
return CF_ACCEPT;
}
// Accept function for Win32
SOCKET Win32Accept(SOCK *sock, SOCKET s, struct sockaddr *addr, int *addrlen, bool ipv6)
{
SOCKET ret;
WIN32_ACCEPT_CHECK_DATA d;
UINT err;
int initial_addrlen = *addrlen;
UINT num_error = 0;
UINT zero = 0;
UINT tmp = 0;
UINT ret_size = 0;
// Validate arguments
if (sock == NULL || s == INVALID_SOCKET)
{
return INVALID_SOCKET;
}
if (sock->hAcceptEvent == NULL)
{
sock->hAcceptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
WSAEventSelect(s, sock->hAcceptEvent, FD_ACCEPT | FD_CLOSE);
}
L_LOOP:
if (sock->CancelAccept)
{
return INVALID_SOCKET;
}
Zero(&d, sizeof(d));
d.IsIPv6 = ipv6;
*addrlen = initial_addrlen;
Zero(addr, initial_addrlen);
ret = WSAAccept(s, addr, addrlen, Win32AcceptCheckCallback, (DWORD_PTR)&d);
if (ret == INVALID_SOCKET)
{
err = WSAGetLastError();
if (err == WSAEWOULDBLOCK)
{
//Debug("!!! WSAAccept: WSAEWOULDBLOCK\n");
UINT wait_ret = WaitForSingleObject(sock->hAcceptEvent, 1234);
if (wait_ret == WAIT_OBJECT_0 || wait_ret == WAIT_TIMEOUT)
{
goto L_LOOP;
}
Debug("!!! WaitForSingleObject Error. ret=%u GetLastError=%u\n", wait_ret, GetLastError());
}
num_error++;
Debug("!!! WSAAccept Error: %u rej=%u num=%u tick=%I64u\n", err, d.Rejected, num_error, Tick64());
if (d.Rejected && err == WSAECONNREFUSED)
{
goto L_LOOP;
}
if (err == WSAETIMEDOUT)
{
goto L_LOOP;
}
}
else
{
// Remove a new socket from the event
WSAEventSelect(ret, sock->hAcceptEvent, 0);
// Restore the new socket to synchronized
WSAIoctl(ret, FIONBIO, &zero, sizeof(zero), &tmp, sizeof(tmp), &ret_size, NULL, NULL);
}
return ret;
}
#endif // OS_WIN32
#define USE_OLD_GETIP
// Set the arp_filter in Linux
void SetLinuxArpFilter()
{
char *filename = "/proc/sys/net/ipv4/conf/all/arp_filter";
char *data = "1\n";
IO *o;
o = FileCreate(filename);
if (o == NULL)
{
return;
}
FileWrite(o, data, StrLen(data));
FileFlush(o);
FileClose(o);
}
// Determine whether the string is a IPv6 mask
bool IsIpMask6(char *str)
{
IP mask;
// Validate arguments
if (str == NULL)
{
return false;
}
return StrToMask6(&mask, str);
}
// Determine whether the string is a IPv6 address
bool IsStrIPv6Address(char *str)
{
IP ip;
// Validate arguments
if (str == NULL)
{
return false;
}
if (StrToIP6(&ip, str) == false)
{
return false;
}
return true;
}
// Convert the subnet mask to an integer
UINT SubnetMaskToInt6(IP *a)
{
UINT i;
// Validate arguments
if (IsIP6(a) == false)
{
return 0;
}
for (i = 0; i <= 128; i++)
{
IP tmp;
IntToSubnetMask6(&tmp, i);
if (CmpIpAddr(a, &tmp) == 0)
{
return i;
}
}
return 0;
}
UINT SubnetMaskToInt4(IP *a)
{
UINT i;
// Validate arguments
if (IsIP4(a) == false)
{
return 0;
}
for (i = 0; i <= 32; i++)
{
IP tmp;
IntToSubnetMask4(&tmp, i);
if (CmpIpAddr(a, &tmp) == 0)
{
return i;
}
}
return 0;
}
UINT SubnetMaskToInt(IP *a)
{
if (IsIP6(a))
{
return SubnetMaskToInt6(a);
}
else
{
return SubnetMaskToInt4(a);
}
}
// Determine whether the specified IP address is a subnet mask
bool IsSubnetMask6(IP *a)
{
UINT i;
// Validate arguments
if (IsIP6(a) == false)
{
return false;
}
for (i = 0; i <= 128; i++)
{
IP tmp;
IntToSubnetMask6(&tmp, i);
if (CmpIpAddr(a, &tmp) == 0)
{
return true;
}
}
return false;
}
// Generate a local address from the MAC address
void GenerateEui64LocalAddress(IP *a, UCHAR *mac)
{
UCHAR tmp[8];
// Validate arguments
if (a == NULL || mac == NULL)
{
return;
}
GenerateEui64Address6(tmp, mac);
ZeroIP6(a);
a->ipv6_addr[0] = 0xfe;
a->ipv6_addr[1] = 0x80;
Copy(&a->ipv6_addr[8], tmp, 8);
}
// Generate the EUI-64 address from the MAC address
void GenerateEui64Address6(UCHAR *dst, UCHAR *mac)
{
// Validate arguments
if (dst == NULL || mac == NULL)
{
return;
}
Copy(dst, mac, 3);
Copy(dst + 5, mac, 3);
dst[3] = 0xff;
dst[4] = 0xfe;
dst[0] = ((~(dst[0] & 0x02)) & 0x02) | (dst[0] & 0xfd);
}
// Examine whether two IP addresses are in the same network
bool IsInSameNetwork6ByStr(char *ip1, char *ip2, char *subnet)
{
IP p1, p2, s;
if (StrToIP6(&p1, ip1) == false)
{
return false;
}
if (StrToIP6(&p2, ip2) == false)
{
return false;
}
if (StrToMask6(&s, subnet) == false)
{
return false;
}
return IsInSameNetwork6(&p1, &p2, &s);
}
bool IsInSameNetwork6(IP *a1, IP *a2, IP *subnet)
{
IP prefix1, prefix2;
// Validate arguments
if (IsIP6(a1) == false || IsIP6(a2) == false || IsIP6(subnet) == false)
{
return false;
}
if (a1->ipv6_scope_id != a2->ipv6_scope_id)
{
return false;
}
GetPrefixAddress6(&prefix1, a1, subnet);
GetPrefixAddress6(&prefix2, a2, subnet);
if (CmpIpAddr(&prefix1, &prefix2) == 0)
{
return true;
}
return false;
}
bool IsInSameNetwork4(IP *a1, IP *a2, IP *subnet)
{
IP net1, net2;
// Validate arguments
if (IsIP4(a1) == false || IsIP4(a2) == false || IsIP4(subnet) == false)
{
return false;
}
IPAnd4(&net1, a1, subnet);
IPAnd4(&net2, a2, subnet);
if (CmpIpAddr(&net1, &net2) == 0)
{
return true;
}
return false;
}
bool IsInSameNetwork4Standard(IP *a1, IP *a2)
{
IP subnet;
SetIP(&subnet, 255, 255, 0, 0);
return IsInSameNetwork4(a1, a2, &subnet);
}
// Get the prefix address
void GetPrefixAddress6(IP *dst, IP *ip, IP *subnet)
{
// Validate arguments
if (dst == NULL || ip == NULL || subnet == NULL)
{
return;
}
IPAnd6(dst, ip, subnet);
dst->ipv6_scope_id = ip->ipv6_scope_id;
}
// Get the type of the IPv6 address
UINT GetIPv6AddrType(IPV6_ADDR *addr)
{
IP ip;
// Validate arguments
if (addr == NULL)
{
return 0;
}
IPv6AddrToIP(&ip, addr);
return GetIPAddrType6(&ip);
}
UINT GetIPAddrType6(IP *ip)
{
UINT ret = 0;
// Validate arguments
if (IsIP6(ip) == false)
{
return 0;
}
if (ip->ipv6_addr[0] == 0xff)
{
IP all_node, all_router;
GetAllNodeMulticaseAddress6(&all_node);
GetAllRouterMulticastAddress6(&all_router);
ret |= IPV6_ADDR_MULTICAST;
if (Cmp(ip->ipv6_addr, all_node.ipv6_addr, 16) == 0)
{
ret |= IPV6_ADDR_ALL_NODE_MULTICAST;
}
else if (Cmp(ip->ipv6_addr, all_router.ipv6_addr, 16) == 0)
{
ret |= IPV6_ADDR_ALL_ROUTER_MULTICAST;
}
else
{
if (ip->ipv6_addr[1] == 0x02 && ip->ipv6_addr[2] == 0 && ip->ipv6_addr[3] == 0 &&
ip->ipv6_addr[4] == 0 && ip->ipv6_addr[5] == 0 && ip->ipv6_addr[6] == 0 &&
ip->ipv6_addr[7] == 0 && ip->ipv6_addr[8] == 0 && ip->ipv6_addr[9] == 0 &&
ip->ipv6_addr[10] == 0 && ip->ipv6_addr[11] == 0x01 && ip->ipv6_addr[12] == 0xff)
{
ret |= IPV6_ADDR_SOLICIATION_MULTICAST;
}
}
}
else
{
ret |= IPV6_ADDR_UNICAST;
if (ip->ipv6_addr[0] == 0xfe && (ip->ipv6_addr[1] & 0xc0) == 0x80)
{
ret |= IPV6_ADDR_LOCAL_UNICAST;
}
else
{
ret |= IPV6_ADDR_GLOBAL_UNICAST;
if (IsZero(&ip->ipv6_addr, 16))
{
ret |= IPV6_ADDR_ZERO;
}
else
{
IP loopback;
GetLoopbackAddress6(&loopback);
if (Cmp(ip->ipv6_addr, loopback.ipv6_addr, 16) == 0)
{
ret |= IPV6_ADDR_LOOPBACK;
}
}
}
}
return ret;
}
// Loopback address
void GetLoopbackAddress6(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return;
}
ZeroIP6(ip);
ip->ipv6_addr[15] = 0x01;
}
// All-nodes multicast address
void GetAllNodeMulticaseAddress6(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return;
}
ZeroIP6(ip);
ip->ipv6_addr[0] = 0xff;
ip->ipv6_addr[1] = 0x02;
ip->ipv6_addr[15] = 0x01;
}
// All-routers multicast address
void GetAllRouterMulticastAddress6(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return;
}
ZeroIP6(ip);
ip->ipv6_addr[0] = 0xff;
ip->ipv6_addr[1] = 0x02;
ip->ipv6_addr[15] = 0x02;
}
// Logical operation of the IPv4 address
void IPAnd4(IP *dst, IP *a, IP *b)
{
UINT i;
// Validate arguments
if (dst == NULL || a == NULL || b == NULL || IsIP4(a) == false || IsIP4(b) == false)
{
Zero(dst, sizeof(IP));
return;
}
i = IPToUINT(a) & IPToUINT(b);
UINTToIP(dst, i);
}
// Logical operation of the IPv6 address
void IPAnd6(IP *dst, IP *a, IP *b)
{
UINT i;
// Validate arguments
if (dst == NULL || IsIP6(a) == false || IsIP6(b) == false)
{
ZeroIP6(dst);
return;
}
ZeroIP6(dst);
for (i = 0; i < 16; i++)
{
dst->ipv6_addr[i] = a->ipv6_addr[i] & b->ipv6_addr[i];
}
}
// Creating a subnet mask
void IntToSubnetMask6(IP *ip, UINT i)
{
UINT j = i / 8;
UINT k = i % 8;
UINT z;
IP a;
ZeroIP6(&a);
for (z = 0; z < 16; z++)
{
if (z < j)
{
a.ipv6_addr[z] = 0xff;
}
else if (z == j)
{
a.ipv6_addr[z] = ~(0xff >> k);
}
}
Copy(ip, &a, sizeof(IP));
}
// Convert the IP address to a string
void IP6AddrToStr(char *str, UINT size, IPV6_ADDR *addr)
{
// Validate arguments
if (str == NULL || addr == NULL)
{
return;
}
IPToStr6Array(str, size, addr->Value);
}
void IPToStr6Array(char *str, UINT size, UCHAR *bytes)
{
IP ip;
// Validate arguments
if (str == NULL || bytes == NULL)
{
return;
}
SetIP6(&ip, bytes);
IPToStr6(str, size, &ip);
}
void IPToStr6(char *str, UINT size, IP *ip)
{
char tmp[MAX_SIZE];
IPToStr6Inner(tmp, ip);
StrCpy(str, size, tmp);
}
void IPToStr6Inner(char *str, IP *ip)
{
UINT i;
USHORT values[8];
UINT zero_started_index;
UINT max_zero_len;
UINT max_zero_start;
IP a;
// Validate arguments
if (str == NULL || ip == NULL)
{
return;
}
Copy(&a, ip, sizeof(IP));
for (i = 0; i < 8; i++)
{
Copy(&values[i], &a.ipv6_addr[i * 2], sizeof(USHORT));
values[i] = Endian16(values[i]);
}
// Search for omittable part
zero_started_index = INFINITE;
max_zero_len = 0;
max_zero_start = INFINITE;
for (i = 0; i < 9; i++)
{
USHORT v = (i != 8 ? values[i] : 1);
if (v == 0)
{
if (zero_started_index == INFINITE)
{
zero_started_index = i;
}
}
else
{
UINT zero_len;
if (zero_started_index != INFINITE)
{
zero_len = i - zero_started_index;
if (zero_len >= 2)
{
if (max_zero_len < zero_len)
{
max_zero_start = zero_started_index;
max_zero_len = zero_len;
}
}
zero_started_index = INFINITE;
}
}
}
// Format a string
StrCpy(str, 0, "");
for (i = 0; i < 8; i++)
{
char tmp[16];
ToHex(tmp, values[i]);
StrLower(tmp);
if (i == max_zero_start)
{
if (i == 0)
{
StrCat(str, 0, "::");
}
else
{
StrCat(str, 0, ":");
}
i += max_zero_len - 1;
}
else
{
StrCat(str, 0, tmp);
if (i != 7)
{
StrCat(str, 0, ":");
}
}
}
// Scope ID
if (ip->ipv6_scope_id != 0)
{
char tmp[64];
StrCat(str, 0, "%");
ToStr(tmp, ip->ipv6_scope_id);
StrCat(str, 0, tmp);
}
}
// Convert the string to an IP address
bool StrToIP6(IP *ip, char *str)
{
TOKEN_LIST *t;
char tmp[MAX_PATH];
IP a;
UINT i;
UINT scope_id = 0;
// Validate arguments
if (str == NULL || ip == NULL)
{
return false;
}
ZeroIP6(&a);
StrCpy(tmp, sizeof(tmp), str);
Trim(tmp);
if (StartWith(tmp, "[") && EndWith(tmp, "]"))
{
// If the string is enclosed in square brackets, remove brackets
StrCpyAllowOverlap(tmp, sizeof(tmp), &tmp[1]);
if (StrLen(tmp) >= 1)
{
tmp[StrLen(tmp) - 1] = 0;
}
}
// Remove the scope ID by analyzing if there is it
i = SearchStrEx(tmp, "%", 0, false);
if (i != INFINITE)
{
char ss[MAX_PATH];
StrCpy(ss, sizeof(ss), &tmp[i + 1]);
tmp[i] = 0;
Trim(tmp);
Trim(ss);
scope_id = ToInt(ss);
}
// Tokenize
t = ParseTokenWithNullStr(tmp, ":");
if (t->NumTokens >= 3 && t->NumTokens <= 8)
{
UINT i, n;
bool b = true;
UINT k = 0;
n = 0;
for (i = 0; i < t->NumTokens; i++)
{
char *str = t->Token[i];
if (i != 0 && i != (t->NumTokens - 1) && StrLen(str) == 0)
{
n++;
if (n == 1)
{
k += 2 * (8 - t->NumTokens + 1);
}
else
{
b = false;
break;
}
}
else
{
UCHAR chars[2];
if (CheckIPItemStr6(str) == false)
{
b = false;
break;
}
IPItemStrToChars6(chars, str);
a.ipv6_addr[k++] = chars[0];
a.ipv6_addr[k++] = chars[1];
}
}
if (n != 0 && n != 1)
{
b = false;
}
else if (n == 0 && t->NumTokens != 8)
{
b = false;
}
if (b == false)
{
FreeToken(t);
return false;
}
}
else
{
FreeToken(t);
return false;
}
FreeToken(t);
Copy(ip, &a, sizeof(IP));
ip->ipv6_scope_id = scope_id;
return true;
}
bool StrToIP6Addr(IPV6_ADDR *ip, char *str)
{
IP ip2;
// Validate arguments
if (ip == NULL || str == NULL)
{
Zero(ip, sizeof(IPV6_ADDR));
return false;
}
if (StrToIP6(&ip2, str) == false)
{
return false;
}
if (IPToIPv6Addr(ip, &ip2) == false)
{
return false;
}
return true;
}
// Convert an IP address character to the UCHAR type
void IPItemStrToChars6(UCHAR *chars, char *str)
{
char tmp[5];
BUF *b;
UINT len;
// Validate arguments
if (chars == NULL)
{
return;
}
Zero(tmp, sizeof(tmp));
len = StrLen(str);
switch (len)
{
case 0:
tmp[0] = tmp[1] = tmp[2] = tmp[3] = '0';
break;
case 1:
tmp[0] = tmp[1] = tmp[2] = '0';
tmp[3] = str[0];
break;
case 2:
tmp[0] = tmp[1] = '0';
tmp[2] = str[0];
tmp[3] = str[1];
break;
case 3:
tmp[0] = '0';
tmp[1] = str[0];
tmp[2] = str[1];
tmp[3] = str[2];
break;
case 4:
tmp[0] = str[0];
tmp[1] = str[1];
tmp[2] = str[2];
tmp[3] = str[3];
break;
}
b = StrToBin(tmp);
chars[0] = ((UCHAR *)b->Buf)[0];
chars[1] = ((UCHAR *)b->Buf)[1];
FreeBuf(b);
}
// Check whether invalid characters are included in the element string of the IP address
bool CheckIPItemStr6(char *str)
{
UINT i, len;
// Validate arguments
if (str == NULL)
{
return false;
}
len = StrLen(str);
if (len >= 5)
{
// Invalid length
return false;
}
for (i = 0; i < len; i++)
{
char c = str[i];
if ((c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F') ||
(c >= '0' && c <= '9'))
{
}
else
{
return false;
}
}
return true;
}
// Create an IPv4 address of all zero
void ZeroIP4(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return;
}
Zero(ip, sizeof(IP));
}
// Create an IPv6 address of all zero
void ZeroIP6(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return;
}
SetIP6(ip, NULL);
}
// Get the IP address of the localhost
void GetLocalHostIP6(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return;
}
ZeroIP6(ip);
ip->ipv6_addr[15] = 1;
}
void GetLocalHostIP4(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return;
}
SetIP(ip, 127, 0, 0, 1);
}
// Check whether the specified address is a localhost
bool IsLocalHostIP6(IP *ip)
{
IP local;
// Validate arguments
if (ip == NULL)
{
return false;
}
if (IsIP6(ip) == false)
{
return false;
}
GetLocalHostIP6(&local);
if (CmpIpAddr(&local, ip) == 0)
{
return true;
}
return false;
}
bool IsLocalHostIP4(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return false;
}
if (IsIP4(ip) == false)
{
return false;
}
if (ip->addr[0] == 127)
{
return true;
}
return false;
}
bool IsLocalHostIP(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return false;
}
if (IsIP4(ip))
{
return IsLocalHostIP4(ip);
}
else
{
return IsLocalHostIP6(ip);
}
}
// Convert the IPV6_ADDR to an IP
void IPv6AddrToIP(IP *ip, IPV6_ADDR *addr)
{
// Validate arguments
if (ip == NULL || addr == NULL)
{
return;
}
SetIP6(ip, addr->Value);
}
// Convert the IP to an IPV6_ADDR
bool IPToIPv6Addr(IPV6_ADDR *addr, IP *ip)
{
UINT i;
// Validate arguments
if (addr == NULL || ip == NULL)
{
Zero(addr, sizeof(IPV6_ADDR));
return false;
}
if (IsIP6(ip) == false)
{
Zero(addr, sizeof(IPV6_ADDR));
return false;
}
for (i = 0; i < 16; i++)
{
addr->Value[i] = ip->ipv6_addr[i];
}
return true;
}
// Set an IPv6 address
void SetIP6(IP *ip, UCHAR *value)
{
// Validate arguments
if (ip == NULL)
{
return;
}
Zero(ip, sizeof(IP));
ip->addr[0] = 192;
ip->addr[1] = 0;
ip->addr[2] = 2;
ip->addr[3] = 254;
if (value != NULL)
{
UINT i;
for (i = 0; i < 16; i++)
{
ip->ipv6_addr[i] = value[i];
}
}
}
// Check whether the specified address is a IPv6 address
bool IsIP6(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return false;
}
if (ip->addr[0] == 192 && ip->addr[1] == 0 && ip->addr[2] == 2 && ip->addr[3] == 254)
{
return true;
}
return false;
}
bool IsIP4(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return false;
}
return (IsIP6(ip) ? false : true);
}
// Copy the IP address
void CopyIP(IP *dst, IP *src)
{
Copy(dst, src, sizeof(IP));
}
// Utility functions about IP and MAC address types
// Identify whether the IP address is a normal unicast address
bool IsValidUnicastIPAddress4(IP *ip)
{
UINT i;
// Validate arguments
if (IsIP4(ip) == false)
{
return false;
}
if (IsZeroIP(ip))
{
return false;
}
if (ip->addr[0] >= 224 && ip->addr[0] <= 239)
{
// IPv4 Multicast
return false;
}
/// TODO: this is kinda incorrect, but for the correct parsing we need the netmask anyway
for (i = 0; i < 4; i++)
{
if (ip->addr[i] != 255)
{
return true;
}
}
return false;
}
bool IsValidUnicastIPAddressUINT4(UINT ip)
{
IP a;
UINTToIP(&a, ip);
return IsValidUnicastIPAddress4(&a);
}
bool IsValidUnicastIPAddress6(IP *ip)
{
UINT ipv6Type;
if (!IsIP6(ip))
{
return false;
}
if (IsZeroIP(ip))
{
return false;
}
ipv6Type = GetIPAddrType6(ip);
if (!(ipv6Type & IPV6_ADDR_LOCAL_UNICAST) &&
!(ipv6Type & IPV6_ADDR_GLOBAL_UNICAST))
{
return false;
}
return true;
}
// Check whether the MAC address is valid
bool IsMacInvalid(UCHAR *mac)
{
UINT i;
// Validate arguments
if (mac == NULL)
{
return false;
}
for (i = 0; i < 6; i++)
{
if (mac[i] != 0x00)
{
return false;
}
}
return true;
}
// Check whether the MAC address is a broadcast address
bool IsMacBroadcast(UCHAR *mac)
{
UINT i;
// Validate arguments
if (mac == NULL)
{
return false;
}
for (i = 0; i < 6; i++)
{
if (mac[i] != 0xff)
{
return false;
}
}
return true;
}
// Check wether the MAC address is an IPv4 multicast or an IPv6 multicast
bool IsMacMulticast(UCHAR *mac)
{
// Validate arguments
if (mac == NULL)
{
return false;
}
if (mac[0] == 0x01 &&
mac[1] == 0x00 &&
mac[2] == 0x5e)
{
// Multicast IPv4 and other IANA multicasts
return true;
}
if (mac[0] == 0x01)
{
// That's not a really reserved for multicast range, but it seems like anything with 0x01 is used as multicast anyway
// Remove or specify if it causes problems
return true;
}
if (mac[0] == 0x33 &&
mac[1] == 0x33)
{
// Multicast IPv6
return true;
}
return false;
}
// Check wether the MAC address is a unicast one
bool IsMacUnicast(UCHAR *mac)
{
// Validate arguments
if (mac == NULL)
{
return false;
}
if (IsMacInvalid(mac))
{
return false;
}
if (IsMacBroadcast(mac))
{
return false;
}
if (IsMacMulticast(mac))
{
return false;
}
return true;
}
// Get the number of clients connected from the specified IP address
UINT GetNumIpClient(IP *ip)
{
IP_CLIENT *c;
UINT ret = 0;
// Validate arguments
if (ip == NULL)
{
return 0;
}
LockList(ip_clients);
{
c = SearchIpClient(ip);
if (c != NULL)
{
ret = c->NumConnections;
}
}
UnlockList(ip_clients);
return ret;
}
// Add to the IP client entry
void AddIpClient(IP *ip)
{
IP_CLIENT *c;
// Validate arguments
if (ip == NULL)
{
return;
}
LockList(ip_clients);
{
c = SearchIpClient(ip);
if (c == NULL)
{
c = ZeroMallocFast(sizeof(IP_CLIENT));
Copy(&c->IpAddress, ip, sizeof(IP));
c->NumConnections = 0;
Add(ip_clients, c);
}
c->NumConnections++;
}
UnlockList(ip_clients);
//Debug("AddIpClient: %r\n", ip);
}
// Remove from the IP client list
void DelIpClient(IP *ip)
{
IP_CLIENT *c;
// Validate arguments
if (ip == NULL)
{
return;
}
LockList(ip_clients);
{
c = SearchIpClient(ip);
if (c != NULL)
{
c->NumConnections--;
if (c->NumConnections == 0)
{
Delete(ip_clients, c);
Free(c);
}
}
}
UnlockList(ip_clients);
//Debug("DelIpClient: %r\n", ip);
}
// Search for the IP client entry
IP_CLIENT *SearchIpClient(IP *ip)
{
IP_CLIENT t;
// Validate arguments
if (ip == NULL)
{
return NULL;
}
Zero(&t, sizeof(t));
Copy(&t.IpAddress, ip, sizeof(IP));
return Search(ip_clients, &t);
}
// Initialization of the client list
void InitIpClientList()
{
ip_clients = NewList(CompareIpClientList);
}
// Release of the client list
void FreeIpClientList()
{
UINT i;
for (i = 0; i < LIST_NUM(ip_clients); i++)
{
IP_CLIENT *c = LIST_DATA(ip_clients, i);
Free(c);
}
ReleaseList(ip_clients);
ip_clients = NULL;
}
// Comparison of the client list entries
int CompareIpClientList(void *p1, void *p2)
{
IP_CLIENT *c1, *c2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
c1 = *(IP_CLIENT **)p1;
c2 = *(IP_CLIENT **)p2;
if (c1 == NULL || c2 == NULL)
{
return 0;
}
return CmpIpAddr(&c1->IpAddress, &c2->IpAddress);
}
// Normalization of the MAC address
bool NormalizeMacAddress(char *dst, UINT size, char *src)
{
BUF *b;
bool ret = false;
// Validate arguments
if (dst == NULL || src == NULL)
{
return false;
}
b = StrToBin(src);
if (b != NULL && b->Size == 6)
{
ret = true;
BinToStr(dst, size, b->Buf, b->Size);
}
FreeBuf(b);
return ret;
}
// Identify whether the IP address is empty
bool IsZeroIP(IP *ip)
{
return IsZeroIp(ip);
}
bool IsZeroIp(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return true;
}
if (IsIP6(ip) == false)
{
return IsZero(ip->addr, sizeof(ip->addr));
}
else
{
return IsZero(ip->ipv6_addr, sizeof(ip->ipv6_addr));
}
}
bool IsZeroIP6Addr(IPV6_ADDR *addr)
{
// Validate arguments
if (addr == NULL)
{
return true;
}
return IsZero(addr, sizeof(IPV6_ADDR));
}
// Examine whether the specified IP address is meaningful as a host
bool IsHostIPAddress4(IP *ip)
{
UINT a;
// Validate arguments
if (ip == NULL)
{
return false;
}
a = IPToUINT(ip);
if (a == 0 || a == 0xffffffff)
{
return false;
}
return true;
}
bool IsHostIPAddress32(UINT ip)
{
IP p;
UINTToIP(&p, ip);
return IsHostIPAddress4(&p);
}
// Check whether the specified IP address and subnet mask indicates a network correctly
bool IsNetworkAddress4(IP *ip, IP *mask)
{
UINT a, b;
// Validate arguments
if (ip == NULL || mask == NULL)
{
return false;
}
if (IsIP4(ip) == false || IsIP4(mask) == false)
{
return false;
}
if (IsSubnetMask4(mask) == false)
{
return false;
}
a = IPToUINT(ip);
b = IPToUINT(mask);
if ((a & b) == a)
{
return true;
}
return false;
}
bool IsNetworkAddress32(UINT ip, UINT mask)
{
IP a, b;
UINTToIP(&a, ip);
UINTToIP(&b, mask);
return IsNetworkAddress4(&a, &b);
}
// Convert the integer to a subnet mask
UINT IntToSubnetMask32(UINT i)
{
UINT ret = 0xFFFFFFFF;
switch (i)
{
case 0:
ret = 0x00000000;
break;
case 1:
ret = 0x80000000;
break;
case 2:
ret = 0xC0000000;
break;
case 3:
ret = 0xE0000000;
break;
case 4:
ret = 0xF0000000;
break;
case 5:
ret = 0xF8000000;
break;
case 6:
ret = 0xFC000000;
break;
case 7:
ret = 0xFE000000;
break;
case 8:
ret = 0xFF000000;
break;
case 9:
ret = 0xFF800000;
break;
case 10:
ret = 0xFFC00000;
break;
case 11:
ret = 0xFFE00000;
break;
case 12:
ret = 0xFFF00000;
break;
case 13:
ret = 0xFFF80000;
break;
case 14:
ret = 0xFFFC0000;
break;
case 15:
ret = 0xFFFE0000;
break;
case 16:
ret = 0xFFFF0000;
break;
case 17:
ret = 0xFFFF8000;
break;
case 18:
ret = 0xFFFFC000;
break;
case 19:
ret = 0xFFFFE000;
break;
case 20:
ret = 0xFFFFF000;
break;
case 21:
ret = 0xFFFFF800;
break;
case 22:
ret = 0xFFFFFC00;
break;
case 23:
ret = 0xFFFFFE00;
break;
case 24:
ret = 0xFFFFFF00;
break;
case 25:
ret = 0xFFFFFF80;
break;
case 26:
ret = 0xFFFFFFC0;
break;
case 27:
ret = 0xFFFFFFE0;
break;
case 28:
ret = 0xFFFFFFF0;
break;
case 29:
ret = 0xFFFFFFF8;
break;
case 30:
ret = 0xFFFFFFFC;
break;
case 31:
ret = 0xFFFFFFFE;
break;
case 32:
ret = 0xFFFFFFFF;
break;
}
if (IsLittleEndian())
{
ret = Swap32(ret);
}
return ret;
}
void IntToSubnetMask4(IP *ip, UINT i)
{
UINT m;
// Validate arguments
if (ip == NULL)
{
return;
}
m = IntToSubnetMask32(i);
UINTToIP(ip, m);
}
// Examine whether the specified IP address is a subnet mask
bool IsSubnetMask(IP *ip)
{
if (IsIP6(ip))
{
return IsSubnetMask6(ip);
}
else
{
return IsSubnetMask4(ip);
}
}
bool IsSubnetMask4(IP *ip)
{
UINT i;
// Validate arguments
if (ip == NULL)
{
return false;
}
if (IsIP6(ip))
{
return false;
}
i = IPToUINT(ip);
if (IsLittleEndian())
{
i = Swap32(i);
}
switch (i)
{
case 0x00000000:
case 0x80000000:
case 0xC0000000:
case 0xE0000000:
case 0xF0000000:
case 0xF8000000:
case 0xFC000000:
case 0xFE000000:
case 0xFF000000:
case 0xFF800000:
case 0xFFC00000:
case 0xFFE00000:
case 0xFFF00000:
case 0xFFF80000:
case 0xFFFC0000:
case 0xFFFE0000:
case 0xFFFF0000:
case 0xFFFF8000:
case 0xFFFFC000:
case 0xFFFFE000:
case 0xFFFFF000:
case 0xFFFFF800:
case 0xFFFFFC00:
case 0xFFFFFE00:
case 0xFFFFFF00:
case 0xFFFFFF80:
case 0xFFFFFFC0:
case 0xFFFFFFE0:
case 0xFFFFFFF0:
case 0xFFFFFFF8:
case 0xFFFFFFFC:
case 0xFFFFFFFE:
case 0xFFFFFFFF:
return true;
}
return false;
}
bool IsSubnetMask32(UINT ip)
{
IP p;
UINTToIP(&p, ip);
return IsSubnetMask4(&p);
}
#ifdef OS_UNIX // Code for UNIX
// Turn on and off the non-blocking mode of the socket
void UnixSetSocketNonBlockingMode(int fd, bool nonblock)
{
UINT flag = 0;
// Validate arguments
if (fd == INVALID_SOCKET)
{
return;
}
if (nonblock)
{
flag = 1;
}
#ifdef FIONBIO
ioctl(fd, FIONBIO, &flag);
#else // FIONBIO
{
int flag = fcntl(fd, F_GETFL, 0);
if (flag != -1)
{
if (nonblock)
{
flag |= O_NONBLOCK;
}
else
{
flag = flag & ~O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
}
}
}
#endif // FIONBIO
}
// Do Nothing
ROUTE_TABLE *UnixGetRouteTable()
{
ROUTE_TABLE *ret = ZeroMalloc(sizeof(ROUTE_TABLE));
ret->NumEntry = 0;
ret->Entry = ZeroMalloc(0);
return ret;
}
// Do Nothing
bool UnixAddRouteEntry(ROUTE_ENTRY *e, bool *already_exists)
{
return true;
}
// Do Nothing
void UnixDeleteRouteEntry(ROUTE_ENTRY *e)
{
return;
}
// Do Nothing
UINT UnixGetVLanInterfaceID(char *instance_name)
{
return 1;
}
// Do Nothing
char **UnixEnumVLan(char *tag_name)
{
char **list;
list = ZeroMalloc(sizeof(char *));
return list;
}
// Get the IP address of the default DNS server
bool UnixGetDefaultDns(IP *ip)
{
BUF *b;
// Validate arguments
if (ip == NULL)
{
return false;
}
Lock(unix_dns_server_addr_lock);
{
if (IsZero(&unix_dns_server, sizeof(IP)) == false)
{
Copy(ip, &unix_dns_server, sizeof(IP));
Unlock(unix_dns_server_addr_lock);
return true;
}
ip->addr[0] = 127;
ip->addr[1] = 0;
ip->addr[2] = 0;
ip->addr[3] = 1;
b = ReadDump("/etc/resolv.conf");
if (b != NULL)
{
char *s;
bool f = false;
while ((s = CfgReadNextLine(b)) != NULL)
{
TOKEN_LIST *t = ParseToken(s, "\" \t,");
if (t->NumTokens == 2)
{
if (StrCmpi(t->Token[0], "nameserver") == 0)
{
StrToIP(ip, t->Token[1]);
f = true;
}
}
FreeToken(t);
Free(s);
if (f)
{
break;
}
}
FreeBuf(b);
}
Copy(&unix_dns_server, ip, sizeof(IP));
}
Unlock(unix_dns_server_addr_lock);
return true;
}
// Select procedure
void UnixSelect(SOCKSET *set, UINT timeout, CANCEL *c1, CANCEL *c2)
{
UINT reads[MAXIMUM_WAIT_OBJECTS];
UINT writes[MAXIMUM_WAIT_OBJECTS];
UINT num_read, num_write, i;
UINT p1, p2;
SOCK_EVENT *sock_events[MAXIMUM_WAIT_OBJECTS];
UINT num_sock_events;
SOCK *s;
UCHAR tmp[MAX_SIZE];
int ret;
bool any_of_tubes_are_readable = false;
// Initialization of array
Zero(reads, sizeof(reads));
Zero(writes, sizeof(writes));
Zero(sock_events, sizeof(sock_events));
num_read = num_write = num_sock_events = 0;
// Setting the event array
if (set != NULL)
{
for (i = 0; i < set->NumSocket; i++)
{
s = set->Sock[i];
if (s != NULL)
{
UnixInitAsyncSocket(s);
if (s->Type == SOCK_INPROC)
{
TUBE *t = s->RecvTube;
if (t != NULL)
{
reads[num_read++] = t->SockEvent->pipe_read;
sock_events[num_sock_events++] = t->SockEvent;
if (t->SockEvent->current_pipe_data != 0)
{
any_of_tubes_are_readable = true;
}
}
}
else
{
if (s->NoNeedToRead == false)
{
reads[num_read++] = s->socket;
}
}
if (s->BulkRecvTube != NULL)
{
TUBE *t = s->BulkRecvTube;
if (t != NULL)
{
reads[num_read++] = t->SockEvent->pipe_read;
sock_events[num_sock_events++] = t->SockEvent;
if (t->SockEvent->current_pipe_data != 0)
{
any_of_tubes_are_readable = true;
}
}
}
if (s->WriteBlocked)
{
writes[num_write++] = s->socket;
}
}
}
}
if (timeout == 0)
{
return;
}
p1 = p2 = -1;
if (c1 != NULL)
{
reads[num_read++] = p1 = c1->pipe_read;
if (c1->SpecialFlag)
{
if (c1->pipe_special_read2 != -1 && c1->pipe_special_read2 != 0)
{
reads[num_read++] = c1->pipe_special_read2;
}
if (c1->pipe_special_read3 != -1 && c1->pipe_special_read3 != 0)
{
reads[num_read++] = c1->pipe_special_read3;
}
}
}
if (c2 != NULL)
{
reads[num_read++] = p2 = c2->pipe_read;
if (c2->SpecialFlag)
{
if (c2->pipe_special_read2 != -1 && c2->pipe_special_read2 != 0)
{
reads[num_read++] = c2->pipe_special_read2;
}
if (c2->pipe_special_read3 != -1 && c2->pipe_special_read3 != 0)
{
reads[num_read++] = c2->pipe_special_read3;
}
}
}
// Call the select
if (any_of_tubes_are_readable == false)
{
UnixSelectInner(num_read, reads, num_write, writes, timeout);
}
// Read from the pipe
if (c1 != NULL && c1->SpecialFlag == false && p1 != -1)
{
do
{
ret = read(p1, tmp, sizeof(tmp));
}
while (ret >= 1);
}
if (c2 != NULL && c2->SpecialFlag == false && p2 != -1)
{
do
{
ret = read(p2, tmp, sizeof(tmp));
}
while (ret >= 1);
}
// Read from the pipe of sockevent
for (i = 0; i < num_sock_events; i++)
{
SOCK_EVENT *e = sock_events[i];
e->current_pipe_data = 0;
do
{
ret = read(e->pipe_read, tmp, sizeof(tmp));
}
while (ret >= 1);
}
}
// Cancel
void UnixCancel(CANCEL *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
UnixWritePipe(c->pipe_write);
}
// Release of the cancel object
void UnixCleanupCancel(CANCEL *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
if (c->SpecialFlag == false)
{
UnixDeletePipe(c->pipe_read, c->pipe_write);
}
Free(c);
}
// Creating a new cancel object
CANCEL *UnixNewCancel()
{
CANCEL *c = ZeroMallocFast(sizeof(CANCEL));
c->ref = NewRef();
c->SpecialFlag = false;
UnixNewPipe(&c->pipe_read, &c->pipe_write);
c->pipe_special_read2 = c->pipe_special_read3 = -1;
return c;
}
// Add the socket to the socket event
void UnixJoinSockToSockEvent(SOCK *sock, SOCK_EVENT *event)
{
// Validate arguments
if (sock == NULL || event == NULL || sock->AsyncMode)
{
return;
}
if (sock->ListenMode != false || (sock->Type == SOCK_TCP && sock->Connected == false))
{
return;
}
sock->AsyncMode = true;
LockList(event->SockList);
{
Add(event->SockList, sock);
AddRef(sock->ref);
}
UnlockList(event->SockList);
// Make the socket asynchronous mode
if (sock->Type != SOCK_INPROC)
{
UnixSetSocketNonBlockingMode(sock->socket, true);
}
// Increase the reference count of the SOCK_EVENT
AddRef(event->ref);
sock->SockEvent = event;
// Set the socket event
SetSockEvent(event);
}
// Wait for a socket event
bool UnixWaitSockEvent(SOCK_EVENT *event, UINT timeout)
{
UINT num_read, num_write;
UINT *reads, *writes;
UINT n;
char tmp[MAX_SIZE];
int readret = 0;
bool event_pipe_is_readable = false;
// Validate arguments
if (event == NULL)
{
return false;
}
LockList(event->SockList);
{
UINT i;
reads = ZeroMallocFast(sizeof(SOCK *) * (LIST_NUM(event->SockList) + 1));
num_write = 0;
num_read = 0;
for (i = 0; i < LIST_NUM(event->SockList); i++)
{
SOCK *s = LIST_DATA(event->SockList, i);
if (s->NoNeedToRead == false)
{
reads[num_read++] = s->socket;
}
if (s->WriteBlocked)
{
num_write++;
}
}
reads[num_read++] = event->pipe_read;
if (event->current_pipe_data != 0)
{
event_pipe_is_readable = true;
}
writes = ZeroMallocFast(sizeof(SOCK *) * num_write);
n = 0;
for (i = 0; i < (num_read - 1); i++)
{
SOCK *s = LIST_DATA(event->SockList, i);
if (s->WriteBlocked)
{
writes[n++] = s->socket;
}
}
}
UnlockList(event->SockList);
if (event_pipe_is_readable == false)
{
UnixSelectInner(num_read, reads, num_write, writes, timeout);
}
event->current_pipe_data = 0;
do
{
readret = read(event->pipe_read, tmp, sizeof(tmp));
}
while (readret >= 1);
Free(reads);
Free(writes);
return true;
}
// Set the socket event
void UnixSetSockEvent(SOCK_EVENT *event)
{
// Validate arguments
if (event == NULL)
{
return;
}
if (event->current_pipe_data <= 100)
{
UnixWritePipe(event->pipe_write);
event->current_pipe_data++;
}
}
// This is a helper function for select()
int safe_fd_set(int fd, fd_set *fds, int *max_fd) {
FD_SET(fd, fds);
if (fd > *max_fd) {
*max_fd = fd;
}
return 0;
}
// Execute 'select' for the socket
void UnixSelectInner(UINT num_read, UINT *reads, UINT num_write, UINT *writes, UINT timeout)
{
#ifdef UNIX_MACOS
fd_set rfds; //read descriptors
fd_set wfds; //write descriptors
int max_fd = 0; //maximum descriptor id
struct timeval tv; //timeval for timeout
#else // UNIX_MACOS
struct pollfd *p;
#endif // UNIX_MACOS
UINT num;
UINT i;
UINT n;
UINT num_read_total, num_write_total;
if (num_read != 0 && reads == NULL)
{
num_read = 0;
}
if (num_write != 0 && writes == NULL)
{
num_write = 0;
}
if (timeout == 0)
{
return;
}
num_read_total = num_write_total = 0;
for (i = 0; i < num_read; i++)
{
if (reads[i] != INVALID_SOCKET)
{
num_read_total++;
}
}
for (i = 0; i < num_write; i++)
{
if (writes[i] != INVALID_SOCKET)
{
num_write_total++;
}
}
num = num_read_total + num_write_total;
#ifdef UNIX_MACOS
FD_ZERO(&rfds); //zero out descriptor set for read descriptors
FD_ZERO(&wfds); //same for write
#else // UNIX_MACOS
p = ZeroMallocFast(sizeof(struct pollfd) * num);
#endif // UNIX_MACOS
n = 0;
for (i = 0; i < num_read; i++)
{
if (reads[i] != INVALID_SOCKET)
{
#ifdef UNIX_MACOS
safe_fd_set(reads[i], &rfds, &max_fd);
#else // UNIX_MACOS
struct pollfd *pfd = &p[n++];
pfd->fd = reads[i];
pfd->events = POLLIN | POLLPRI | POLLERR | POLLHUP;
#endif // UNIX_MACOS
}
}
for (i = 0; i < num_write; i++)
{
if (writes[i] != INVALID_SOCKET)
{
#ifdef UNIX_MACOS
safe_fd_set(writes[i], &wfds, &max_fd);
#else // UNIX_MACOS
struct pollfd *pfd = &p[n++];
pfd->fd = writes[i];
pfd->events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLOUT;
#endif // UNIX_MACOS
}
}
if (num != 0)
{
#ifdef UNIX_MACOS
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000l;
select(max_fd + 1, &rfds, &wfds, NULL, timeout == INFINITE ? NULL : &tv);
#else // UNIX_MACOS
(void)poll(p, num, timeout == INFINITE ? -1 : (int)timeout);
#endif // UNIX_MACOS
}
else
{
SleepThread(timeout);
}
#ifndef UNIX_MACOS
Free(p);
#endif // not UNIX_MACOS
}
// Clean-up of the socket event
void UnixCleanupSockEvent(SOCK_EVENT *event)
{
UINT i;
// Validate arguments
if (event == NULL)
{
return;
}
for (i = 0; i < LIST_NUM(event->SockList); i++)
{
SOCK *s = LIST_DATA(event->SockList, i);
ReleaseSock(s);
}
ReleaseList(event->SockList);
UnixDeletePipe(event->pipe_read, event->pipe_write);
Free(event);
}
// Create a socket event
SOCK_EVENT *UnixNewSockEvent()
{
SOCK_EVENT *e = ZeroMallocFast(sizeof(SOCK_EVENT));
e->SockList = NewList(NULL);
e->ref = NewRef();
UnixNewPipe(&e->pipe_read, &e->pipe_write);
return e;
}
// Close the pipe
void UnixDeletePipe(int p1, int p2)
{
if (p1 != -1)
{
close(p1);
}
if (p2 != -1)
{
close(p2);
}
}
// Write to the pipe
void UnixWritePipe(int pipe_write)
{
char c = 1;
write(pipe_write, &c, 1);
}
// Create a new pipe
void UnixNewPipe(int *pipe_read, int *pipe_write)
{
int fd[2];
// Validate arguments
if (pipe_read == NULL || pipe_write == NULL)
{
return;
}
fd[0] = fd[1] = 0;
pipe(fd);
*pipe_read = fd[0];
*pipe_write = fd[1];
UnixSetSocketNonBlockingMode(*pipe_write, true);
UnixSetSocketNonBlockingMode(*pipe_read, true);
}
// Release the asynchronous socket
void UnixFreeAsyncSocket(SOCK *sock)
{
UINT p;
// Validate arguments
if (sock == NULL)
{
return;
}
Lock(sock->lock);
{
if (sock->AsyncMode == false)
{
Unlock(sock->lock);
return;
}
sock->AsyncMode = false;
// Examine whether this socket are associated to SockEvent
if (sock->SockEvent != NULL)
{
SOCK_EVENT *e = sock->SockEvent;
AddRef(e->ref);
p = e->pipe_write;
LockList(e->SockList);
{
if (Delete(e->SockList, sock))
{
ReleaseSock(sock);
}
}
UnlockList(e->SockList);
// Release the socket event
ReleaseSockEvent(sock->SockEvent);
sock->SockEvent = NULL;
SetSockEvent(e);
ReleaseSockEvent(e);
}
}
Unlock(sock->lock);
}
// Set the socket to asynchronous mode
void UnixInitAsyncSocket(SOCK *sock)
{
// Validate arguments
if (sock == NULL)
{
return;
}
if (sock->AsyncMode)
{
// The socket has been set in asynchronous mode already
return;
}
if (sock->ListenMode != false || ((sock->Type == SOCK_TCP || sock->Type == SOCK_INPROC) && sock->Connected == false))
{
return;
}
sock->AsyncMode = true;
if (sock->Type != SOCK_INPROC)
{
UnixSetSocketNonBlockingMode(sock->socket, true);
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
if (sock->ssl != NULL && sock->ssl->s3 != NULL)
{
sock->Ssl_Init_Async_SendAlert[0] = sock->ssl->s3->send_alert[0];
sock->Ssl_Init_Async_SendAlert[1] = sock->ssl->s3->send_alert[1];
}
#endif
}
// Initializing the socket library
void UnixInitSocketLibrary()
{
// Do not do anything special
}
// Release of the socket library
void UnixFreeSocketLibrary()
{
// Do not do anything special
}
#endif // OS_UNIX
#ifdef OS_WIN32 // Code for Windows
NETWORK_WIN32_FUNCTIONS *w32net;
// Comparison of IP_ADAPTER_INDEX_MAP
int CompareIpAdapterIndexMap(void *p1, void *p2)
{
IP_ADAPTER_INDEX_MAP *a1, *a2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
a1 = *(IP_ADAPTER_INDEX_MAP **)p1;
a2 = *(IP_ADAPTER_INDEX_MAP **)p2;
if (a1 == NULL || a2 == NULL)
{
return 0;
}
if (a1->Index > a2->Index)
{
return 1;
}
else if (a1->Index < a2->Index)
{
return -1;
}
else
{
return 0;
}
}
// Update the IP address of the adapter
bool Win32RenewAddressByGuid(char *guid)
{
IP_ADAPTER_INDEX_MAP a;
// Validate arguments
if (guid == NULL)
{
return false;
}
Zero(&a, sizeof(a));
if (Win32GetAdapterFromGuid(&a, guid) == false)
{
return false;
}
return Win32RenewAddress(&a);
}
bool Win32RenewAddress(void *a)
{
DWORD ret;
// Validate arguments
if (a == NULL)
{
return false;
}
if (w32net->IpRenewAddress == NULL)
{
return false;
}
ret = w32net->IpRenewAddress(a);
if (ret == NO_ERROR)
{
return true;
}
else
{
Debug("IpRenewAddress: Error: %u\n", ret);
return false;
}
}
// Release the IP address of the adapter
bool Win32ReleaseAddress(void *a)
{
DWORD ret;
// Validate arguments
if (a == NULL)
{
return false;
}
if (w32net->IpReleaseAddress == NULL)
{
return false;
}
ret = w32net->IpReleaseAddress(a);
if (ret == NO_ERROR)
{
return true;
}
else
{
Debug("IpReleaseAddress: Error: %u\n", ret);
return false;
}
}
bool Win32ReleaseAddressByGuid(char *guid)
{
IP_ADAPTER_INDEX_MAP a;
// Validate arguments
if (guid == NULL)
{
return false;
}
Zero(&a, sizeof(a));
if (Win32GetAdapterFromGuid(&a, guid) == false)
{
return false;
}
return Win32ReleaseAddress(&a);
}
void Win32ReleaseAddressByGuidExThread(THREAD *t, void *param)
{
WIN32_RELEASEADDRESS_THREAD_PARAM *p;
// Validate arguments
if (t == NULL || param == NULL)
{
return;
}
p = (WIN32_RELEASEADDRESS_THREAD_PARAM *)param;
AddRef(p->Ref);
NoticeThreadInit(t);
AddWaitThread(t);
if (p->Renew == false)
{
p->Ok = Win32ReleaseAddressByGuid(p->Guid);
}
else
{
p->Ok = Win32RenewAddressByGuid(p->Guid);
}
ReleaseWin32ReleaseAddressByGuidThreadParam(p);
DelWaitThread(t);
}
bool Win32RenewAddressByGuidEx(char *guid, UINT timeout)
{
return Win32ReleaseOrRenewAddressByGuidEx(guid, timeout, true);
}
bool Win32ReleaseAddressByGuidEx(char *guid, UINT timeout)
{
return Win32ReleaseOrRenewAddressByGuidEx(guid, timeout, false);
}
bool Win32ReleaseOrRenewAddressByGuidEx(char *guid, UINT timeout, bool renew)
{
THREAD *t;
WIN32_RELEASEADDRESS_THREAD_PARAM *p;
bool ret = false;
UINT64 start_tick = 0;
UINT64 end_tick = 0;
// Validate arguments
if (guid == NULL)
{
return false;
}
if (timeout == 0)
{
timeout = INFINITE;
}
p = ZeroMalloc(sizeof(WIN32_RELEASEADDRESS_THREAD_PARAM));
p->Ref = NewRef();
StrCpy(p->Guid, sizeof(p->Guid), guid);
p->Timeout = timeout;
p->Renew = renew;
t = NewThread(Win32ReleaseAddressByGuidExThread, p);
WaitThreadInit(t);
start_tick = Tick64();
end_tick = start_tick + (UINT64)timeout;
while (true)
{
UINT64 now = Tick64();
UINT64 remain;
UINT remain32;
if (now >= end_tick)
{
break;
}
remain = end_tick - now;
remain32 = MIN((UINT)remain, 100);
if (WaitThread(t, remain32))
{
break;
}
}
ReleaseThread(t);
if (p->Ok)
{
ret = true;
}
ReleaseWin32ReleaseAddressByGuidThreadParam(p);
return ret;
}
void ReleaseWin32ReleaseAddressByGuidThreadParam(WIN32_RELEASEADDRESS_THREAD_PARAM *p)
{
// Validate arguments
if (p == NULL)
{
return;
}
if (Release(p->Ref) == 0)
{
Free(p);
}
}
// Get the adapter by the GUID
bool Win32GetAdapterFromGuid(void *a, char *guid)
{
bool ret = false;
IP_INTERFACE_INFO *info;
UINT size;
int i;
LIST *o;
wchar_t tmp[MAX_SIZE];
// Validate arguments
if (a == NULL || guid == NULL)
{
return false;
}
if (w32net->GetInterfaceInfo == NULL)
{
return false;
}
UniFormat(tmp, sizeof(tmp), L"\\DEVICE\\TCPIP_%S", guid);
size = sizeof(IP_INTERFACE_INFO);
info = ZeroMallocFast(size);
if (w32net->GetInterfaceInfo(info, &size) == ERROR_INSUFFICIENT_BUFFER)
{
Free(info);
info = ZeroMallocFast(size);
}
if (w32net->GetInterfaceInfo(info, &size) != NO_ERROR)
{
Free(info);
return false;
}
o = NewListFast(CompareIpAdapterIndexMap);
for (i = 0; i < info->NumAdapters; i++)
{
IP_ADAPTER_INDEX_MAP *a = &info->Adapter[i];
Add(o, a);
}
Sort(o);
for (i = 0; i < (int)(LIST_NUM(o)); i++)
{
IP_ADAPTER_INDEX_MAP *e = LIST_DATA(o, i);
if (UniStrCmpi(e->Name, tmp) == 0)
{
Copy(a, e, sizeof(IP_ADAPTER_INDEX_MAP));
ret = true;
break;
}
}
ReleaseList(o);
Free(info);
return ret;
}
// Clear the DNS cache on Win32
void Win32FlushDnsCache()
{
Run("ipconfig.exe", "/flushdns", true, false);
}
// Update the DHCP address of the specified LAN card
void Win32RenewDhcp9x(UINT if_id)
{
IP_INTERFACE_INFO *info;
UINT size;
int i;
LIST *o;
// Validate arguments
if (if_id == 0)
{
return;
}
size = sizeof(IP_INTERFACE_INFO);
info = ZeroMallocFast(size);
if (w32net->GetInterfaceInfo(info, &size) == ERROR_INSUFFICIENT_BUFFER)
{
Free(info);
info = ZeroMallocFast(size);
}
if (w32net->GetInterfaceInfo(info, &size) != NO_ERROR)
{
Free(info);
return;
}
o = NewListFast(CompareIpAdapterIndexMap);
for (i = 0; i < info->NumAdapters; i++)
{
IP_ADAPTER_INDEX_MAP *a = &info->Adapter[i];
Add(o, a);
}
Sort(o);
for (i = 0; i < (int)(LIST_NUM(o)); i++)
{
IP_ADAPTER_INDEX_MAP *a = LIST_DATA(o, i);
if (a->Index == if_id)
{
char arg[MAX_PATH];
Format(arg, sizeof(arg), "/renew %u", i);
Run("ipconfig.exe", arg, true, false);
}
}
ReleaseList(o);
Free(info);
}
// Release the DHCP address of the specified LAN card
void Win32ReleaseDhcp9x(UINT if_id, bool wait)
{
IP_INTERFACE_INFO *info;
UINT size;
int i;
LIST *o;
// Validate arguments
if (if_id == 0)
{
return;
}
size = sizeof(IP_INTERFACE_INFO);
info = ZeroMallocFast(size);
if (w32net->GetInterfaceInfo(info, &size) == ERROR_INSUFFICIENT_BUFFER)
{
Free(info);
info = ZeroMallocFast(size);
}
if (w32net->GetInterfaceInfo(info, &size) != NO_ERROR)
{
Free(info);
return;
}
o = NewListFast(CompareIpAdapterIndexMap);
for (i = 0; i < info->NumAdapters; i++)
{
IP_ADAPTER_INDEX_MAP *a = &info->Adapter[i];
Add(o, a);
}
Sort(o);
for (i = 0; i < (int)(LIST_NUM(o)); i++)
{
IP_ADAPTER_INDEX_MAP *a = LIST_DATA(o, i);
if (a->Index == if_id)
{
char arg[MAX_PATH];
Format(arg, sizeof(arg), "/release %u", i);
Run("ipconfig.exe", arg, true, wait);
}
}
ReleaseList(o);
Free(info);
}
// Enumerate a list of virtual LAN cards that contains the specified string
char **Win32EnumVLan(char *tag_name)
{
MIB_IFTABLE *p;
UINT ret;
UINT size_needed;
UINT num_retry = 0;
UINT i;
LIST *o;
char **ss;
// Validate arguments
if (tag_name == 0)
{
return NULL;
}
RETRY:
p = ZeroMallocFast(sizeof(MIB_IFTABLE));
size_needed = 0;
// Examine the needed size
ret = w32net->GetIfTable(p, &size_needed, 0);
if (ret == ERROR_INSUFFICIENT_BUFFER)
{
// Re-allocate the memory block of the needed size
Free(p);
p = ZeroMallocFast(size_needed);
}
else if (ret != NO_ERROR)
{
// Acquisition failure
FAILED:
Free(p);
return NULL;
}
// Actually get
ret = w32net->GetIfTable(p, &size_needed, FALSE);
if (ret != NO_ERROR)
{
// Acquisition failure
if ((++num_retry) >= 5)
{
goto FAILED;
}
Free(p);
goto RETRY;
}
// Search
ret = 0;
o = NewListFast(CompareStr);
for (i = 0; i < p->dwNumEntries; i++)
{
MIB_IFROW *r = &p->table[i];
if (SearchStrEx(r->bDescr, tag_name, 0, false) != INFINITE)
{
char *s = CopyStr(r->bDescr);
Add(o, s);
}
}
Free(p);
// Sort
Sort(o);
// Convert to string
ss = ZeroMallocFast(sizeof(char *) * (LIST_NUM(o) + 1));
for (i = 0; i < LIST_NUM(o); i++)
{
ss[i] = LIST_DATA(o, i);
}
ss[LIST_NUM(o)] = NULL;
ReleaseList(o);
return ss;
}
// Get the ID of the virtual LAN card from the instance name of the virtual LAN card
UINT Win32GetVLanInterfaceID(char *instance_name)
{
MIB_IFTABLE *p;
UINT ret;
UINT size_needed;
UINT num_retry = 0;
UINT i;
char ps_miniport_str[MAX_SIZE];
char ps_miniport_str2[MAX_SIZE];
UINT min_len = 0x7FFFFFFF;
// Validate arguments
if (instance_name == 0)
{
return 0;
}
RETRY:
p = ZeroMallocFast(sizeof(MIB_IFTABLE));
size_needed = 0;
// Examine the needed size
ret = w32net->GetIfTable(p, &size_needed, 0);
if (ret == ERROR_INSUFFICIENT_BUFFER)
{
// Re-allocate the memory block of the needed size
Free(p);
p = ZeroMallocFast(size_needed);
}
else if (ret != NO_ERROR)
{
// Acquisition failure
FAILED:
Free(p);
Debug("******** GetIfTable Failed 1. Err = %u\n", ret);
return 0;
}
// Actually get
ret = w32net->GetIfTable(p, &size_needed, FALSE);
if (ret != NO_ERROR)
{
// Acquisition failure
if ((++num_retry) >= 5)
{
goto FAILED;
}
Free(p);
Debug("******** GetIfTable Failed 2. Err = %u\n", ret);
goto RETRY;
}
// "%s - Packet scheduler miniport"
Format(ps_miniport_str, sizeof(ps_miniport_str), "%s - ", instance_name);
Format(ps_miniport_str2, sizeof(ps_miniport_str2), "%s (Microsoft", instance_name);
// Search
ret = 0;
for (i = 0; i < p->dwNumEntries; i++)
{
MIB_IFROW *r = &p->table[i];
if (instance_name[0] != '@')
{
if (StrCmpi(r->bDescr, instance_name) == 0 || StartWith(r->bDescr, ps_miniport_str) || StartWith(r->bDescr, ps_miniport_str2))
{
UINT len = StrLen(r->bDescr);
if (len < min_len)
{
ret = r->dwIndex;
min_len = len;
}
}
}
else
{
if (SearchStrEx(r->bDescr, &instance_name[1], 0, false) != INFINITE)
{
ret = r->dwIndex;
}
}
//Debug("if[%u] (dwIndex=%u): %u, %s\n", i, r->dwIndex, r->dwType, r->bDescr);
}
Free(p);
return ret;
}
// Get the DNS suffix in another way
bool Win32GetDnsSuffix(char *domain, UINT size)
{
IP_ADAPTER_ADDRESSES_XP *info;
IP_ADAPTER_ADDRESSES_XP *cur;
UINT info_size;
bool ret = false;
// Validate arguments
ClearStr(domain, size);
if (domain == NULL)
{
return false;
}
if (w32net->GetAdaptersAddresses == NULL)
{
return false;
}
info_size = 0;
info = ZeroMalloc(sizeof(IP_ADAPTER_ADDRESSES_XP));
if (w32net->GetAdaptersAddresses(AF_INET, 0, NULL, info, &info_size) == ERROR_BUFFER_OVERFLOW)
{
Free(info);
info = ZeroMalloc(info_size);
}
if (w32net->GetAdaptersAddresses(AF_INET, 0, NULL, info, &info_size) != NO_ERROR)
{
Free(info);
return false;
}
cur = info;
while (cur != NULL)
{
if (UniIsEmptyStr(cur->DnsSuffix) == false)
{
UniToStr(domain, size, cur->DnsSuffix);
ret = true;
break;
}
cur = cur->Next;
}
Free(info);
return ret;
}
// Get the DNS server address of the default
bool Win32GetDefaultDns(IP *ip, char *domain, UINT size)
{
FIXED_INFO *info;
UINT info_size;
char *dns_name;
// Validate arguments
ClearStr(domain, size);
if (ip == NULL)
{
return false;
}
Zero(ip, sizeof(IP));
if (w32net->GetNetworkParams == NULL)
{
return false;
}
info_size = 0;
info = ZeroMallocFast(sizeof(FIXED_INFO));
if (w32net->GetNetworkParams(info, &info_size) == ERROR_BUFFER_OVERFLOW)
{
Free(info);
info = ZeroMallocFast(info_size);
}
if (w32net->GetNetworkParams(info, &info_size) != NO_ERROR)
{
Free(info);
return false;
}
if (info->DnsServerList.IpAddress.String == NULL)
{
Free(info);
return false;
}
dns_name = info->DnsServerList.IpAddress.String;
StrToIP(ip, dns_name);
if (domain != NULL)
{
StrCpy(domain, size, info->DomainName);
Trim(domain);
}
Free(info);
return true;
}
// IP conversion function for Win32
void Win32UINTToIP(IP *ip, UINT i)
{
UINTToIP(ip, i);
}
// IP conversion function for Win32
UINT Win32IPToUINT(IP *ip)
{
return IPToUINT(ip);
}
// Remove a routing entry from the routing table
void Win32DeleteRouteEntry(ROUTE_ENTRY *e)
{
MIB_IPFORWARDROW *p;
// Validate arguments
if (e == NULL)
{
return;
}
p = ZeroMallocFast(sizeof(MIB_IPFORWARDROW));
Win32RouteEntryToIpForwardRow(p, e);
// Delete
w32net->DeleteIpForwardEntry(p);
Free(p);
}
// Add a routing entry to the routing table
bool Win32AddRouteEntry(ROUTE_ENTRY *e, bool *already_exists)
{
bool ret = false;
bool dummy = false;
MIB_IPFORWARDROW *p;
UINT err = 0;
// Validate arguments
if (e == NULL)
{
return false;
}
if (already_exists == NULL)
{
already_exists = &dummy;
}
*already_exists = false;
p = ZeroMallocFast(sizeof(MIB_IPFORWARDROW));
Win32RouteEntryToIpForwardRow(p, e);
// Adding
err = w32net->CreateIpForwardEntry(p);
if (err != 0)
{
if (err == ERROR_OBJECT_ALREADY_EXISTS)
{
Debug("CreateIpForwardEntry: Already Exists\n");
*already_exists = true;
ret = true;
}
else
{
Debug("CreateIpForwardEntry Error: %u\n", err);
ret = false;
}
}
else
{
ret = true;
}
Free(p);
return ret;
}
// Get the routing table
ROUTE_TABLE *Win32GetRouteTable()
{
ROUTE_TABLE *t = ZeroMallocFast(sizeof(ROUTE_TABLE));
MIB_IPFORWARDTABLE *p;
UINT ret;
UINT size_needed;
UINT num_retry = 0;
LIST *o;
UINT i;
ROUTE_ENTRY *e;
RETRY:
p = ZeroMallocFast(sizeof(MIB_IFTABLE));
size_needed = 0;
// Examine the needed size
ret = w32net->GetIpForwardTable(p, &size_needed, 0);
if (ret == ERROR_INSUFFICIENT_BUFFER)
{
// Re-allocate the memory block of the needed size
Free(p);
p = ZeroMallocFast(size_needed);
}
else if (ret != NO_ERROR)
{
// Acquisition failure
FAILED:
Free(p);
t->Entry = MallocFast(0);
return t;
}
// Actually get
ret = w32net->GetIpForwardTable(p, &size_needed, FALSE);
if (ret != NO_ERROR)
{
// Acquisition failure
if ((++num_retry) >= 5)
{
goto FAILED;
}
Free(p);
goto RETRY;
}
// Add to the list along
o = NewListFast(Win32CompareRouteEntryByMetric);
for (i = 0; i < p->dwNumEntries; i++)
{
e = ZeroMallocFast(sizeof(ROUTE_ENTRY));
Win32IpForwardRowToRouteEntry(e, &p->table[i]);
Add(o, e);
}
Free(p);
// Sort by metric
Sort(o);
// Combine the results
t->NumEntry = LIST_NUM(o);
t->Entry = ToArrayEx(o, true);
ReleaseList(o);
return t;
}
// Sort the routing entries by metric
int Win32CompareRouteEntryByMetric(void *p1, void *p2)
{
ROUTE_ENTRY *e1, *e2;
// Validate arguments
if (p1 == NULL || p2 == NULL)
{
return 0;
}
e1 = *(ROUTE_ENTRY **)p1;
e2 = *(ROUTE_ENTRY **)p2;
if (e1 == NULL || e2 == NULL)
{
return 0;
}
if (e1->Metric > e2->Metric)
{
return 1;
}
else if (e1->Metric == e2->Metric)
{
return 0;
}
else
{
return -1;
}
}
// Convert the ROUTE_ENTRY to a MIB_IPFORWARDROW
void Win32RouteEntryToIpForwardRow(void *ip_forward_row, ROUTE_ENTRY *entry)
{
MIB_IPFORWARDROW *r;
// Validate arguments
if (entry == NULL || ip_forward_row == NULL)
{
return;
}
r = (MIB_IPFORWARDROW *)ip_forward_row;
Zero(r, sizeof(MIB_IPFORWARDROW));
// IP address
r->dwForwardDest = Win32IPToUINT(&entry->DestIP);
// Subnet mask
r->dwForwardMask = Win32IPToUINT(&entry->DestMask);
// Gateway IP address
r->dwForwardNextHop = Win32IPToUINT(&entry->GatewayIP);
// Local routing flag
if (entry->LocalRouting)
{
// Local
r->dwForwardType = 3;
}
else
{
// Remote router
r->dwForwardType = 4;
}
// Protocol
r->dwForwardProto = r->dwForwardType - 1; // Subtract by 1 in most cases
if (entry->PPPConnection)
{
// Isn't this a PPP? Danger!
r->dwForwardProto++;
}
// Metric
r->dwForwardMetric1 = entry->Metric;
if (MsIsVista() == false)
{
r->dwForwardMetric2 = r->dwForwardMetric3 = r->dwForwardMetric4 = r->dwForwardMetric5 = INFINITE;
}
else
{
r->dwForwardMetric2 = r->dwForwardMetric3 = r->dwForwardMetric4 = r->dwForwardMetric5 = 0;
r->dwForwardAge = 163240;
}
// Interface ID
r->dwForwardIfIndex = entry->InterfaceID;
Debug("Win32RouteEntryToIpForwardRow()\n");
Debug(" r->dwForwardDest=%X\n", r->dwForwardDest);
Debug(" r->dwForwardMask=%X\n", r->dwForwardMask);
Debug(" r->dwForwardNextHop=%X\n", r->dwForwardNextHop);
Debug(" r->dwForwardType=%u\n", r->dwForwardType);
Debug(" r->dwForwardProto=%u\n", r->dwForwardProto);
Debug(" r->dwForwardMetric1=%u\n", r->dwForwardMetric1);
Debug(" r->dwForwardMetric2=%u\n", r->dwForwardMetric2);
Debug(" r->dwForwardIfIndex=%u\n", r->dwForwardIfIndex);
}
// Convert the MIB_IPFORWARDROW to a ROUTE_ENTRY
void Win32IpForwardRowToRouteEntry(ROUTE_ENTRY *entry, void *ip_forward_row)
{
MIB_IPFORWARDROW *r;
// Validate arguments
if (entry == NULL || ip_forward_row == NULL)
{
return;
}
r = (MIB_IPFORWARDROW *)ip_forward_row;
Zero(entry, sizeof(ROUTE_ENTRY));
// IP address
Win32UINTToIP(&entry->DestIP, r->dwForwardDest);
// Subnet mask
Win32UINTToIP(&entry->DestMask, r->dwForwardMask);
// Gateway IP address
Win32UINTToIP(&entry->GatewayIP, r->dwForwardNextHop);
// Local routing flag
if (r->dwForwardType == 3)
{
entry->LocalRouting = true;
}
else
{
entry->LocalRouting = false;
}
if (entry->LocalRouting && r->dwForwardProto == 3)
{
// PPP. Danger!
entry->PPPConnection = true;
}
// Metric
entry->Metric = r->dwForwardMetric1;
// Interface ID
entry->InterfaceID = r->dwForwardIfIndex;
}
// Initializing the socket library
void Win32InitSocketLibrary()
{
WSADATA data;
Zero(&data, sizeof(data));
WSAStartup(MAKEWORD(2, 2), &data);
// Load the DLL functions
w32net = ZeroMalloc(sizeof(NETWORK_WIN32_FUNCTIONS));
w32net->hIpHlpApi32 = LoadLibrary("iphlpapi.dll");
w32net->hIcmp = LoadLibrary("icmp.dll");
if (w32net->hIpHlpApi32 != NULL)
{
w32net->CreateIpForwardEntry =
(DWORD (__stdcall *)(PMIB_IPFORWARDROW))
GetProcAddress(w32net->hIpHlpApi32, "CreateIpForwardEntry");
w32net->DeleteIpForwardEntry =
(DWORD (__stdcall *)(PMIB_IPFORWARDROW))
GetProcAddress(w32net->hIpHlpApi32, "DeleteIpForwardEntry");
w32net->GetIfTable =
(DWORD (__stdcall *)(PMIB_IFTABLE, PULONG, BOOL))
GetProcAddress(w32net->hIpHlpApi32, "GetIfTable");
w32net->GetIfTable2 =
(DWORD (__stdcall *)(void **))
GetProcAddress(w32net->hIpHlpApi32, "GetIfTable2");
w32net->FreeMibTable =
(void (__stdcall *)(PVOID))
GetProcAddress(w32net->hIpHlpApi32, "FreeMibTable");
w32net->GetIpForwardTable =
(DWORD (__stdcall *)(PMIB_IPFORWARDTABLE, PULONG, BOOL))
GetProcAddress(w32net->hIpHlpApi32, "GetIpForwardTable");
w32net->GetNetworkParams =
(DWORD (__stdcall *)(PFIXED_INFO,PULONG))
GetProcAddress(w32net->hIpHlpApi32, "GetNetworkParams");
w32net->GetAdaptersAddresses =
(ULONG (__stdcall *)(ULONG,ULONG,PVOID,PIP_ADAPTER_ADDRESSES,PULONG))
GetProcAddress(w32net->hIpHlpApi32, "GetAdaptersAddresses");
w32net->IpRenewAddress =
(DWORD (__stdcall *)(PIP_ADAPTER_INDEX_MAP))
GetProcAddress(w32net->hIpHlpApi32, "IpRenewAddress");
w32net->IpReleaseAddress =
(DWORD (__stdcall *)(PIP_ADAPTER_INDEX_MAP))
GetProcAddress(w32net->hIpHlpApi32, "IpReleaseAddress");
w32net->GetInterfaceInfo =
(DWORD (__stdcall *)(PIP_INTERFACE_INFO, PULONG))
GetProcAddress(w32net->hIpHlpApi32, "GetInterfaceInfo");
w32net->GetAdaptersInfo =
(DWORD (__stdcall *)(PIP_ADAPTER_INFO, PULONG))
GetProcAddress(w32net->hIpHlpApi32, "GetAdaptersInfo");
w32net->GetExtendedTcpTable =
(DWORD (__stdcall *)(PVOID,PDWORD,BOOL,ULONG,_TCP_TABLE_CLASS,ULONG))
GetProcAddress(w32net->hIpHlpApi32, "GetExtendedTcpTable");
w32net->AllocateAndGetTcpExTableFromStack =
(DWORD (__stdcall *)(PVOID *,BOOL,HANDLE,DWORD,DWORD))
GetProcAddress(w32net->hIpHlpApi32, "AllocateAndGetTcpExTableFromStack");
w32net->GetTcpTable =
(DWORD (__stdcall *)(PMIB_TCPTABLE,PDWORD,BOOL))
GetProcAddress(w32net->hIpHlpApi32, "GetTcpTable");
w32net->NotifyRouteChange =
(DWORD (__stdcall *)(PHANDLE,LPOVERLAPPED))
GetProcAddress(w32net->hIpHlpApi32, "NotifyRouteChange");
w32net->CancelIPChangeNotify =
(BOOL (__stdcall *)(LPOVERLAPPED))
GetProcAddress(w32net->hIpHlpApi32, "CancelIPChangeNotify");
w32net->NhpAllocateAndGetInterfaceInfoFromStack =
(DWORD (__stdcall *)(IP_INTERFACE_NAME_INFO **,PDWORD,BOOL,HANDLE,DWORD))
GetProcAddress(w32net->hIpHlpApi32, "NhpAllocateAndGetInterfaceInfoFromStack");
w32net->IcmpCreateFile =
(HANDLE (__stdcall *)())
GetProcAddress(w32net->hIpHlpApi32, "IcmpCreateFile");
w32net->IcmpCloseHandle =
(BOOL (__stdcall *)(HANDLE))
GetProcAddress(w32net->hIpHlpApi32, "IcmpCloseHandle");
w32net->IcmpSendEcho =
(DWORD (__stdcall *)(HANDLE,IPAddr,LPVOID,WORD,PIP_OPTION_INFORMATION,LPVOID,DWORD,DWORD))
GetProcAddress(w32net->hIpHlpApi32, "IcmpSendEcho");
}
if (w32net->hIcmp != NULL)
{
if (w32net->IcmpCreateFile == NULL || w32net->IcmpCloseHandle == NULL || w32net->IcmpSendEcho == NULL)
{
w32net->IcmpCreateFile =
(HANDLE (__stdcall *)())
GetProcAddress(w32net->hIcmp, "IcmpCreateFile");
w32net->IcmpCloseHandle =
(BOOL (__stdcall *)(HANDLE))
GetProcAddress(w32net->hIcmp, "IcmpCloseHandle");
w32net->IcmpSendEcho =
(DWORD (__stdcall *)(HANDLE,IPAddr,LPVOID,WORD,PIP_OPTION_INFORMATION,LPVOID,DWORD,DWORD))
GetProcAddress(w32net->hIcmp, "IcmpSendEcho");
}
}
if (w32net->IcmpCreateFile == NULL || w32net->IcmpCloseHandle == NULL || w32net->IcmpSendEcho == NULL)
{
w32net->IcmpCreateFile = NULL;
w32net->IcmpCloseHandle = NULL;
w32net->IcmpSendEcho = NULL;
}
}
// Release of the socket library
void Win32FreeSocketLibrary()
{
if (w32net != NULL)
{
if (w32net->hIpHlpApi32 != NULL)
{
FreeLibrary(w32net->hIpHlpApi32);
}
if (w32net->hIcmp != NULL)
{
FreeLibrary(w32net->hIcmp);
}
Free(w32net);
w32net = NULL;
}
WSACleanup();
}
// Cancel
void Win32Cancel(CANCEL *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
SetEvent((HANDLE)c->hEvent);
}
// Cleanup of the cancel object
void Win32CleanupCancel(CANCEL *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
if (c->SpecialFlag == false)
{
CloseHandle(c->hEvent);
}
Free(c);
}
// New cancel object
CANCEL *Win32NewCancel()
{
CANCEL *c = ZeroMallocFast(sizeof(CANCEL));
c->ref = NewRef();
c->SpecialFlag = false;
c->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
return c;
}
// Waiting for a socket event
bool Win32WaitSockEvent(SOCK_EVENT *event, UINT timeout)
{
// Validate arguments
if (event == NULL || timeout == 0)
{
return false;
}
if (WaitForSingleObject((HANDLE)event->hEvent, timeout) == WAIT_OBJECT_0)
{
return true;
}
else
{
return false;
}
}
// Clean-up of the socket event
void Win32CleanupSockEvent(SOCK_EVENT *event)
{
// Validate arguments
if (event == NULL)
{
return;
}
CloseHandle((HANDLE)event->hEvent);
Free(event);
}
// Set of the socket event
void Win32SetSockEvent(SOCK_EVENT *event)
{
// Validate arguments
if (event == NULL)
{
return;
}
SetEvent((HANDLE)event->hEvent);
}
// Creating a socket event
SOCK_EVENT *Win32NewSockEvent()
{
SOCK_EVENT *e = ZeroMallocFast(sizeof(SOCK_EVENT));
e->ref = NewRef();
e->hEvent = (void *)CreateEvent(NULL, FALSE, FALSE, NULL);
return e;
}
// Associate the socket with socket event and set it to asynchronous mode
void Win32JoinSockToSockEvent(SOCK *sock, SOCK_EVENT *event)
{
HANDLE hEvent;
// Validate arguments
if (sock == NULL || event == NULL || sock->AsyncMode)
{
return;
}
if (sock->ListenMode != false || (sock->Type != SOCK_UDP && sock->Connected == false))
{
return;
}
sock->AsyncMode = true;
hEvent = event->hEvent;
// Association
WSAEventSelect(sock->socket, hEvent, FD_READ | FD_WRITE | FD_CLOSE);
// Increase the reference count of the SOCK_EVENT
AddRef(event->ref);
sock->SockEvent = event;
}
// Set the socket to asynchronous mode
void Win32InitAsyncSocket(SOCK *sock)
{
// Validate arguments
if (sock == NULL)
{
return;
}
if (sock->AsyncMode)
{
// This socket is already in asynchronous mode
return;
}
if (sock->ListenMode || ((sock->Type == SOCK_TCP || sock->Type == SOCK_INPROC) && sock->Connected == false))
{
return;
}
sock->AsyncMode = true;
if (sock->Type == SOCK_INPROC)
{
// Fetch the event of the TUBE
TUBE *t = sock->RecvTube;
if (t != NULL)
{
if (t->SockEvent != NULL)
{
sock->hEvent = t->SockEvent->hEvent;
}
}
}
else
{
// Creating an Event
sock->hEvent = (void *)CreateEvent(NULL, FALSE, FALSE, NULL);
// Association
WSAEventSelect(sock->socket, sock->hEvent, FD_READ | FD_WRITE | FD_CLOSE);
}
}
// Release the asynchronous socket
void Win32FreeAsyncSocket(SOCK *sock)
{
// Validate arguments
if (sock == NULL)
{
return;
}
// Asynchronous socket
if (sock->hEvent != NULL)
{
if (sock->Type != SOCK_INPROC)
{
CloseHandle((HANDLE)sock->hEvent);
}
}
sock->hEvent = NULL;
sock->AsyncMode = false;
// Socket event
if (sock->SockEvent != NULL)
{
ReleaseSockEvent(sock->SockEvent);
sock->SockEvent = NULL;
}
}
// Select function for Win32
void Win32Select(SOCKSET *set, UINT timeout, CANCEL *c1, CANCEL *c2)
{
HANDLE array[MAXIMUM_WAIT_OBJECTS];
UINT n, i;
SOCK *s;
// Initialization of array
Zero(array, sizeof(array));
n = 0;
// Setting the event array
if (set != NULL)
{
for (i = 0; i < set->NumSocket; i++)
{
s = set->Sock[i];
if (s != NULL)
{
Win32InitAsyncSocket(s);
if (s->hEvent != NULL)
{
array[n++] = (HANDLE)s->hEvent;
}
if (s->BulkRecvTube != NULL)
{
array[n++] = (HANDLE)s->BulkRecvTube->SockEvent->hEvent;
}
}
}
}
if (c1 != NULL && c1->hEvent != NULL)
{
array[n++] = c1->hEvent;
}
if (c2 != NULL && c2->hEvent != NULL)
{
array[n++] = c2->hEvent;
}
if (timeout == 0)
{
return;
}
if (n == 0)
{
// Call normal waiting function if no events to wait are registered
SleepThread(timeout);
}
else
{
// Wait for the event if events are registered at least one
if (n == 1)
{
// Calling a lightweight version If the event is only one
WaitForSingleObject(array[0], timeout);
}
else
{
// In case of multiple events
WaitForMultipleObjects(n, array, false, timeout);
}
}
}
#endif // OS_WIN32
// Check whether the IPv6 is supported
bool IsIPv6Supported()
{
#ifdef NO_IPV6
return false;
#else // NO_IPV6
SOCKET s;
s = socket(AF_INET6, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
return false;
}
closesocket(s);
return true;
#endif // NO_IPV6
}
// Get the host name from the host cache
bool GetHostCache(char *hostname, UINT size, IP *ip)
{
bool ret;
// Validate arguments
if (hostname == NULL || ip == NULL)
{
return false;
}
ret = false;
LockList(HostCacheList);
{
HOSTCACHE t, *c;
Zero(&t, sizeof(t));
Copy(&t.IpAddress, ip, sizeof(IP));
c = Search(HostCacheList, &t);
if (c != NULL)
{
if (IsEmptyStr(c->HostName) == false)
{
ret = true;
StrCpy(hostname, size, c->HostName);
}
else
{
ret = true;
StrCpy(hostname, size, "");
}
}
}
UnlockList(HostCacheList);
return ret;
}
// Add to the host name cache
void AddHostCache(IP *ip, char *hostname)
{
// Validate arguments
if (ip == NULL || hostname == NULL)
{
return;
}
if (IsNetworkNameCacheEnabled() == false)
{
return;
}
LockList(HostCacheList);
{
HOSTCACHE t, *c;
UINT i;
LIST *o;
Zero(&t, sizeof(t));
Copy(&t.IpAddress, ip, sizeof(IP));
c = Search(HostCacheList, &t);
if (c == NULL)
{
c = ZeroMalloc(sizeof(HOSTCACHE));
Copy(&c->IpAddress, ip, sizeof(IP));
Add(HostCacheList, c);
}
StrCpy(c->HostName, sizeof(c->HostName), hostname);
c->Expires = Tick64() + (UINT64)EXPIRES_HOSTNAME;
o = NewListFast(NULL);
for (i = 0; i < LIST_NUM(HostCacheList); i++)
{
HOSTCACHE *c = LIST_DATA(HostCacheList, i);
if (c->Expires <= Tick64())
{
Add(o, c);
}
}
for (i = 0; i < LIST_NUM(o); i++)
{
HOSTCACHE *c = LIST_DATA(o, i);
if (Delete(HostCacheList, c))
{
Free(c);
}
}
ReleaseList(o);
}
UnlockList(HostCacheList);
}
// Comparison of host name cache entries
int CompareHostCache(void *p1, void *p2)
{
HOSTCACHE *c1, *c2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
c1 = *(HOSTCACHE **)p1;
c2 = *(HOSTCACHE **)p2;
if (c1 == NULL || c2 == NULL)
{
return 0;
}
return CmpIpAddr(&c1->IpAddress, &c2->IpAddress);
}
// Release of the host name cache
void FreeHostCache()
{
UINT i;
for (i = 0; i < LIST_NUM(HostCacheList); i++)
{
HOSTCACHE *c = LIST_DATA(HostCacheList, i);
Free(c);
}
ReleaseList(HostCacheList);
HostCacheList = NULL;
}
// Initialization of the host name cache
void InitHostCache()
{
HostCacheList = NewList(CompareHostCache);
}
// Add the thread to the thread waiting list
void AddWaitThread(THREAD *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
AddRef(t->ref);
LockList(WaitThreadList);
{
Add(WaitThreadList, t);
}
UnlockList(WaitThreadList);
}
// Remove the thread from the waiting list
void DelWaitThread(THREAD *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
LockList(WaitThreadList);
{
if (Delete(WaitThreadList, t))
{
ReleaseThread(t);
}
}
UnlockList(WaitThreadList);
}
// Creating a thread waiting list
void InitWaitThread()
{
WaitThreadList = NewList(NULL);
}
// Release of the thread waiting list
void FreeWaitThread()
{
UINT i, num;
THREAD **threads;
LockList(WaitThreadList);
{
num = LIST_NUM(WaitThreadList);
threads = ToArray(WaitThreadList);
DeleteAll(WaitThreadList);
}
UnlockList(WaitThreadList);
for (i = 0; i < num; i++)
{
THREAD *t = threads[i];
WaitThread(t, INFINITE);
ReleaseThread(t);
}
Free(threads);
ReleaseList(WaitThreadList);
WaitThreadList = NULL;
}
// Get a domain name for UNIX
bool UnixGetDomainName(char *name, UINT size)
{
bool ret = false;
BUF *b = ReadDump("/etc/resolv.conf");
if (b == NULL)
{
return false;
}
while (true)
{
char *s = CfgReadNextLine(b);
TOKEN_LIST *t;
if (s == NULL)
{
break;
}
Trim(s);
t = ParseToken(s, " \t");
if (t != NULL)
{
if (t->NumTokens == 2)
{
if (StrCmpi(t->Token[0], "domain") == 0)
{
StrCpy(name, size, t->Token[1]);
ret = true;
}
}
FreeToken(t);
}
Free(s);
}
FreeBuf(b);
return ret;
}
// Get the domain name
bool GetDomainName(char *name, UINT size)
{
bool ret = false;
IP ip;
// Validate arguments
ClearStr(name, size);
if (name == NULL)
{
return false;
}
#ifdef OS_WIN32
ClearStr(name, size);
ret = Win32GetDefaultDns(&ip, name, size);
if (ret == false || IsEmptyStr(name))
{
ret = Win32GetDnsSuffix(name, size);
}
#else // OS_WIN32
ret = UnixGetDomainName(name, size);
#endif // OS_WIN32
if (ret == false)
{
return false;
}
return (IsEmptyStr(name) ? false : true);
}
// Get the default DNS server
bool GetDefaultDns(IP *ip)
{
#ifdef OS_WIN32
return Win32GetDefaultDns(ip, NULL, 0);
#else
return UnixGetDefaultDns(ip);
#endif // OS_WIN32
}
// Creating a socket event
SOCK_EVENT *NewSockEvent()
{
#ifdef OS_WIN32
return Win32NewSockEvent();
#else
return UnixNewSockEvent();
#endif // OS_WIN32
}
// Set of the socket event
void SetSockEvent(SOCK_EVENT *event)
{
#ifdef OS_WIN32
Win32SetSockEvent(event);
#else
UnixSetSockEvent(event);
#endif // OS_WIN32
}
// Clean-up of the socket event
void CleanupSockEvent(SOCK_EVENT *event)
{
#ifdef OS_WIN32
Win32CleanupSockEvent(event);
#else
UnixCleanupSockEvent(event);
#endif // OS_WIN32
}
// Waiting for the socket event
bool WaitSockEvent(SOCK_EVENT *event, UINT timeout)
{
bool ret = false;
#ifdef OS_WIN32
ret = Win32WaitSockEvent(event, timeout);
#else
ret = UnixWaitSockEvent(event, timeout);
#endif // OS_WIN32
return ret;
}
// Release of the socket event
void ReleaseSockEvent(SOCK_EVENT *event)
{
// Validate arguments
if (event == NULL)
{
return;
}
if (Release(event->ref) == 0)
{
CleanupSockEvent(event);
}
}
// Let belonging the socket to the socket event
void JoinSockToSockEvent(SOCK *sock, SOCK_EVENT *event)
{
// Validate arguments
if (sock == NULL || event == NULL)
{
return;
}
if (sock->Type == SOCK_INPROC)
{
// Set the SockEvent on the receiver TUBE for in-process type socket
SetTubeSockEvent(sock->RecvTube, event);
return;
}
if (sock->BulkRecvTube != NULL)
{
// Set the SockEvent on the receiver TUBE in case of R-UDP socket
SetTubeSockEvent(sock->BulkRecvTube, event);
}
#ifdef OS_WIN32
Win32JoinSockToSockEvent(sock, event);
#else
UnixJoinSockToSockEvent(sock, event);
#endif // OS_WIN32
}
// New special cancel object
CANCEL *NewCancelSpecial(void *hEvent)
{
CANCEL *c;
// Validate arguments
if (hEvent == NULL)
{
return NULL;
}
c = ZeroMalloc(sizeof(CANCEL));
c->ref = NewRef();
c->SpecialFlag = true;
#ifdef OS_WIN32
c->hEvent = (HANDLE)hEvent;
#else // OS_WIN32
c->pipe_read = (int)hEvent;
c->pipe_write = -1;
#endif // OS_WIN32
return c;
}
// Creating a cancel object
CANCEL *NewCancel()
{
CANCEL *c = NULL;
#ifdef OS_WIN32
c = Win32NewCancel();
#else
c = UnixNewCancel();
#endif // OS_WIN32
return c;
}
// Release of the cancel object
void ReleaseCancel(CANCEL *c)
{
// Validate arguments
if (c == NULL)
{
return;
}
if (Release(c->ref) == 0)
{
CleanupCancel(c);
}
}
// Clean up of the cancel object
void CleanupCancel(CANCEL *c)
{
#ifdef OS_WIN32
Win32CleanupCancel(c);
#else
UnixCleanupCancel(c);
#endif
}
// Cancellation triggered
void Cancel(CANCEL *c)
{
#ifdef OS_WIN32
Win32Cancel(c);
#else
UnixCancel(c);
#endif
}
// Calculate the optimal route from the specified routing table
ROUTE_ENTRY *GetBestRouteEntryFromRouteTableEx(ROUTE_TABLE *table, IP *ip, UINT exclude_if_id)
{
UINT i;
ROUTE_ENTRY *ret = NULL;
ROUTE_ENTRY *tmp = NULL;
UINT64 min_score = 0;
// Validate arguments
if (ip == NULL || table == NULL)
{
return NULL;
}
if (IsIP6(ip))
{
// IPv6 is not supported
return NULL;
}
// Select routing table entry by following rule
// 1. Largest subnet mask
// 2. Smallest metric value
for (i = 0; i < table->NumEntry; i++)
{
ROUTE_ENTRY *e = table->Entry[i];
UINT dest, net, mask;
dest = IPToUINT(ip);
net = IPToUINT(&e->DestIP);
mask = IPToUINT(&e->DestMask);
if (exclude_if_id != 0)
{
if (e->InterfaceID == exclude_if_id)
{
continue;
}
}
// Mask test
if ((dest & mask) == (net & mask))
{
// Calculate the score
UINT score_high32 = mask;
UINT score_low32 = 0xFFFFFFFF - e->Metric;
UINT64 score64 = (UINT64)score_high32 * (UINT64)0x80000000 * (UINT64)2 + (UINT64)score_low32;
if (score64 == 0)
{
score64 = 1;
}
e->InnerScore = score64;
}
}
tmp = NULL;
// Search for the item with maximum score
for (i = 0; i < table->NumEntry; i++)
{
ROUTE_ENTRY *e = table->Entry[i];
if (e->InnerScore != 0)
{
if (e->InnerScore >= min_score)
{
tmp = e;
min_score = e->InnerScore;
}
}
}
if (tmp != NULL)
{
UINT dest, gateway, mask;
// Generate an entry
ret = ZeroMallocFast(sizeof(ROUTE_ENTRY));
Copy(&ret->DestIP, ip, sizeof(IP));
ret->DestMask.addr[0] = 255;
ret->DestMask.addr[1] = 255;
ret->DestMask.addr[2] = 255;
ret->DestMask.addr[3] = 255;
Copy(&ret->GatewayIP, &tmp->GatewayIP, sizeof(IP));
ret->InterfaceID = tmp->InterfaceID;
ret->LocalRouting = tmp->LocalRouting;
ret->OldIfMetric = tmp->Metric;
ret->Metric = 1;
ret->PPPConnection = tmp->PPPConnection;
// Calculation related to routing control
dest = IPToUINT(&tmp->DestIP);
gateway = IPToUINT(&tmp->GatewayIP);
mask = IPToUINT(&tmp->DestMask);
if ((dest & mask) == (gateway & mask))
{
#ifdef OS_WIN32
if (MsIsVista() == false)
{
// Adjust for Windows
ret->PPPConnection = true;
}
#endif // OS_WIN32
}
}
return ret;
}
// Release the routing entry
void FreeRouteEntry(ROUTE_ENTRY *e)
{
// Validate arguments
if (e == NULL)
{
return;
}
Free(e);
}
// Get the best route entry by analyzing the current routing table
ROUTE_ENTRY *GetBestRouteEntry(IP *ip)
{
return GetBestRouteEntryEx(ip, 0);
}
ROUTE_ENTRY *GetBestRouteEntryEx(IP *ip, UINT exclude_if_id)
{
ROUTE_TABLE *table;
ROUTE_ENTRY *e = NULL;
// Validate arguments
if (ip == NULL)
{
return NULL;
}
table = GetRouteTable();
if (table == NULL)
{
return NULL;
}
e = GetBestRouteEntryFromRouteTableEx(table, ip, exclude_if_id);
FreeRouteTable(table);
return e;
}
// Get the interface ID of the virtual LAN card
UINT GetVLanInterfaceID(char *tag_name)
{
UINT ret = 0;
#ifdef OS_WIN32
ret = Win32GetVLanInterfaceID(tag_name);
#else // OS_WIN32
ret = UnixGetVLanInterfaceID(tag_name);
#endif // OS_WIN32
return ret;
}
// Release of enumeration variable of virtual LAN card
void FreeEnumVLan(char **s)
{
char *a;
UINT i;
// Validate arguments
if (s == NULL)
{
return;
}
i = 0;
while (true)
{
a = s[i++];
if (a == NULL)
{
break;
}
Free(a);
}
Free(s);
}
// Enumeration of virtual LAN cards
char **EnumVLan(char *tag_name)
{
char **ret = NULL;
#ifdef OS_WIN32
ret = Win32EnumVLan(tag_name);
#else // OS_WIN32
ret = UnixEnumVLan(tag_name);
#endif // OS_WIN32
return ret;
}
// Display the routing table
void DebugPrintRouteTable(ROUTE_TABLE *r)
{
UINT i;
// Validate arguments
if (r == NULL)
{
return;
}
if (IsDebug() == false)
{
return;
}
Debug("---- Routing Table (%u Entries) ----\n", r->NumEntry);
for (i = 0; i < r->NumEntry; i++)
{
Debug(" ");
DebugPrintRoute(r->Entry[i]);
}
Debug("------------------------------------\n");
}
// Display the routing table entry
void DebugPrintRoute(ROUTE_ENTRY *e)
{
char tmp[MAX_SIZE];
// Validate arguments
if (e == NULL)
{
return;
}
if (IsDebug() == false)
{
return;
}
RouteToStr(tmp, sizeof(tmp), e);
Debug("%s\n", tmp);
}
// Convert the routing table entry to string
void RouteToStr(char *str, UINT str_size, ROUTE_ENTRY *e)
{
char dest_ip[MAX_PATH];
char dest_mask[MAX_PATH];
char gateway_ip[MAX_PATH];
// Validate arguments
if (str == NULL || e == NULL)
{
return;
}
IPToStr(dest_ip, sizeof(dest_ip), &e->DestIP);
IPToStr(dest_mask, sizeof(dest_mask), &e->DestMask);
IPToStr(gateway_ip, sizeof(gateway_ip), &e->GatewayIP);
Format(str, str_size, "%s/%s %s m=%u oif=%u if=%u lo=%u p=%u",
dest_ip, dest_mask, gateway_ip,
e->Metric, e->OldIfMetric, e->InterfaceID,
e->LocalRouting, e->PPPConnection);
}
// Delete the routing table
void DeleteRouteEntry(ROUTE_ENTRY *e)
{
Debug("DeleteRouteEntry();\n");
#ifdef OS_WIN32
Win32DeleteRouteEntry(e);
#else // OS_WIN32
UnixDeleteRouteEntry(e);
#endif
}
// Add to the routing table
bool AddRouteEntry(ROUTE_ENTRY *e)
{
bool dummy = false;
return AddRouteEntryEx(e, &dummy);
}
bool AddRouteEntryEx(ROUTE_ENTRY *e, bool *already_exists)
{
bool ret = false;
Debug("AddRouteEntryEx();\n");
#ifdef OS_WIN32
ret = Win32AddRouteEntry(e, already_exists);
#else // OS_WIN32
ret = UnixAddRouteEntry(e, already_exists);
#endif
return ret;
}
// Get the routing table
ROUTE_TABLE *GetRouteTable()
{
ROUTE_TABLE *t = NULL;
UINT i;
BUF *buf = NewBuf();
UCHAR hash[MD5_SIZE];
#ifdef OS_WIN32
t = Win32GetRouteTable();
#else //OS_WIN32
t = UnixGetRouteTable();
#endif // OS_WIN32
WriteBuf(buf, &t->NumEntry, sizeof(t->NumEntry));
for (i = 0; i < t->NumEntry; i++)
{
ROUTE_ENTRY *e = t->Entry[i];
WriteBuf(buf, e, sizeof(ROUTE_ENTRY));
}
Md5(hash, buf->Buf, buf->Size);
FreeBuf(buf);
Copy(&t->HashedValue, hash, sizeof(t->HashedValue));
return t;
}
// Release of the routing table
void FreeRouteTable(ROUTE_TABLE *t)
{
UINT i;
// Validate arguments
if (t == NULL)
{
return;
}
for (i = 0; i < t->NumEntry; i++)
{
Free(t->Entry[i]);
}
Free(t->Entry);
Free(t);
}
// UDP receiving
UINT RecvFrom(SOCK *sock, IP *src_addr, UINT *src_port, void *data, UINT size)
{
struct sockaddr_in addr;
int ret = 0;
#ifdef OS_WIN32
int socklen = sizeof(addr);
#else
socklen_t socklen = sizeof(addr);
#endif
// Validate arguments
if (sock != NULL)
{
if (sock->IPv6)
{
return RecvFrom6(sock, src_addr, src_port, data, size);
}
sock->IgnoreRecvErr = false;
}
else
{
return 0;
}
if (src_addr == NULL || src_port == NULL || data == NULL || size == 0)
{
return 0;
}
if (sock->Type != SOCK_UDP || sock->socket == INVALID_SOCKET)
{
return 0;
}
ret = recvfrom(sock->socket, data, size, 0, (struct sockaddr *)&addr, &socklen);
if (ret > 0)
{
InAddrToIP(src_addr, &addr.sin_addr);
*src_port = (UINT)ntohs(addr.sin_port);
if (sock->IsRawSocket)
{
*src_port = sock->LocalPort;
}
Lock(sock->lock);
{
sock->RecvNum++;
sock->RecvSize += (UINT64)ret;
}
Unlock(sock->lock);
return (UINT)ret;
}
else
{
#ifdef OS_WIN32
if (WSAGetLastError() == WSAECONNRESET || WSAGetLastError() == WSAENETRESET || WSAGetLastError() == WSAEMSGSIZE || WSAGetLastError() == WSAENETUNREACH ||
WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAEUSERS || WSAGetLastError() == WSAEADDRNOTAVAIL || WSAGetLastError() == WSAEADDRNOTAVAIL)
{
sock->IgnoreRecvErr = true;
}
else if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS)
{
return SOCK_LATER;
}
else
{
Debug("RecvFrom(): recvfrom() failed with error: %u\n", WSAGetLastError());
}
#else
if (errno == ECONNREFUSED || errno == ECONNRESET || errno == EMSGSIZE || errno == ENOBUFS || errno == ENOMEM || errno == EINTR)
{
sock->IgnoreRecvErr = true;
}
else if (errno == EAGAIN)
{
return SOCK_LATER;
}
else
{
Debug("RecvFrom(): recvfrom() failed with error: %s\n", strerror(errno));
}
#endif
return 0;
}
}
UINT RecvFrom6(SOCK *sock, IP *src_addr, UINT *src_port, void *data, UINT size)
{
struct sockaddr_in6 addr;
int ret = 0;
#ifdef OS_WIN32
int socklen = sizeof(addr);
#else
socklen_t socklen = sizeof(addr);
#endif
// Validate arguments
if (sock != NULL)
{
sock->IgnoreRecvErr = false;
}
else
{
return 0;
}
if (src_addr == NULL || src_port == NULL || data == NULL || size == 0)
{
return 0;
}
if (sock->Type != SOCK_UDP || sock->socket == INVALID_SOCKET)
{
return 0;
}
ret = recvfrom(sock->socket, data, size, 0, (struct sockaddr *)&addr, &socklen);
if (ret > 0)
{
InAddrToIP6(src_addr, &addr.sin6_addr);
src_addr->ipv6_scope_id = addr.sin6_scope_id;
*src_port = (UINT)ntohs(addr.sin6_port);
if (sock->IsRawSocket)
{
*src_port = sock->LocalPort;
}
Lock(sock->lock);
{
sock->RecvNum++;
sock->RecvSize += (UINT64)ret;
}
Unlock(sock->lock);
return (UINT)ret;
}
else
{
#ifdef OS_WIN32
if (WSAGetLastError() == WSAECONNRESET || WSAGetLastError() == WSAENETRESET || WSAGetLastError() == WSAEMSGSIZE || WSAGetLastError() == WSAENETUNREACH ||
WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAEUSERS || WSAGetLastError() == WSAEADDRNOTAVAIL || WSAGetLastError() == WSAEADDRNOTAVAIL)
{
sock->IgnoreRecvErr = true;
}
else if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS)
{
return SOCK_LATER;
}
else
{
Debug("RecvFrom(): recvfrom() failed with error: %u\n", WSAGetLastError());
}
#else
if (errno == ECONNREFUSED || errno == ECONNRESET || errno == EMSGSIZE || errno == ENOBUFS || errno == ENOMEM || errno == EINTR)
{
sock->IgnoreRecvErr = true;
}
else if (errno == EAGAIN)
{
return SOCK_LATER;
}
else
{
Debug("RecvFrom(): recvfrom() failed with error: %s\n", strerror(errno));
}
#endif
return 0;
}
}
// UDP transmission
UINT SendTo(SOCK *sock, IP *dest_addr, UINT dest_port, void *data, UINT size)
{
return SendToEx(sock, dest_addr, dest_port, data, size, false);
}
UINT SendToEx(SOCK *sock, IP *dest_addr, UINT dest_port, void *data, UINT size, bool broadcast)
{
SOCKET s;
int ret;
struct sockaddr_in addr;
// Validate arguments
if (sock != NULL)
{
sock->IgnoreSendErr = false;
}
if (sock == NULL || dest_addr == NULL || (sock->IsRawSocket == false && dest_port == 0) || data == NULL)
{
return 0;
}
if (dest_port >= 65536 && sock->IsRawSocket == false)
{
return 0;
}
if (sock->Type != SOCK_UDP || sock->socket == INVALID_SOCKET)
{
return 0;
}
if (size == 0)
{
return 0;
}
if (sock->IPv6)
{
return SendTo6Ex(sock, dest_addr, dest_port, data, size, broadcast);
}
if (IsIP4(dest_addr) == false)
{
return 0;
}
s = sock->socket;
Zero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
if (sock->IsRawSocket == false)
{
addr.sin_port = htons((USHORT)dest_port);
}
IPToInAddr(&addr.sin_addr, dest_addr);
if ((dest_addr->addr[0] == 255 && dest_addr->addr[1] == 255 &&
dest_addr->addr[2] == 255 && dest_addr->addr[3] == 255) ||
(dest_addr->addr[0] >= 224 && dest_addr->addr[0] <= 239)
|| broadcast)
{
if (sock->UdpBroadcast == false)
{
bool yes = true;
sock->UdpBroadcast = true;
(void)setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&yes, sizeof(yes));
}
}
ret = sendto(s, data, size, 0, (struct sockaddr *)&addr, sizeof(addr));
if (ret != (int)size)
{
sock->IgnoreSendErr = false;
#ifdef OS_WIN32
if (WSAGetLastError() == WSAECONNRESET || WSAGetLastError() == WSAENETRESET || WSAGetLastError() == WSAEMSGSIZE || WSAGetLastError() == WSAENETUNREACH ||
WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAEUSERS || WSAGetLastError() == WSAEINVAL || WSAGetLastError() == WSAEADDRNOTAVAIL)
{
sock->IgnoreSendErr = true;
}
else if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS)
{
return SOCK_LATER;
}
else
{
UINT e = WSAGetLastError();
Debug("SendTo Error; %u\n", e);
}
#else // OS_WIN32
if (errno == ECONNREFUSED || errno == ECONNRESET || errno == EMSGSIZE || errno == ENOBUFS || errno == ENOMEM || errno == EINTR)
{
sock->IgnoreSendErr = true;
}
else if (errno == EAGAIN)
{
return SOCK_LATER;
}
#endif // OS_WIN32
return 0;
}
Lock(sock->lock);
{
sock->SendSize += (UINT64)size;
sock->SendNum++;
}
Unlock(sock->lock);
return ret;
}
UINT SendTo6Ex(SOCK *sock, IP *dest_addr, UINT dest_port, void *data, UINT size, bool broadcast)
{
SOCKET s;
int ret;
struct sockaddr_in6 addr;
UINT type;
// Validate arguments
if (sock != NULL)
{
sock->IgnoreSendErr = false;
}
if (sock == NULL || dest_addr == NULL || (sock->IsRawSocket == false && dest_port == 0) || data == NULL)
{
return 0;
}
if (dest_port >= 65536 && sock->IsRawSocket == false)
{
return 0;
}
if (sock->Type != SOCK_UDP || sock->socket == INVALID_SOCKET)
{
return 0;
}
if (size == 0)
{
return 0;
}
if (IsIP6(dest_addr) == false)
{
return 0;
}
s = sock->socket;
Zero(&addr, sizeof(addr));
addr.sin6_family = AF_INET6;
if (sock->IsRawSocket == false)
{
addr.sin6_port = htons((USHORT)dest_port);
}
IPToInAddr6(&addr.sin6_addr, dest_addr);
addr.sin6_scope_id = dest_addr->ipv6_scope_id;
type = GetIPAddrType6(dest_addr);
if ((type & IPV6_ADDR_MULTICAST) || broadcast)
{
if (sock->UdpBroadcast == false)
{
bool yes = true;
sock->UdpBroadcast = true;
(void)setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&yes, sizeof(yes));
}
}
ret = sendto(s, data, size, 0, (struct sockaddr *)&addr, sizeof(addr));
if (ret != (int)size)
{
sock->IgnoreSendErr = false;
#ifdef OS_WIN32
if (WSAGetLastError() == WSAECONNRESET || WSAGetLastError() == WSAENETRESET || WSAGetLastError() == WSAEMSGSIZE || WSAGetLastError() == WSAENETUNREACH ||
WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAEUSERS || WSAGetLastError() == WSAEINVAL || WSAGetLastError() == WSAEADDRNOTAVAIL)
{
sock->IgnoreSendErr = true;
}
else if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS)
{
return SOCK_LATER;
}
else
{
UINT e = WSAGetLastError();
}
#else // OS_WIN32
if (errno == ECONNREFUSED || errno == ECONNRESET || errno == EMSGSIZE || errno == ENOBUFS || errno == ENOMEM || errno == EINTR)
{
sock->IgnoreSendErr = true;
}
else if (errno == EAGAIN)
{
return SOCK_LATER;
}
#endif // OS_WIN32
return 0;
}
Lock(sock->lock);
{
sock->SendSize += (UINT64)size;
sock->SendNum++;
}
Unlock(sock->lock);
return ret;
}
// Open a UDP port (port number is random, but determine the randomness in the seed)
SOCK *NewUDPEx2Rand(bool ipv6, IP *ip, void *rand_seed, UINT rand_seed_size, UINT num_retry)
{
UINT i;
// Validate arguments
if (rand_seed == NULL || rand_seed_size == 0)
{
return NULL;
}
if (num_retry == 0)
{
num_retry = RAND_UDP_PORT_DEFAULT_NUM_RETRY;
}
for (i = 0; i < (num_retry + 1); i++)
{
BUF *buf = NewBuf();
UCHAR hash[SHA1_SIZE];
UINT port = 0;
SOCK *s;
WriteBuf(buf, rand_seed, rand_seed_size);
WriteBufInt(buf, i);
Sha1(hash, buf->Buf, buf->Size);
FreeBuf(buf);
port = READ_UINT(hash);
port = RAND_UDP_PORT_START + (port % (RAND_UDP_PORT_END - RAND_UDP_PORT_START));
s = NewUDPEx2(port, ipv6, ip);
if (s != NULL)
{
return s;
}
}
return NewUDPEx2(0, ipv6, ip);
}
// Open the UDP port (based on the EXE path and machine key)
SOCK *NewUDPEx2RandMachineAndExePath(bool ipv6, IP *ip, UINT num_retry, UCHAR rand_port_id)
{
BUF *b;
char machine_name[MAX_SIZE];
wchar_t exe_path[MAX_PATH];
char *product_id = NULL;
UCHAR hash[SHA1_SIZE];
#ifdef OS_WIN32
product_id = MsRegReadStr(REG_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ProductId");
if (product_id == NULL)
{
product_id = MsRegReadStr(REG_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "ProductId");
}
#endif // OS_WIN32
b = NewBuf();
GetMachineHostName(machine_name, sizeof(machine_name));
Trim(machine_name);
StrUpper(machine_name);
GetExeNameW(exe_path, sizeof(exe_path));
UniTrim(exe_path);
UniStrUpper(exe_path);
WriteBuf(b, machine_name, StrSize(machine_name));
WriteBuf(b, exe_path, UniStrSize(exe_path));
WriteBuf(b, product_id, StrSize(product_id));
WriteBufChar(b, rand_port_id);
//WriteBufInt(b, GetHostIPAddressHash32());
Sha1(hash, b->Buf, b->Size);
FreeBuf(b);
Free(product_id);
return NewUDPEx2Rand(ipv6, ip, hash, sizeof(hash), num_retry);
}
// Set the DF bit of the socket
void ClearSockDfBit(SOCK *s)
{
#ifdef IP_PMTUDISC_DONT
#ifdef IP_MTU_DISCOVER
UINT value = IP_PMTUDISC_DONT;
if (s == NULL)
{
return;
}
(void)setsockopt(s->socket, IPPROTO_IP, IP_MTU_DISCOVER, (char *)&value, sizeof(value));
#endif // IP_MTU_DISCOVER
#endif // IP_PMTUDISC_DONT
}
// Set the header-include option
void SetRawSockHeaderIncludeOption(SOCK *s, bool enable)
{
UINT value = BOOL_TO_INT(enable);
if (s == NULL || s->IsRawSocket == false)
{
return;
}
(void)setsockopt(s->socket, IPPROTO_IP, IP_HDRINCL, (char *)&value, sizeof(value));
s->RawIP_HeaderIncludeFlag = enable;
}
// Create and initialize the UDP socket
// If port is specified as 0, system assigns a certain port.
SOCK *NewUDP(UINT port)
{
return NewUDPEx(port, false);
}
SOCK *NewUDPEx(UINT port, bool ipv6)
{
return NewUDPEx2(port, ipv6, NULL);
}
SOCK *NewUDPEx2(UINT port, bool ipv6, IP *ip)
{
if (ipv6 == false)
{
return NewUDP4(port, ip);
}
else
{
return NewUDP6(port, ip);
}
}
SOCK *NewUDPEx3(UINT port, IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return NewUDPEx2(port, false, NULL);
}
if (IsIP4(ip))
{
return NewUDPEx2(port, false, ip);
}
else
{
return NewUDPEx2(port, true, ip);
}
}
SOCK *NewUDP4(UINT port, IP *ip)
{
SOCK *sock;
SOCKET s;
struct sockaddr_in addr;
// Validate arguments
if (ip != NULL && IsIP4(ip) == false)
{
return NULL;
}
if (IS_SPECIAL_PORT(port) == false)
{
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
}
else
{
s = socket(AF_INET, SOCK_RAW, GET_SPECIAL_PORT(port));
}
if (s == INVALID_SOCKET)
{
return NULL;
}
Zero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
if (ip == NULL || IsZeroIP(ip))
{
addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
IPToInAddr(&addr.sin_addr, ip);
}
if (port == 0 || IS_SPECIAL_PORT(port))
{
addr.sin_port = 0;
}
else
{
addr.sin_port = htons((USHORT)port);
}
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
// Failure
if (port != 0)
{
bool true_flag = true;
(void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&true_flag, sizeof(bool));
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
bool false_flag = false;
(void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&false_flag, sizeof(bool));
#ifdef SO_EXCLUSIVEADDRUSE
(void)setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&true_flag, sizeof(bool));
#endif // SO_EXCLUSIVEADDRUSE
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
closesocket(s);
return NULL;
}
}
}
else
{
closesocket(s);
return NULL;
}
}
sock = NewSock();
sock->Type = SOCK_UDP;
sock->Connected = false;
sock->AsyncMode = false;
sock->ServerMode = false;
if (port != 0)
{
sock->ServerMode = true;
}
sock->socket = s;
InitUdpSocketBufferSize((int)s);
if (IS_SPECIAL_PORT(port))
{
bool no = false;
(void)setsockopt(sock->socket, IPPROTO_IP, IP_HDRINCL, (char *)&no, sizeof(no));
sock->IsRawSocket = true;
sock->RawSocketIPProtocol = GET_SPECIAL_PORT(port);
}
QuerySocketInformation(sock);
return sock;
}
SOCK *NewUDP6(UINT port, IP *ip)
{
SOCK *sock;
SOCKET s;
struct sockaddr_in6 addr;
// Validate arguments
if (ip != NULL && IsIP6(ip) == false)
{
return NULL;
}
if (IS_SPECIAL_PORT(port) == false)
{
s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
}
else
{
s = socket(AF_INET6, SOCK_RAW, GET_SPECIAL_PORT(port));
}
if (s == INVALID_SOCKET)
{
return NULL;
}
Zero(&addr, sizeof(addr));
addr.sin6_family = AF_INET6;
if (port == 0)
{
addr.sin6_port = 0;
}
else
{
addr.sin6_port = htons((USHORT)port);
}
if (ip != NULL && IsZeroIP(ip) == false)
{
IPToInAddr6(&addr.sin6_addr, ip);
addr.sin6_scope_id = ip->ipv6_scope_id;
}
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
// Failure
if (port != 0)
{
bool true_flag = true;
(void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&true_flag, sizeof(bool));
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
bool false_flag = false;
(void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&false_flag, sizeof(bool));
#ifdef SO_EXCLUSIVEADDRUSE
(void)setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&true_flag, sizeof(bool));
#endif // SO_EXCLUSIVEADDRUSE
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
closesocket(s);
return NULL;
}
}
}
else
{
closesocket(s);
return NULL;
}
}
sock = NewSock();
sock->Type = SOCK_UDP;
sock->Connected = false;
sock->AsyncMode = false;
sock->ServerMode = false;
sock->IPv6 = true;
if (port != 0)
{
sock->ServerMode = true;
}
sock->socket = s;
InitUdpSocketBufferSize(s);
if (IS_SPECIAL_PORT(port))
{
bool no = false;
#ifdef IPV6_HDRINCL
(void)setsockopt(sock->socket, IPPROTO_IP, IPV6_HDRINCL, (char *)&no, sizeof(no));
#endif // IPV6_HDRINCL
(void)setsockopt(sock->socket, IPPROTO_IP, IP_HDRINCL, (char *)&no, sizeof(no));
sock->IsRawSocket = true;
sock->RawSocketIPProtocol = GET_SPECIAL_PORT(port);
}
QuerySocketInformation(sock);
return sock;
}
// Select function
void Select(SOCKSET *set, UINT timeout, CANCEL *c1, CANCEL *c2)
{
#ifdef OS_WIN32
Win32Select(set, timeout, c1, c2);
#else
UnixSelect(set, timeout, c1, c2);
#endif // OS_WIN32
}
// Add a socket to the socket set
void AddSockSet(SOCKSET *set, SOCK *sock)
{
// Validate arguments
if (set == NULL || sock == NULL)
{
return;
}
if (sock->Type == SOCK_TCP && sock->Connected == false)
{
return;
}
if (set->NumSocket >= MAX_SOCKSET_NUM)
{
// Upper limit
return;
}
set->Sock[set->NumSocket++] = sock;
}
// Initializing the socket set
void InitSockSet(SOCKSET *set)
{
// Validate arguments
if (set == NULL)
{
return;
}
Zero(set, sizeof(SOCKSET));
}
// Receive data and discard all of them
bool RecvAllWithDiscard(SOCK *sock, UINT size, bool secure)
{
static UCHAR buffer[4096];
UINT recv_size, sz, ret;
if (sock == NULL)
{
return false;
}
if (size == 0)
{
return true;
}
if (sock->AsyncMode)
{
return false;
}
recv_size = 0;
while (true)
{
sz = MIN(size - recv_size, sizeof(buffer));
ret = Recv(sock, buffer, sz, secure);
if (ret == 0)
{
return false;
}
if (ret == SOCK_LATER)
{
// I suppose that this is safe because the RecvAll() function is used only
// if the sock->AsyncMode == true. And the Recv() function may return
// SOCK_LATER only if the sock->AsyncMode == false. Therefore the call of
// Recv() function in the RecvAll() function never returns SOCK_LATER.
return false;
}
recv_size += ret;
if (recv_size >= size)
{
return true;
}
}
}
// Receive all by TCP
bool RecvAll(SOCK *sock, void *data, UINT size, bool secure)
{
UINT recv_size, sz, ret;
// Validate arguments
if (sock == NULL || data == NULL)
{
return false;
}
if (size == 0)
{
return true;
}
if (sock->AsyncMode)
{
return false;
}
recv_size = 0;
while (true)
{
sz = size - recv_size;
ret = Recv(sock, (UCHAR *)data + recv_size, sz, secure);
if (ret == 0)
{
return false;
}
if (ret == SOCK_LATER)
{
// I suppose that this is safe because the RecvAll() function is used only
// if the sock->AsyncMode == true. And the Recv() function may return
// SOCK_LATER only if the sock->AsyncMode == false. Therefore the call of
// Recv() function in the RecvAll() function never returns SOCK_LATER.
return false;
}
recv_size += ret;
if (recv_size >= size)
{
return true;
}
}
}
// Send the TCP send buffer
bool SendNow(SOCK *sock, int secure)
{
bool ret;
// Validate arguments
if (sock == NULL || sock->AsyncMode != false)
{
return false;
}
if (sock->SendBuf->Size == 0)
{
return true;
}
ret = SendAll(sock, sock->SendBuf->Buf, sock->SendBuf->Size, secure);
ClearBuf(sock->SendBuf);
return ret;
}
// Append to the TCP send buffer
void SendAdd(SOCK *sock, void *data, UINT size)
{
// Validate arguments
if (sock == NULL || data == NULL || size == 0 || sock->AsyncMode != false)
{
return;
}
WriteBuf(sock->SendBuf, data, size);
}
// Send all by TCP
bool SendAll(SOCK *sock, void *data, UINT size, bool secure)
{
UCHAR *buf;
UINT sent_size;
UINT ret;
// Validate arguments
if (sock == NULL || data == NULL)
{
return false;
}
if (sock->AsyncMode)
{
return false;
}
if (size == 0)
{
return true;
}
buf = (UCHAR *)data;
sent_size = 0;
while (true)
{
ret = Send(sock, buf, size - sent_size, secure);
if (ret == 0)
{
return false;
}
sent_size += ret;
buf += ret;
if (sent_size >= size)
{
return true;
}
}
}
// Set the cipher algorithm name to want to use
void SetWantToUseCipher(SOCK *sock, char *name)
{
// Validate arguments
if (sock == NULL || name == NULL)
{
return;
}
if (sock->WaitToUseCipher)
{
Free(sock->WaitToUseCipher);
}
sock->WaitToUseCipher = CopyStr(name);
}
// Add all the chain certificates in the chain_certs directory
void AddChainSslCertOnDirectory(struct ssl_ctx_st *ctx)
{
wchar_t dirname[MAX_SIZE];
wchar_t exedir[MAX_SIZE];
wchar_t txtname[MAX_SIZE];
DIRLIST *dir;
LIST *o;
UINT i;
// Validate arguments
if (ctx == NULL)
{
return;
}
o = NewListFast(NULL);
GetDbDirW(exedir, sizeof(exedir));
CombinePathW(dirname, sizeof(dirname), exedir, L"chain_certs");
MakeDirExW(dirname);
CombinePathW(txtname, sizeof(txtname), dirname, L"Readme_Chain_Certs.txt");
if (IsFileExistsW(txtname) == false)
{
FileCopyW(L"|chain_certs.txt", txtname);
}
dir = EnumDirW(dirname);
if (dir != NULL)
{
for (i = 0; i < dir->NumFiles; i++)
{
DIRENT *e = dir->File[i];
if (e->Folder == false)
{
wchar_t tmp[MAX_SIZE];
X *x;
CombinePathW(tmp, sizeof(tmp), dirname, e->FileNameW);
x = FileToXW(tmp);
if (x != NULL)
{
UINT j;
bool exists = false;
UCHAR hash[SHA1_SIZE];
GetXDigest(x, hash, true);
for (j = 0; j < LIST_NUM(o); j++)
{
UCHAR *hash2 = LIST_DATA(o, j);
if (Cmp(hash, hash2, SHA1_SIZE) == 0)
{
exists = true;
}
}
if (exists == false)
{
AddChainSslCert(ctx, x);
Add(o, Clone(hash, SHA1_SIZE));
}
FreeX(x);
}
}
}
FreeDir(dir);
}
for (i = 0; i < LIST_NUM(o); i++)
{
UCHAR *hash = LIST_DATA(o, i);
Free(hash);
}
ReleaseList(o);
}
// Add the chain certificate
bool AddChainSslCert(struct ssl_ctx_st *ctx, X *x)
{
bool ret = false;
X *x_copy;
// Validate arguments
if (ctx == NULL || x == NULL)
{
return ret;
}
x_copy = CloneX(x);
if (x_copy != NULL)
{
if (x_copy->root_cert)
{
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
X509_STORE_add_cert(store, x_copy->x509);
X509_free(x_copy->x509);
}
else
{
SSL_CTX_add_extra_chain_cert(ctx, x_copy->x509);
}
x_copy->do_not_free = true;
ret = true;
FreeX(x_copy);
}
return ret;
}
// Start a TCP-SSL communication
bool StartSSL(SOCK *sock, X *x, K *priv)
{
return StartSSLEx(sock, x, priv, 0, NULL);
}
bool StartSSLEx(SOCK *sock, X *x, K *priv, UINT ssl_timeout, char *sni_hostname)
{
X509 *x509;
EVP_PKEY *key;
UINT prev_timeout = 1024;
SSL_CTX *ssl_ctx;
#ifdef UNIX_SOLARIS
SOCKET_TIMEOUT_PARAM *ttparam;
#endif //UNIX_SOLARIS
// Validate arguments
if (sock == NULL)
{
Debug("StartSSL Error: #0\n");
return false;
}
if (sock->Connected && sock->Type == SOCK_INPROC && sock->ListenMode == false)
{
sock->SecureMode = true;
return true;
}
if (sock->Connected == false || sock->socket == INVALID_SOCKET ||
sock->ListenMode != false)
{
Debug("StartSSL Error: #1\n");
return false;
}
if (x != NULL && priv == NULL)
{
Debug("StartSSL Error: #2\n");
return false;
}
if (ssl_timeout == 0)
{
ssl_timeout = TIMEOUT_SSL_CONNECT;
}
if (sock->SecureMode)
{
//Debug("StartSSL Error: #3\n");
// SSL communication has already started
return true;
}
Lock(sock->ssl_lock);
if (sock->SecureMode)
{
//Debug("StartSSL Error: #4\n");
// SSL communication has already started
Unlock(sock->ssl_lock);
return true;
}
ssl_ctx = NewSSLCtx(sock->ServerMode);
Lock(openssl_lock);
{
if (sock->ServerMode)
{
#ifdef SSL_OP_NO_SSLv3
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3);
#endif // SSL_OP_NO_SSLv3
#ifdef SSL_OP_NO_TLSv1
if (sock->SslAcceptSettings.Tls_Disable1_0)
{
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1);
}
#endif // SSL_OP_NO_TLSv1
#ifdef SSL_OP_NO_TLSv1_1
if (sock->SslAcceptSettings.Tls_Disable1_1)
{
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
}
#endif // SSL_OP_NO_TLSv1_1
#ifdef SSL_OP_NO_TLSv1_2
if (sock->SslAcceptSettings.Tls_Disable1_2)
{
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
}
#endif // SSL_OP_NO_TLSv1_2
#ifdef SSL_OP_NO_TLSv1_3
if (sock->SslAcceptSettings.Tls_Disable1_3)
{
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3);
}
#endif // SSL_OP_NO_TLSv1_3
Unlock(openssl_lock);
AddChainSslCertOnDirectory(ssl_ctx);
Lock(openssl_lock);
}
else
{
#ifdef SSL_OP_NO_SSLv3
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3);
#endif // SSL_OP_NO_SSLv3
}
sock->ssl = SSL_new(ssl_ctx);
SSL_set_fd(sock->ssl, (int)sock->socket);
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
if (sock->ServerMode == false)
{
if (IsEmptyStr(sni_hostname) == false)
{
// Set the SNI host name
SSL_set_tlsext_host_name(sock->ssl, sni_hostname);
}
}
#endif // SSL_CTRL_SET_TLSEXT_HOSTNAME
}
Unlock(openssl_lock);
if (x != NULL)
{
// Check the certificate and the private key
if (CheckXandK(x, priv))
{
// Use the certificate
x509 = x->x509;
key = priv->pkey;
Lock(openssl_lock);
{
SSL_use_certificate(sock->ssl, x509);
SSL_use_PrivateKey(sock->ssl, key);
}
Unlock(openssl_lock);
}
}
if (sock->WaitToUseCipher != NULL)
{
// Set the cipher algorithm name to want to use
Lock(openssl_lock);
{
if (SSL_set_cipher_list(sock->ssl, sock->WaitToUseCipher) == 0)
SSL_set_cipher_list(sock->ssl, DEFAULT_CIPHER_LIST);
}
Unlock(openssl_lock);
}
if (sock->ServerMode)
{
// Lock(ssl_connect_lock);
// Run the time-out thread for SOLARIS
#ifdef UNIX_SOLARIS
ttparam = NewSocketTimeout(sock);
#endif // UNIX_SOLARIS
// Server mode
if (SSL_accept(sock->ssl) <= 0)
{
// Stop the timeout thread
#ifdef UNIX_SOLARIS
FreeSocketTimeout(ttparam);
#endif // UNIX_SOLARIS
// Unlock(ssl_connect_lock);
// SSL-Accept failure
Lock(openssl_lock);
{
SSL_free(sock->ssl);
sock->ssl = NULL;
}
Unlock(openssl_lock);
Unlock(sock->ssl_lock);
Debug("StartSSL Error: #5\n");
FreeSSLCtx(ssl_ctx);
return false;
}
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
#ifdef TLSEXT_NAMETYPE_host_name
if (true)
{
// Get the SNI host name
const char *sni_recv_hostname = SSL_get_servername(sock->ssl, TLSEXT_NAMETYPE_host_name);
if (IsEmptyStr((char *)sni_recv_hostname) == false)
{
StrCpy(sock->SniHostname, sizeof(sock->SniHostname), (char *)sni_recv_hostname);
}
}
#endif // TLSEXT_NAMETYPE_host_name
#endif // SSL_CTRL_SET_TLSEXT_HOSTNAME
// Stop the timeout thread
#ifdef UNIX_SOLARIS
FreeSocketTimeout(ttparam);
#endif // UNIX_SOLARIS
// Unlock(ssl_connect_lock);
}
else
{
prev_timeout = GetTimeout(sock);
SetTimeout(sock, ssl_timeout);
Lock(ssl_connect_lock);
// Client mode
if (SSL_connect(sock->ssl) <= 0)
{
Unlock(ssl_connect_lock);
// SSL-connect failure
Lock(openssl_lock);
{
SSL_free(sock->ssl);
sock->ssl = NULL;
}
Unlock(openssl_lock);
Unlock(sock->ssl_lock);
Debug("StartSSL Error: #5\n");
SetTimeout(sock, prev_timeout);
FreeSSLCtx(ssl_ctx);
return false;
}
Unlock(ssl_connect_lock);
SetTimeout(sock, prev_timeout);
}
// SSL communication is initiated
sock->SecureMode = true;
// Get the certificate of the remote host
Lock(openssl_lock);
{
x509 = SSL_get_peer_certificate(sock->ssl);
sock->SslVersion = SSL_get_version(sock->ssl);
}
Unlock(openssl_lock);
if (x509 == NULL)
{
// The certificate does not exist on the remote host
sock->RemoteX = NULL;
}
else
{
// Got a certificate
sock->RemoteX = X509ToX(x509);
}
// Get the certificate of local host
Lock(openssl_lock);
{
x509 = SSL_get_certificate(sock->ssl);
}
Unlock(openssl_lock);
if (x509 == NULL)
{
// The certificate does not exist on the remote host
sock->LocalX = NULL;
}
else
{
X *local_x;
// Got a certificate
local_x = X509ToX(x509);
local_x->do_not_free = true;
sock->LocalX = CloneX(local_x);
FreeX(local_x);
}
// Automatic retry mode
SSL_set_mode(sock->ssl, SSL_MODE_AUTO_RETRY);
// Strange flag
SSL_set_mode(sock->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
sock->ssl_ctx = ssl_ctx;
// Get the algorithm name used to encrypt
Lock(openssl_lock);
{
sock->CipherName = CopyStr((char *)SSL_get_cipher(sock->ssl));
}
Unlock(openssl_lock);
Unlock(sock->ssl_lock);
#ifdef ENABLE_SSL_LOGGING
if (sock->ServerMode)
{
SockEnableSslLogging(sock);
}
#endif // ENABLE_SSL_LOGGING
return true;
}
#ifdef ENABLE_SSL_LOGGING
// Enable SSL logging
void SockEnableSslLogging(SOCK *s)
{
char dirname[MAX_PATH];
char tmp[MAX_PATH];
char dtstr[MAX_PATH];
char fn1[MAX_PATH], fn2[MAX_PATH];
// Validate arguments
if (s == NULL)
{
return;
}
if (s->IsSslLoggingEnabled)
{
return;
}
s->IsSslLoggingEnabled = true;
GetDateTimeStrMilli64ForFileName(dtstr, sizeof(dtstr), LocalTime64());
Format(tmp, sizeof(tmp), "%s__%r_%u__%r_%u", dtstr,
&s->LocalIP, s->LocalPort, &s->RemoteIP, s->RemotePort);
CombinePath(dirname, sizeof(dirname), SSL_LOGGING_DIRNAME, tmp);
MakeDirEx(dirname);
CombinePath(fn1, sizeof(fn1), dirname, "send.c");
CombinePath(fn2, sizeof(fn2), dirname, "recv.c");
s->SslLogging_Send = FileCreate(fn1);
s->SslLogging_Recv = FileCreate(fn2);
s->SslLogging_Lock = NewLock();
}
// Close SSL logging
void SockCloseSslLogging(SOCK *s)
{
// Validate arguments
if (s == NULL)
{
return;
}
if (s->IsSslLoggingEnabled == false)
{
return;
}
s->IsSslLoggingEnabled = false;
FileClose(s->SslLogging_Recv);
s->SslLogging_Recv = NULL;
FileClose(s->SslLogging_Send);
s->SslLogging_Send = NULL;
DeleteLock(s->SslLogging_Lock);
s->SslLogging_Lock = NULL;
}
// Write SSL log
void SockWriteSslLog(SOCK *s, void *send_data, UINT send_size, void *recv_data, UINT recv_size)
{
// Validate arguments
if (s == NULL)
{
return;
}
if (s->IsSslLoggingEnabled == false)
{
return;
}
Lock(s->SslLogging_Lock);
{
if (s->SslLogging_Send != NULL)
{
if (send_size >= 1 && send_data != NULL)
{
FileWrite(s->SslLogging_Send, send_data, send_size);
}
}
if (s->SslLogging_Recv != NULL)
{
if (recv_size >= 1 && recv_data != NULL)
{
FileWrite(s->SslLogging_Recv, recv_data, recv_size);
}
}
}
Unlock(s->SslLogging_Lock);
}
#endif // ENABLE_SSL_LOGGING
// Set the flag to indicate that the socket doesn't require reading
void SetNoNeedToRead(SOCK *sock)
{
// Validate arguments
if (sock == NULL)
{
return;
}
sock->NoNeedToRead = true;
}
// TCP-SSL receive
UINT SecureRecv(SOCK *sock, void *data, UINT size)
{
int ret, e = SSL_ERROR_NONE;
SSL *ssl;
#ifdef UNIX_SOLARIS
SOCKET_TIMEOUT_PARAM *ttparam;
#endif //UNIX_SOLARIS
ssl = sock->ssl;
if (sock->AsyncMode)
{
// Confirm whether the data is readable even 1 byte in the case of asynchronous mode.
// To read data results blocking, if there is no readable data.
// We must avoid blocking.
char c;
Lock(sock->ssl_lock);
{
if (sock->Connected == false)
{
Unlock(sock->ssl_lock);
Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0;
}
ret = SSL_peek(ssl, &c, sizeof(c));
}
Unlock(sock->ssl_lock);
if (ret == 0)
{
// The communication have been disconnected
Disconnect(sock);
Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0;
}
if (ret < 0)
{
// An error has occurred
e = SSL_get_error(ssl, ret);
if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_SSL)
{
if (e == SSL_ERROR_SSL
#if OPENSSL_VERSION_NUMBER < 0x10100000L
&&
sock->ssl->s3->send_alert[0] == SSL3_AL_FATAL &&
sock->ssl->s3->send_alert[0] != sock->Ssl_Init_Async_SendAlert[0] &&
sock->ssl->s3->send_alert[1] != sock->Ssl_Init_Async_SendAlert[1]
#endif
)
{
Debug("%s %u SSL Fatal Error on ASYNC socket !!!\n", __FILE__, __LINE__);
Disconnect(sock);
return 0;
}
// Packet has not arrived yet, that is not to be read
return SOCK_LATER;
}
}
}
// Receive
Lock(sock->ssl_lock);
{
if (sock->Connected == false)
{
Unlock(sock->ssl_lock);
Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0;
}
#ifdef OS_UNIX
if (sock->AsyncMode == false)
{
sock->CallingThread = pthread_self();
}
#endif // OS_UNIX
// Run the time-out thread for SOLARIS
#ifdef UNIX_SOLARIS
ttparam = NewSocketTimeout(sock);
#endif // UNIX_SOLARIS
ret = SSL_read(ssl, data, size);
// Stop the timeout thread
#ifdef UNIX_SOLARIS
FreeSocketTimeout(ttparam);
#endif // UNIX_SOLARIS
#ifdef OS_UNIX
if (sock->AsyncMode == false)
{
sock->CallingThread = 0;
}
#endif // OS_UNIX
if (ret < 0)
{
e = SSL_get_error(ssl, ret);
}
}
Unlock(sock->ssl_lock);
#ifdef ENABLE_SSL_LOGGING
if (ret > 0)
{
SockWriteSslLog(sock, NULL, 0, data, ret);
}
#endif // ENABLE_SSL_LOGGING
if (ret > 0)
{
// Successful reception
sock->RecvSize += (UINT64)ret;
sock->RecvNum++;
return (UINT)ret;
}
if (ret == 0)
{
// Disconnect the communication
Disconnect(sock);
//Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0;
}
if (sock->AsyncMode)
{
if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_SSL)
{
if (e == SSL_ERROR_SSL
#if OPENSSL_VERSION_NUMBER < 0x10100000L
&&
sock->ssl->s3->send_alert[0] == SSL3_AL_FATAL &&
sock->ssl->s3->send_alert[0] != sock->Ssl_Init_Async_SendAlert[0] &&
sock->ssl->s3->send_alert[1] != sock->Ssl_Init_Async_SendAlert[1]
#endif
)
{
Debug("%s %u SSL Fatal Error on ASYNC socket !!!\n", __FILE__, __LINE__);
Disconnect(sock);
return 0;
}
// Packet has not yet arrived
return SOCK_LATER;
}
}
Disconnect(sock);
Debug("%s %u SecureRecv() Disconnect\n", __FILE__, __LINE__);
return 0;
}
// TCP-SSL transmission
UINT SecureSend(SOCK *sock, void *data, UINT size)
{
int ret, e = SSL_ERROR_NONE;
SSL *ssl;
ssl = sock->ssl;
if (sock->AsyncMode)
{
// Asynchronous mode
SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
}
// Transmission
Lock(sock->ssl_lock);
{
if (sock->Connected == false)
{
Unlock(sock->ssl_lock);
Debug("%s %u SecureSend() Disconnect\n", __FILE__, __LINE__);
return 0;
}
ret = SSL_write(ssl, data, size);
if (ret < 0)
{
e = SSL_get_error(ssl, ret);
}
}
Unlock(sock->ssl_lock);
#ifdef ENABLE_SSL_LOGGING
if (ret > 0)
{
SockWriteSslLog(sock, data, ret, NULL, 0);
}
#endif // ENABLE_SSL_LOGGING
if (ret > 0)
{
// Successful transmission
sock->SendSize += (UINT64)ret;
sock->SendNum++;
sock->WriteBlocked = false;
return (UINT)ret;
}
if (ret == 0)
{
// Disconnect
Debug("%s %u SecureSend() Disconnect\n", __FILE__, __LINE__);
Disconnect(sock);
return 0;
}
if (sock->AsyncMode)
{
// Confirmation of the error value
if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_SSL)
{
sock->WriteBlocked = true;
return SOCK_LATER;
}
Debug("%s %u e=%u\n", __FILE__, __LINE__, e);
}
//Debug("%s %u SecureSend() Disconnect\n", __FILE__, __LINE__);
Disconnect(sock);
return 0;
}
// Peep the TCP
UINT Peek(SOCK *sock, void *data, UINT size)
{
SOCKET s;
int ret;
// Validate arguments
if (sock == NULL || data == NULL || size == 0)
{
return 0;
}
if (sock->Type == SOCK_INPROC)
{
return 0;
}
if (sock->Type != SOCK_TCP || sock->Connected == false || sock->ListenMode != false ||
sock->socket == INVALID_SOCKET)
{
return 0;
}
if (sock->AsyncMode)
{
return 0;
}
// Receive
s = sock->socket;
ret = recv(s, data, size, MSG_PEEK);
//Debug("Peek: %u\n", ret);
if (ret > 0)
{
return ret;
}
return 0;
}
// TCP receive
UINT Recv(SOCK *sock, void *data, UINT size, bool secure)
{
SOCKET s;
int ret;
#ifdef UNIX_SOLARIS
SOCKET_TIMEOUT_PARAM *ttparam;
#endif //UNIX_SOLARIS
// Validate arguments
if (sock == NULL || data == NULL || size == 0)
{
return 0;
}
sock->NoNeedToRead = false;
if (sock->Type == SOCK_INPROC)
{
return RecvInProc(sock, data, size);
}
if (sock->Type != SOCK_TCP || sock->Connected == false || sock->ListenMode != false ||
sock->socket == INVALID_SOCKET)
{
return 0;
}
if (secure != false && sock->SecureMode == false)
{
return 0;
}
if (secure)
{
return SecureRecv(sock, data, size);
}
// Receive
s = sock->socket;
#ifdef OS_UNIX
if (sock->AsyncMode == false)
{
sock->CallingThread = pthread_self();
}
#endif // OS_UNIX
// Start of the timeout thread for SOLARIS
#ifdef UNIX_SOLARIS
ttparam = NewSocketTimeout(sock);
#endif // UNIX_SOLARIS
ret = recv(s, data, size, 0);
// Stop the timeout thread
#ifdef UNIX_SOLARIS
FreeSocketTimeout(ttparam);
#endif // UNIX_SOLARIS
#ifdef OS_UNIX
if (sock->AsyncMode == false)
{
sock->CallingThread = 0;
}
#endif // OS_UNIX
if (ret > 0)
{
// Successful reception
Lock(sock->lock);
{
sock->RecvSize += (UINT64)ret;
sock->SendNum++;
}
Unlock(sock->lock);
return (UINT)ret;
}
// Transmission failure
if (sock->AsyncMode)
{
// In asynchronous mode, examine the error
if (ret == SOCKET_ERROR)
{
#ifdef OS_WIN32
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// In blocking
return SOCK_LATER;
}
else
{
//Debug("Socket Error: %u\n", WSAGetLastError());
}
#else // OS_WIN32
if (errno == EAGAIN)
{
// In blocking
return SOCK_LATER;
}
#endif // OS_WIN32
}
}
// Disconnected
Disconnect(sock);
return 0;
}
// TCP transmission
UINT Send(SOCK *sock, void *data, UINT size, bool secure)
{
SOCKET s;
int ret;
// Validate arguments
if (sock == NULL || data == NULL || size == 0)
{
return 0;
}
if (sock->Type == SOCK_INPROC)
{
return SendInProc(sock, data, size);
}
size = MIN(size, MAX_SEND_BUF_MEM_SIZE);
if (sock->Type != SOCK_TCP || sock->Connected == false || sock->ListenMode != false ||
sock->socket == INVALID_SOCKET)
{
return 0;
}
if (secure != false && sock->SecureMode == false)
{
return 0;
}
if (secure)
{
return SecureSend(sock, data, size);
}
// Transmission
s = sock->socket;
ret = send(s, data, size, 0);
if (ret > 0)
{
// Successful transmission
Lock(sock->lock);
{
sock->SendSize += (UINT64)ret;
sock->SendNum++;
}
Unlock(sock->lock);
sock->WriteBlocked = false;
return (UINT)ret;
}
// Transmission failure
if (sock->AsyncMode)
{
// In asynchronous mode, examine the error
if (ret == SOCKET_ERROR)
{
#ifdef OS_WIN32
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// In blocking
sock->WriteBlocked = true;
return SOCK_LATER;
}
else
{
//Debug("Socket Error: %u\n", WSAGetLastError());
}
#else // OS_WIN32
if (errno == EAGAIN)
{
// In blocking
sock->WriteBlocked = true;
return SOCK_LATER;
}
#endif // OS_WIN32
}
}
// Disconnected
Disconnect(sock);
return 0;
}
// Get the time-out value (in milliseconds)
UINT GetTimeout(SOCK *sock)
{
// Validate arguments
if (sock == NULL)
{
return INFINITE;
}
if (sock->Type != SOCK_TCP && sock->Type != SOCK_INPROC)
{
return INFINITE;
}
return sock->TimeOut;
}
// Setting the time-out value (in milliseconds)
void SetTimeout(SOCK *sock, UINT timeout)
{
// Validate arguments
if (sock == NULL)
{
return;
}
if (sock->Type == SOCK_UDP)
{
return;
}
if (timeout == INFINITE)
{
timeout = TIMEOUT_INFINITE;
}
sock->TimeOut = timeout;
// Debug("SetTimeout(%u)\n",timeout);
if (sock->Type != SOCK_INPROC)
{
#ifdef OS_WIN32
setsockopt(sock->socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(UINT));
setsockopt(sock->socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(UINT));
#endif
#ifdef OS_UNIX
#ifndef UNIX_SOLARIS
{
struct timeval tv_timeout;
tv_timeout.tv_sec = timeout / 1000; // miliseconds to seconds
tv_timeout.tv_usec = (timeout % 1000) * 1000; // miliseconds to microseconds
(void)setsockopt(sock->socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv_timeout, sizeof(tv_timeout));
(void)setsockopt(sock->socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_timeout, sizeof(tv_timeout));
}
#endif // UNIX_SOLARIS
#endif // OS_UNIX
}
}
// Disable GetHostName call by accepting new TCP connection
void DisableGetHostNameWhenAcceptInit()
{
disable_gethostname_by_accept = true;
}
// Initialize the connection acceptance
void AcceptInit(SOCK *s)
{
AcceptInitEx(s, false);
}
void AcceptInitEx(SOCK *s, bool no_lookup_hostname)
{
char tmp[MAX_SIZE];
// Validate arguments
if (s == NULL)
{
return;
}
Zero(tmp, sizeof(tmp));
if (disable_gethostname_by_accept == false && no_lookup_hostname == false)
{
if (GetHostName(tmp, sizeof(tmp), &s->RemoteIP) == false ||
IsEmptyStr(tmp))
{
IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
}
}
else
{
IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
}
if (s->RemoteHostname != NULL)
{
Free(s->RemoteHostname);
}
s->RemoteHostname = CopyStr(tmp);
}
// TCP connection acceptance (IPv4)
SOCK *Accept(SOCK *sock)
{
SOCK *ret;
SOCKET s, new_socket;
int size;
struct sockaddr_in addr;
bool true_flag = true;
// Validate arguments
if (sock == NULL)
{
return NULL;
}
if (sock->Type == SOCK_INPROC)
{
return AcceptInProc(sock);
}
if (sock->Type == SOCK_REVERSE_LISTEN)
{
return AcceptReverse(sock);
}
if (sock->Type == SOCK_RUDP_LISTEN)
{
return AcceptRUDP(sock);
}
if (sock->ListenMode == false || sock->Type != SOCK_TCP || sock->ServerMode == false)
{
return NULL;
}
if (sock->CancelAccept)
{
return NULL;
}
if (sock->IPv6)
{
return Accept6(sock);
}
s = sock->socket;
if (s == INVALID_SOCKET)
{
return NULL;
}
Zero(&addr, sizeof(addr));
size = sizeof(addr);
#ifdef OS_UNIX
#if defined(UNIX_LINUX) || defined(UNIX_MACOS)
UnixIgnoreSignalForThread(SIGUSR1);
#endif // defined(UNIX_LINUX) || defined(UNIX_MACOS)
sock->CallingThread = pthread_self();
#endif // OS_UNIX
#ifdef OS_WIN32
if (sock->EnableConditionalAccept)
{
new_socket = Win32Accept(sock, s, (struct sockaddr *)&addr,(int *)&size, false);
}
else
{
new_socket = accept(s, (struct sockaddr *)&addr,(int *)&size);
}
#else // OS_WIN32
new_socket = accept(s, (struct sockaddr *)&addr,(int *)&size);
#endif // OS_WIN32
#ifdef OS_UNIX
sock->CallingThread = 0;
#endif // OS_UNIX
if (new_socket == INVALID_SOCKET)
{
if (sock->CancelAccept)
{
sock->AcceptCanceled = true;
}
return NULL;
}
if (sock->CancelAccept)
{
sock->AcceptCanceled = true;
closesocket(new_socket);
return NULL;
}
ret = NewSock();
ret->socket = new_socket;
ret->Connected = true;
ret->AsyncMode = false;
ret->Type = SOCK_TCP;
ret->ServerMode = true;
ret->SecureMode = false;
// Configuring the TCP options
(void)setsockopt(ret->socket, IPPROTO_TCP, TCP_NODELAY, (char *)&true_flag, sizeof(bool));
// Initialization of the time-out value
SetTimeout(ret, TIMEOUT_INFINITE);
// Socket information
QuerySocketInformation(ret);
if (IsLocalHostIP(&ret->RemoteIP) == false)
{
ret->IpClientAdded = true;
AddIpClient(&ret->RemoteIP);
}
if (IsZeroIp(&sock->LocalIP) == false && IsLocalHostIP(&sock->LocalIP) == false)
{
IP current_ip;
if (GetCurrentGlobalIP(&current_ip, false) == false)
{
SetCurrentGlobalIP(&sock->LocalIP, false);
}
}
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_NATIVE_V4);
AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "IPv4");
return ret;
}
// TCP connection acceptance (IPv6)
SOCK *Accept6(SOCK *sock)
{
SOCK *ret;
SOCKET s, new_socket;
int size;
struct sockaddr_in6 addr;
bool true_flag = true;
// Validate arguments
if (sock == NULL)
{
return NULL;
}
if (sock->ListenMode == false || sock->Type != SOCK_TCP || sock->ServerMode == false)
{
return NULL;
}
if (sock->CancelAccept)
{
return NULL;
}
if (sock->IPv6 == false)
{
return NULL;
}
s = sock->socket;
if (s == INVALID_SOCKET)
{
return NULL;
}
Zero(&addr, sizeof(addr));
size = sizeof(addr);
#ifdef OS_UNIX
#if defined(UNIX_LINUX) || defined(UNIX_MACOS)
UnixIgnoreSignalForThread(SIGUSR1);
#endif // defined(UNIX_LINUX) || defined(UNIX_MACOS)
sock->CallingThread = pthread_self();
#endif // OS_UNIX
#ifdef OS_WIN32
if (sock->EnableConditionalAccept)
{
new_socket = Win32Accept(sock, s, (struct sockaddr *)&addr,(int *)&size, true);
}
else
{
new_socket = accept(s, (struct sockaddr *)&addr,(int *)&size);
}
#else // OS_WIN32
new_socket = accept(s, (struct sockaddr *)&addr,(int *)&size);
#endif // OS_WIN32
#ifdef OS_UNIX
sock->CallingThread = 0;
#endif // OS_UNIX
if (new_socket == INVALID_SOCKET)
{
if (sock->CancelAccept)
{
sock->AcceptCanceled = true;
}
return NULL;
}
if (sock->CancelAccept)
{
sock->AcceptCanceled = true;
closesocket(new_socket);
return NULL;
}
ret = NewSock();
ret->socket = new_socket;
ret->Connected = true;
ret->AsyncMode = false;
ret->Type = SOCK_TCP;
ret->ServerMode = true;
ret->SecureMode = false;
// Configuring the TCP options
(void)setsockopt(ret->socket, IPPROTO_TCP, TCP_NODELAY, (char *)&true_flag, sizeof(bool));
// Initialize the time-out value
SetTimeout(ret, TIMEOUT_INFINITE);
// Socket information
QuerySocketInformation(ret);
if (IsLocalHostIP(&ret->RemoteIP) == false)
{
ret->IpClientAdded = true;
AddIpClient(&ret->RemoteIP);
}
if (IsZeroIp(&sock->LocalIP) == false && IsLocalHostIP(&sock->LocalIP) == false)
{
IP current_ip;
if (GetCurrentGlobalIP(&current_ip, true) == false)
{
SetCurrentGlobalIP(&sock->LocalIP, true);
}
}
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_NATIVE_V6);
AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "IPv6");
return ret;
}
// Standby for TCP (IPv6)
SOCK *ListenEx6(UINT port, bool local_only)
{
return ListenEx62(port, local_only, false);
}
SOCK *ListenEx62(UINT port, bool local_only, bool enable_ca)
{
SOCKET s;
SOCK *sock;
struct sockaddr_in6 addr;
struct in6_addr in;
bool true_flag = true;
IP localhost;
UINT backlog = SOMAXCONN;
// Validate arguments
if (port == 0 || port >= 65536)
{
return NULL;
}
#ifdef OS_WIN32
if (MsIsVista() == false)
{
// Disable the Conditional Accept due to a bug in Windows
enable_ca = false;
}
#endif // OS_WIN32
// Initialization
Zero(&addr, sizeof(addr));
Zero(&in, sizeof(in));
GetLocalHostIP6(&localhost);
addr.sin6_port = htons((UINT)port);
addr.sin6_family = AF_INET6;
if (local_only)
{
IPToInAddr6(&addr.sin6_addr, &localhost);
enable_ca = false;
}
// Creating a socket
s = socket(AF_INET6, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
return NULL;
}
#ifdef OS_UNIX
// It is necessary to set the IPv6 Only flag on a UNIX system
(void)setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &true_flag, sizeof(true_flag));
#endif // OS_UNIX
#ifdef OS_UNIX
// This only have enabled for UNIX system since there is a bug
// in the implementation of REUSEADDR in Windows OS
(void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&true_flag, sizeof(bool));
#endif // OS_UNIX
if (bind(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_in6)) != 0)
{
// Bind failure
closesocket(s);
return NULL;
}
#ifdef OS_WIN32
if (enable_ca)
{
if (MsIsWinXPOrGreater())
{
setsockopt(s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (char *)&true_flag, sizeof(bool));
backlog = 1;
}
}
#endif // OS_WIN32
if (listen(s, backlog))
{
// Listen failure
closesocket(s);
return NULL;
}
// Success
sock = NewSock();
sock->Connected = false;
sock->AsyncMode = false;
sock->ServerMode = true;
sock->Type = SOCK_TCP;
sock->socket = s;
sock->ListenMode = true;
sock->SecureMode = false;
sock->LocalPort = port;
sock->IPv6 = true;
sock->LocalOnly = local_only;
sock->EnableConditionalAccept = enable_ca;
return sock;
}
// Standby for the TCP
SOCK *Listen(UINT port)
{
return ListenEx(port, false);
}
SOCK *ListenEx(UINT port, bool local_only)
{
return ListenEx2(port, local_only, false, NULL);
}
SOCK *ListenEx2(UINT port, bool local_only, bool enable_ca, IP *listen_ip)
{
SOCKET s;
SOCK *sock;
struct sockaddr_in addr;
struct in_addr in;
bool true_flag = true;
IP localhost;
UINT backlog = SOMAXCONN;
// Validate arguments
if (port == 0 || port >= 65536)
{
return NULL;
}
#ifdef OS_WIN32
if (MsIsVista() == false)
{
// Disable the Conditional Accept due to a bug in Windows
enable_ca = false;
}
#endif // OS_WIN32
// Initialization
Zero(&addr, sizeof(addr));
Zero(&in, sizeof(in));
SetIP(&localhost, 127, 0, 0, 1);
addr.sin_port = htons((UINT)port);
if (listen_ip == NULL)
{
*((UINT *)&addr.sin_addr) = htonl(INADDR_ANY);
}
else
{
IPToInAddr(&addr.sin_addr, listen_ip);
}
addr.sin_family = AF_INET;
if (local_only)
{
IPToInAddr(&addr.sin_addr, &localhost);
enable_ca = false;
}
// Creating a socket
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
return NULL;
}
#ifdef OS_UNIX
// This only have enabled for UNIX system since there is a bug
// in the implementation of REUSEADDR in Windows OS
(void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&true_flag, sizeof(bool));
#endif // OS_UNIX
if (bind(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0)
{
// Bind failure
closesocket(s);
return NULL;
}
#ifdef OS_WIN32
if (enable_ca)
{
if (MsIsWinXPOrGreater())
{
setsockopt(s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (char *)&true_flag, sizeof(bool));
backlog = 1;
}
}
#endif // OS_WIN32
if (listen(s, backlog))
{
// Listen failure
closesocket(s);
return NULL;
}
// Success
sock = NewSock();
sock->Connected = false;
sock->AsyncMode = false;
sock->ServerMode = true;
sock->Type = SOCK_TCP;
sock->socket = s;
sock->ListenMode = true;
sock->SecureMode = false;
sock->LocalPort = port;
sock->LocalOnly = local_only;
sock->EnableConditionalAccept = enable_ca;
return sock;
}
// TCP disconnect
void Disconnect(SOCK *sock)
{
SOCKET s;
bool true_flag = true;
bool false_flag = false;
// Validate arguments
if (sock == NULL)
{
return;
}
sock->Disconnecting = true;
#ifdef ENABLE_SSL_LOGGING
SockCloseSslLogging(sock);
#endif // ENABLE_SSL_LOGGING
#ifdef OS_UNIX
UnixFreeAsyncSocket(sock);
#endif // UnixFreeAsyncSocket
if (sock->Type == SOCK_TCP && sock->ListenMode)
{
bool no_tcp_check_port = false;
// Connect to localhost if the socket is in listening
sock->CancelAccept = true;
#if defined(UNIX_LINUX) || defined(UNIX_MACOS)
{
pthread_t t = sock->CallingThread;
// Send a signal to the socket to abort accept() forcibly on Linux
if (t != 0)
{
pthread_kill(t, SIGUSR1);
SleepThread(200);
}
}
#endif // defined(UNIX_LINUX) || defined(UNIX_MACOS)
#ifdef OS_WIN32
if (sock->hAcceptEvent != NULL)
{
SetEvent(sock->hAcceptEvent);
no_tcp_check_port = true;
}
#endif // OS_WIN32
if (sock->AcceptCanceled == false)
{
if (no_tcp_check_port == false)
{
if (sock->IPv6 == false)
{
CheckTCPPort("127.0.0.1", sock->LocalPort);
}
else
{
CheckTCPPort("::1", sock->LocalPort);
}
}
}
}
Lock(disconnect_function_lock);
Lock(sock->disconnect_lock);
if (sock->Type == SOCK_TCP)
{
if (sock->socket != INVALID_SOCKET)
{
// Forced disconnection flag
#ifdef SO_DONTLINGER
(void)setsockopt(sock->socket, SOL_SOCKET, SO_DONTLINGER, (char *)&true_flag, sizeof(bool));
#else // SO_DONTLINGER
(void)setsockopt(sock->socket, SOL_SOCKET, SO_LINGER, (char *)&false_flag, sizeof(bool));
#endif // SO_DONTLINGER
// setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (char *)&true_flag, sizeof(bool));
}
// TCP socket
Lock(sock->lock);
{
if (sock->socket == INVALID_SOCKET)
{
Unlock(sock->lock);
Unlock(sock->disconnect_lock);
Unlock(disconnect_function_lock);
return;
}
s = sock->socket;
if (sock->Connected)
{
struct linger ling;
Zero(&ling, sizeof(ling));
#if 0
// SSL disconnect
Lock(sock->ssl_lock);
{
if (sock->SecureMode)
{
SSL_shutdown(sock->ssl);
}
}
Unlock(sock->ssl_lock);
#endif
// Disconnect
shutdown(s, 2);
}
// Close the socket
closesocket(s);
#ifdef OS_UNIX
#ifdef FIX_SSL_BLOCKING
if (sock->CallingThread != NULL)
{
pthread_kill(sock->CallingThread, 64);
}
#endif // FIX_SSL_BLOCKING
#endif // OS_UNIX
// Release the SSL
Lock(sock->ssl_lock);
{
if (sock->SecureMode)
{
if (sock->ssl != NULL)
{
Lock(openssl_lock);
{
SSL_free(sock->ssl);
FreeSSLCtx(sock->ssl_ctx);
}
Unlock(openssl_lock);
sock->ssl = NULL;
sock->ssl_ctx = NULL;
}
sock->Connected = false;
sock->SecureMode = false;
}
}
Unlock(sock->ssl_lock);
// Initialization
sock->socket = INVALID_SOCKET;
sock->Type = 0;
sock->AsyncMode = false;
sock->Connected = false;
sock->ListenMode = false;
sock->SecureMode = false;
if (sock->IpClientAdded)
{
DelIpClient(&sock->RemoteIP);
sock->IpClientAdded = false;
}
}
Unlock(sock->lock);
if (sock->BulkSendTube != NULL)
{
TubeDisconnect(sock->BulkSendTube);
}
if (sock->BulkRecvTube != NULL)
{
TubeDisconnect(sock->BulkRecvTube);
}
}
else if (sock->Type == SOCK_UDP)
{
// UDP socket
Lock(sock->lock);
{
if (sock->socket == INVALID_SOCKET)
{
Unlock(sock->lock);
Unlock(sock->disconnect_lock);
Unlock(disconnect_function_lock);
return;
}
s = sock->socket;
// Close the socket
closesocket(s);
// Initialization
sock->socket = INVALID_SOCKET;
sock->Type = 0;
sock->AsyncMode = false;
sock->Connected = false;
sock->ListenMode = false;
sock->SecureMode = false;
}
Unlock(sock->lock);
}
else if (sock->Type == SOCK_INPROC)
{
// In-process socket
if (sock->ListenMode)
{
// Stop the Accept process
sock->CancelAccept = true;
Set(sock->InProcAcceptEvent);
LockQueue(sock->InProcAcceptQueue);
{
while (true)
{
SOCK *ss = GetNext(sock->InProcAcceptQueue);
if (ss == NULL)
{
break;
}
Disconnect(ss);
ReleaseSock(ss);
}
}
UnlockQueue(sock->InProcAcceptQueue);
}
else
{
// Disconnect the Tube
TubeDisconnect(sock->SendTube);
TubeDisconnect(sock->RecvTube);
sock->socket = INVALID_SOCKET;
sock->AsyncMode = false;
sock->Connected = false;
sock->ListenMode = false;
sock->SecureMode = false;
}
}
else if (sock->Type == SOCK_RUDP_LISTEN)
{
// RUDP Listen socket
if (sock->ListenMode)
{
// Stop the Accept process
sock->CancelAccept = true;
Set(sock->R_UDP_Stack->NewSockConnectEvent);
sock->R_UDP_Stack->Halt = true;
Set(sock->R_UDP_Stack->HaltEvent);
SetSockEvent(sock->R_UDP_Stack->SockEvent);
}
}
else if (sock->Type == SOCK_REVERSE_LISTEN)
{
// Reverse Listen socket
if (sock->ListenMode)
{
// Stop the Accept process
sock->CancelAccept = true;
Set(sock->ReverseAcceptEvent);
LockQueue(sock->ReverseAcceptQueue);
{
while (true)
{
SOCK *ss = GetNext(sock->ReverseAcceptQueue);
if (ss == NULL)
{
break;
}
Disconnect(ss);
ReleaseSock(ss);
}
}
UnlockQueue(sock->ReverseAcceptQueue);
}
}
Unlock(sock->disconnect_lock);
Unlock(disconnect_function_lock);
}
typedef struct TCP_PORT_CHECK
{
REF *ref;
char hostname[MAX_SIZE];
UINT port;
bool ok;
} TCP_PORT_CHECK;
// Check whether the TCP port can be connected
bool CheckTCPPortEx(char *hostname, UINT port, UINT timeout)
{
SOCK *s;
// Validate arguments
if (hostname == NULL || port == 0 || port >= 65536)
{
return false;
}
if (timeout == 0)
{
timeout = TIMEOUT_TCP_PORT_CHECK;
}
s = ConnectEx(hostname, port, timeout);
if (s == NULL)
{
return false;
}
else
{
Disconnect(s);
ReleaseSock(s);
return true;
}
}
bool CheckTCPPort(char *hostname, UINT port)
{
return CheckTCPPortEx(hostname, port, TIMEOUT_TCP_PORT_CHECK);
}
#ifdef OS_UNIX
// Connection with timeout (UNIX version)
int connect_timeout(SOCKET s, struct sockaddr *addr, int size, int timeout, bool *cancel_flag)
{
SOCKSET set;
bool ok = false;
UINT64 start_time;
// Validate arguments
if (s == INVALID_SOCKET || addr == NULL)
{
return -1;
}
if (timeout == 0)
{
timeout = TIMEOUT_TCP_PORT_CHECK;
}
UnixSetSocketNonBlockingMode(s, true);
start_time = Tick64();
while (true)
{
int ret;
ret = connect(s, addr, size);
if (ret == 0 || errno == EISCONN)
{
ok = true;
break;
}
else
{
if (((start_time + (UINT64)timeout) <= Tick64()) || (errno != EAGAIN && errno != EINPROGRESS && errno != EALREADY))
{
// Failure
break;
}
else if (*cancel_flag)
{
// Cancel
break;
}
else
{
// Connecting
SleepThread(50);
UnixSelectInner(1, (UINT *)&s, 1, (UINT *)&s, 100);
}
}
}
UnixSetSocketNonBlockingMode(s, false);
if (ok)
{
return 0;
}
else
{
return -1;
}
}
#else
// Connection with timeout (Win32 version)
int connect_timeout(SOCKET s, struct sockaddr *addr, int size, int timeout, bool *cancel_flag)
{
UINT64 start_time;
bool ok = false;
bool timeouted = false;
WSAEVENT hEvent;
UINT zero = 0;
UINT tmp = 0;
UINT ret_size = 0;
bool is_nt = false;
// Validate arguments
if (s == INVALID_SOCKET || addr == NULL)
{
return -1;
}
if (timeout == 0)
{
timeout = TIMEOUT_TCP_PORT_CHECK;
}
is_nt = OS_IS_WINDOWS_NT(GetOsInfo()->OsType);
// Create an event
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// Associate the socket with the event
WSAEventSelect(s, hEvent, FD_CONNECT);
start_time = Tick64();
while (true)
{
int ret;
ret = connect(s, addr, size);
if (ret == 0)
{
ok = true;
break;
}
else
{
int err = WSAGetLastError();
//Debug("err=%u\n", err);
//Debug("cancel_flag=%u\n", *cancel_flag);
if (timeouted && ((err == WSAEALREADY) || (err == WSAEWOULDBLOCK && !is_nt)))
{
// Time-out
ok = false;
break;
}
if (*cancel_flag)
{
// Cancel
ok = false;
break;
}
if (err == WSAEISCONN || (err == WSAEINVAL && is_nt))
{
ok = true;
break;
}
if (((start_time + (UINT64)timeout) <= Tick64()) || (err != WSAEWOULDBLOCK && err != WSAEALREADY && (is_nt || err != WSAEINVAL)))
{
// Failure (timeout)
break;
}
else
{
SleepThread(10);
// Connecting
if (WaitForSingleObject(hEvent, 100) == WAIT_OBJECT_0)
{
timeouted = true;
}
}
}
}
// Remove the socket from the event
WSAEventSelect(s, hEvent, 0);
// Restore to synchronized socket
WSAIoctl(s, FIONBIO, &zero, sizeof(zero), &tmp, sizeof(tmp), &ret_size, NULL, NULL);
// Close the event
CloseHandle(hEvent);
if (ok)
{
return 0;
}
else
{
return -1;
}
}
#endif // OS_UNIX
// Set the TOS value of the socket
void SetSockTos(SOCK *s, int tos)
{
// Validate arguments
if (s == NULL)
{
return;
}
if (s->CurrentTos == tos)
{
return;
}
#ifdef IP_TOS
(void)setsockopt(s->socket, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int));
#endif // IP_TOS
s->CurrentTos = tos;
}
// Set the priority of the socket
void SetSockHighPriority(SOCK *s, bool flag)
{
// Validate arguments
if (s == NULL)
{
return;
}
SetSockTos(s, (flag ? 16 : 0));
}
// Connect to the IPv4 host using a socket
SOCKET ConnectTimeoutIPv4(IP *ip, UINT port, UINT timeout, bool *cancel_flag)
{
SOCKET s;
struct sockaddr_in sockaddr4;
struct in_addr addr4;
Zero(&sockaddr4, sizeof(sockaddr4));
Zero(&addr4, sizeof(addr4));
// Generate a sockaddr_in
IPToInAddr(&addr4, ip);
sockaddr4.sin_port = htons((USHORT)port);
sockaddr4.sin_family = AF_INET;
sockaddr4.sin_addr.s_addr = addr4.s_addr;
// Socket creation
s = socket(AF_INET, SOCK_STREAM, 0);
if (s != INVALID_SOCKET)
{
// Connection
if (connect_timeout(s, (struct sockaddr *)&sockaddr4, sizeof(struct sockaddr_in), timeout, cancel_flag) != 0)
{
// Connection failure
closesocket(s);
s = INVALID_SOCKET;
}
}
return s;
}
// Identify whether the HTTPS server to be connected is a SoftEther VPN
bool DetectIsServerSoftEtherVPN(SOCK *s)
{
HTTP_HEADER *h;
char ip_str[MAX_SIZE];
char *send_str;
UINT content_len;
BUF *recv_buf;
void *socket_buffer;
UINT socket_buffer_size = 32768;
bool ok = false;
// Validate arguments
if (s == NULL)
{
return false;
}
IPToStr(ip_str, sizeof(ip_str), &s->RemoteIP);
// Request generation
h = NewHttpHeaderEx("GET", "/", "HTTP/1.1", true);
AddHttpValue(h, NewHttpValue("X-VPN", "1"));
AddHttpValue(h, NewHttpValue("Host", ip_str));
AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
AddHttpValue(h, NewHttpValue("Accept-Language", "ja"));
AddHttpValue(h, NewHttpValue("User-Agent", DEFAULT_USER_AGENT));
AddHttpValue(h, NewHttpValue("Pragma", "no-cache"));
AddHttpValue(h, NewHttpValue("Cache-Control", "no-cache"));
send_str = HttpHeaderToStr(h);
FreeHttpHeader(h);
// Transmission
if (SendAll(s, send_str, StrLen(send_str), true) == false)
{
Free(send_str);
return false;
}
Free(send_str);
// Receive
h = RecvHttpHeader(s);
if (h == NULL)
{
return false;
}
// Get the length of the content
content_len = GetContentLength(h);
FreeHttpHeader(h);
if (content_len == 0 || content_len >= (1024 * 1024))
{
return false;
}
// Receive contents
recv_buf = NewBuf();
socket_buffer = Malloc(socket_buffer_size);
while (true)
{
UINT recvsize = MIN(socket_buffer_size, content_len - recv_buf->Size);
UINT size;
if (recvsize == 0)
{
ok = true;
break;
}
size = Recv(s, socket_buffer, recvsize, true);
if (size == 0)
{
// Disconnected
break;
}
WriteBuf(recv_buf, socket_buffer, size);
}
SeekBuf(recv_buf, 0, 0);
Free(socket_buffer);
if (ok)
{
// Examine to confirm whether the incoming data is a SoftEther VPN protocol
char tmp[1024];
Zero(tmp, sizeof(tmp));
Copy(tmp, recv_buf->Buf, MIN(recv_buf->Size, (sizeof(tmp) - 1)));
ok = false;
if (StartWith(tmp, http_detect_server_startwith))
{
ok = true;
}
else if (InStr(tmp, http_detect_server_tag_future))
{
ok = true;
}
}
FreeBuf(recv_buf);
return ok;
}
// TCP connection thread
void ConnectThreadForTcp(THREAD *thread, void *param)
{
SOCK *sock;
char hostname[MAX_SIZE];
CONNECT_TCP_RUDP_PARAM *p = (CONNECT_TCP_RUDP_PARAM *)param;
if (thread == NULL || p == NULL)
{
return;
}
// Delay
if (p->Delay >= 1)
{
WaitEx(NULL, p->Delay, p->CancelFlag);
}
// Connecting process
IPToStr(hostname, sizeof(hostname), &p->Ip);
sock = ConnectEx3(hostname, p->Port, p->Timeout, p->CancelFlag, NULL, NULL, false, true);
if (sock != NULL && p->Tcp_TryStartSsl)
{
bool ssl_ret = false;
p->Tcp_InNegotiation = true;
// Attempt the SSL negotiation to take this opportunity
Lock(p->CancelLock);
{
if ((*p->CancelFlag) == false)
{
p->CancelDisconnectSock = sock;
AddRef(sock->ref);
}
else
{
Debug("User Cancel to StartSSL.\n");
goto LABEL_CANCEL;
}
}
Unlock(p->CancelLock);
// Start the SSL communication
ssl_ret = StartSSLEx(sock, NULL, NULL, 0, p->Hostname);
if (ssl_ret)
{
// Identify whether the HTTPS server to be connected is a SoftEther VPN
SetTimeout(sock, (10 * 1000));
ssl_ret = DetectIsServerSoftEtherVPN(sock);
SetTimeout(sock, INFINITE);
if (ssl_ret == false)
{
Debug("DetectIsServerSoftEtherVPN Error.\n");
}
}
Lock(p->CancelLock);
{
ReleaseSock(p->CancelDisconnectSock);
p->CancelDisconnectSock = NULL;
}
LABEL_CANCEL:
Unlock(p->CancelLock);
if (ssl_ret == false)
{
// SSL negotiation failure
Disconnect(sock);
ReleaseSock(sock);
Debug("Fail to StartSSL.\n");
sock = NULL;
}
}
p->Result_Tcp_Sock = sock;
p->Ok = (p->Result_Tcp_Sock == NULL ? false : true);
p->FinishedTick = Tick64();
p->Finished = true;
p->Tcp_InNegotiation = false;
Set(p->FinishEvent);
}
// R-UDP over ICMP / over DNS connection thread
void ConnectThreadForOverDnsOrIcmp(THREAD *thread, void *param)
{
SOCK *sock;
CONNECT_TCP_RUDP_PARAM *p = (CONNECT_TCP_RUDP_PARAM *)param;
if (thread == NULL || p == NULL)
{
return;
}
// Delay
if (p->Delay >= 1)
{
WaitEx(NULL, p->Delay, p->CancelFlag);
}
// Connecting process
sock = NewRUDPClientDirect(p->SvcName, &p->Ip,
(p->RUdpProtocol == RUDP_PROTOCOL_DNS ? 53 : MAKE_SPECIAL_PORT(IP_PROTO_ICMPV4)),
&p->NatT_ErrorCode, p->Timeout, p->CancelFlag, NULL, NULL,
(p->RUdpProtocol == RUDP_PROTOCOL_DNS ? 0 : MAKE_SPECIAL_PORT(IP_PROTO_ICMPV4)),
(p->RUdpProtocol == RUDP_PROTOCOL_DNS ? true : false));
p->Result_Nat_T_Sock = sock;
p->Ok = (p->Result_Nat_T_Sock == NULL ? false : true);
p->FinishedTick = Tick64();
p->Finished = true;
Set(p->FinishEvent);
}
// R-UDP (via NAT-T) connection thread
void ConnectThreadForRUDP(THREAD *thread, void *param)
{
SOCK *sock;
CONNECT_TCP_RUDP_PARAM *p = (CONNECT_TCP_RUDP_PARAM *)param;
if (thread == NULL || p == NULL)
{
return;
}
// Delay
if (p->Delay >= 1)
{
WaitEx(NULL, p->Delay, p->CancelFlag);
}
// Connecting process
sock = NewRUDPClientNatT(p->SvcName, &p->Ip, &p->NatT_ErrorCode, p->Timeout, p->CancelFlag, p->HintStr, p->TargetHostname);
p->Result_Nat_T_Sock = sock;
p->Ok = (p->Result_Nat_T_Sock == NULL ? false : true);
p->FinishedTick = Tick64();
p->Finished = true;
Set(p->FinishEvent);
}
// TCP connection
SOCK *Connect(char *hostname, UINT port)
{
return ConnectEx(hostname, port, 0);
}
SOCK *ConnectEx(char *hostname, UINT port, UINT timeout)
{
return ConnectEx2(hostname, port, timeout, NULL);
}
SOCK *ConnectEx2(char *hostname, UINT port, UINT timeout, bool *cancel_flag)
{
return ConnectEx3(hostname, port, timeout, cancel_flag, NULL, NULL, false, true);
}
SOCK *ConnectEx3(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname)
{
return ConnectEx4(hostname, port, timeout, cancel_flag, nat_t_svc_name, nat_t_error_code, try_start_ssl, no_get_hostname, NULL);
}
SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, char *nat_t_svc_name, UINT *nat_t_error_code, bool try_start_ssl, bool no_get_hostname, IP *ret_ip)
{
SOCK *sock;
SOCKET s;
struct linger ling;
IP ip4;
IP ip6;
bool true_flag = true;
bool false_flag = false;
char tmp[MAX_SIZE];
IP current_ip;
bool is_ipv6 = false;
bool dummy = false;
bool use_natt = false;
char hostname_original[MAX_SIZE];
char hint_str[MAX_SIZE];
bool force_use_natt = false;
UINT dummy_int = 0;
IP dummy_ret_ip;
// Validate arguments
if (hostname == NULL || port == 0 || port >= 65536 || IsEmptyStr(hostname))
{
return NULL;
}
if (timeout == 0)
{
timeout = TIMEOUT_TCP_PORT_CHECK;
}
if (cancel_flag == NULL)
{
cancel_flag = &dummy;
}
if (nat_t_error_code == NULL)
{
nat_t_error_code = &dummy_int;
}
Zero(&dummy_ret_ip, sizeof(IP));
if (ret_ip == NULL)
{
ret_ip = &dummy_ret_ip;
}
Zero(hint_str, sizeof(hint_str));
StrCpy(hostname_original, sizeof(hostname_original), hostname);
use_natt = (IsEmptyStr(nat_t_svc_name) ? false : true);
if (use_natt)
{
// In case of using NAT-T, split host name if the '/' is included in the host name
UINT i = SearchStrEx(hostname, "/", 0, false);
if (i == INFINITE)
{
// Not included
StrCpy(hostname_original, sizeof(hostname_original), hostname);
}
else
{
// Included
StrCpy(hostname_original, sizeof(hostname_original), hostname);
hostname_original[i] = 0;
// Force to use the NAT-T
force_use_natt = true;
// Copy the hint string
StrCpy(hint_str, sizeof(hint_str), hostname + i + 1);
if (StrCmpi(hint_str, "tcp") == 0 || StrCmpi(hint_str, "disable") == 0
|| StrCmpi(hint_str, "disabled") == 0
|| StrCmpi(hint_str, "no") == 0 || StrCmpi(hint_str, "none") == 0)
{
// Force not to use the NAT-T
force_use_natt = false;
use_natt = false;
}
}
}
else
{
StrCpy(hostname_original, sizeof(hostname_original), hostname);
}
Zero(&current_ip, sizeof(current_ip));
Zero(&ip4, sizeof(ip4));
Zero(&ip6, sizeof(ip6));
if (IsZeroIp(ret_ip) == false)
{
// Skip name resolution
if (IsIP6(ret_ip))
{
Copy(&ip6, ret_ip, sizeof(IP));
}
else
{
Copy(&ip4, ret_ip, sizeof(IP));
}
//Debug("Using cached IP address: %s = %r\n", hostname_original, ret_ip);
}
else
{
// Forward resolution
if (GetIP46Ex(&ip4, &ip6, hostname_original, 0, cancel_flag) == false)
{
return NULL;
}
}
if (IsZeroIp(&ip4) == false && IsIPLocalHostOrMySelf(&ip4))
{
// NAT-T isn't used in the case of connection to localhost
force_use_natt = false;
use_natt = false;
}
s = INVALID_SOCKET;
// Attempt to connect with IPv4
if (IsZeroIp(&ip4) == false)
{
if (use_natt == false)
{
// Normal connection without using NAT-T
s = ConnectTimeoutIPv4(&ip4, port, timeout, cancel_flag);
if (s != INVALID_SOCKET)
{
Copy(&current_ip, &ip4, sizeof(IP));
Copy(ret_ip, &ip4, sizeof(IP));
}
}
else if (force_use_natt)
{
// The connection by forcing the use of NAT-T (not to connection with normal TCP)
SOCK *nat_t_sock = NewRUDPClientNatT(nat_t_svc_name, &ip4, nat_t_error_code, timeout, cancel_flag,
hint_str, hostname);
if (nat_t_sock != NULL)
{
StrCpy(nat_t_sock->UnderlayProtocol, sizeof(nat_t_sock->UnderlayProtocol), SOCK_UNDERLAY_NAT_T);
AddProtocolDetailsStr(nat_t_sock->ProtocolDetails, sizeof(nat_t_sock->ProtocolDetails), "RUDP");
}
Copy(ret_ip, &ip4, sizeof(IP));
return nat_t_sock;
}
else
{
// Use the connections using NAT-T with normal TCP connection together
// (Use multiple threads to try to connect in four connection methods concurrently)
CONNECT_TCP_RUDP_PARAM p1, p2, p3, p4;
EVENT *finish_event;
THREAD *t1, *t2, *t3, *t4;
UINT64 start_tick = Tick64();
UINT64 giveup_for_all_tick = start_tick + (UINT64)SOCK_CONNECT_WAIT_FOR_ICMP_AND_DNS_AT_LEAST;
bool cancel_flag2 = false;
SOCK *cancel_sock = NULL;
finish_event = NewEvent();
Zero(&p1, sizeof(p1));
Zero(&p2, sizeof(p2));
Zero(&p3, sizeof(p3));
Zero(&p4, sizeof(p4));
// p1: TCP
StrCpy(p1.Hostname, sizeof(p1.Hostname), hostname_original);
Copy(&p1.Ip, &ip4, sizeof(IP));
p1.Port = port;
p1.Timeout = timeout;
p1.CancelFlag = &cancel_flag2;
p1.FinishEvent = finish_event;
p1.Tcp_TryStartSsl = try_start_ssl;
p1.CancelLock = NewLock();
// p2: NAT-T
StrCpy(p2.Hostname, sizeof(p2.Hostname), hostname_original);
Copy(&p2.Ip, &ip4, sizeof(IP));
p2.Port = port;
p2.Timeout = timeout;
p2.CancelFlag = &cancel_flag2;
p2.FinishEvent = finish_event;
StrCpy(p2.HintStr, sizeof(p2.HintStr), hint_str);
StrCpy(p2.TargetHostname, sizeof(p2.TargetHostname), hostname);
StrCpy(p2.SvcName, sizeof(p2.SvcName), nat_t_svc_name);
p2.Delay = 30; // Delay by 30ms
// p3: over ICMP
StrCpy(p3.Hostname, sizeof(p3.Hostname), hostname_original);
Copy(&p3.Ip, &ip4, sizeof(IP));
p3.Port = port;
p3.Timeout = timeout;
p3.CancelFlag = &cancel_flag2;
p3.FinishEvent = finish_event;
StrCpy(p3.SvcName, sizeof(p3.SvcName), nat_t_svc_name);
p3.RUdpProtocol = RUDP_PROTOCOL_ICMP;
p3.Delay = 200; // Delay by 200ms
// p4: over DNS
StrCpy(p4.Hostname, sizeof(p4.Hostname), hostname_original);
Copy(&p4.Ip, &ip4, sizeof(IP));
p4.Port = port;
p4.Timeout = timeout;
p4.CancelFlag = &cancel_flag2;
p4.FinishEvent = finish_event;
StrCpy(p4.SvcName, sizeof(p4.SvcName), nat_t_svc_name);
p4.RUdpProtocol = RUDP_PROTOCOL_DNS;
p4.Delay = 100; // Delay by 100ms
t1 = NewThread(ConnectThreadForTcp, &p1);
t2 = NewThread(ConnectThreadForRUDP, &p2);
t4 = NewThread(ConnectThreadForOverDnsOrIcmp, &p4);
t3 = NewThread(ConnectThreadForOverDnsOrIcmp, &p3);
while (true)
{
UINT64 now = Tick64();
if (*cancel_flag)
{
// Cancel by the user
break;
}
if (p1.Finished && p2.Finished)
{
// Results for both the TCP and the NAT-T were confirmed
if (now >= giveup_for_all_tick)
{
// Wait at least minimum time until successful of the ICMP or the DNS
break;
}
if (p3.Ok || p4.Ok)
{
// Exit the loop immediately if any of the ICMP or the DNS is successful
break;
}
}
if (p1.Finished && p1.Ok)
{
// Have successfully connected by TCP
break;
}
if (p2.Finished && p2.Ok)
{
UINT p1_wait_time;
UINT64 tcp_giveup_tick;
UINT p2_spent_time;
// Have successfully connected by R-UDP
if (p1.Finished)
{
// Result of TCP is confirmed
break;
}
// Calculate the time takes to complete connection of R-UDP
p2_spent_time = (UINT)(p2.FinishedTick - start_tick);
// Decide the grace time for results of TCP until settled.
// The grace time is four times the duration of the R-UDP, and at least 400 milliseconds from the start,
// and up to 2500 milliseconds after the R-UDP results settled
p1_wait_time = p2_spent_time * 4;
p1_wait_time = MAX(p1_wait_time, 400);
//Debug("p2_spent_time = %u, p1_wait_time = %u\n", p2_spent_time, p1_wait_time);
tcp_giveup_tick = start_tick + (UINT64)p1_wait_time;
tcp_giveup_tick = MIN(tcp_giveup_tick, (p2.FinishedTick + 2500ULL));
if (now >= tcp_giveup_tick)
{
// Result of the TCP is uncertain, but give up
if (p1.Finished || p1.Tcp_InNegotiation == false)
{
// Break only when TCP SSL negotiation is not being processed
break;
}
}
}
Wait(finish_event, 25);
}
cancel_flag2 = true;
Lock(p1.CancelLock);
{
if (p1.CancelDisconnectSock != NULL)
{
cancel_sock = p1.CancelDisconnectSock;
AddRef(cancel_sock->ref);
}
}
Unlock(p1.CancelLock);
if (cancel_sock != NULL)
{
Disconnect(cancel_sock);
ReleaseSock(cancel_sock);
}
WaitThread(t1, INFINITE);
WaitThread(t2, INFINITE);
WaitThread(t3, INFINITE);
WaitThread(t4, INFINITE);
ReleaseThread(t1);
ReleaseThread(t2);
ReleaseThread(t3);
ReleaseThread(t4);
ReleaseEvent(finish_event);
DeleteLock(p1.CancelLock);
if (*cancel_flag)
{
// Abandon all the results because the user canceled
Disconnect(p1.Result_Nat_T_Sock);
ReleaseSock(p1.Result_Nat_T_Sock);
Disconnect(p2.Result_Nat_T_Sock);
ReleaseSock(p2.Result_Nat_T_Sock);
Disconnect(p3.Result_Nat_T_Sock);
ReleaseSock(p3.Result_Nat_T_Sock);
Disconnect(p4.Result_Nat_T_Sock);
ReleaseSock(p4.Result_Nat_T_Sock);
return NULL;
}
if (p1.Ok)
{
char hostname[MAX_SIZE];
// Use the results of the TCP
// Dispose other results
Disconnect(p2.Result_Nat_T_Sock);
ReleaseSock(p2.Result_Nat_T_Sock);
Disconnect(p3.Result_Nat_T_Sock);
ReleaseSock(p3.Result_Nat_T_Sock);
Disconnect(p4.Result_Nat_T_Sock);
ReleaseSock(p4.Result_Nat_T_Sock);
if (GetHostName(hostname, sizeof(hostname), &ip4))
{
Free(p1.Result_Tcp_Sock->RemoteHostname);
p1.Result_Tcp_Sock->RemoteHostname = CopyStr(hostname);
}
Copy(ret_ip, &ip4, sizeof(IP));
return p1.Result_Tcp_Sock;
}
else if (p2.Ok)
{
// Use the results of the R-UDP
// Dispose other results
Disconnect(p3.Result_Nat_T_Sock);
ReleaseSock(p3.Result_Nat_T_Sock);
Disconnect(p4.Result_Nat_T_Sock);
ReleaseSock(p4.Result_Nat_T_Sock);
StrCpy(p2.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p2.Result_Nat_T_Sock->UnderlayProtocol), SOCK_UNDERLAY_NAT_T);
AddProtocolDetailsStr(p2.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p2.Result_Nat_T_Sock->UnderlayProtocol), "RUDP/UDP");
Copy(ret_ip, &ip4, sizeof(IP));
return p2.Result_Nat_T_Sock;
}
else if (p4.Ok)
{
// Use this if over-DNS success
// Dispose other results
Disconnect(p3.Result_Nat_T_Sock);
ReleaseSock(p3.Result_Nat_T_Sock);
StrCpy(p4.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p4.Result_Nat_T_Sock->UnderlayProtocol), SOCK_UNDERLAY_DNS);
AddProtocolDetailsStr(p4.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p4.Result_Nat_T_Sock->UnderlayProtocol), "RUDP/DNS");
Copy(ret_ip, &ip4, sizeof(IP));
return p4.Result_Nat_T_Sock;
}
else if (p3.Ok)
{
// Use this if over ICMP success
StrCpy(p3.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p3.Result_Nat_T_Sock->UnderlayProtocol), SOCK_UNDERLAY_ICMP);
AddProtocolDetailsStr(p3.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p3.Result_Nat_T_Sock->UnderlayProtocol), "RUDP/ICMP");
Copy(ret_ip, &ip4, sizeof(IP));
return p3.Result_Nat_T_Sock;
}
else
{
// Continue the process if all trials failed
*nat_t_error_code = p2.NatT_ErrorCode;
}
}
}
// Attempt to connect with IPv6
if (s == INVALID_SOCKET && IsZeroIp(&ip6) == false)
{
struct sockaddr_in6 sockaddr6;
struct in6_addr addr6;
Zero(&sockaddr6, sizeof(sockaddr6));
Zero(&addr6, sizeof(addr6));
// Generation of the sockaddr_in6
IPToInAddr6(&addr6, &ip6);
sockaddr6.sin6_port = htons((USHORT)port);
sockaddr6.sin6_family = AF_INET6;
sockaddr6.sin6_scope_id = ip6.ipv6_scope_id;
Copy(&sockaddr6.sin6_addr, &addr6, sizeof(addr6));
// Socket creation
s = socket(AF_INET6, SOCK_STREAM, 0);
if (s != INVALID_SOCKET)
{
// Connection
if (connect_timeout(s, (struct sockaddr *)&sockaddr6, sizeof(struct sockaddr_in6), timeout, cancel_flag) != 0)
{
// Connection failure
closesocket(s);
s = INVALID_SOCKET;
}
else
{
Copy(&current_ip, &ip6, sizeof(IP));
is_ipv6 = true;
Copy(ret_ip, &ip6, sizeof(IP));
}
}
}
if (s == INVALID_SOCKET)
{
// Connection fails on both of IPv4, IPv6
return NULL;
}
// Creating a SOCK
sock = NewSock();
sock->socket = s;
sock->Type = SOCK_TCP;
sock->ServerMode = false;
StrCpy(sock->UnderlayProtocol, sizeof(sock->UnderlayProtocol), is_ipv6 ? SOCK_UNDERLAY_NATIVE_V6 : SOCK_UNDERLAY_NATIVE_V4);
AddProtocolDetailsStr(sock->ProtocolDetails, sizeof(sock->ProtocolDetails), is_ipv6 ? "IPv6" : "IPv4");
// Host name resolution
if (no_get_hostname || (GetHostName(tmp, sizeof(tmp), &current_ip) == false))
{
StrCpy(tmp, sizeof(tmp), hostname_original);
}
//Debug("PTR: %s\n", tmp);
sock->RemoteHostname = CopyStr(tmp);
// Debug("new socket: %u\n", s);
Zero(&ling, sizeof(ling));
// Forced disconnection flag
#ifdef SO_DONTLINGER
(void)setsockopt(sock->socket, SOL_SOCKET, SO_DONTLINGER, (char *)&true_flag, sizeof(bool));
#else // SO_DONTLINGER
(void)setsockopt(sock->socket, SOL_SOCKET, SO_LINGER, (char *)&false_flag, sizeof(bool));
#endif // SO_DONTLINGER
// setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (char *)&true_flag, sizeof(bool));
// Configuring TCP options
(void)setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *)&true_flag, sizeof(bool));
// Initialization of the time-out value
SetTimeout(sock, TIMEOUT_INFINITE);
// Get the socket information
QuerySocketInformation(sock);
if (IsZeroIp(&sock->LocalIP) == false && IsLocalHostIP(&sock->LocalIP) == false)
{
IP current_ip;
if (GetCurrentGlobalIP(&current_ip, is_ipv6) == false)
{
SetCurrentGlobalIP(&sock->LocalIP, is_ipv6);
}
}
sock->Connected = true;
sock->AsyncMode = false;
sock->SecureMode = false;
sock->IPv6 = is_ipv6;
return sock;
}
// Add a protocol details strings
void AddProtocolDetailsStr(char *dst, UINT dst_size, char *str)
{
TOKEN_LIST *t1, *t2;
UINT i, j;
if (dst == NULL || str == NULL)
{
return;
}
t1 = ParseTokenWithoutNullStr(dst, " ");
t2 = ParseTokenWithoutNullStr(str, " ");
for (i = 0; i < t2->NumTokens; i++)
{
bool exists = false;
for (j = 0; j < t1->NumTokens; j++)
{
if (StrCmpi(t1->Token[j], t2->Token[i]) == 0)
{
exists = true;
break;
}
}
if (exists == false)
{
StrCat(dst, dst_size, t2->Token[i]);
StrCat(dst, dst_size, " ");
}
}
FreeToken(t1);
FreeToken(t2);
}
void AddProtocolDetailsKeyValueStr(char *dst, UINT dst_size, char *key, char *value)
{
char tmp[128];
StrCpy(tmp, sizeof(tmp), key);
StrCat(tmp, sizeof(tmp), "=");
StrCat(tmp, sizeof(tmp), value);
AddProtocolDetailsStr(dst, dst_size, tmp);
}
void AddProtocolDetailsKeyValueInt(char *dst, UINT dst_size, char *key, UINT value)
{
char tmp[128];
ToStr(tmp, value);
AddProtocolDetailsKeyValueStr(dst, dst_size, key, tmp);
}
// Setting the buffer size of the socket
bool SetSocketBufferSize(SOCKET s, bool send, UINT size)
{
int value = (int)size;
// Validate arguments
if (s == INVALID_SOCKET)
{
return false;
}
if (setsockopt(s, SOL_SOCKET, (send ? SO_SNDBUF : SO_RCVBUF), (char *)&value, sizeof(int)) != 0)
{
return false;
}
return true;
}
UINT SetSocketBufferSizeWithBestEffort(SOCKET s, bool send, UINT size)
{
// Validate arguments
if (s == INVALID_SOCKET)
{
return 0;
}
while (true)
{
if (SetSocketBufferSize(s, send, size))
{
return size;
}
size = (UINT)((double)size / 1.5);
if (size <= 32767)
{
return 0;
}
}
}
// Initialize the buffer size of the UDP socket
void InitUdpSocketBufferSize(SOCKET s)
{
SetSocketBufferSizeWithBestEffort(s, true, UDP_MAX_BUFFER_SIZE);
SetSocketBufferSizeWithBestEffort(s, false, UDP_MAX_BUFFER_SIZE);
}
// Get the socket information
void QuerySocketInformation(SOCK *sock)
{
// Validate arguments
if (sock == NULL)
{
return;
}
Lock(sock->lock);
{
struct sockaddr_in6 sockaddr6;
struct in6_addr *addr6;
int size;
UINT dw;
UINT opt_value = 0;
if (sock->Type == SOCK_TCP)
{
// Get the information of the remote host
size = sizeof(sockaddr6);
if (getpeername(sock->socket, (struct sockaddr *)&sockaddr6, (int *)&size) == 0)
{
if (size >= sizeof(struct sockaddr_in6))
{
sock->RemotePort = (UINT)ntohs(sockaddr6.sin6_port);
addr6 = &sockaddr6.sin6_addr;
InAddrToIP6(&sock->RemoteIP, addr6);
sock->RemoteIP.ipv6_scope_id = sockaddr6.sin6_scope_id;
}
else
{
struct sockaddr_in *sockaddr;
struct in_addr *addr;
sockaddr = (struct sockaddr_in *)&sockaddr6;
sock->RemotePort = (UINT)ntohs(sockaddr->sin_port);
addr = &sockaddr->sin_addr;
InAddrToIP(&sock->RemoteIP, addr);
}
}
}
// Get the local host information
size = sizeof(sockaddr6);
if (getsockname(sock->socket, (struct sockaddr *)&sockaddr6, (int *)&size) == 0)
{
if (size >= sizeof(struct sockaddr_in6))
{
sock->LocalPort = (UINT)ntohs(sockaddr6.sin6_port);
addr6 = &sockaddr6.sin6_addr;
InAddrToIP6(&sock->LocalIP, addr6);
sock->LocalIP.ipv6_scope_id = sockaddr6.sin6_scope_id;
}
else
{
struct sockaddr_in *sockaddr;
struct in_addr *addr;
sockaddr = (struct sockaddr_in *)&sockaddr6;
sock->LocalPort = (UINT)ntohs(sockaddr->sin_port);
addr = &sockaddr->sin_addr;
InAddrToIP(&sock->LocalIP, addr);
}
}
if (sock->IsRawSocket)
{
sock->LocalPort = sock->RemotePort = MAKE_SPECIAL_PORT(sock->RawSocketIPProtocol);
}
if (sock->Type == SOCK_UDP)
{
sock->UdpMaxMsgSize = UDP_MAX_MSG_SIZE_DEFAULT;
#ifdef OS_WIN32
if (true)
{
// Get the buffer size that can be transmitted and received at once
UINT max_value = 0;
int len = sizeof(UINT);
if (getsockopt(sock->socket, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&max_value, &len) == 0)
{
sock->UdpMaxMsgSize = max_value;
}
}
#endif // OS_WIN32
}
if (sock->IPv6)
{
#ifdef IPV6_UNICAST_HOPS
opt_value = IPV6_UNICAST_HOPS;
#endif // IPV6_UNICAST_HOPS
}
else
{
#ifdef IP_TTL
opt_value = IP_TTL;
#endif // IP_TTL
}
// Support of the TTL value
size = sizeof(UINT);
if (opt_value == 0 ||
getsockopt(sock->socket, (sock->IPv6 ? IPPROTO_IPV6 : IPPROTO_IP), opt_value, (char *)&dw, &size) != 0)
{
sock->IsTtlSupported = false;
}
else
{
sock->IsTtlSupported = true;
sock->CurrentTtl = dw;
}
}
Unlock(sock->lock);
}
// Setting the TTL value
bool SetTtl(SOCK *sock, UINT ttl)
{
UINT dw;
int size;
UINT opt_value = 0;
// Validate arguments
if (sock == NULL)
{
return false;
}
if (sock->IsTtlSupported == false)
{
return false;
}
if (sock->CurrentTtl == ttl)
{
return true;
}
dw = ttl;
size = sizeof(UINT);
if (sock->IPv6)
{
#ifdef IPV6_UNICAST_HOPS
opt_value = IPV6_UNICAST_HOPS;
#endif // IPV6_UNICAST_HOPS
}
else
{
#ifdef IP_TTL
opt_value = IP_TTL;
#endif // IP_TTL
}
if (opt_value == 0 ||
setsockopt(sock->socket, (sock->IPv6 ? IPPROTO_IPV6 : IPPROTO_IP), opt_value, (char *)&dw, size) == false)
{
return false;
}
sock->CurrentTtl = ttl;
return true;
}
// Release of the socket
void ReleaseSock(SOCK *s)
{
// Validate arguments
if (s == NULL)
{
return;
}
if (Release(s->ref) == 0)
{
if (s->ListenMode == false && s->ServerMode)
{
Print("");
}
CleanupSock(s);
}
}
// Clean-up of the socket
void CleanupSock(SOCK *s)
{
// Validate arguments
if (s == NULL)
{
return;
}
// {Debug("CleanupSock: Disconnect() Called: %s %u\n", __FILE__, __LINE__);Disconnect(s);}
Disconnect(s);
if (s->InProcAcceptQueue != NULL)
{
while (true)
{
SOCK *ss = GetNext(s->InProcAcceptQueue);
if (ss == NULL)
{
break;
}
Disconnect(ss);
ReleaseSock(ss);
}
ReleaseQueue(s->InProcAcceptQueue);
}
if (s->InProcAcceptEvent != NULL)
{
ReleaseEvent(s->InProcAcceptEvent);
}
if (s->ReverseAcceptQueue != NULL)
{
while (true)
{
SOCK *ss = GetNext(s->ReverseAcceptQueue);
if (ss == NULL)
{
break;
}
Disconnect(ss);
ReleaseSock(ss);
}
ReleaseQueue(s->ReverseAcceptQueue);
}
if (s->ReverseAcceptEvent != NULL)
{
ReleaseEvent(s->ReverseAcceptEvent);
}
if (s->SendTube != NULL)
{
TubeDisconnect(s->SendTube);
ReleaseTube(s->SendTube);
}
if (s->RecvTube != NULL)
{
TubeDisconnect(s->RecvTube);
ReleaseTube(s->RecvTube);
}
if (s->BulkRecvTube != NULL)
{
TubeDisconnect(s->BulkRecvTube);
ReleaseTube(s->BulkRecvTube);
}
if (s->BulkSendTube != NULL)
{
TubeDisconnect(s->BulkSendTube);
ReleaseTube(s->BulkSendTube);
}
if (s->BulkSendKey != NULL)
{
ReleaseSharedBuffer(s->BulkSendKey);
}
if (s->BulkRecvKey != NULL)
{
ReleaseSharedBuffer(s->BulkRecvKey);
}
if (s->InProcRecvFifo != NULL)
{
ReleaseFifo(s->InProcRecvFifo);
}
if (s->R_UDP_Stack != NULL)
{
FreeRUDP(s->R_UDP_Stack);
}
#ifdef OS_WIN32
Win32FreeAsyncSocket(s);
#else // OS_WIN32
UnixFreeAsyncSocket(s);
#endif // OS_WIN32
FreeBuf(s->SendBuf);
if (s->socket != INVALID_SOCKET)
{
#ifdef OS_WIN32
closesocket(s->socket);
#else // OS_WIN32
close(s->socket);
#endif // OS_WIN32
}
Free(s->RemoteHostname);
#ifdef OS_WIN32
if (s->hAcceptEvent != NULL)
{
CloseHandle(s->hAcceptEvent);
}
#endif // OS_WIN32
// Release the certificate
if (s->RemoteX != NULL)
{
FreeX(s->RemoteX);
s->RemoteX = NULL;
}
if (s->LocalX != NULL)
{
FreeX(s->LocalX);
s->LocalX = NULL;
}
// Cipher algorithm name
if (s->CipherName != NULL)
{
Free(s->CipherName);
s->CipherName = NULL;
}
Free(s->WaitToUseCipher);
DeleteLock(s->lock);
DeleteLock(s->ssl_lock);
DeleteLock(s->disconnect_lock);
Dec(num_tcp_connections);
Free(s);
}
// Creating a new socket
SOCK *NewSock()
{
SOCK *s = ZeroMallocFast(sizeof(SOCK));
s->ref = NewRef();
s->lock = NewLock();
s->SendBuf = NewBuf();
s->socket = INVALID_SOCKET;
s->ssl_lock = NewLock();
s->disconnect_lock = NewLock();
Inc(num_tcp_connections);
return s;
}
// Convert the IP to UINT
UINT IPToUINT(IP *ip)
{
UCHAR *b;
UINT i, value = 0;
// Validate arguments
if (ip == NULL)
{
return 0;
}
b = (UCHAR *)&value;
for (i = 0; i < 4; i++)
{
b[i] = ip->addr[i];
}
return value;
}
// Convert UINT to IP
void UINTToIP(IP *ip, UINT value)
{
UCHAR *b;
UINT i;
// Validate arguments
if (ip == NULL)
{
return;
}
ZeroIP4(ip);
b = (UCHAR *)&value;
for (i = 0; i < 4; i++)
{
ip->addr[i] = b[i];
}
}
// Get the host name of the computer
void GetMachineHostName(char *name, UINT size)
{
char tmp[MAX_SIZE];
UINT i, len;
// Validate arguments
if (name == NULL)
{
return;
}
GetMachineName(tmp, sizeof(tmp));
len = StrLen(tmp);
for (i = 0; i < len; i++)
{
if (tmp[i] == '.')
{
tmp[i] = 0;
}
}
ConvertSafeFileName(name, size, tmp);
}
// Get the computer name from 'hosts'
bool GetMachineNameFromHosts(char *name, UINT size)
{
bool ret = false;
char *s;
BUF *b;
// Validate arguments
if (name == NULL)
{
return false;
}
b = ReadDump("/etc/hosts");
if (b == NULL)
{
return false;
}
while (true)
{
s = CfgReadNextLine(b);
if (s == NULL)
{
break;
}
else
{
TOKEN_LIST *t = ParseToken(s, " \t");
if (t != NULL)
{
if (t->NumTokens >= 2)
{
if (StrCmpi(t->Token[0], "127.0.0.1") == 0)
{
UINT i;
for (i = 1; i < t->NumTokens; i++)
{
if (StartWith(t->Token[i], "localhost") == false)
{
StrCpy(name, size, t->Token[i]);
ret = true;
}
}
}
}
}
FreeToken(t);
}
Free(s);
}
FreeBuf(b);
return ret;
}
// Get the computer name of this computer
void GetMachineName(char *name, UINT size)
{
GetMachineNameEx(name, size, false);
}
void GetMachineNameEx(char *name, UINT size, bool no_load_hosts)
{
static char name_cache[MAX_SIZE];
static bool name_cached = false;
char tmp[MAX_SIZE];
char tmp2[MAX_SIZE];
// Validate arguments
if (name == NULL)
{
return;
}
Lock(machine_name_lock);
{
if (name_cached != false)
{
StrCpy(name, size, name_cache);
Unlock(machine_name_lock);
return;
}
ClearStr(tmp, sizeof(tmp));
if (gethostname(tmp, MAX_SIZE) != 0)
{
StrCpy(name, size, "Unknown");
Unlock(machine_name_lock);
return;
}
ClearStr(name, size);
StrCpy(name, size, tmp);
if (IsEmptyStr(name) || StartWith(name, "localhost"))
{
#ifdef OS_WIN32
ClearStr(name, size);
MsGetComputerName(name, size);
#endif // OS_WIN32
}
if (IsEmptyStr(name) || StartWith(name, "localhost"))
{
if (no_load_hosts == false && OS_IS_UNIX(GetOsInfo()->OsType))
{
if (GetMachineNameFromHosts(tmp2, sizeof(tmp2)))
{
StrCpy(name, size, tmp2);
}
}
}
StrCpy(name_cache, sizeof(name_cache), name);
name_cached = true;
}
Unlock(machine_name_lock);
}
// Host name acquisition thread
void GetHostNameThread(THREAD *t, void *p)
{
IP *ip;
char hostname[256];
// Validate arguments
if (t == NULL || p == NULL)
{
return;
}
ip = (IP *)p;
AddWaitThread(t);
NoticeThreadInit(t);
if (GetHostNameInner(hostname, sizeof(hostname), ip))
{
AddHostCache(ip, hostname);
}
Free(ip);
DelWaitThread(t);
}
// Get the host name
bool GetHostName(char *hostname, UINT size, IP *ip)
{
THREAD *t;
IP *p_ip;
bool ret;
// Validate arguments
if (hostname == NULL || ip == NULL)
{
return false;
}
if (GetHostCache(hostname, size, ip))
{
if (IsEmptyStr(hostname) == false)
{
return true;
}
else
{
return false;
}
}
p_ip = ZeroMalloc(sizeof(IP));
Copy(p_ip, ip, sizeof(IP));
t = NewThread(GetHostNameThread, p_ip);
WaitThreadInit(t);
WaitThread(t, TIMEOUT_HOSTNAME);
ReleaseThread(t);
ret = GetHostCache(hostname, size, ip);
if (ret == false)
{
if (IsIP4(ip))
{
ret = GetNetBiosName(hostname, size, ip);
if (ret)
{
AddHostCache(ip, hostname);
}
}
}
else
{
if (IsEmptyStr(hostname))
{
ret = false;
}
}
if (ret == false)
{
AddHostCache(ip, "");
StrCpy(hostname, size, "");
}
return ret;
}
// Perform a DNS reverse query
bool GetHostNameInner(char *hostname, UINT size, IP *ip)
{
struct in_addr addr;
struct sockaddr_in sa;
char tmp[MAX_SIZE];
char ip_str[64];
// Validate arguments
if (hostname == NULL || ip == NULL)
{
return false;
}
if (IsIP6(ip))
{
return GetHostNameInner6(hostname, size, ip);
}
// Reverse resolution
IPToInAddr(&addr, ip);
Zero(&sa, sizeof(sa));
sa.sin_family = AF_INET;
#if defined(UNIX_BSD) || defined(UNIX_MACOS)
sa.sin_len = INET_ADDRSTRLEN;
#endif // UNIX_BSD || UNIX_MACOS
Copy(&sa.sin_addr, &addr, sizeof(struct in_addr));
sa.sin_port = 0;
if (getnameinfo((struct sockaddr *)&sa, sizeof(sa), tmp, sizeof(tmp), NULL, 0, 0) != 0)
{
return false;
}
IPToStr(ip_str, sizeof(ip_str), ip);
if (StrCmpi(tmp, ip_str) == 0)
{
return false;
}
if (IsEmptyStr(tmp))
{
return false;
}
StrCpy(hostname, size, tmp);
return true;
}
bool GetHostNameInner6(char *hostname, UINT size, IP *ip)
{
struct in6_addr addr;
struct sockaddr_in6 sa;
char tmp[MAX_SIZE];
char ip_str[256];
// Validate arguments
if (hostname == NULL || ip == NULL)
{
return false;
}
// Reverse resolution
IPToInAddr6(&addr, ip);
Zero(&sa, sizeof(sa));
sa.sin6_family = AF_INET6;
#if defined(UNIX_BSD) || defined(UNIX_MACOS)
sa.sin6_len = INET6_ADDRSTRLEN;
#endif // UNIX_BSD || UNIX_MACOS
Copy(&sa.sin6_addr, &addr, sizeof(struct in6_addr));
sa.sin6_port = 0;
if (getnameinfo((struct sockaddr *)&sa, sizeof(sa), tmp, sizeof(tmp), NULL, 0, 0) != 0)
{
return false;
}
IPToStr(ip_str, sizeof(ip_str), ip);
if (StrCmpi(tmp, ip_str) == 0)
{
return false;
}
if (IsEmptyStr(tmp))
{
return false;
}
StrCpy(hostname, size, tmp);
return true;
}
#define NUM_NBT_QUERYS_SEND 3
// Get the NetBIOS name of the machine from the IP address
bool GetNetBiosName(char *name, UINT size, IP *ip)
{
SOCK *s;
UINT i, j;
bool flag = false;
bool ok = false;
NBTREQUEST req;
UCHAR buf[1024];
USHORT tran_id[NUM_NBT_QUERYS_SEND];
UINT64 timeout_tick;
// Validate arguments
if (name == NULL || ip == NULL)
{
return false;
}
IPToStr(name, size, ip);
for (i = 0; i < NUM_NBT_QUERYS_SEND; i++)
{
tran_id[i] = Rand16();
}
s = NewUDP(0);
if (s == NULL)
{
return false;
}
for (j = 0; j < NUM_NBT_QUERYS_SEND; j++)
{
Zero(&req, sizeof(req));
req.TransactionId = Endian16(tran_id[j]);
req.NumQuestions = Endian16(1);
req.Query[0] = 0x20;
req.Query[1] = 0x43;
req.Query[2] = 0x4b;
for (i = 3; i <= 32; i++)
{
req.Query[i] = 0x41;
}
req.Query[35] = 0x21;
req.Query[37] = 0x01;
if (SendTo(s, ip, 137, &req, sizeof(req)) == 0)
{
ReleaseSock(s);
return false;
}
}
timeout_tick = Tick64() + (UINT64)TIMEOUT_NETBIOS_HOSTNAME;
while (1)
{
UINT ret;
IP src_ip;
UINT src_port;
SOCKSET set;
if (Tick64() >= timeout_tick)
{
break;
}
InitSockSet(&set);
AddSockSet(&set, s);
Select(&set, 100, NULL, NULL);
if (flag == false)
{
flag = true;
}
else
{
SleepThread(10);
}
ret = RecvFrom(s, &src_ip, &src_port, buf, sizeof(buf));
if (ret == SOCK_LATER)
{
continue;
}
else if (ret == 0)
{
break;
}
else
{
if (ret >= sizeof(NBTRESPONSE))
{
NBTRESPONSE *r = (NBTRESPONSE *)buf;
bool b = false;
UINT i;
USHORT id = Endian16(r->TransactionId);
for (i = 0; i < NUM_NBT_QUERYS_SEND; i++)
{
if (id == tran_id[i])
{
b = true;
break;
}
}
if (b)
{
if (r->Flags != 0 && r->NumQuestions == 0 && r->AnswerRRs >= 1)
{
if (r->Response[0] == 0x20 && r->Response[1] == 0x43 &&
r->Response[2] == 0x4b)
{
if (r->Response[34] == 0x00 && r->Response[35] == 0x21 &&
r->Response[36] == 0x00 && r->Response[37] == 0x01)
{
char *a = (char *)(&r->Response[45]);
if (StrCheckLen(a, 15))
{
if (IsEmptyStr(a) == false)
{
StrCpy(name, size, a);
Trim(name);
ok = true;
}
else
{
ok = false;
break;
}
}
}
}
}
}
}
}
}
ReleaseSock(s);
return ok;
}
// Set the IP address
void SetIP(IP *ip, UCHAR a1, UCHAR a2, UCHAR a3, UCHAR a4)
{
// Validate arguments
if (ip == NULL)
{
return;
}
Zero(ip, sizeof(IP));
ip->addr[0] = a1;
ip->addr[1] = a2;
ip->addr[2] = a3;
ip->addr[3] = a4;
}
UINT SetIP32(UCHAR a1, UCHAR a2, UCHAR a3, UCHAR a4)
{
IP ip;
Zero(&ip, sizeof(ip));
SetIP(&ip, a1, a2, a3, a4);
return IPToUINT(&ip);
}
// Obtain in both v4 and v6 results with a DNS forward lookup
bool GetIP46Ex(IP *ip4, IP *ip6, char *hostname, UINT timeout, bool *cancel)
{
IP a, b;
bool ok_a, ok_b;
// Validate arguments
if (ip4 == NULL || ip6 == NULL || hostname == NULL)
{
return false;
}
ZeroIP4(ip4);
ZeroIP6(ip6);
ok_a = ok_b = false;
if (GetIP6Ex(&a, hostname, timeout, cancel))
{
ok_a = true;
}
if (GetIP4Ex(&b, hostname, timeout, cancel))
{
ok_b = true;
}
if (ok_a)
{
if (IsIP4(&a))
{
Copy(ip4, &a, sizeof(IP));
}
}
if (ok_b)
{
if (IsIP4(&b))
{
Copy(ip4, &b, sizeof(IP));
}
if (IsIP6(&b))
{
Copy(ip6, &b, sizeof(IP));
}
}
if (ok_a)
{
if (IsIP6(&a))
{
Copy(ip6, &a, sizeof(IP));
}
}
if (IsZeroIp(ip4) && IsZeroIp(ip6))
{
return false;
}
return true;
}
// Clean-up of the parameters for GetIP thread
void CleanupGetIPThreadParam(GETIP_THREAD_PARAM *p)
{
// Validate arguments
if (p == NULL)
{
return;
}
Free(p);
}
// Release of the parameters of the GetIP for thread
void ReleaseGetIPThreadParam(GETIP_THREAD_PARAM *p)
{
// Validate arguments
if (p == NULL)
{
return;
}
if (Release(p->Ref) == 0)
{
CleanupGetIPThreadParam(p);
}
}
// Thread to perform to query the DNS forward lookup (with timeout)
void GetIP4Ex6ExThread(THREAD *t, void *param)
{
GETIP_THREAD_PARAM *p;
// Validate arguments
if (t == NULL || param == NULL)
{
return;
}
p = (GETIP_THREAD_PARAM *)param;
AddRef(p->Ref);
NoticeThreadInit(t);
AddWaitThread(t);
// Execution of resolution
if (p->IPv6 == false)
{
// IPv4
p->Ok = GetIP4Inner(&p->Ip, p->HostName);
}
else
{
// IPv6
p->Ok = GetIP6Inner(&p->Ip, p->HostName);
}
ReleaseGetIPThreadParam(p);
DelWaitThread(t);
Dec(getip_thread_counter);
}
// Perform a forward DNS query (with timeout)
bool GetIP4Ex6Ex(IP *ip, char *hostname_arg, UINT timeout, bool ipv6, bool *cancel)
{
return GetIP4Ex6Ex2(ip, hostname_arg, timeout, ipv6, cancel, false);
}
bool GetIP4Ex6Ex2(IP *ip, char *hostname_arg, UINT timeout, bool ipv6, bool *cancel, bool only_direct_dns)
{
GETIP_THREAD_PARAM *p;
THREAD *t;
bool ret = false;
UINT64 start_tick = 0;
UINT64 end_tick = 0;
UINT64 spent_time = 0;
UINT64 now;
UINT n;
bool use_dns_proxy = false;
char hostname[260];
UINT i;
bool timed_out;
// Validate arguments
if (ip == NULL || hostname_arg == NULL)
{
return false;
}
if (timeout == 0)
{
timeout = TIMEOUT_GETIP;
}
Zero(hostname, sizeof(hostname));
StrCpy(hostname, sizeof(hostname), hostname_arg);
i = SearchStrEx(hostname, "/", 0, true);
if (i != INFINITE)
{
hostname[i] = 0;
}
if (ipv6 == false)
{
IP ip2;
if (StrToIP(&ip2, hostname) && IsZeroIp(&ip2) == false)
{
if (IsIP4(&ip2))
{
// IPv4 address direct specification
Copy(ip, &ip2, sizeof(IP));
return true;
}
else
{
// IPv6 address direct specification
return false;
}
}
}
else
{
IP ip2;
if (StrToIP(&ip2, hostname) && IsZeroIp(&ip2) == false)
{
if (IsIP6(&ip2))
{
// IPv6 address direct specification
Copy(ip, &ip2, sizeof(IP));
return true;
}
else
{
// IPv4 address direct specification
return false;
}
}
}
if (only_direct_dns == false)
{
if (ipv6 == false)
{
if (IsUseDnsProxy())
{
use_dns_proxy = true;
}
}
}
// check the quota
start_tick = Tick64();
end_tick = start_tick + (UINT64)timeout;
n = 0;
timed_out = false;
while (true)
{
UINT64 now = Tick64();
UINT64 remain;
UINT remain32;
if (GetGetIpThreadMaxNum() > GetCurrentGetIpThreadNum())
{
// below the quota
break;
}
if (now >= end_tick)
{
// timeouted
timed_out = true;
break;
}
if (cancel != NULL && (*cancel))
{
// cancelled
timed_out = true;
break;
}
remain = end_tick - now;
remain32 = MIN((UINT)remain, 100);
SleepThread(remain32);
n++;
}
now = Tick64();
spent_time = now - start_tick;
if (n == 0)
{
spent_time = 0;
}
if ((UINT)spent_time >= timeout)
{
timed_out = true;
}
if (timed_out)
{
IP ip2;
// timed out, cancelled
if (QueryDnsCache(&ip2, hostname))
{
ret = true;
Copy(ip, &ip2, sizeof(IP));
}
Debug("GetIP4Ex6Ex2: Worker thread quota exceeded: max=%u current=%u\n",
GetGetIpThreadMaxNum(), GetCurrentGetIpThreadNum());
return ret;
}
// Increment the counter
Inc(getip_thread_counter);
if (spent_time != 0)
{
Debug("GetIP4Ex6Ex2: Waited for %u msecs to create a worker thread.\n",
spent_time);
}
timeout -= (UINT)spent_time;
p = ZeroMalloc(sizeof(GETIP_THREAD_PARAM));
p->Ref = NewRef();
StrCpy(p->HostName, sizeof(p->HostName), hostname);
p->IPv6 = ipv6;
p->Timeout = timeout;
p->Ok = false;
t = NewThread(GetIP4Ex6ExThread, p);
WaitThreadInit(t);
if (cancel == NULL)
{
WaitThread(t, timeout);
}
else
{
start_tick = Tick64();
end_tick = start_tick + (UINT64)timeout;
while (true)
{
UINT64 now = Tick64();
UINT64 remain;
UINT remain32;
if (*cancel)
{
break;
}
if (now >= end_tick)
{
break;
}
remain = end_tick - now;
remain32 = MIN((UINT)remain, 100);
if (WaitThread(t, remain32))
{
break;
}
}
}
ReleaseThread(t);
if (p->Ok)
{
ret = true;
Copy(ip, &p->Ip, sizeof(IP));
}
else
{
IP ip2;
#if 0
if (only_direct_dns == false)
{
if (ipv6)
{
UINT flets_type = DetectFletsType();
// if I'm in the FLETs of NTT East,
// try to get an IP address using the DNS proxy server
if ((flets_type & FLETS_DETECT_TYPE_EAST_BFLETS_PRIVATE) &&
GetIPViaDnsProxyForJapanFlets(ip, hostname, true, 0, cancel, NULL))
{
// B FLETs
ret = true;
}
else if ((flets_type & FLETS_DETECT_TYPE_EAST_NGN_PRIVATE) &&
GetIPViaDnsProxyForJapanFlets(ip, hostname, true, 0, cancel, FLETS_NGN_EAST_DNS_PROXY_HOSTNAME))
{
// FLET'S Hikar-Next (NTT East)
ret = true;
}
else if ((flets_type & FLETS_DETECT_TYPE_WEST_NGN_PRIVATE) &&
GetIPViaDnsProxyForJapanFlets(ip, hostname, true, 0, cancel, FLETS_NGN_WEST_DNS_PROXY_HOSTNAME))
{
// FLET'S Hikar-Next (NTT West)
ret = true;
}
}
}
#endif
if (QueryDnsCache(&ip2, hostname))
{
ret = true;
Copy(ip, &ip2, sizeof(IP));
}
}
ReleaseGetIPThreadParam(p);
return ret;
}
bool GetIP4Ex(IP *ip, char *hostname, UINT timeout, bool *cancel)
{
return GetIP4Ex6Ex(ip, hostname, timeout, false, cancel);
}
bool GetIP6Ex(IP *ip, char *hostname, UINT timeout, bool *cancel)
{
return GetIP4Ex6Ex(ip, hostname, timeout, true, cancel);
}
bool GetIP4(IP *ip, char *hostname)
{
return GetIP4Ex(ip, hostname, 0, NULL);
}
bool GetIP6(IP *ip, char *hostname)
{
return GetIP6Ex(ip, hostname, 0, NULL);
}
// Perform a DNS forward lookup query
bool GetIP(IP *ip, char *hostname)
{
return GetIPEx(ip, hostname, false);
}
bool GetIPEx(IP *ip, char *hostname, bool ipv6)
{
if (ipv6 == false)
{
return GetIP4(ip, hostname);
}
else
{
return GetIP6(ip, hostname);
}
}
bool GetIP6Inner(IP *ip, char *hostname)
{
struct sockaddr_in6 in;
struct in6_addr addr;
struct addrinfo hint;
struct addrinfo *info;
// Validate arguments
if (ip == NULL || hostname == NULL)
{
return false;
}
if (IsEmptyStr(hostname))
{
return false;
}
if (StrCmpi(hostname, "localhost") == 0)
{
GetLocalHostIP6(ip);
return true;
}
if (StrToIP6(ip, hostname) == false && StrToIP(ip, hostname) == false)
{
// Forward resolution
Zero(&hint, sizeof(hint));
hint.ai_family = AF_INET6;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
info = NULL;
if (getaddrinfo(hostname, NULL, &hint, &info) != 0 ||
info->ai_family != AF_INET6)
{
if (info)
{
freeaddrinfo(info);
}
return QueryDnsCacheEx(ip, hostname, true);
}
// Forward resolution success
Copy(&in, info->ai_addr, sizeof(struct sockaddr_in6));
freeaddrinfo(info);
Copy(&addr, &in.sin6_addr, sizeof(addr));
InAddrToIP6(ip, &addr);
}
// Save Cache
NewDnsCache(hostname, ip);
return true;
}
bool GetIP4Inner(IP *ip, char *hostname)
{
struct sockaddr_in in;
struct in_addr addr;
struct addrinfo hint;
struct addrinfo *info;
// Validate arguments
if (ip == NULL || hostname == NULL)
{
return false;
}
if (IsEmptyStr(hostname))
{
return false;
}
if (StrCmpi(hostname, "localhost") == 0)
{
SetIP(ip, 127, 0, 0, 1);
return true;
}
if (StrToIP6(ip, hostname) == false && StrToIP(ip, hostname) == false)
{
// Forward resolution
Zero(&hint, sizeof(hint));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
info = NULL;
if (getaddrinfo(hostname, NULL, &hint, &info) != 0 ||
info->ai_family != AF_INET)
{
if (info)
{
freeaddrinfo(info);
}
return QueryDnsCache(ip, hostname);
}
// Forward resolution success
Copy(&in, info->ai_addr, sizeof(struct sockaddr_in));
freeaddrinfo(info);
Copy(&addr, &in.sin_addr, sizeof(addr));
InAddrToIP(ip, &addr);
}
// Save Cache
NewDnsCache(hostname, ip);
return true;
}
// Search in the DNS cache
bool QueryDnsCache(IP *ip, char *hostname)
{
return QueryDnsCacheEx(ip, hostname, false);
}
bool QueryDnsCacheEx(IP *ip, char *hostname, bool ipv6)
{
DNSCACHE *c;
char tmp[MAX_SIZE];
// Validate arguments
if (ip == NULL || hostname == NULL)
{
return false;
}
GenDnsCacheKeyName(tmp, sizeof(tmp), hostname, ipv6);
c = FindDnsCache(tmp);
if (c == NULL)
{
return false;
}
Copy(ip, &c->IpAddress, sizeof(IP));
return true;
}
// Convert the IP to a string
void IPToUniStr(wchar_t *str, UINT size, IP *ip)
{
char tmp[128];
IPToStr(tmp, sizeof(tmp), ip);
StrToUni(str, size, tmp);
}
// Convert the IP to a string (32bit UINT)
void IPToUniStr32(wchar_t *str, UINT size, UINT ip)
{
char tmp[128];
IPToStr32(tmp, sizeof(tmp), ip);
StrToUni(str, size, tmp);
}
// Convert the IP to a string (32bit UINT)
void IPToStr32(char *str, UINT size, UINT ip)
{
IP ip_st;
// Validate arguments
if (str == NULL)
{
return;
}
UINTToIP(&ip_st, ip);
IPToStr(str, size, &ip_st);
}
// Convert IPv4 or IPv6 to a string
void IPToStr4or6(char *str, UINT size, UINT ip_4_uint, UCHAR *ip_6_bytes)
{
IP ip4;
IP ip6;
IP ip;
// Validate arguments
if (str == NULL)
{
return;
}
Zero(&ip, sizeof(ip));
UINTToIP(&ip4, ip_4_uint);
SetIP6(&ip6, ip_6_bytes);
if (IsIP6(&ip4) || (IsZeroIp(&ip4) && (IsZeroIp(&ip6) == false)))
{
Copy(&ip, &ip6, sizeof(IP));
}
else
{
Copy(&ip, &ip4, sizeof(IP));
}
IPToStr(str, size, &ip);
}
// Convert the IP to a string
void IPToStr(char *str, UINT size, IP *ip)
{
// Validate arguments
if (str == NULL || ip == NULL)
{
return;
}
if (IsIP6(ip))
{
IPToStr6(str, size, ip);
}
else
{
IPToStr4(str, size, ip);
}
}
// Convert the IPv4 to a string
void IPToStr4(char *str, UINT size, IP *ip)
{
// Validate arguments
if (str == NULL || ip == NULL)
{
return;
}
// Conversion
snprintf(str, size != 0 ? size : 64, "%u.%u.%u.%u", ip->addr[0], ip->addr[1], ip->addr[2], ip->addr[3]);
}
// Convert the string to an IP
bool StrToIP(IP *ip, char *str)
{
TOKEN_LIST *token;
char *tmp;
UINT i;
// Validate arguments
if (ip == NULL || str == NULL)
{
return false;
}
if (StrToIP6(ip, str))
{
return true;
}
Zero(ip, sizeof(IP));
tmp = CopyStr(str);
Trim(tmp);
token = ParseToken(tmp, ".");
Free(tmp);
if (token->NumTokens != 4)
{
FreeToken(token);
return false;
}
for (i = 0; i < 4; i++)
{
char *s = token->Token[i];
if (s[0] < '0' || s[0] > '9' ||
(ToInt(s) >= 256))
{
FreeToken(token);
return false;
}
}
Zero(ip, sizeof(IP));
for (i = 0; i < 4; i++)
{
ip->addr[i] = (UCHAR)ToInt(token->Token[i]);
}
FreeToken(token);
return true;
}
UINT StrToIP32(char *str)
{
IP ip;
// Validate arguments
if (str == NULL)
{
return 0;
}
if (StrToIP(&ip, str) == false)
{
return 0;
}
return IPToUINT(&ip);
}
UINT UniStrToIP32(wchar_t *str)
{
UINT ret;
char *tmp;
tmp = CopyUniToStr(str);
ret = StrToIP32(tmp);
Free(tmp);
return ret;
}
// Convert the IP to the in_addr
void IPToInAddr(struct in_addr *addr, IP *ip)
{
UINT i;
// Validate arguments
if (addr == NULL || ip == NULL)
{
return;
}
Zero(addr, sizeof(struct in_addr));
if (IsIP6(ip) == false)
{
for (i = 0; i < 4; i++)
{
((UCHAR *)addr)[i] = ip->addr[i];
}
}
}
// Convert the IP to the in6_addr
void IPToInAddr6(struct in6_addr *addr, IP *ip)
{
UINT i;
// Validate arguments
if (addr == NULL || ip == NULL)
{
return;
}
Zero(addr, sizeof(struct in6_addr));
if (IsIP6(ip))
{
for (i = 0; i < 16; i++)
{
((UCHAR *)addr)[i] = ip->ipv6_addr[i];
}
}
}
// Convert the in_addr to the IP
void InAddrToIP(IP *ip, struct in_addr *addr)
{
UINT i;
// Validate arguments
if (ip == NULL || addr == NULL)
{
return;
}
Zero(ip, sizeof(IP));
for (i = 0; i < 4; i++)
{
ip->addr[i] = ((UCHAR *)addr)[i];
}
}
// Convert the in6_addr to the IP
void InAddrToIP6(IP *ip, struct in6_addr *addr)
{
UINT i;
// Validate arguments
if (ip == NULL || addr == NULL)
{
return;
}
ZeroIP6(ip);
for (i = 0; i < 16; i++)
{
ip->ipv6_addr[i] = ((UCHAR *)addr)[i];
}
}
// Search in the DNS cache
DNSCACHE *FindDnsCache(char *hostname)
{
return FindDnsCacheEx(hostname, false);
}
DNSCACHE *FindDnsCacheEx(char *hostname, bool ipv6)
{
DNSCACHE *c;
char tmp[MAX_SIZE];
if (hostname == NULL)
{
return NULL;
}
GenDnsCacheKeyName(tmp, sizeof(tmp), hostname, ipv6);
LockDnsCache();
{
DNSCACHE t;
t.HostName = tmp;
c = Search(DnsCache, &t);
}
UnlockDnsCache();
return c;
}
// Generate the IPv4 / IPv6 key name for the DNS cache
void GenDnsCacheKeyName(char *dst, UINT size, char *src, bool ipv6)
{
// Validate arguments
if (dst == NULL || src == NULL)
{
return;
}
if (ipv6 == false)
{
StrCpy(dst, size, src);
}
else
{
Format(dst, size, "%s@ipv6", src);
}
}
// Registration of the new DNS cache
void NewDnsCache(char *hostname, IP *ip)
{
NewDnsCacheEx(hostname, ip, IsIP6(ip));
}
void NewDnsCacheEx(char *hostname, IP *ip, bool ipv6)
{
DNSCACHE *c;
char tmp[MAX_PATH];
// Validate arguments
if (hostname == NULL || ip == NULL)
{
return;
}
if (IsNetworkNameCacheEnabled() == false)
{
return;
}
GenDnsCacheKeyName(tmp, sizeof(tmp), hostname, ipv6);
LockDnsCache();
{
DNSCACHE t;
// Search for anything matches to the hostname first
t.HostName = tmp;
c = Search(DnsCache, &t);
if (c == NULL)
{
// Newly register
c = ZeroMalloc(sizeof(DNSCACHE));
c->HostName = CopyStr(tmp);
Copy(&c->IpAddress, ip, sizeof(IP));
Add(DnsCache, c);
}
else
{
// Update
Copy(&c->IpAddress, ip, sizeof(IP));
}
}
UnlockDnsCache();
}
// Name comparison of the DNS cache entries
int CompareDnsCache(void *p1, void *p2)
{
DNSCACHE *c1, *c2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
c1 = *(DNSCACHE **)p1;
c2 = *(DNSCACHE **)p2;
if (c1 == NULL || c2 == NULL)
{
return 0;
}
return StrCmpi(c1->HostName, c2->HostName);
}
// Initialization of the DNS cache
void InitDnsCache()
{
// Creating a List
DnsCache = NewList(CompareDnsCache);
}
// Release of the DNS cache
void FreeDnsCache()
{
LockDnsCache();
{
DNSCACHE *c;
UINT i;
for (i = 0; i < LIST_NUM(DnsCache); i++)
{
// Release the memory for the entry
c = LIST_DATA(DnsCache, i);
Free(c->HostName);
Free(c);
}
}
UnlockDnsCache();
// Release the list
ReleaseList(DnsCache);
DnsCache = NULL;
}
// Lock the DNS cache
void LockDnsCache()
{
LockList(DnsCache);
}
// Unlock the DNS cache
void UnlockDnsCache()
{
UnlockList(DnsCache);
}
// DH temp key callback
DH *TmpDhCallback(SSL *ssl, int is_export, int keylength)
{
DH *ret = NULL;
if (dh_param != NULL)
{
ret = dh_param->dh;
}
return ret;
}
// Create the SSL_CTX
struct ssl_ctx_st *NewSSLCtx(bool server_mode)
{
struct ssl_ctx_st *ctx = SSL_CTX_new(SSLv23_method());
#ifdef SSL_OP_NO_TICKET
SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
#endif // SSL_OP_NO_TICKET
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
if (server_mode)
{
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
}
#endif // SSL_OP_CIPHER_SERVER_PREFERENCE
SSL_CTX_set_tmp_dh_callback(ctx, TmpDhCallback);
#ifdef SSL_CTX_set_ecdh_auto
SSL_CTX_set_ecdh_auto(ctx, 1);
#endif // SSL_CTX_set_ecdh_auto
if (server_mode)
{
SSL_CTX_set_ssl_version(ctx, SSLv23_server_method());
}
else
{
SSL_CTX_set_ssl_version(ctx, SSLv23_client_method());
}
return ctx;
}
// Release of the SSL_CTX
void FreeSSLCtx(struct ssl_ctx_st *ctx)
{
// Validate arguments
if (ctx == NULL)
{
return;
}
SSL_CTX_free(ctx);
}
// The number of get ip threads
void SetGetIpThreadMaxNum(UINT num)
{
max_getip_thread = num;
}
UINT GetGetIpThreadMaxNum()
{
UINT ret = max_getip_thread;
if (ret == 0)
{
ret = 0x7FFFFFFF;
}
return ret;
}
UINT GetCurrentGetIpThreadNum()
{
return Count(getip_thread_counter);
}
// Initialize the network communication module
void InitNetwork()
{
disable_gethostname_by_accept = false;
InitDynList();
host_ip_address_list_cache_lock = NewLock();
host_ip_address_list_cache_last = 0;
num_tcp_connections = NewCounter();
getip_thread_counter = NewCounter();
// Initialization of client list
InitIpClientList();
// Thread related initialization
InitWaitThread();
// Initialization of the host name cache
InitHostCache();
#ifdef OS_WIN32
// Initializing the socket library
Win32InitSocketLibrary();
#else
UnixInitSocketLibrary();
#endif // OS_WIN32
// Initialization of the DNS cache
InitDnsCache();
// Locking initialization
machine_name_lock = NewLock();
disconnect_function_lock = NewLock();
aho = NewLock();
machine_ip_process_hash_lock = NewLock();
socket_library_lock = NewLock();
//ssl_connect_lock = NewLock(); //2012.9.28 Not required for recent OpenSSL
// ssl_accept_lock = NewLock();
dns_lock = NewLock();
unix_dns_server_addr_lock = NewLock();
Zero(&unix_dns_server, sizeof(unix_dns_server));
local_mac_list_lock = NewLock();
current_global_ip_lock = NewLock();
current_fqdn_lock = NewLock();
current_global_ip_set = false;
disable_cache = false;
Zero(rand_port_numbers, sizeof(rand_port_numbers));
SetGetIpThreadMaxNum(DEFAULT_GETIP_THREAD_MAX_NUM);
}
// Enable the network name cache
void EnableNetworkNameCache()
{
disable_cache = false;
}
// Disable the network name cache
void DisableNetworkNameCache()
{
disable_cache = true;
}
// Get whether the network name cache is enabled
bool IsNetworkNameCacheEnabled()
{
return !disable_cache;
}
// Get the cipher algorithm list
TOKEN_LIST *GetCipherList()
{
UINT i;
SSL *ssl;
SSL_CTX *ctx;
const char *name;
STACK_OF(SSL_CIPHER) *sk;
TOKEN_LIST *ciphers = ZeroMalloc(sizeof(TOKEN_LIST));
ctx = NewSSLCtx(true);
if (ctx == NULL)
{
return ciphers;
}
#ifdef SSL_OP_NO_SSLv3
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
#endif
ssl = SSL_new(ctx);
if (ssl == NULL)
{
FreeSSLCtx(ctx);
return ciphers;
}
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
sk = SSL_get1_supported_ciphers(ssl);
#else
sk = SSL_get_ciphers(ssl);
#endif
for (i = 0; i < (UINT)sk_SSL_CIPHER_num(sk); i++)
{
const SSL_CIPHER *c = sk_SSL_CIPHER_value(sk, i);
name = SSL_CIPHER_get_name(c);
if (IsEmptyStr((char *)name))
{
break;
}
ciphers->NumTokens++;
if (ciphers->Token != NULL)
{
ciphers->Token = ReAlloc(ciphers->Token, sizeof(char *) * ciphers->NumTokens);
}
else
{
ciphers->Token = Malloc(sizeof(char *));
}
ciphers->Token[i] = CopyStr((char *)name);
}
sk_SSL_CIPHER_free(sk);
SSL_free(ssl);
return ciphers;
}
// Get the TCP connections counter
COUNTER *GetNumTcpConnectionsCounter()
{
return num_tcp_connections;
}
// Get the current global IP address
bool GetCurrentGlobalIP(IP *ip, bool ipv6)
{
bool ret = false;
// Validate arguments
if (ip == NULL)
{
return false;
}
Zero(ip, sizeof(IP));
Lock(current_global_ip_lock);
{
if (ipv6 == false)
{
Copy(ip, &current_glocal_ipv4, sizeof(IP));
}
else
{
Copy(ip, &current_glocal_ipv6, sizeof(IP));
}
ret = current_global_ip_set;
}
Unlock(current_global_ip_lock);
return ret;
}
// Check whether the specified IP address is assigned to the local host
bool IsIPMyHost(IP *ip)
{
LIST *o;
UINT i;
bool ret = false;
// Validate arguments
if (ip == NULL)
{
return false;
}
if (IsZeroIp(ip))
{
return false;
}
// Search to check whether it matches to any of the IP of the local host
o = GetHostIPAddressList();
for (i = 0; i < LIST_NUM(o); i++)
{
IP *p = LIST_DATA(o, i);
if (CmpIpAddr(p, ip) == 0)
{
// Matched
ret = true;
break;
}
}
FreeHostIPAddressList(o);
if (ret == false)
{
if (IsLocalHostIP(ip))
{
// localhost IP addresses
ret = true;
}
}
return ret;
}
// Check whether the specified IP address is a private IP address
bool IsIPPrivate(IP *ip)
{
// Validate arguments
if (ip == NULL)
{
return false;
}
if (ip->addr[0] == 10)
{
return true;
}
if (ip->addr[0] == 172)
{
if (ip->addr[1] >= 16 && ip->addr[1] <= 31)
{
return true;
}
}
if (ip->addr[0] == 192 && ip->addr[1] == 168)
{
return true;
}
if (ip->addr[0] == 169 && ip->addr[1] == 254)
{
return true;
}
if (ip->addr[0] == 100)
{
if (ip->addr[1] >= 64 && ip->addr[1] <= 127)
{
return true;
}
}
if (g_private_ip_list != NULL)
{
if (IsIP4(ip))
{
UINT ip4 = IPToUINT(ip);
return IsOnPrivateIPFile(ip4);
}
}
return false;
}
// Read a private IP list file
void LoadPrivateIPFile()
{
BUF *b = ReadDump(PRIVATE_IP_TXT_FILENAME);
LIST *o;
if (b == NULL)
{
return;
}
o = NewList(NULL);
while (true)
{
char *line = CfgReadNextLine(b);
if (line == NULL)
{
break;
}
Trim(line);
if (IsEmptyStr(line) == false)
{
UINT ip = 0, mask = 0;
if (ParseIpAndSubnetMask4(line, &ip, &mask))
{
PRIVATE_IP_SUBNET *p = ZeroMalloc(sizeof(PRIVATE_IP_SUBNET));
p->Ip = ip;
p->Mask = mask;
p->Ip2 = ip & mask;
Add(o, p);
}
}
Free(line);
}
g_private_ip_list = o;
g_use_privateip_file = true;
FreeBuf(b);
}
// Examine whether the specified IP address is in the private IP file
bool IsOnPrivateIPFile(UINT ip)
{
bool ret = false;
if (g_private_ip_list != NULL)
{
LIST *o = g_private_ip_list;
UINT i;
for (i = 0; i < LIST_NUM(o); i++)
{
PRIVATE_IP_SUBNET *p = LIST_DATA(o, i);
if ((ip & p->Mask) == p->Ip2)
{
ret = true;
}
}
}
return ret;
}
// Free the private IP file
void FreePrivateIPFile()
{
if (g_private_ip_list != NULL)
{
LIST *o = g_private_ip_list;
UINT i;
g_private_ip_list = NULL;
for (i = 0; i < LIST_NUM(o); i++)
{
PRIVATE_IP_SUBNET *p = LIST_DATA(o, i);
Free(p);
}
ReleaseList(o);
}
g_use_privateip_file = false;
}
// Check whether the specified IP address is in the same network to this computer
bool IsIPAddressInSameLocalNetwork(IP *a)
{
bool ret = false;
LIST *o;
UINT i;
// Validate arguments
if (a == NULL)
{
return false;
}
o = GetHostIPAddressList();
if (o != NULL)
{
for (i = 0; i < LIST_NUM(o); i++)
{
IP *p = LIST_DATA(o, i);
if (IsIP4(p))
{
if (IsZeroIp(p) == false && p->addr[0] != 127)
{
if (IsInSameNetwork4Standard(p, a))
{
ret = true;
break;
}
}
}
}
FreeHostIPAddressList(o);
}
return ret;
}
// Guess the IPv4, IPv6 global address from the IP address list of the current interface
void GetCurrentGlobalIPGuess(IP *ip, bool ipv6)
{
LIST *o;
UINT i;
// Validate arguments
if (ip == NULL)
{
return;
}
Zero(ip, sizeof(IP));
o = GetHostIPAddressList();
if (ipv6 == false)
{
// IPv4
for (i = 0; i < LIST_NUM(o); i++)
{
IP *p = LIST_DATA(o, i);
if (IsIP4(p))
{
if (IsZeroIp(p) == false && IsIPPrivate(p) == false && p->addr[0] != 127)
{
Copy(ip, p, sizeof(IP));
}
}
}
if (IsZeroIp(ip))
{
for (i = 0; i < LIST_NUM(o); i++)
{
IP *p = LIST_DATA(o, i);
if (IsIP4(p))
{
if (IsZeroIp(p) == false && IsIPPrivate(p) && p->addr[0] != 127)
{
Copy(ip, p, sizeof(IP));
}
}
}
}
if (IsZeroIp(ip))
{
SetIP(ip, 127, 0, 0, 1);
}
}
else
{
// IPv6
for (i = 0; i < LIST_NUM(o); i++)
{
IP *p = LIST_DATA(o, i);
if (IsIP6(p))
{
UINT type = GetIPAddrType6(p);
if ((type & IPV6_ADDR_GLOBAL_UNICAST) && ((type & IPV6_ADDR_ZERO) == 0) && ((type & IPV6_ADDR_LOOPBACK) == 0))
{
Copy(ip, p, sizeof(IP));
}
}
}
}
FreeHostIPAddressList(o);
}
// Record the current global IP address
void SetCurrentGlobalIP(IP *ip, bool ipv6)
{
// Validate arguments
if (ip == NULL)
{
return;
}
if (IsZeroIp(ip))
{
return;
}
Lock(current_global_ip_lock);
{
if (ipv6 == false)
{
Copy(&current_glocal_ipv4, ip, sizeof(IP));
}
else
{
Copy(&current_glocal_ipv6, ip, sizeof(IP));
}
current_global_ip_set = true;
}
Unlock(current_global_ip_lock);
}
// Release of the network communication module
void FreeNetwork()
{
if (dh_param != NULL)
{
DhFree(dh_param);
dh_param = NULL;
}
// Release of thread-related
FreeWaitThread();
Zero(&unix_dns_server, sizeof(unix_dns_server));
// Release the locks
DeleteLock(unix_dns_server_addr_lock);
DeleteLock(dns_lock);
DeleteLock(ssl_accept_lock);
DeleteLock(machine_name_lock);
DeleteLock(disconnect_function_lock);
DeleteLock(aho);
DeleteLock(socket_library_lock);
DeleteLock(ssl_connect_lock);
DeleteLock(machine_ip_process_hash_lock);
machine_name_lock = NULL;
ssl_accept_lock = machine_name_lock = disconnect_function_lock =
aho = socket_library_lock = ssl_connect_lock = machine_ip_process_hash_lock = NULL;
// Release of the DNS cache
FreeDnsCache();
// Release of the host name cache
FreeHostCache();
#ifdef OS_WIN32
// Release of the socket library
Win32FreeSocketLibrary();
#else
UnixFreeSocketLibrary();
#endif // OS_WIN32
DeleteCounter(num_tcp_connections);
num_tcp_connections = NULL;
// Release of client list
FreeIpClientList();
DeleteLock(current_global_ip_lock);
current_global_ip_lock = NULL;
DeleteLock(current_fqdn_lock);
current_fqdn_lock = NULL;
// Release of the local MAC list
if (local_mac_list != NULL)
{
FreeNicList(local_mac_list);
local_mac_list = NULL;
}
DeleteLock(local_mac_list_lock);
local_mac_list_lock = NULL;
DeleteLock(host_ip_address_list_cache_lock);
host_ip_address_list_cache_lock = NULL;
FreeHostIPAddressList(host_ip_address_cache);
host_ip_address_cache = NULL;
FreeDynList();
DeleteCounter(getip_thread_counter);
getip_thread_counter = NULL;
}
// Stop all the sockets in the list and delete it
void StopSockList(SOCKLIST *sl)
{
SOCK **ss;
UINT num, i;
// Validate arguments
if (sl == NULL)
{
return;
}
LockList(sl->SockList);
{
num = LIST_NUM(sl->SockList);
ss = ToArray(sl->SockList);
DeleteAll(sl->SockList);
}
UnlockList(sl->SockList);
for (i = 0; i < num; i++)
{
SOCK *s = ss[i];
Disconnect(s);
ReleaseSock(s);
}
Free(ss);
}
// Delete the socket list
void FreeSockList(SOCKLIST *sl)
{
// Validate arguments
if (sl == NULL)
{
return;
}
StopSockList(sl);
ReleaseList(sl->SockList);
Free(sl);
}
// Creating a socket list
SOCKLIST *NewSockList()
{
SOCKLIST *sl = ZeroMallocFast(sizeof(SOCKLIST));
sl->SockList = NewList(NULL);
return sl;
}
// Time-out thread of the socket on Solaris
void SocketTimeoutThread(THREAD *t, void *param)
{
SOCKET_TIMEOUT_PARAM *ttparam;
ttparam = (SOCKET_TIMEOUT_PARAM *)param;
// Wait for time-out period
Select(NULL, ttparam->sock->TimeOut, ttparam->cancel, NULL);
// Disconnect if it is blocked
if(! ttparam->unblocked)
{
// Debug("Socket timeouted\n");
closesocket(ttparam->sock->socket);
}
else
{
// Debug("Socket timeout cancelled\n");
}
}
// Initialize and start the thread for time-out
SOCKET_TIMEOUT_PARAM *NewSocketTimeout(SOCK *sock)
{
SOCKET_TIMEOUT_PARAM *ttp;
if(! sock->AsyncMode && sock->TimeOut != TIMEOUT_INFINITE)
{
// Debug("NewSockTimeout(%u)\n",sock->TimeOut);
ttp = (SOCKET_TIMEOUT_PARAM *)Malloc(sizeof(SOCKET_TIMEOUT_PARAM));
// Set the parameters of the time-out thread
ttp->cancel = NewCancel();
ttp->sock = sock;
ttp->unblocked = false;
ttp->thread = NewThread(SocketTimeoutThread, ttp);
return ttp;
}
return NULL;
}
// Stop and free the thread for timeout
void FreeSocketTimeout(SOCKET_TIMEOUT_PARAM *ttp)
{
if(ttp == NULL)
{
return;
}
ttp->unblocked = true;
Cancel(ttp->cancel);
WaitThread(ttp->thread, INFINITE);
ReleaseCancel(ttp->cancel);
ReleaseThread(ttp->thread);
Free(ttp);
// Debug("FreeSocketTimeout succeed\n");
return;
}
// Parse the IP address and subnet mask
bool ParseIpAndSubnetMask46(char *src, IP *ip, IP *mask)
{
// Validate arguments
if (src == NULL || ip == NULL || mask == NULL)
{
return false;
}
if (ParseIpAndMask46(src, ip, mask) == false)
{
return false;
}
if (IsIP4(ip))
{
return IsSubnetMask4(mask);
}
else
{
return IsSubnetMask6(mask);
}
}
bool ParseIpAndSubnetMask4(char *src, UINT *ip, UINT *mask)
{
IP ip2, mask2;
// Validate arguments
if (src == NULL)
{
return false;
}
if (ParseIpAndSubnetMask46(src, &ip2, &mask2) == false)
{
return false;
}
if (IsIP4(&ip2) == false)
{
return false;
}
if (ip != NULL)
{
*ip = IPToUINT(&ip2);
}
if (mask != NULL)
{
*mask = IPToUINT(&mask2);
}
return true;
}
// Parse the IP address and the mask
bool ParseIpAndMask46(char *src, IP *ip, IP *mask)
{
TOKEN_LIST *t;
char *ipstr;
char *subnetstr;
bool ret = false;
IP ip2;
IP mask2;
// Validate arguments
if (src == NULL || ip == NULL || mask == NULL)
{
return false;
}
Zero(&ip2, sizeof(IP));
Zero(&mask2, sizeof(IP));
t = ParseToken(src, "/");
if (t->NumTokens != 2)
{
FreeToken(t);
return false;
}
ipstr = t->Token[0];
subnetstr = t->Token[1];
Trim(ipstr);
Trim(subnetstr);
if (StrToIP(&ip2, ipstr))
{
if (StrToIP(&mask2, subnetstr))
{
// Compare the kind of the mask part and the IP address part to confirm same
if (IsIP6(&ip2) && IsIP6(&mask2))
{
// Both are IPv6
ret = true;
Copy(ip, &ip2, sizeof(IP));
Copy(mask, &mask2, sizeof(IP));
}
else if (IsIP4(&ip2) && IsIP4(&mask2))
{
// Both are IPv4
ret = true;
Copy(ip, &ip2, sizeof(IP));
Copy(mask, &mask2, sizeof(IP));
}
}
else
{
if (IsNum(subnetstr))
{
UINT i = ToInt(subnetstr);
// Mask part is a number
if (IsIP6(&ip2) && i <= 128)
{
ret = true;
Copy(ip, &ip2, sizeof(IP));
IntToSubnetMask6(mask, i);
}
else if (i <= 32)
{
ret = true;
Copy(ip, &ip2, sizeof(IP));
IntToSubnetMask4(mask, i);
}
}
}
}
FreeToken(t);
return ret;
}
bool ParseIpAndMask4(char *src, UINT *ip, UINT *mask)
{
IP ip_ip, ip_mask;
if (ParseIpAndMask46(src, &ip_ip, &ip_mask) == false)
{
return false;
}
if (IsIP4(&ip_ip) == false)
{
return false;
}
if (ip != NULL)
{
*ip = IPToUINT(&ip_ip);
}
if (mask != NULL)
{
*mask = IPToUINT(&ip_mask);
}
return true;
}
bool ParseIpAndMask6(char *src, IP *ip, IP *mask)
{
if (ParseIpAndMask46(src, ip, mask) == false)
{
return false;
}
if (IsIP6(ip) == false)
{
return false;
}
return true;
}
// Check whether the specification of the IPv4 address is correct
bool IsIpStr4(char *str)
{
// Validate arguments
if (str == NULL)
{
return false;
}
if (StrToIP32(str) == 0 && StrCmpi(str, "0.0.0.0") != 0)
{
return false;
}
return true;
}
// Check whether the specification of the IPv6 address is correct
bool IsIpStr6(char *str)
{
IP ip;
// Validate arguments
if (str == NULL)
{
return false;
}
if (StrToIP6(&ip, str) == false)
{
return false;
}
return true;
}
// Convert the string to an IPv6 mask
bool StrToMask6(IP *mask, char *str)
{
// Validate arguments
if (mask == NULL || str == NULL)
{
return false;
}
if (str[0] == '/')
{
str++;
}
if (IsNum(str))
{
UINT n = ToInt(str);
if (n <= 128)
{
IntToSubnetMask6(mask, n);
return true;
}
else
{
return false;
}
}
else
{
if (StrToIP(mask, str) == false)
{
return false;
}
else
{
return IsIP6(mask);
}
}
}
bool StrToMask6Addr(IPV6_ADDR *mask, char *str)
{
IP ip;
if (StrToMask6(&ip, str) == false)
{
return false;
}
if (IPToIPv6Addr(mask, &ip) == false)
{
return false;
}
return true;
}
// Convert the IPv4 / IPv6 mask to a string
void MaskToStr(char *str, UINT size, IP *mask)
{
MaskToStrEx(str, size, mask, false);
}
void MaskToStrEx(char *str, UINT size, IP *mask, bool always_full_address)
{
// Validate arguments
if (str == NULL || mask == NULL)
{
return;
}
if (always_full_address == false && IsSubnetMask(mask))
{
ToStr(str, SubnetMaskToInt(mask));
}
else
{
IPToStr(str, size, mask);
}
}
void MaskToStr32(char *str, UINT size, UINT mask)
{
MaskToStr32Ex(str, size, mask, false);
}
void MaskToStr32Ex(char *str, UINT size, UINT mask, bool always_full_address)
{
IP ip;
UINTToIP(&ip, mask);
MaskToStrEx(str, size, &ip, always_full_address);
}
void Mask6AddrToStrEx(char *str, UINT size, IPV6_ADDR *mask, bool always_full_address)
{
IP ip;
// Validate arguments
if (str == NULL || mask == NULL)
{
StrCpy(str, size, "");
return;
}
IPv6AddrToIP(&ip, mask);
MaskToStrEx(str, size, &ip, always_full_address);
}
void Mask6AddrToStr(char *str, UINT size, IPV6_ADDR *mask)
{
Mask6AddrToStrEx(str, size, mask, false);
}
// Disconnecting of the tube
void TubeDisconnect(TUBE *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
if (t->TubePairData == NULL)
{
return;
}
Lock(t->TubePairData->Lock);
{
t->TubePairData->IsDisconnected = true;
Set(t->TubePairData->Event1);
Set(t->TubePairData->Event2);
SetSockEvent(t->TubePairData->SockEvent1);
SetSockEvent(t->TubePairData->SockEvent2);
}
Unlock(t->TubePairData->Lock);
}
// Creating a tube pair
void NewTubePair(TUBE **t1, TUBE **t2, UINT size_of_header)
{
TUBEPAIR_DATA *d;
// Validate arguments
if (t1 == NULL || t2 == NULL)
{
return;
}
*t1 = NewTube(size_of_header);
*t2 = NewTube(size_of_header);
(*t1)->IndexInTubePair = 0;
(*t2)->IndexInTubePair = 1;
d = NewTubePairData();
AddRef(d->Ref);
(*t1)->TubePairData = d;
(*t2)->TubePairData = d;
d->Event1 = (*t1)->Event;
d->Event2 = (*t2)->Event;
AddRef(d->Event1->ref);
AddRef(d->Event2->ref);
}
// Creating a tube pair data
TUBEPAIR_DATA *NewTubePairData()
{
TUBEPAIR_DATA *d = ZeroMalloc(sizeof(TUBEPAIR_DATA));
d->Ref = NewRef();
d->Lock = NewLock();
return d;
}
// Set the SockEvent to the tube
void SetTubeSockEvent(TUBE *t, SOCK_EVENT *e)
{
// Validate arguments
if (t == NULL)
{
return;
}
Lock(t->Lock);
{
TUBEPAIR_DATA *d;
if (t->SockEvent != e)
{
if (t->SockEvent != NULL)
{
ReleaseSockEvent(t->SockEvent);
}
if (e != NULL)
{
AddRef(e->ref);
}
t->SockEvent = e;
}
d = t->TubePairData;
if (d != NULL)
{
Lock(d->Lock);
{
SOCK_EVENT **sep = (t->IndexInTubePair == 0 ? &d->SockEvent1 : &d->SockEvent2);
if (*sep != e)
{
if (*sep != NULL)
{
ReleaseSockEvent(*sep);
}
if (e != NULL)
{
AddRef(e->ref);
}
*sep = e;
}
}
Unlock(d->Lock);
}
}
Unlock(t->Lock);
}
// Release of the tube pair data
void ReleaseTubePairData(TUBEPAIR_DATA *d)
{
// Validate arguments
if (d == NULL)
{
return;
}
if (Release(d->Ref) == 0)
{
CleanupTubePairData(d);
}
}
void CleanupTubePairData(TUBEPAIR_DATA *d)
{
// Validate arguments
if (d == NULL)
{
return;
}
ReleaseEvent(d->Event1);
ReleaseEvent(d->Event2);
ReleaseSockEvent(d->SockEvent1);
ReleaseSockEvent(d->SockEvent2);
DeleteLock(d->Lock);
Free(d);
}
// Check whether the tube is connected to the opponent still
bool IsTubeConnected(TUBE *t)
{
// Validate arguments
if (t == NULL)
{
return false;
}
if (t->TubePairData == NULL)
{
return true;
}
if (t->TubePairData->IsDisconnected)
{
return false;
}
return true;
}
// Send the data to the tube
bool TubeSend(TUBE *t, void *data, UINT size, void *header)
{
return TubeSendEx(t, data, size, header, false);
}
bool TubeSendEx(TUBE *t, void *data, UINT size, void *header, bool no_flush)
{
return TubeSendEx2(t, data, size, header, no_flush, 0);
}
bool TubeSendEx2(TUBE *t, void *data, UINT size, void *header, bool no_flush, UINT max_num_in_queue)
{
// Validate arguments
if (t == NULL || data == NULL || size == 0)
{
return false;
}
if (IsTubeConnected(t) == false)
{
return false;
}
LockQueue(t->Queue);
{
if (max_num_in_queue == 0 || (t->Queue->num_item <= max_num_in_queue))
{
InsertQueue(t->Queue, NewTubeData(data, size, header, t->SizeOfHeader));
}
else
{
no_flush = true;
}
}
UnlockQueue(t->Queue);
if (no_flush == false)
{
Set(t->Event);
SetSockEvent(t->SockEvent);
}
return true;
}
// Flush the tube
void TubeFlush(TUBE *t)
{
TubeFlushEx(t, false);
}
void TubeFlushEx(TUBE *t, bool force)
{
// Validate arguments
if (t == NULL)
{
return;
}
if (IsTubeConnected(t) == false)
{
return;
}
if (force == false)
{
if (t->Queue->num_item == 0)
{
return;
}
}
Set(t->Event);
SetSockEvent(t->SockEvent);
}
// Receive the data from the tube (asynchronous)
TUBEDATA *TubeRecvAsync(TUBE *t)
{
TUBEDATA *d;
// Validate arguments
if (t == NULL)
{
return NULL;
}
if (IsTubeConnected(t) == false)
{
return NULL;
}
LockQueue(t->Queue);
{
d = GetNext(t->Queue);
}
UnlockQueue(t->Queue);
return d;
}
// Get the SockEvent associated with the tube
SOCK_EVENT *GetTubeSockEvent(TUBE *t)
{
SOCK_EVENT *e = NULL;
// Validate arguments
if (t == NULL)
{
return NULL;
}
Lock(t->Lock);
{
if (t->SockEvent != NULL)
{
AddRef(t->SockEvent->ref);
e = t->SockEvent;
}
}
Unlock(t->Lock);
return e;
}
// Receive the data from the tube (synchronous)
TUBEDATA *TubeRecvSync(TUBE *t, UINT timeout)
{
UINT64 start_tick, timeout_tick;
TUBEDATA *d = NULL;
// Validate arguments
if (t == NULL)
{
return NULL;
}
if (IsTubeConnected(t) == false)
{
return NULL;
}
start_tick = Tick64();
timeout_tick = start_tick + (UINT64)timeout;
while (true)
{
UINT64 now = Tick64();
UINT remain_time;
SOCK_EVENT *e;
UINT interval;
d = NULL;
if (IsTubeConnected(t) == false)
{
break;
}
LockQueue(t->Queue);
{
d = GetNext(t->Queue);
}
UnlockQueue(t->Queue);
if (d != NULL)
{
break;
}
if (timeout != INFINITE && now >= timeout_tick)
{
return NULL;
}
remain_time = (UINT)(timeout_tick - now);
e = GetTubeSockEvent(t);
interval = (timeout == INFINITE ? INFINITE : remain_time);
if (e == NULL)
{
Wait(t->Event, interval);
}
else
{
WaitSockEvent(e, interval);
ReleaseSockEvent(e);
}
}
return d;
}
// Creating a tube
TUBE *NewTube(UINT size_of_header)
{
TUBE *t = ZeroMalloc(sizeof(TUBE));
t->Event = NewEvent();
t->Queue = NewQueue();
t->Ref = NewRef();
t->Lock = NewLock();
t->SockEvent = NewSockEvent();
t->SizeOfHeader = size_of_header;
return t;
}
// Release of the tube
void ReleaseTube(TUBE *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
if (Release(t->Ref) == 0)
{
CleanupTube(t);
}
}
void CleanupTube(TUBE *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
while (true)
{
TUBEDATA *d = GetNext(t->Queue);
if (d == NULL)
{
break;
}
FreeTubeData(d);
}
ReleaseQueue(t->Queue);
ReleaseEvent(t->Event);
ReleaseSockEvent(t->SockEvent);
ReleaseTubePairData(t->TubePairData);
DeleteLock(t->Lock);
Free(t);
}
// Creating a tube data
TUBEDATA *NewTubeData(void *data, UINT size, void *header, UINT header_size)
{
TUBEDATA *d;
// Validate arguments
if (size == 0 || data == NULL)
{
return NULL;
}
d = ZeroMalloc(sizeof(TUBEDATA));
d->Data = Clone(data, size);
d->DataSize = size;
if (header != NULL)
{
d->Header = Clone(header, header_size);
d->HeaderSize = header_size;
}
else
{
d->Header = ZeroMalloc(header_size);
}
return d;
}
// Release of the tube data
void FreeTubeData(TUBEDATA *d)
{
// Validate arguments
if (d == NULL)
{
return;
}
Free(d->Data);
Free(d->Header);
Free(d);
}
// Release of the IP address list of the host
void FreeHostIPAddressList(LIST *o)
{
UINT i;
// Validate arguments
if (o == NULL)
{
return;
}
for (i = 0; i < LIST_NUM(o); i++)
{
IP *ip = LIST_DATA(o, i);
Free(ip);
}
ReleaseList(o);
}
// Get whether the specified IP address is held by this host
bool IsMyIPAddress(IP *ip)
{
LIST *o;
UINT i;
bool ret = false;
// Validate arguments
if (ip == NULL)
{
return false;
}
o = GetHostIPAddressList();
for (i = 0; i < LIST_NUM(o); i++)
{
IP *a = LIST_DATA(o, i);
if (CmpIpAddr(ip, a) == 0)
{
ret = true;
break;
}
}
FreeHostIPAddressList(o);
return ret;
}
// Add the IP address to the list
void AddHostIPAddressToList(LIST *o, IP *ip)
{
IP *r;
// Validate arguments
if (o == NULL || ip == NULL)
{
return;
}
r = Search(o, ip);
if (r != NULL)
{
return;
}
Insert(o, Clone(ip, sizeof(IP)));
}
// Comparison of the IP address list items
int CmpIpAddressList(void *p1, void *p2)
{
IP *ip1, *ip2;
UINT r;
// Validate arguments
if (p1 == NULL || p2 == NULL)
{
return 0;
}
ip1 = *(IP **)p1;
ip2 = *(IP **)p2;
if (ip1 == NULL || ip2 == NULL)
{
return 0;
}
// IPv4 < IPv6
r = COMPARE_RET(IsIP6(ip1), IsIP6(ip2));
if (r != 0)
{
return r;
}
// any > specified IP
if (IsZeroIP(ip1) && IsZeroIP(ip2) == false)
{
return 1;
}
if (IsZeroIP(ip1) == false && IsZeroIP(ip2))
{
return -1;
}
// local > others
if (IsLocalHostIP(ip1) && IsLocalHostIP(ip2) == false)
{
return 1;
}
if (IsLocalHostIP(ip1) == false && IsLocalHostIP(ip2))
{
return -1;
}
// ip address
r = CmpIpAddr(ip1, ip2);
if (r != 0)
{
return r;
}
// interface index
if (IsIP6(ip1))
{
r = COMPARE_RET(ip1->ipv6_scope_id, ip2->ipv6_scope_id);
}
else
{
r = 0;
}
return r;
}
// Get the IP address list hash of the host
UINT64 GetHostIPAddressListHash()
{
UINT i;
LIST *o;
BUF *buf = NewBuf();
UCHAR hash[SHA1_SIZE];
UINT64 ret = 0;
o = GetHostIPAddressList();
if (o != NULL)
{
for (i = 0; i < LIST_NUM(o); i++)
{
IP *ip = LIST_DATA(o, i);
char tmp[128];
Zero(tmp, sizeof(tmp));
IPToStr(tmp, sizeof(tmp), ip);
WriteBufStr(buf, tmp);
}
FreeHostIPAddressList(o);
}
WriteBufStr(buf, "test");
Sha1(hash, buf->Buf, buf->Size);
FreeBuf(buf);
Copy(&ret, hash, sizeof(UINT64));
ret = Endian64(ret);
return ret;
}
// Get the IP address list of the host (using cache)
LIST *GetHostIPAddressList()
{
LIST *o = NULL;
if (host_ip_address_list_cache_lock == NULL)
{
return GetHostIPAddressListInternal();
}
Lock(host_ip_address_list_cache_lock);
{
UINT64 now = Tick64();
if (host_ip_address_list_cache_last == 0 ||
((host_ip_address_list_cache_last + (UINT64)HOST_IP_ADDRESS_LIST_CACHE) < now) ||
host_ip_address_cache == NULL)
{
if (host_ip_address_cache != NULL)
{
FreeHostIPAddressList(host_ip_address_cache);
}
host_ip_address_cache = GetHostIPAddressListInternal();
host_ip_address_list_cache_last = now;
}
o = CloneIPAddressList(host_ip_address_cache);
}
Unlock(host_ip_address_list_cache_lock);
if (o == NULL)
{
o = GetHostIPAddressListInternal();
}
return o;
}
// Copy of the IP address list
LIST *CloneIPAddressList(LIST *o)
{
LIST *ret;
UINT i;
// Validate arguments
if (o == NULL)
{
return NULL;
}
ret = NewListFast(CmpIpAddressList);
for (i = 0; i < LIST_NUM(o); i++)
{
IP *ip = LIST_DATA(o, i);
if (ip != NULL)
{
ip = Clone(ip, sizeof(IP));
Add(ret, ip);
}
}
return ret;
}
// Get an IP address list of the host
LIST *GetHostIPAddressListInternal()
{
char hostname[MAX_SIZE];
LIST *o;
IP any6, any4;
IP local6, local4;
bool is_v6_supported = IsIPv6Supported();
GetLocalHostIP4(&local4);
GetLocalHostIP6(&local6);
ZeroIP4(&any4);
ZeroIP6(&any6);
Zero(hostname, sizeof(hostname));
gethostname(hostname, sizeof(hostname));
o = NewListFast(CmpIpAddressList);
// any
AddHostIPAddressToList(o, &any4);
if (is_v6_supported)
{
AddHostIPAddressToList(o, &any6);
}
// localhost
AddHostIPAddressToList(o, &local4);
if (is_v6_supported)
{
AddHostIPAddressToList(o, &local6);
}
#ifndef MAYAQUA_SUPPORTS_GETIFADDRS
// IPv4
if (true)
{
struct sockaddr_in in;
struct in_addr addr;
struct addrinfo hint;
struct addrinfo *info;
Zero(&hint, sizeof(hint));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
info = NULL;
if (getaddrinfo(hostname, NULL, &hint, &info) == 0)
{
if (info->ai_family == AF_INET)
{
struct addrinfo *current = info;
while (current != NULL)
{
IP ip;
Copy(&in, current->ai_addr, sizeof(in));
Copy(&addr, &in.sin_addr, sizeof(addr));
InAddrToIP(&ip, &addr);
AddHostIPAddressToList(o, &ip);
current = current->ai_next;
}
}
freeaddrinfo(info);
}
}
#ifndef UNIX_LINUX
// IPv6
if (is_v6_supported)
{
struct sockaddr_in6 in;
struct in6_addr addr;
struct addrinfo hint;
struct addrinfo *info;
Zero(&hint, sizeof(hint));
hint.ai_family = AF_INET6;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
info = NULL;
if (getaddrinfo(hostname, NULL, &hint, &info) == 0)
{
if (info->ai_family == AF_INET6)
{
struct addrinfo *current = info;
while (current != NULL)
{
IP ip;
Copy(&in, current->ai_addr, sizeof(in));
Copy(&addr, &in.sin6_addr, sizeof(addr));
InAddrToIP6(&ip, &addr);
ip.ipv6_scope_id = in.sin6_scope_id;
AddHostIPAddressToList(o, &ip);
current = current->ai_next;
}
}
freeaddrinfo(info);
}
}
#endif // UNIX_LINUX
#endif // MAYAQUA_SUPPORTS_GETIFADDRS
#ifdef MAYAQUA_SUPPORTS_GETIFADDRS
// If the getifaddrs is available, use this
if (true)
{
struct ifaddrs *aa = NULL;
if (getifaddrs(&aa) == 0)
{
struct ifaddrs *a = aa;
while (a != NULL)
{
if (a->ifa_addr != NULL)
{
struct sockaddr *addr = a->ifa_addr;
if (addr->sa_family == AF_INET)
{
IP ip;
struct sockaddr_in *d = (struct sockaddr_in *)addr;
struct in_addr *addr = &d->sin_addr;
InAddrToIP(&ip, addr);
AddHostIPAddressToList(o, &ip);
}
else if (addr->sa_family == AF_INET6)
{
IP ip;
struct sockaddr_in6 *d = (struct sockaddr_in6 *)addr;
UINT scope_id = d->sin6_scope_id;
struct in6_addr *addr = &d->sin6_addr;
InAddrToIP6(&ip, addr);
ip.ipv6_scope_id = scope_id;
AddHostIPAddressToList(o, &ip);
}
}
a = a->ifa_next;
}
freeifaddrs(aa);
}
}
#endif // MAYAQUA_SUPPORTS_GETIFADDRS
return o;
}
// Get whether the UDP listener opens the specified port
bool IsUdpPortOpened(UDPLISTENER *u, IP *server_ip, UINT port)
{
UINT i;
// Validate arguments
if (u == NULL || port == 0)
{
return false;
}
if (server_ip != NULL)
{
for (i = 0; i < LIST_NUM(u->SockList); i++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, i);
if (us->Sock != NULL && us->HasError == false)
{
if (us->Port == port)
{
if (CmpIpAddr(server_ip, &us->IpAddress) == 0)
{
return true;
}
}
}
}
}
for (i = 0; i < LIST_NUM(u->SockList); i++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, i);
if (us->Sock != NULL && us->HasError == false)
{
if (us->Port == port)
{
if (IsZeroIP(&us->IpAddress))
{
return true;
}
}
}
}
return false;
}
// IP address acquisition thread
void QueryIpThreadMain(THREAD *thread, void *param)
{
QUERYIPTHREAD *t = (QUERYIPTHREAD *)param;
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
while (t->Halt == false)
{
UINT next_wait_time = 0;
IP ip;
bool ok = false;
if (GetIP4Ex(&ip, t->Hostname, 5000, &t->Halt))
{
if (IsZeroIP(&ip) == false)
{
Lock(t->Lock);
{
Copy(&t->Ip, &ip, sizeof(IP));
}
Unlock(t->Lock);
ok = true;
}
}
if (ok == false)
{
next_wait_time = t->IntervalLastNg;
}
else
{
next_wait_time = t->IntervalLastOk;
}
if (t->Halt)
{
break;
}
Wait(t->HaltEvent, next_wait_time);
}
}
// Creating an IP address acquisition thread
QUERYIPTHREAD *NewQueryIpThread(char *hostname, UINT interval_last_ok, UINT interval_last_ng)
{
QUERYIPTHREAD *t;
t = ZeroMalloc(sizeof(QUERYIPTHREAD));
t->HaltEvent = NewEvent();
t->Lock = NewLock();
StrCpy(t->Hostname, sizeof(t->Hostname), hostname);
t->IntervalLastOk = interval_last_ok;
t->IntervalLastNg = interval_last_ng;
t->Thread = NewThread(QueryIpThreadMain, t);
return t;
}
// Get the results of the IP address acquisition thread
bool GetQueryIpThreadResult(QUERYIPTHREAD *t, IP *ip)
{
bool ret = false;
Zero(ip, sizeof(IP));
// Validate arguments
if (t == NULL || ip == NULL)
{
return false;
}
Lock(t->Lock);
if (IsZero(&t->Ip, sizeof(IP)))
{
ret = false;
}
else
{
Copy(ip, &t->Ip, sizeof(IP));
}
Unlock(t->Lock);
return ret;
}
// Release of the IP address acquisition thread
void FreeQueryIpThread(QUERYIPTHREAD *t)
{
// Validate arguments
if (t == NULL)
{
return;
}
t->Halt = true;
Set(t->HaltEvent);
WaitThread(t->Thread, INFINITE);
ReleaseThread(t->Thread);
ReleaseEvent(t->HaltEvent);
DeleteLock(t->Lock);
Free(t);
}
// UDP listener thread
void UdpListenerThread(THREAD *thread, void *param)
{
UDPLISTENER *u = (UDPLISTENER *)param;
UINT i, j, k;
UINT buf_size = 65536;
void *buf;
bool cont_flag;
BUF *ip_list_buf = NewBuf();
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
buf = Malloc(buf_size);
// Initializing the socket list
u->SockList = NewList(NULL);
u->LastCheckTick = 0;
// u->PollMyIpAndPort = true;
// Main loop
while (u->Halt == false)
{
LIST *recv_list;
UINT64 now = Tick64();
UINT interval;
bool stage_changed = false;
IP nat_t_ip;
Zero(&nat_t_ip, sizeof(nat_t_ip));
if (u->LastCheckTick == 0 || (now >= (u->LastCheckTick + UDPLISTENER_CHECK_INTERVAL)))
{
LIST *iplist;
LIST *del_us_list = NewListFast(NULL);
BUF *ip_list_buf_new = NewBuf();
u->LastCheckTick = now;
// Obtain an IP address list
iplist = GetHostIPAddressList();
LockList(u->PortList);
{
for (k = 0; k < LIST_NUM(u->SockList); k++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, k);
us->Mark = false;
}
// If the combination of the IP address and the port number doesn't exist in the list, add it to the list
for (i = 0; i < LIST_NUM(iplist); i++)
{
IP *ip = LIST_DATA(iplist, i);
if (CmpIpAddr(ip, &u->ListenIP) != 0)
{
continue;
}
WriteBuf(ip_list_buf_new, ip, sizeof(IP));
for (j = 0; j < LIST_NUM(u->PortList); j++)
{
UINT k;
UINT *port = LIST_DATA(u->PortList, j);
bool existing = false;
if (IsZeroIP(ip) && (IS_SPECIAL_PORT(*port)))
{
continue;
}
for (k = 0; k < LIST_NUM(u->SockList); k++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, k);
if (CmpIpAddr(&us->IpAddress, ip) == 0 && us->Port == *port)
{
existing = true;
us->Mark = true;
break;
}
}
if (existing == false)
{
UDPLISTENER_SOCK *us = ZeroMalloc(sizeof(UDPLISTENER_SOCK));
Copy(&us->IpAddress, ip, sizeof(IP));
us->Port = *port;
us->Mark = true;
Add(u->SockList, us);
}
}
}
// If any errors suspected or the combination of IP address and port number
// has been regarded to delete already, delete it
for (k = 0; k < LIST_NUM(u->SockList); k++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, k);
if (us->Mark == false || us->HasError)
{
Debug("mark=%u error=%u\n", us->Mark, us->HasError);
Add(del_us_list, us);
}
}
for (i = 0; i < LIST_NUM(del_us_list); i++)
{
UDPLISTENER_SOCK *us = LIST_DATA(del_us_list, i);
char ipstr[MAX_SIZE];
IPToStr(ipstr, sizeof(ipstr), &us->IpAddress);
Debug("Closed UDP Port %u at %s.\n", us->Port, ipstr);
Delete(u->SockList, us);
if (us->Sock != NULL)
{
Disconnect(us->Sock);
ReleaseSock(us->Sock);
}
Free(us);
}
}
UnlockList(u->PortList);
// Open the UDP sockets which is not opend yet
for (k = 0; k < LIST_NUM(u->SockList); k++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, k);
if (us->Sock == NULL)
{
char ipstr[MAX_SIZE];
IPToStr(ipstr, sizeof(ipstr), &us->IpAddress);
if (us->ErrorDebugDisplayed == false)
{
Debug("Opening UDP Port %u at %s ...", us->Port, ipstr);
}
us->Sock = NewUDPEx2(us->Port, IsIP6(&us->IpAddress), &us->IpAddress);
if (us->Sock != NULL)
{
if (us->ErrorDebugDisplayed == false)
{
Debug("Ok.\n");
}
else
{
Debug("Opening UDP Port %u at %s ...", us->Port, ipstr);
Debug("Ok.\n");
}
JoinSockToSockEvent(us->Sock, u->Event);
us->ErrorDebugDisplayed = false;
}
else
{
if (us->ErrorDebugDisplayed == false)
{
Debug("Error.\n");
}
us->ErrorDebugDisplayed = true;
}
}
}
FreeHostIPAddressList(iplist);
ReleaseList(del_us_list);
if (CompareBuf(ip_list_buf, ip_list_buf_new) == false)
{
u->HostIPAddressListChanged = true;
}
FreeBuf(ip_list_buf);
ip_list_buf = ip_list_buf_new;
}
LABEL_RESTART:
stage_changed = false;
recv_list = NewListFast(NULL);
if (u->PollMyIpAndPort)
{
// Create a thread to get a NAT-T IP address if necessary
if (u->GetNatTIpThread == NULL)
{
char natt_hostname[MAX_SIZE];
RUDPGetRegisterHostNameByIP(natt_hostname, sizeof(natt_hostname), NULL);
u->GetNatTIpThread = NewQueryIpThread(natt_hostname, QUERYIPTHREAD_INTERVAL_LAST_OK, QUERYIPTHREAD_INTERVAL_LAST_NG);
GetQueryIpThreadResult(u->GetNatTIpThread, &nat_t_ip);
}
}
// Receive the data that is arriving at the socket
for (k = 0; k < LIST_NUM(u->SockList); k++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, k);
if (us->Sock != NULL)
{
UINT num_ignore_errors = 0;
if (u->PollMyIpAndPort && IsIP4(&us->IpAddress))
{
if (us->NextMyIpAndPortPollTick == 0 || us->NextMyIpAndPortPollTick <= now)
{
// Examine the self IP address and the self port number by using NAT-T server
us->NextMyIpAndPortPollTick = now + (UINT64)GenRandInterval(UDP_NAT_T_NAT_STATUS_CHECK_INTERVAL_MIN, UDP_NAT_T_NAT_STATUS_CHECK_INTERVAL_MAX);
if (IsZeroIP(&nat_t_ip) == false
)
{
UCHAR c = 'A';
SendTo(us->Sock, &nat_t_ip, UDP_NAT_T_PORT, &c, 1);
}
}
}
while (true)
{
IP src_addr;
UINT src_port;
UDPPACKET *p;
UINT size = RecvFrom(us->Sock, &src_addr, &src_port, buf, buf_size);
if (size == 0)
{
// Socket failure
if (us->Sock->IgnoreRecvErr == false)
{
LABEL_FATAL_ERROR:
Debug("RecvFrom has Error.\n");
us->HasError = true;
}
else
{
if ((num_ignore_errors++) >= MAX_NUM_IGNORE_ERRORS)
{
goto LABEL_FATAL_ERROR;
}
}
break;
}
else if (size == SOCK_LATER)
{
// No packet
break;
}
//Debug("UDP %u\n", size);
if (src_port == UDP_NAT_T_PORT && CmpIpAddr(&src_addr, &nat_t_ip) == 0)
{
// Receive a packet in which the IP address and the port number are written from the NAT-T server
if (size >= 8)
{
IP my_ip;
UINT my_port;
if (RUDPParseIPAndPortStr(buf, size, &my_ip, &my_port))
{
Copy(&us->PublicIpAddress, &my_ip, sizeof(IP));
us->PublicPort = my_port;
}
}
}
else
{
// Receive a regular packet
p = NewUdpPacket(&src_addr, src_port, &us->Sock->LocalIP, us->Sock->LocalPort,
Clone(buf, size), size);
if (p->SrcPort == MAKE_SPECIAL_PORT(52))
{
p->SrcPort = p->DestPort = MAKE_SPECIAL_PORT(50);
}
p->Type = u->PacketType;
Add(recv_list, p);
}
stage_changed = true;
}
}
}
// Pass the received packet to the procedure
u->RecvProc(u, recv_list);
// Release the packet
for (i = 0; i < LIST_NUM(recv_list); i++)
{
UDPPACKET *p = LIST_DATA(recv_list, i);
FreeUdpPacket(p);
}
ReleaseList(recv_list);
cont_flag = true;
do
{
// When there are packets to be transmitted, transmit it
LockList(u->SendPacketList);
{
UDPLISTENER_SOCK *last_us = NULL;
IP last_src_ip;
UINT last_src_port;
Zero(&last_src_ip, sizeof(IP));
last_src_port = 0;
for (i = 0; i < LIST_NUM(u->SendPacketList); i++)
{
UDPPACKET *p = LIST_DATA(u->SendPacketList, i);
UDPLISTENER_SOCK *us;
if (last_us != NULL && last_src_port == p->SrcPort && CmpIpAddr(&last_src_ip, &p->SrcIP) == 0)
{
us = last_us;
}
else
{
// Search for a good interface for the transmission
us = DetermineUdpSocketForSending(u, p);
if (us != NULL)
{
last_us = us;
last_src_port = p->SrcPort;
Copy(&last_src_ip, &p->SrcIP, sizeof(IP));
}
}
if (us != NULL)
{
// Send
UINT ret = SendTo(us->Sock, &p->DstIP, p->DestPort, p->Data, p->Size);
if (ret == 0)
{
if (us->Sock->IgnoreSendErr == false)
{
// Socket failure
Debug("SendTo has Error.\n");
us->HasError = true;
last_us = NULL;
}
}
else
{
if (ret != SOCK_LATER)
{
stage_changed = true;
}
}
}
FreeUdpPacket(p);
}
DeleteAll(u->SendPacketList);
}
UnlockList(u->SendPacketList);
if (LIST_NUM(u->SendPacketList) == 0)
{
cont_flag = false;
}
}
while (cont_flag);
if (stage_changed && u->Halt == false)
{
goto LABEL_RESTART;
}
// Timing adjustment
interval = GetNextIntervalForInterrupt(u->Interrupts);
if (interval == INFINITE)
{
interval = UDPLISTENER_WAIT_INTERVAL;
}
else
{
interval = MIN(UDPLISTENER_WAIT_INTERVAL, interval);
}
if (interval >= 1)
{
WaitSockEvent(u->Event, interval);
}
}
if (u->GetNatTIpThread != NULL)
{
FreeQueryIpThread(u->GetNatTIpThread);
}
// Release of the socket list
for (i = 0; i < LIST_NUM(u->SockList); i++)
{
UDPLISTENER_SOCK *us = (UDPLISTENER_SOCK *)LIST_DATA(u->SockList, i);
Disconnect(us->Sock);
ReleaseSock(us->Sock);
Free(us);
}
ReleaseList(u->SockList);
FreeBuf(ip_list_buf);
Free(buf);
}
// Select the best UDP socket to be used for transmission
UDPLISTENER_SOCK *DetermineUdpSocketForSending(UDPLISTENER *u, UDPPACKET *p)
{
UINT i;
// Validate arguments
if (u == NULL || p == NULL)
{
return NULL;
}
for (i = 0; i < LIST_NUM(u->SockList); i++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, i);
if (us->Sock != NULL && us->HasError == false)
{
if (us->Port == p->SrcPort)
{
if (CmpIpAddr(&us->IpAddress, &p->SrcIP) == 0)
{
return us;
}
}
}
}
for (i = 0; i < LIST_NUM(u->SockList); i++)
{
UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, i);
if (us->Sock != NULL && us->HasError == false)
{
if (us->Port == p->SrcPort)
{
if (IsZeroIP(&us->IpAddress))
{
if ((IsIP4(&p->DstIP) && IsIP4(&us->IpAddress)) ||
(IsIP6(&p->DstIP) && IsIP6(&us->IpAddress)))
{
return us;
}
}
}
}
}
return NULL;
}
void FreeTcpRawData(TCP_RAW_DATA *trd)
{
// Validate arguments
if (trd == NULL)
{
return;
}
ReleaseFifo(trd->Data);
Free(trd);
}
TCP_RAW_DATA *NewTcpRawData(IP *src_ip, UINT src_port, IP *dst_ip, UINT dst_port)
{
TCP_RAW_DATA *trd;
// Validate arguments
if (dst_ip == NULL || dst_port == 0)
{
return NULL;
}
trd = ZeroMalloc(sizeof(TCP_RAW_DATA));
Copy(&trd->SrcIP, src_ip, sizeof(IP));
trd->SrcPort = src_port;
Copy(&trd->DstIP, dst_ip, sizeof(IP));
trd->DstPort = dst_port;
trd->Data = NewFifoFast();
return trd;
}
// Release of the UDP packet
void FreeUdpPacket(UDPPACKET *p)
{
// Validate arguments
if (p == NULL)
{
return;
}
Free(p->Data);
Free(p);
}
// Create a new UDP packet
UDPPACKET *NewUdpPacket(IP *src_ip, UINT src_port, IP *dst_ip, UINT dst_port, void *data, UINT size)
{
UDPPACKET *p;
// Validate arguments
if (data == NULL || size == 0 || dst_ip == NULL || dst_port == 0)
{
return NULL;
}
p = ZeroMalloc(sizeof(UDPPACKET));
p->Data = data;
p->Size = size;
Copy(&p->SrcIP, src_ip, sizeof(IP));
p->SrcPort = src_port;
Copy(&p->DstIP, dst_ip, sizeof(IP));
p->DestPort = dst_port;
return p;
}
// Transmit the packets via UDP Listener
void UdpListenerSendPackets(UDPLISTENER *u, LIST *packet_list)
{
UINT num = 0;
// Validate arguments
if (u == NULL || packet_list == NULL)
{
return;
}
LockList(u->SendPacketList);
{
UINT i;
num = LIST_NUM(packet_list);
for (i = 0; i < LIST_NUM(packet_list); i++)
{
UDPPACKET *p = LIST_DATA(packet_list, i);
Add(u->SendPacketList, p);
}
}
UnlockList(u->SendPacketList);
if (num >= 1)
{
SetSockEvent(u->Event);
}
}
// Creating a UDP listener
UDPLISTENER *NewUdpListener(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip)
{
return NewUdpListenerEx(recv_proc, param, listen_ip, INFINITE);
}
UDPLISTENER *NewUdpListenerEx(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip, UINT packet_type)
{
UDPLISTENER *u;
// Validate arguments
if (recv_proc == NULL)
{
return NULL;
}
u = ZeroMalloc(sizeof(UDPLISTENER));
u->Param = param;
u->PacketType = packet_type;
u->PortList = NewList(NULL);
u->Event = NewSockEvent();
if (listen_ip)
{
Copy(&u->ListenIP, listen_ip, sizeof(IP));
}
u->RecvProc = recv_proc;
u->SendPacketList = NewList(NULL);
u->Interrupts = NewInterruptManager();
u->Thread = NewThread(UdpListenerThread, u);
return u;
}
// Stop the UDP listener
void StopUdpListener(UDPLISTENER *u)
{
if (u == NULL)
{
return;
}
u->Halt = true;
SetSockEvent(u->Event);
WaitThread(u->Thread, INFINITE);
}
// Release the UDP listener
void FreeUdpListener(UDPLISTENER *u)
{
UINT i;
// Validate arguments
if (u == NULL)
{
return;
}
StopUdpListener(u);
ReleaseThread(u->Thread);
ReleaseSockEvent(u->Event);
ReleaseIntList(u->PortList);
for (i = 0; i < LIST_NUM(u->SendPacketList); i++)
{
UDPPACKET *p = LIST_DATA(u->SendPacketList, i);
FreeUdpPacket(p);
}
ReleaseList(u->SendPacketList);
FreeInterruptManager(u->Interrupts);
Free(u);
}
// Add the UDP port
void AddPortToUdpListener(UDPLISTENER *u, UINT port)
{
// Validate arguments
if (u == NULL || port == 0)
{
return;
}
LockList(u->PortList);
{
AddIntDistinct(u->PortList, port);
}
UnlockList(u->PortList);
SetSockEvent(u->Event);
}
// Delete all the UDP ports
void DeleteAllPortFromUdpListener(UDPLISTENER *u)
{
// Validate arguments
if (u == NULL)
{
return;
}
LockList(u->PortList);
{
UINT num_ports = LIST_NUM(u->PortList);
UINT *ports = ZeroMalloc(sizeof(UINT) * num_ports);
UINT i;
for (i = 0; i < num_ports; i++)
{
ports[i] = *((UINT *)(LIST_DATA(u->PortList, i)));
}
for (i = 0; i < num_ports; i++)
{
UINT port = ports[i];
DelInt(u->PortList, port);
}
Free(ports);
}
UnlockList(u->PortList);
SetSockEvent(u->Event);
}
// Delete the UDP port
void DeletePortFromUdpListener(UDPLISTENER *u, UINT port)
{
// Validate arguments
if (u == NULL || port == 0)
{
return;
}
LockList(u->PortList);
{
DelInt(u->PortList, port);
}
UnlockList(u->PortList);
SetSockEvent(u->Event);
}
// Sort function of the interrupt management list
int CmpInterruptManagerTickList(void *p1, void *p2)
{
UINT64 *v1, *v2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
v1 = *(UINT64 **)p1;
v2 = *(UINT64 **)p2;
if (v1 == NULL || v2 == NULL)
{
return 0;
}
if (*v1 > *v2)
{
return 1;
}
else if (*v1 < *v2)
{
return -1;
}
else
{
return 0;
}
}
// Initialization of the interrupt management
INTERRUPT_MANAGER *NewInterruptManager()
{
INTERRUPT_MANAGER *m = ZeroMalloc(sizeof(INTERRUPT_MANAGER));
m->TickList = NewList(CmpInterruptManagerTickList);
return m;
}
// Release of the interrupt management
void FreeInterruptManager(INTERRUPT_MANAGER *m)
{
UINT i;
// Validate arguments
if (m == NULL)
{
return;
}
for (i = 0; i < LIST_NUM(m->TickList); i++)
{
UINT64 *v = LIST_DATA(m->TickList, i);
Free(v);
}
ReleaseList(m->TickList);
Free(m);
}
// Add a number to the interrupt management
void AddInterrupt(INTERRUPT_MANAGER *m, UINT64 tick)
{
// Validate arguments
if (tick == 0)
{
return;
}
LockList(m->TickList);
{
if (Search(m->TickList, &tick) == NULL)
{
Insert(m->TickList, Clone(&tick, sizeof(UINT64)));
}
}
UnlockList(m->TickList);
}
// Get the interval to the next calling
UINT GetNextIntervalForInterrupt(INTERRUPT_MANAGER *m)
{
UINT ret = INFINITE;
UINT i;
LIST *o = NULL;
UINT64 now = Tick64();
// Validate arguments
if (m == NULL)
{
return 0;
}
LockList(m->TickList);
{
// Remove entries older than now already
for (i = 0; i < LIST_NUM(m->TickList); i++)
{
UINT64 *v = LIST_DATA(m->TickList, i);
if (now >= *v)
{
ret = 0;
if (o == NULL)
{
o = NewListFast(NULL);
}
Add(o, v);
}
else
{
break;
}
}
for (i = 0; i < LIST_NUM(o); i++)
{
UINT64 *v = LIST_DATA(o, i);
Free(v);
Delete(m->TickList, v);
}
if (o != NULL)
{
ReleaseList(o);
}
if (ret == INFINITE)
{
if (LIST_NUM(m->TickList) >= 1)
{
UINT64 *v = LIST_DATA(m->TickList, 0);
ret = (UINT)(*v - now);
}
}
}
UnlockList(m->TickList);
return ret;
}
// Let that the listening socket for the reverse socket to accept the new socket
void InjectNewReverseSocketToAccept(SOCK *listen_sock, SOCK *s, IP *client_ip, UINT client_port)
{
bool ok = false;
// Validate arguments
if (listen_sock == NULL || s == NULL)
{
return;
}
LockQueue(listen_sock->ReverseAcceptQueue);
{
if (listen_sock->CancelAccept == false && listen_sock->Disconnecting == false)
{
InsertQueue(listen_sock->ReverseAcceptQueue, s);
ok = true;
s->ServerMode = true;
s->IsReverseAcceptedSocket = true;
Copy(&s->RemoteIP, client_ip, sizeof(IP));
s->RemotePort = client_port;
}
}
UnlockQueue(listen_sock->ReverseAcceptQueue);
if (ok == false)
{
Disconnect(s);
ReleaseSock(s);
}
else
{
Set(listen_sock->ReverseAcceptEvent);
}
}
// Create a listening socket for the reverse socket
SOCK *ListenReverse()
{
SOCK *s = NewSock();
s->Type = SOCK_REVERSE_LISTEN;
s->ListenMode = true;
s->ReverseAcceptQueue = NewQueue();
s->ReverseAcceptEvent = NewEvent();
s->Connected = true;
return s;
}
// Accept on the reverse socket
SOCK *AcceptReverse(SOCK *s)
{
// Validate arguments
if (s == NULL || s->Type != SOCK_REVERSE_LISTEN || s->ListenMode == false)
{
return NULL;
}
while (true)
{
SOCK *ret;
if (s->Disconnecting || s->CancelAccept)
{
return NULL;
}
LockQueue(s->ReverseAcceptQueue);
{
ret = GetNext(s->ReverseAcceptQueue);
}
UnlockQueue(s->ReverseAcceptQueue);
if (ret != NULL)
{
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_AZURE);
AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "VPN Azure");
return ret;
}
Wait(s->ReverseAcceptEvent, INFINITE);
}
}
// Start listening on the in-process socket
SOCK *ListenInProc()
{
SOCK *s = NewSock();
s->Type = SOCK_INPROC;
s->ListenMode = true;
s->InProcAcceptQueue = NewQueue();
s->InProcAcceptEvent = NewEvent();
s->Connected = true;
return s;
}
// Accept at the in-process socket
SOCK *AcceptInProc(SOCK *s)
{
// Validate arguments
if (s == NULL || s->Type != SOCK_INPROC || s->ListenMode == false)
{
return NULL;
}
while (true)
{
SOCK *ret;
if (s->Disconnecting || s->CancelAccept)
{
return NULL;
}
LockQueue(s->InProcAcceptQueue);
{
ret = GetNext(s->InProcAcceptQueue);
}
UnlockQueue(s->InProcAcceptQueue);
if (ret != NULL)
{
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_INPROC);
AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "InProc");
return ret;
}
Wait(s->InProcAcceptEvent, INFINITE);
}
}
// Connect by the in-process socket
SOCK *ConnectInProc(SOCK *listen_sock, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port)
{
SOCK *ss, *sc;
bool ok = false;
// Validate arguments
if (listen_sock == NULL || listen_sock->Type != SOCK_INPROC || listen_sock->ListenMode == false)
{
return NULL;
}
NewSocketPair(&sc, &ss, client_ip, client_port, server_ip, server_port);
LockQueue(listen_sock->InProcAcceptQueue);
{
if (listen_sock->CancelAccept == false && listen_sock->Disconnecting == false)
{
InsertQueue(listen_sock->InProcAcceptQueue, ss);
ok = true;
}
}
UnlockQueue(listen_sock->InProcAcceptQueue);
if (ok == false)
{
ReleaseSock(ss);
ReleaseSock(sc);
return NULL;
}
Set(listen_sock->InProcAcceptEvent);
return sc;
}
// Creating a new socket pair
void NewSocketPair(SOCK **client, SOCK **server, IP *client_ip, UINT client_port, IP *server_ip, UINT server_port)
{
IP iptmp;
TUBE *t1, *t2;
SOCK *sc, *ss;
SOCK_EVENT *e1, *e2;
// Validate arguments
if (client == NULL || server == NULL)
{
return;
}
SetIP(&iptmp, 127, 0, 0, 1);
if (client_ip == NULL)
{
client_ip = &iptmp;
}
if (server_ip == NULL)
{
server_ip = &iptmp;
}
// Creating a tube
NewTubePair(&t1, &t2, 0); // t1: C -> S, t2: S -> C
// Creating a socket event
e1 = NewSockEvent();
e2 = NewSockEvent();
SetTubeSockEvent(t1, e1);
SetTubeSockEvent(t2, e2);
sc = NewInProcSocket(t1, t2);
ss = NewInProcSocket(t2, t1);
Copy(&sc->LocalIP, client_ip, sizeof(IP));
sc->LocalPort = client_port;
Copy(&sc->RemoteIP, server_ip, sizeof(IP));
sc->RemotePort = server_port;
Copy(&ss->LocalIP, server_ip, sizeof(IP));
ss->LocalPort = server_port;
Copy(&ss->RemoteIP, client_ip, sizeof(IP));
ss->RemotePort = client_port;
sc->Connected = true;
sc->ServerMode = false;
ss->Connected = true;
ss->ServerMode = true;
SetTimeout(sc, INFINITE);
SetTimeout(ss, INFINITE);
QuerySocketInformation(sc);
QuerySocketInformation(ss);
ReleaseSockEvent(e1);
ReleaseSockEvent(e2);
ReleaseTube(t1);
ReleaseTube(t2);
*client = sc;
*server = ss;
}
// Creating a new in-process socket
SOCK *NewInProcSocket(TUBE *tube_send, TUBE *tube_recv)
{
SOCK *s;
// Validate arguments
if (tube_recv == NULL || tube_send == NULL)
{
return NULL;
}
s = NewSock();
s->Type = SOCK_INPROC;
s->SendTube = tube_send;
s->RecvTube = tube_recv;
AddRef(tube_send->Ref);
AddRef(tube_recv->Ref);
s->InProcRecvFifo = NewFifo();
s->Connected = true;
return s;
}
// Transmission process for the in-process socket
UINT SendInProc(SOCK *sock, void *data, UINT size)
{
if (sock == NULL || sock->Type != SOCK_INPROC || sock->Disconnecting || sock->Connected == false)
{
return 0;
}
if (IsTubeConnected(sock->SendTube) == false)
{
return 0;
}
if (TubeSend(sock->SendTube, data, size, NULL) == false)
{
return 0;
}
return size;
}
// Receiving process for the in-process socket
UINT RecvInProc(SOCK *sock, void *data, UINT size)
{
FIFO *f;
UINT ret;
UINT timeout;
UINT64 giveup_time;
TUBEDATA *d = NULL;
if (sock == NULL || sock->Type != SOCK_INPROC || sock->Disconnecting || sock->Connected == false)
{
return 0;
}
if (IsTubeConnected(sock->SendTube) == false)
{
return 0;
}
f = sock->InProcRecvFifo;
if (f == NULL)
{
return 0;
}
// If there is data in the FIFO, return it immediately
ret = ReadFifo(f, data, size);
if (ret != 0)
{
return ret;
}
timeout = GetTimeout(sock);
giveup_time = Tick64() + (UINT)timeout;
// When there is no data in the FIFO, read the next data from the tube
d = NULL;
while (true)
{
UINT64 now = 0;
UINT interval;
if (sock->AsyncMode == false)
{
now = Tick64();
if (now >= giveup_time)
{
break;
}
}
d = TubeRecvAsync(sock->RecvTube);
if (d != NULL)
{
break;
}
if (IsTubeConnected(sock->RecvTube) == false)
{
break;
}
if (sock->AsyncMode)
{
break;
}
interval = (UINT)(giveup_time - now);
Wait(sock->RecvTube->Event, interval);
}
if (d == NULL)
{
if (IsTubeConnected(sock->RecvTube) == false)
{
return 0;
}
if (sock->AsyncMode == false)
{
// If a timeout occurs in synchronous mode, disconnect ir
Disconnect(sock);
return 0;
}
else
{
// If a timeout occurs in asynchronous mode, returns the blocking error
return SOCK_LATER;
}
}
else
{
// If the received data is larger than the requested size, write the rest to FIFO
if (d->DataSize > size)
{
WriteFifo(f, ((UCHAR *)d->Data) + size, d->DataSize - size);
ret = size;
}
else
{
ret = d->DataSize;
}
Copy(data, d->Data, ret);
FreeTubeData(d);
return ret;
}
}
// Wait for the arrival of data on multiple tubes
void WaitForTubes(TUBE **tubes, UINT num, UINT timeout)
{
// Validate arguments
if (num != 0 && tubes == NULL)
{
return;
}
if (timeout == 0)
{
return;
}
if (num == 0)
{
SleepThread(timeout);
return;
}
#ifdef OS_WIN32
Win32WaitForTubes(tubes, num, timeout);
#else // OS_WIN32
UnixWaitForTubes(tubes, num, timeout);
#endif // OS_WIN32
}
#ifdef OS_WIN32
void Win32WaitForTubes(TUBE **tubes, UINT num, UINT timeout)
{
HANDLE array[MAXIMUM_WAIT_OBJECTS];
UINT i;
Zero(array, sizeof(array));
for (i = 0; i < num; i++)
{
TUBE *t = tubes[i];
array[i] = t->Event->pData;
}
if (num == 1)
{
WaitForSingleObject(array[0], timeout);
}
else
{
WaitForMultipleObjects(num, array, false, timeout);
}
}
#else // OS_WIN32
void UnixWaitForTubes(TUBE **tubes, UINT num, UINT timeout)
{
int *fds;
UINT i;
char tmp[MAX_SIZE];
bool any_of_tubes_are_readable = false;
fds = ZeroMalloc(sizeof(int) * num);
for (i = 0; i < num; i++)
{
fds[i] = tubes[i]->SockEvent->pipe_read;
if (tubes[i]->SockEvent->current_pipe_data != 0)
{
any_of_tubes_are_readable = true;
}
}
if (any_of_tubes_are_readable == false)
{
UnixSelectInner(num, fds, 0, NULL, timeout);
}
for (i = 0; i < num; i++)
{
int fd = fds[i];
int readret;
tubes[i]->SockEvent->current_pipe_data = 0;
do
{
readret = read(fd, tmp, sizeof(tmp));
}
while (readret >= 1);
}
Free(fds);
}
#endif // OS_WIN32
// Creating a Tube Flush List
TUBE_FLUSH_LIST *NewTubeFlushList()
{
TUBE_FLUSH_LIST *f = ZeroMalloc(sizeof(TUBE_FLUSH_LIST));
f->List = NewListFast(NULL);
return f;
}
// Release of the Tube Flush List
void FreeTubeFlushList(TUBE_FLUSH_LIST *f)
{
UINT i;
// Validate arguments
if (f == NULL)
{
return;
}
for (i = 0; i < LIST_NUM(f->List); i++)
{
TUBE *t = LIST_DATA(f->List, i);
ReleaseTube(t);
}
ReleaseList(f->List);
Free(f);
}
// Add a Tube to the Tube Flush List
void AddTubeToFlushList(TUBE_FLUSH_LIST *f, TUBE *t)
{
// Validate arguments
if (f == NULL || t == NULL)
{
return;
}
if (t->IsInFlushList)
{
return;
}
if (IsInList(f->List, t) == false)
{
Add(f->List, t);
AddRef(t->Ref);
t->IsInFlushList = true;
}
}
// Flush the all tubes in the Tube Flush List
void FlushTubeFlushList(TUBE_FLUSH_LIST *f)
{
UINT i;
// Validate arguments
if (f == NULL)
{
return;
}
for (i = 0; i < LIST_NUM(f->List); i++)
{
TUBE *t = LIST_DATA(f->List, i);
TubeFlush(t);
t->IsInFlushList = false;
ReleaseTube(t);
}
DeleteAll(f->List);
}
// Store the error value into PACK
PACK *PackError(UINT error)
{
PACK *p;
p = NewPack();
PackAddInt(p, "error", error);
return p;
}
// Get the error value from PACK
UINT GetErrorFromPack(PACK *p)
{
// Validate arguments
if (p == NULL)
{
return 0;
}
return PackGetInt(p, "error");
}
// Create an entry to PACK for the dummy
void CreateDummyValue(PACK *p)
{
UINT size;
UCHAR *buf;
// Validate arguments
if (p == NULL)
{
return;
}
size = Rand32() % HTTP_PACK_RAND_SIZE_MAX;
buf = Malloc(size);
Rand(buf, size);
PackAddData(p, "pencore", buf, size);
Free(buf);
}
// Receive a line
char *RecvLine(SOCK *s, UINT max_size)
{
BUF *b;
char c;
char *str;
// Validate arguments
if (s == NULL || max_size == 0)
{
return NULL;
}
b = NewBuf();
while (true)
{
UCHAR *buf;
if (RecvAll(s, &c, sizeof(c), s->SecureMode) == false)
{
FreeBuf(b);
return NULL;
}
WriteBuf(b, &c, sizeof(c));
buf = (UCHAR *)b->Buf;
if (b->Size > max_size)
{
FreeBuf(b);
return NULL;
}
if (b->Size >= 1)
{
if (buf[b->Size - 1] == '\n')
{
b->Size--;
if (b->Size >= 1)
{
if (buf[b->Size - 1] == '\r')
{
b->Size--;
}
}
str = Malloc(b->Size + 1);
Copy(str, b->Buf, b->Size);
str[b->Size] = 0;
FreeBuf(b);
return str;
}
}
}
}
// Receive a PACK
PACK *RecvPack(SOCK *s)
{
PACK *p;
BUF *b;
void *data;
UINT sz;
// Validate arguments
if (s == NULL || s->Type != SOCK_TCP)
{
return false;
}
if (RecvAll(s, &sz, sizeof(UINT), s->SecureMode) == false)
{
return false;
}
sz = Endian32(sz);
if (sz > MAX_PACK_SIZE)
{
return false;
}
data = MallocEx(sz, true);
if (RecvAll(s, data, sz, s->SecureMode) == false)
{
Free(data);
return false;
}
b = NewBuf();
WriteBuf(b, data, sz);
SeekBuf(b, 0, 0);
p = BufToPack(b);
FreeBuf(b);
Free(data);
return p;
}
// Receive a PACK (with checking the hash)
PACK *RecvPackWithHash(SOCK *s)
{
PACK *p;
BUF *b;
void *data;
UINT sz;
UCHAR hash1[SHA1_SIZE];
UCHAR hash2[SHA1_SIZE];
// Validate arguments
if (s == NULL || s->Type != SOCK_TCP)
{
return false;
}
if (RecvAll(s, &sz, sizeof(UINT), s->SecureMode) == false)
{
return false;
}
sz = Endian32(sz);
if (sz > MAX_PACK_SIZE)
{
return false;
}
data = MallocEx(sz, true);
if (RecvAll(s, data, sz, s->SecureMode) == false)
{
Free(data);
return false;
}
Sha1(hash1, data, sz);
if (RecvAll(s, hash2, sizeof(hash2), s->SecureMode) == false)
{
Free(data);
return false;
}
if (Cmp(hash1, hash2, SHA1_SIZE) != 0)
{
Free(data);
return false;
}
b = NewBuf();
WriteBuf(b, data, sz);
SeekBuf(b, 0, 0);
p = BufToPack(b);
FreeBuf(b);
Free(data);
return p;
}
// Send a PACK
bool SendPack(SOCK *s, PACK *p)
{
BUF *b;
UINT sz;
// Validate arguments
if (s == NULL || p == NULL || s->Type != SOCK_TCP)
{
return false;
}
b = PackToBuf(p);
sz = Endian32(b->Size);
SendAdd(s, &sz, sizeof(UINT));
SendAdd(s, b->Buf, b->Size);
FreeBuf(b);
return SendNow(s, s->SecureMode);
}
// Send a Pack (with adding a hash)
bool SendPackWithHash(SOCK *s, PACK *p)
{
BUF *b;
UINT sz;
UCHAR hash[SHA1_SIZE];
// Validate arguments
if (s == NULL || p == NULL || s->Type != SOCK_TCP)
{
return false;
}
b = PackToBuf(p);
sz = Endian32(b->Size);
SendAdd(s, &sz, sizeof(UINT));
SendAdd(s, b->Buf, b->Size);
Sha1(hash, b->Buf, b->Size);
SendAdd(s, hash, sizeof(hash));
FreeBuf(b);
return SendNow(s, s->SecureMode);
}
// Get SNI name from the SSL packet
bool GetSniNameFromSslPacket(UCHAR *packet_buf, UINT packet_size, char *sni, UINT sni_size)
{
BUF *buf;
bool ret = false;
UCHAR content_type;
USHORT version;
USHORT handshake_length;
// Validate arguments
if (packet_buf == NULL || packet_size <= 11)
{
return false;
}
if (!(packet_buf[0] == 0x16 && packet_buf[1] >= 0x03 &&
packet_buf[5] == 0x01 && packet_buf[6] == 0x00 &&
packet_buf[9] >= 0x03))
{
return false;
}
buf = NewBufFromMemory(packet_buf, packet_size);
if (ReadBuf(buf, &content_type, sizeof(UCHAR)) == sizeof(UCHAR) &&
ReadBuf(buf, &version, sizeof(USHORT)) == sizeof(USHORT) &&
ReadBuf(buf, &handshake_length, sizeof(USHORT)) == sizeof(USHORT))
{
version = Endian16(version);
handshake_length = Endian16(handshake_length);
if (content_type == 0x16 && version >= 0x0301)
{
UCHAR *handshake_data = Malloc(handshake_length);
if (ReadBuf(buf, handshake_data, handshake_length) == handshake_length)
{
BUF *buf2 = NewBufFromMemory(handshake_data, handshake_length);
USHORT handshake_type;
USHORT handshake_length_2;
if (ReadBuf(buf2, &handshake_type, sizeof(USHORT)) == sizeof(USHORT) &&
ReadBuf(buf2, &handshake_length_2, sizeof(USHORT)) == sizeof(USHORT))
{
handshake_type = Endian16(handshake_type);
handshake_length_2 = Endian16(handshake_length_2);
if (handshake_type == 0x0100 && handshake_length_2 <= (handshake_length - 4))
{
USHORT version2;
if (ReadBuf(buf2, &version2, sizeof(USHORT)) == sizeof(USHORT))
{
version2 = Endian16(version2);
if (version2 >= 0x0301)
{
UCHAR rand[32];
if (ReadBuf(buf2, rand, sizeof(rand)) == sizeof(rand))
{
UCHAR session_id_len;
if (ReadBuf(buf2, &session_id_len, sizeof(UCHAR)) == sizeof(UCHAR))
{
if (ReadBuf(buf2, NULL, session_id_len) == session_id_len)
{
USHORT cipher_len;
if (ReadBuf(buf2, &cipher_len, sizeof(USHORT)) == sizeof(USHORT))
{
cipher_len = Endian16(cipher_len);
if (ReadBuf(buf2, NULL, cipher_len) == cipher_len)
{
UCHAR comps_len;
if (ReadBuf(buf2, &comps_len, sizeof(UCHAR)) == sizeof(UCHAR))
{
if (ReadBuf(buf2, NULL, comps_len) == comps_len)
{
USHORT ext_length;
if (ReadBuf(buf2, &ext_length, sizeof(USHORT)) == sizeof(USHORT))
{
UCHAR *ext_buf;
ext_length = Endian16(ext_length);
ext_buf = Malloc(ext_length);
if (ReadBuf(buf2, ext_buf, ext_length) == ext_length)
{
BUF *ebuf = NewBufFromMemory(ext_buf, ext_length);
while (ret == false)
{
USHORT type;
USHORT data_len;
UCHAR *data;
if (ReadBuf(ebuf, &type, sizeof(USHORT)) != sizeof(USHORT))
{
break;
}
if (ReadBuf(ebuf, &data_len, sizeof(USHORT)) != sizeof(USHORT))
{
break;
}
type = Endian16(type);
data_len = Endian16(data_len);
data = Malloc(data_len);
if (ReadBuf(ebuf, data, data_len) != data_len)
{
Free(data);
break;
}
if (type == 0x0000)
{
BUF *dbuf = NewBufFromMemory(data, data_len);
USHORT total_len;
if (ReadBuf(dbuf, &total_len, sizeof(USHORT)) == sizeof(USHORT))
{
UCHAR c;
total_len = Endian16(total_len);
if (ReadBuf(dbuf, &c, sizeof(UCHAR)) == sizeof(UCHAR))
{
if (c == 0)
{
USHORT name_len;
if (ReadBuf(dbuf, &name_len, sizeof(USHORT)) == sizeof(USHORT))
{
char *name_buf;
name_len = Endian16(name_len);
name_buf = ZeroMalloc(name_len + 1);
if (ReadBuf(dbuf, name_buf, name_len) == name_len)
{
if (StrLen(name_buf) >= 1)
{
ret = true;
StrCpy(sni, sni_size, name_buf);
}
}
Free(name_buf);
}
}
}
}
FreeBuf(dbuf);
}
Free(data);
}
FreeBuf(ebuf);
}
Free(ext_buf);
}
}
}
}
}
}
}
}
}
}
}
}
FreeBuf(buf2);
}
Free(handshake_data);
}
}
FreeBuf(buf);
if (ret)
{
Trim(sni);
if (IsEmptyStr(sni))
{
ret = false;
}
}
return ret;
}
void SetDhParam(DH_CTX *dh)
{
if (dh_param)
{
DhFree(dh_param);
}
dh_param = dh;
}