From 749497dde0a1dd08c434a73b9d4e93dc3e3326d9 Mon Sep 17 00:00:00 2001 From: dnobori Date: Sat, 4 Jan 2014 22:00:08 +0900 Subject: v4.03-9408-rtm --- src/Mayaqua/Network.c | 21774 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 21774 insertions(+) create mode 100644 src/Mayaqua/Network.c (limited to 'src/Mayaqua/Network.c') diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c new file mode 100644 index 00000000..3025c5e3 --- /dev/null +++ b/src/Mayaqua/Network.c @@ -0,0 +1,21774 @@ +// SoftEther VPN Source Code +// Mayaqua Kernel +// +// SoftEther VPN Server, Client and Bridge are free software under GPLv2. +// +// Copyright (c) 2012-2014 Daiyuu Nobori. +// Copyright (c) 2012-2014 SoftEther VPN Project, University of Tsukuba, Japan. +// Copyright (c) 2012-2014 SoftEther Corporation. +// +// All Rights Reserved. +// +// http://www.softether.org/ +// +// Author: Daiyuu Nobori +// Comments: Tetsuo Sugiyama, Ph.D. +// +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License version 2 +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE +// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE. +// +// +// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, +// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, +// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS +// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS +// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER +// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL +// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING, +// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR +// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND +// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO +// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, +// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION +// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN +// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE. +// +// USE ONLY IN JAPAN. DO NOT USE IT IN OTHER COUNTRIES. IMPORTING THIS +// SOFTWARE INTO OTHER COUNTRIES IS AT YOUR OWN RISK. SOME COUNTRIES +// PROHIBIT ENCRYPTED COMMUNICATIONS. USING THIS SOFTWARE IN OTHER +// COUNTRIES MIGHT BE RESTRICTED. +// +// +// DEAR SECURITY EXPERTS +// --------------------- +// +// If you find a bug or a security vulnerability please kindly inform us +// about the problem immediately so that we can fix the security problem +// to protect a lot of users around the world as soon as possible. +// +// Our e-mail address for security reports is: +// softether-vpn-security [at] softether.org +// +// Please note that the above e-mail address is not a technical support +// inquiry address. If you need technical assistance, please visit +// http://www.softether.org/ and ask your question on the users forum. +// +// Thank you for your cooperation. + + +// Network.c +// Network communication module + +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#endif // WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) +#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_404_str[] = "\r\n\r\n404 Not Found\r\n\r\n

Not Found

\r\nThe requested URL $TARGET$ was not found on this server.

\r\n


\r\n
HTTP Server at $HOST$ Port $PORT$
\r\n\r\n"; +static char http_403_str[] = "\r\n\r\n403 Forbidden\r\n\r\n

Forbidden

\r\nYou don't have permission to access $TARGET$\r\non this server.

\r\n


\r\n
HTTP Server at $HOST$ Port $PORT$
\r\n\r\n"; +static char http_500_str[] = "\r\n\r\n500 Server Error\r\n\r\n

Server Error

\r\nServer Error

\r\n


\r\n
HTTP Server at $HOST$ Port $PORT$
\r\n\r\n"; +static char http_501_str[] = "\r\n\r\n501 Method Not Implemented\r\n\r\n

Method Not Implemented

\r\n$METHOD$ to $TARGET$ not supported.

\r\nInvalid method in request $METHOD$ $TARGET$ $VERSION$

\r\n


\r\n
HTTP Server at $HOST$ Port $PORT$
\r\n\r\n"; +static char http_detect_server_startwith[] = "\r\n\r\n403 Forbidden\r\n\r\n

Forbidden

\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 TOKEN_LIST *cipher_list_token = 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 char *cipher_list = "RC4-MD5 RC4-SHA AES128-SHA AES256-SHA DES-CBC-SHA DES-CBC3-SHA"; +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; + +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; +} + +// Check whether the string in the string list appears in the specified string +bool IsInStrByStrList(char *str, char *str_list) +{ + TOKEN_LIST *t; + bool ret = false; + // Validate arguments + if (str == NULL || str_list == NULL) + { + return false; + } + + t = ParseTokenWithoutNullStr(str_list, ", \t\r\n"); + if (t != NULL) + { + UINT i; + + for (i = 0;i < t->NumTokens;i++) + { + char *s = t->Token[i]; + + if (StrLen(s) >= 1) + { + if (InStrEx(str, s, true)) + { + ret = true; + break; + } + } + } + } + + FreeToken(t); + + return ret; +} + + +// Search whether the IP address exists on the IP address list string +bool IsIpInStrList(IP *ip, char *ip_list) +{ + char ip_str[128]; + TOKEN_LIST *t; + bool ret = false; + // Validate arguments + if (ip == NULL || ip_list == NULL) + { + return false; + } + + Zero(ip_str, sizeof(ip_str)); + IPToStr(ip_str, sizeof(ip_str), ip); + + t = ParseTokenWithoutNullStr(ip_list, ", \t\r\n"); + + if (t != NULL) + { + UINT i; + + for (i = 0;i < t->NumTokens;i++) + { + char *s = t->Token[i]; + + if (StrCmpi(s, ip_str) == 0) + { + ret = true; + break; + } + } + } + + FreeToken(t); + + return ret; +} + + +// Disable NAT-T function globally +void DisableRDUPServerGlobally() +{ + g_no_rudp_server = true; +} + +// Disable NAT-T registration globally +void DisableRUDPRegisterGlobally() +{ + g_no_rudp_register = true; +} + +// Lower the priority of the host at NAT-T +void SetNatTLowPriority() +{ + g_natt_low_priority = true; +} + +// Extract only the host name part from FQDN +void GetSimpleHostname(char *hostname, UINT hostname_size, char *fqdn) +{ + UINT i; + ClearStr(hostname, hostname_size); + // Validate arguments + if (hostname == NULL || fqdn == NULL) + { + return; + } + + StrCpy(hostname, hostname_size, fqdn); + Trim(hostname); + + i = SearchStrEx(hostname, ".", 0, true); + if (i != INFINITE) + { + hostname[i] = 0; + } +} + +// 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 IsUseDnsProxy(); +} + +#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); + + HashSha1(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 IsMacAddressLocal(void *addr) +{ + LIST *o; + bool ret; + // Validate arguments + if (addr == NULL) + { + return false; + } + + o = GetNicList(); + + ret = IsMacAddressLocalInner(o, addr); + + FreeNicList(o); + + return ret; +} +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(); +#endif // OS_WIN32 + +#ifdef UNIX_LINUX +#endif // UNIX_LINUX + + if (o == NULL) + { + return NewListFast(NULL); + } + + return o; +} + +#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, 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, true, 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) + { + // 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); + } + } + 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)); + + // 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) + { + 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; + + 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 (IsZeroIp(&r->NatT_IP_Safe) == false) + { + if (g_no_rudp_register == 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 (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) + { + 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", CEDAR_PRODUCT_STR); + 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 && 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 + { + 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; + } + } + } + } + } +} + +// 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 buf_size; + UINT padding_size; + UINT i; + CRYPT *c; + UCHAR crypt_key_src[SHA1_SIZE * 2]; + UCHAR crypt_key[SHA1_SIZE]; + UINT icmp_type; + UCHAR sign[SHA1_SIZE]; + UCHAR iv[SHA1_SIZE + 1]; + // Validate arguments + if (r == NULL || se == NULL || (data == NULL && data_size != 0)) + { + return; + } + + 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); + HashSha1(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); + HashSha1(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); +} +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) +{ + 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); + 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); + break; + + case RUDP_PROTOCOL_DNS: + StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_DNS); + break; + + case RUDP_PROTOCOL_ICMP: + StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_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); + HashSha1(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->UseHMac == false) + { + Copy(sign, p, SHA1_SIZE); + Copy(p, se->BulkRecvKey->Data, SHA1_SIZE); + HashSha1(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 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; + 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; + } + + // Validate the signature + if (se->UseHMac == false) + { + Copy(sign, p, SHA1_SIZE); + Copy(p, se->BulkRecvKey->Data, SHA1_SIZE); + HashSha1(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 + { + } + } + 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); + HashSha1(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); + HashSha1(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); + HashSha1(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); + HashSha1(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 + HashSha1(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[SHA1_SIZE]; + UCHAR bulk_recv_key[SHA1_SIZE]; + 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"); + HashSha1(key1, b->Buf, b->Size); + FreeBuf(b); + + b = NewBuf(); + WriteBuf(b, init_key, SHA1_SIZE); + WriteBuf(b, key1, SHA1_SIZE); + WriteBufStr(b, "yasushineko"); + HashSha1(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"); + HashSha1(se->Magic_KeepAliveRequest, b->Buf, b->Size); + FreeBuf(b); + b = NewBuf(); + WriteBuf(b, init_key, SHA1_SIZE); + WriteBufStr(b, "Magic_KeepAliveResponse"); + HashSha1(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)); + 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]; + + HashSha1(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 + HashSha1(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]; + + HashSha1(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[0], tmp[1], 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; + // 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)) + { + next_getip_tick = now + (UINT64)UDP_NAT_T_GET_IP_INTERVAL; + } + 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)) + { + 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) +{ + SOCK *s; + IP t; + char *hostname = UDP_NAT_T_GET_PRIVATE_IP_TCP_SERVER; + // Validate arguments + if (ip == NULL) + { + return false; + } + + if (IsUseAlternativeHostname()) + { + hostname = UDP_NAT_T_GET_PRIVATE_IP_TCP_SERVER_ALT; + } + + 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(hostname, UDP_NAT_T_PORT_FOR_TCP_3, 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; +} + +// 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; +} + +// Get the results of the port number that is determined at random +UINT RUDPGetRandPortNumber(UCHAR rand_port_id) +{ + UINT ret; + // Validate arguments + if (rand_port_id == 0) + { + return 0; + } + + ret = rand_port_numbers[rand_port_id]; + + Debug("rand_port_id[%u] = %u\n", rand_port_id, ret); + 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)); + + HashSha1(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; + + 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) + { + // 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); + 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) + { + 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); + 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) +{ + 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); + + 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) +{ + 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 = NewUDP(port); + } + else + { + sock = NewUDPEx2RandMachineAndExePath(false, NULL, 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)); + + 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(); + + StrCpy(tmp, sizeof(tmp), r->SvcName); + Trim(tmp); + StrLower(tmp); + + HashSha1(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); + HashSha1(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; + } + } + + 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) + { + 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); + } + + 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); + + HashSha1(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 *ListenAnyPortEx(bool local_only) +{ + return ListenAnyPortEx2(local_only, false); +} +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; +} + +int cb_test(int a, X509_STORE_CTX *ctx) +{ + WHERE; + return 1; +} + +// Create a new SSL pipe +SSL_PIPE *NewSslPipe(bool server_mode, X *x, K *k, DH_CTX *dh) +{ + SSL_PIPE *s; + SSL *ssl; + SSL_CTX *ssl_ctx = NewSSLCtx(); + + Lock(openssl_lock); + { + if (server_mode) + { + SSL_CTX_set_ssl_version(ssl_ctx, TLSv1_server_method()); + + AddChainSslCertOnDirectory(ssl_ctx); + + if (dh != NULL) + { + SSL_CTX_set_tmp_dh(ssl_ctx, dh->dh); + } + } + else + { + SSL_CTX_set_ssl_version(ssl_ctx, TLSv1_client_method()); + } + + //SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, cb_test); + + if (dh != NULL) + { + SSL_CTX_set_options(ssl_ctx, SSL_OP_SINGLE_DH_USE); + } + + ssl = SSL_new(ssl_ctx); + } + 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; + +// Function for determining whether accept or not in Win32 +int CALLBACK Win32AcceptCheckCallback_Delay(LPWSABUF lpCallerId, LPWSABUF lpCallerData, LPQOS pQos, + LPQOS lpGQOS, LPWSABUF lpCalleeId, LPWSABUF lpCalleeData, + GROUP FAR * g, DWORD_PTR dwCallbackData) +{ + return CF_DEFER; +} + +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_XP(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; + // Validate arguments + if (s == INVALID_SOCKET) + { + return INVALID_SOCKET; + } + +L_LOOP: + + 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(); + + num_error++; + + Debug("!!! WSAAccept Error: %u rej=%u num=%u tick=%I64u\n", WSAGetLastError(), d.Rejected, num_error, Tick64()); + + if (d.Rejected && err == WSAECONNREFUSED) + { + goto L_LOOP; + } + + if (err == WSAETIMEDOUT) + { + goto L_LOOP; + } + } + + return ret; +} + +// 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 + + +// Get whether the aquirement of the Process ID of the TCP connection succeed +bool CanGetTcpProcessId() +{ + UINT i; + bool ret = false; + LIST *o = GetTcpTableList(); + + if (o == NULL) + { + return false; + } + + for (i = 0;i < LIST_NUM(o);i++) + { + TCPTABLE *t = LIST_DATA(o, i); + + if (t->ProcessId != 0) + { + ret = true; + break; + } + } + + FreeTcpTableList(o); + + return ret; +} + + + + +#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 global address from the MAC address +void GenerateEui64GlobalAddress(IP *ip, IP *prefix, IP *subnet, UCHAR *mac) +{ + UCHAR tmp[8]; + IP a; + IP subnet_not; + IP or1, or2; + // Validate arguments + if (ip == NULL || prefix == NULL || subnet == NULL || mac == NULL) + { + return; + } + + GenerateEui64Address6(tmp, mac); + + ZeroIP6(&a); + + Copy(&a.ipv6_addr[8], tmp, 8); + + IPNot6(&subnet_not, subnet); + IPAnd6(&or1, &a, &subnet_not); + IPAnd6(&or2, prefix, subnet); + + IPOr6(ip, &or1, &or2); +} + +// 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; +} + +// Check whether it is a network address prefix +bool IsNetworkAddress6(IP *ip, IP *subnet) +{ + return IsNetworkPrefixAddress6(ip, subnet); +} +bool IsNetworkPrefixAddress6(IP *ip, IP *subnet) +{ + IP host; + // Validate arguments + if (ip == NULL || subnet == NULL) + { + return false; + } + + if (IsIP6(ip) == false || IsIP6(subnet) == false) + { + return false; + } + + GetHostAddress6(&host, ip, subnet); + + if (IsZeroIp(&host)) + { + return true; + } + + return false; +} + +// Check whether the unicast address is available +bool CheckUnicastAddress(IP *ip) +{ + // Validate arguments + if (ip == NULL) + { + return false; + } + + if ((GetIPAddrType6(ip) & IPV6_ADDR_UNICAST) == 0) + { + return false; + } + + return true; +} + +// Get the host address +void GetHostAddress6(IP *dst, IP *ip, IP *subnet) +{ + IP not; + // Validate arguments + if (dst == NULL || ip == NULL || subnet == NULL) + { + return; + } + + IPNot6(¬, subnet); + + IPAnd6(dst, ip, ¬); + + dst->ipv6_scope_id = ip->ipv6_scope_id; +} + +// 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 solicited-node multicast address +void GetSoliciationMulticastAddr6(IP *dst, IP *src) +{ + IP prefix; + IP mask104; + IP or1, or2; + + // Validate arguments + if (dst == NULL || src == NULL) + { + return; + } + + ZeroIP6(&prefix); + prefix.ipv6_addr[0] = 0xff; + prefix.ipv6_addr[1] = 0x02; + prefix.ipv6_addr[11] = 0x01; + prefix.ipv6_addr[12] = 0xff; + + IntToSubnetMask6(&mask104, 104); + + IPAnd6(&or1, &prefix, &mask104); + IPAnd6(&or2, src, &mask104); + + IPOr6(dst, &or1, &or2); + + dst->ipv6_scope_id = src->ipv6_scope_id; +} + +// Generate a MAC address corresponding to the multicast address +void GenerateMulticastMacAddress6(UCHAR *mac, IP *ip) +{ + // Validate arguments + if (mac == NULL) + { + return; + } + + mac[0] = 0x33; + mac[1] = 0x33; + mac[2] = ip->ipv6_addr[12]; + mac[3] = ip->ipv6_addr[13]; + mac[4] = ip->ipv6_addr[14]; + mac[5] = ip->ipv6_addr[15]; +} + +// 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; +} + +// Address that all of the bits are set +void GetAllFilledAddress6(IP *ip) +{ + UINT i; + // Validate arguments + if (ip == NULL) + { + return; + } + + ZeroIP6(ip); + + for (i = 0;i < 15;i++) + { + ip->ipv6_addr[i] = 0xff; + } +} + +// 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 IPNot4(IP *dst, IP *a) +{ + UINT i; + // Validate arguments + if (dst == NULL || a == NULL || IsIP4(a) == false) + { + Zero(dst, sizeof(IP)); + return; + } + + i = IPToUINT(a); + i = ~i; + + UINTToIP(dst, i); +} +void IPOr4(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); +} +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]; + } +} +void IPOr6(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]; + } +} +void IPNot6(IP *dst, IP *a) +{ + UINT i; + // Validate arguments + if (dst == NULL || IsIP6(a) == false) + { + ZeroIP6(dst); + return; + } + + ZeroIP6(dst); + for (i = 0;i < 16;i++) + { + dst->ipv6_addr[i] = ~(a->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 omitable 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 + StrCpy(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] = 223; + ip->addr[1] = 255; + ip->addr[2] = 255; + 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] == 223 && ip->addr[1] == 255 && ip->addr[2] == 255 && ip->addr[3] == 254) + { + return true; + } + + return false; +} +bool IsIP4(IP *ip) +{ + // Validate arguments + if (ip == NULL) + { + return false; + } + + return (IsIP6(ip) ? false : true); +} + +// Examine whether the version of the two IP addresses are same +bool IsSameIPVer(IP *ip1, IP *ip2) +{ + // Validate arguments + if (ip1 == NULL || ip2 == NULL) + { + return false; + } + + if (IsIP4(ip1) && IsIP4(ip2)) + { + return true; + } + + if (IsIP6(ip1) && IsIP6(ip2)) + { + return true; + } + + return false; +} + +// Copy the IP address +void CopyIP(IP *dst, IP *src) +{ + Copy(dst, src, sizeof(IP)); +} + +// Check the length of the IPv6 subnet +bool CheckSubnetLength6(UINT i) +{ + if (i >= 1 && i <= 127) + { + return true; + } + + return false; +} + +// Get the process ID of the corresponding TCP connection by the socket +UINT GetTcpProcessIdFromSocket(SOCK *s) +{ + LIST *o; + TCPTABLE *t; + UINT pid = 0; + // Validate arguments + if (s == NULL) + { + return 0; + } + + o = GetTcpTableList(); + if (o == NULL) + { + return 0; + } + + t = GetTcpTableFromEndPoint(o, &s->LocalIP, s->LocalPort, + &s->RemoteIP, s->RemotePort); + + if (t != NULL) + { + pid = t->ProcessId; + } + + FreeTcpTableList(o); + + return pid; +} +UINT GetTcpProcessIdFromSocketReverse(SOCK *s) +{ + LIST *o; + TCPTABLE *t; + UINT pid = 0; + // Validate arguments + if (s == NULL) + { + return 0; + } + + o = GetTcpTableList(); + if (o == NULL) + { + return 0; + } + + t = GetTcpTableFromEndPoint(o, &s->RemoteIP, s->RemotePort, + &s->LocalIP, s->LocalPort); + + if (t != NULL) + { + pid = t->ProcessId; + } + + FreeTcpTableList(o); + + return pid; +} + +// Search in the TCP table by the end point +TCPTABLE *GetTcpTableFromEndPoint(LIST *o, IP *local_ip, UINT local_port, IP *remote_ip, UINT remote_port) +{ + IP local; + UINT i; + // Validate arguments + if (o == NULL) + { + return NULL; + } + + SetIP(&local, 127, 0, 0, 1); + + if (local_ip == NULL) + { + local_ip = &local; + } + + if (remote_ip == NULL) + { + remote_ip = &local; + } + + for (i = 0;i < LIST_NUM(o);i++) + { + TCPTABLE *t = LIST_DATA(o, i); + + if (t->Status == TCP_STATE_SYN_SENT || t->Status == TCP_STATE_SYN_RCVD || + t->Status == TCP_STATE_ESTAB) + { + if (CmpIpAddr(&t->LocalIP, local_ip) == 0) + { + if (CmpIpAddr(&t->RemoteIP, remote_ip) == 0) + { + if (t->LocalPort == local_port) + { + if (t->RemotePort == remote_port) + { + return t; + } + } + } + } + } + } + + return NULL; +} + +// Get the TCP table list (Win32) +#ifdef OS_WIN32 +LIST *Win32GetTcpTableList() +{ + LIST *o; + + // Windows XP SP2 or later + o = Win32GetTcpTableListByGetExtendedTcpTable(); + if (o != NULL) + { + return o; + } + + // Windows XP or later + o = Win32GetTcpTableListByAllocateAndGetTcpExTableFromStack(); + if (o != NULL) + { + return o; + } + + // For legacy Windows + return Win32GetTcpTableListByGetTcpTable(); +} + +// Get the TCP table list (for Windows XP SP2 or later) +LIST *Win32GetTcpTableListByGetExtendedTcpTable() +{ + UINT need_size; + UINT i; + MIB_TCPTABLE_OWNER_PID *table; + bool ok = false; + LIST *o; + if (w32net->GetExtendedTcpTable == NULL) + { + return NULL; + } + + for (i = 0;i < 128;i++) + { + UINT ret; + table = MallocFast(sizeof(MIB_TCPTABLE_OWNER_PID)); + need_size = sizeof(MIB_TCPTABLE_OWNER_PID); + ret = w32net->GetExtendedTcpTable(table, &need_size, true, AF_INET, _TCP_TABLE_OWNER_PID_ALL, 0); + if (ret == NO_ERROR) + { + ok = true; + break; + } + else + { + Free(table); + if (ret != ERROR_INSUFFICIENT_BUFFER) + { + return NULL; + } + } + + table = MallocFast(need_size); + + ret = w32net->GetExtendedTcpTable(table, &need_size, true, AF_INET, _TCP_TABLE_OWNER_PID_ALL, 0); + if (ret == NO_ERROR) + { + ok = true; + break; + } + else + { + Free(table); + + if (ret != ERROR_INSUFFICIENT_BUFFER) + { + return NULL; + } + } + } + + if (ok == false) + { + return NULL; + } + + o = NewListEx(NULL, true); + + for (i = 0;i < table->dwNumEntries;i++) + { + MIB_TCPROW_OWNER_PID *r = &table->table[i]; + TCPTABLE *t = ZeroMallocFast(sizeof(TCPTABLE)); + + UINTToIP(&t->LocalIP, r->dwLocalAddr); + t->LocalPort = Endian16((USHORT)r->dwLocalPort); + + if (r->dwState != TCP_STATE_LISTEN) + { + UINTToIP(&t->RemoteIP, r->dwRemoteAddr); + t->RemotePort = Endian16((USHORT)r->dwRemotePort); + } + + t->Status = r->dwState; + t->ProcessId = r->dwOwningPid; + + Add(o, t); + } + + Free(table); + + return o; +} + +// Get the TCP table list (Windows XP or later) +LIST *Win32GetTcpTableListByAllocateAndGetTcpExTableFromStack() +{ + HANDLE heap; + UINT i; + MIB_TCPTABLE_OWNER_PID *table; + bool ok = false; + LIST *o; + if (w32net->AllocateAndGetTcpExTableFromStack == NULL) + { + return NULL; + } + + heap = GetProcessHeap(); + + if (w32net->AllocateAndGetTcpExTableFromStack(&table, true, heap, HEAP_GROWABLE, AF_INET) != ERROR_SUCCESS) + { + return NULL; + } + + o = NewListEx(NULL, true); + + for (i = 0;i < table->dwNumEntries;i++) + { + MIB_TCPROW_OWNER_PID *r = &table->table[i]; + TCPTABLE *t = ZeroMallocFast(sizeof(TCPTABLE)); + + UINTToIP(&t->LocalIP, r->dwLocalAddr); + t->LocalPort = Endian16((USHORT)r->dwLocalPort); + + if (r->dwState != TCP_STATE_LISTEN) + { + UINTToIP(&t->RemoteIP, r->dwRemoteAddr); + t->RemotePort = Endian16((USHORT)r->dwRemotePort); + } + + t->ProcessId = r->dwOwningPid; + t->Status = r->dwState; + + Add(o, t); + } + + HeapFree(heap, 0, table); + + return o; +} + +// Get the TCP table list (For legacy Windows) +LIST *Win32GetTcpTableListByGetTcpTable() +{ + UINT need_size; + UINT i; + MIB_TCPTABLE *table; + bool ok = false; + LIST *o; + if (w32net->GetTcpTable == NULL) + { + return NULL; + } + + for (i = 0;i < 128;i++) + { + UINT ret; + table = MallocFast(sizeof(MIB_TCPTABLE)); + need_size = sizeof(MIB_TCPTABLE); + ret = w32net->GetTcpTable(table, &need_size, true); + if (ret == NO_ERROR) + { + ok = true; + break; + } + else + { + Free(table); + if (ret != ERROR_INSUFFICIENT_BUFFER) + { + return NULL; + } + } + + table = MallocFast(need_size); + + ret = w32net->GetTcpTable(table, &need_size, true); + if (ret == NO_ERROR) + { + ok = true; + break; + } + else + { + Free(table); + + if (ret != ERROR_INSUFFICIENT_BUFFER) + { + return NULL; + } + } + } + + if (ok == false) + { + return NULL; + } + + o = NewListEx(NULL, true); + + for (i = 0;i < table->dwNumEntries;i++) + { + MIB_TCPROW *r = &table->table[i]; + TCPTABLE *t = ZeroMallocFast(sizeof(TCPTABLE)); + + UINTToIP(&t->LocalIP, r->dwLocalAddr); + t->LocalPort = Endian16((USHORT)r->dwLocalPort); + + if (r->dwState != TCP_STATE_LISTEN) + { + UINTToIP(&t->RemoteIP, r->dwRemoteAddr); + t->RemotePort = Endian16((USHORT)r->dwRemotePort); + } + + t->Status = r->dwState; + + Add(o, t); + } + + Free(table); + + return o; +} + +#endif // OS_WIN32 + +// Display the TCP table +void PrintTcpTableList(LIST *o) +{ + UINT i; + // Validate arguments + if (o == NULL) + { + Print("o == NULL\n\n"); + return; + } + + Print("--- TCPTABLE: %u Entries ---\n", LIST_NUM(o)); + for (i = 0;i < LIST_NUM(o);i++) + { + char tmp1[MAX_PATH], tmp2[MAX_PATH]; + TCPTABLE *t = LIST_DATA(o, i); + + IPToStr(tmp1, sizeof(tmp1), &t->LocalIP); + IPToStr(tmp2, sizeof(tmp2), &t->RemoteIP); + + Print("%s:%u <--> %s:%u state=%u pid=%u\n", + tmp1, t->LocalPort, + tmp2, t->RemotePort, + t->Status, + t->ProcessId); + } + Print("------\n\n"); +} + +// Comparison of TCP table items +int CompareTcpTable(void *p1, void *p2) +{ + TCPTABLE *t1, *t2; + if (p1 == NULL || p2 == NULL) + { + return 0; + } + t1 = *(TCPTABLE **)p1; + t2 = *(TCPTABLE **)p2; + if (t1 == NULL || t2 == NULL) + { + return 0; + } + + return Cmp(t1, t2, sizeof(TCPTABLE)); +} + +// Get the TCP table list +LIST *GetTcpTableList() +{ +#ifdef OS_WIN32 + return Win32GetTcpTableList(); +#else // OS_WIN32 + return NULL; +#endif // OS_WIN32 +} + +// Release the TCP table list +void FreeTcpTableList(LIST *o) +{ + UINT i; + // Validate arguments + if (o == NULL) + { + return; + } + + for (i = 0;i < LIST_NUM(o);i++) + { + TCPTABLE *t = LIST_DATA(o, i); + + Free(t); + } + + ReleaseList(o); +} + +// 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 IsNetworkAddress(IP *ip, IP *mask) +{ + if (IsIP4(ip)) + { + return IsNetworkAddress4(ip, mask); + } + else + { + return IsNetworkAddress6(ip, mask); + } +} +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); +} + +// Network release mode +void SetNetworkReleaseMode() +{ + NetworkReleaseMode = true; +} + +#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 +void UnixIpForwardRowToRouteEntry(ROUTE_ENTRY *entry, void *ip_forward_row) +{ +} + +// Do Nothing +void UnixRouteEntryToIpForwardRow(void *ip_forward_row, ROUTE_ENTRY *entry) +{ +} + +// Do Nothing +int UnixCompareRouteEntryByMetric(void *p1, void *p2) +{ + return 1; +} + +// 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; +} + +// Do Nothing +void UnixRenewDhcp() +{ +} + +// 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 (c2 != NULL) + { + reads[num_read++] = p2 = c2->pipe_read; + } + + // 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); + + 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++; + } +} + +// Execute 'select' for the socket +void UnixSelectInner(UINT num_read, UINT *reads, UINT num_write, UINT *writes, UINT timeout) +{ + struct pollfd *p; + 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; + p = ZeroMallocFast(sizeof(struct pollfd) * num); + + n = 0; + + for (i = 0;i < num_read;i++) + { + if (reads[i] != INVALID_SOCKET) + { + struct pollfd *pfd = &p[n++]; + pfd->fd = reads[i]; + pfd->events = POLLIN | POLLPRI | POLLERR | POLLHUP; + } + } + + for (i = 0;i < num_write;i++) + { + if (writes[i] != INVALID_SOCKET) + { + struct pollfd *pfd = &p[n++]; + pfd->fd = writes[i]; + pfd->events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLOUT; + } + } + + if (num != 0) + { + poll(p, num, timeout == INFINITE ? -1 : (int)timeout); + } + else + { + SleepThread(timeout); + } + + Free(p); +} + +// 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); + } +} + +// 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; +} + +// Test +void Win32NetworkTest() +{ + IP_INTERFACE_INFO *info; + UINT size; + int i; + LIST *o; + + 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); + + DoNothing(); + } + + ReleaseList(o); + + Free(info); +} + +// 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); +} + +// Re-obtain an IP address from a DHCP server +void Win32RenewDhcp() +{ + if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType)) + { + Run("ipconfig.exe", "/renew", true, false); + if (MsIsVista()) + { + Run("ipconfig.exe", "/renew6", true, false); + } + else + { + Run("netsh.exe", "int ipv6 renew", true, false); + } + } + else + { + Run("ipconfig.exe", "/renew_all", true, false); + } +} + +// 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; +} + +// Check the cipher list name +bool CheckCipherListName(char *name) +{ + UINT i; + // Validate arguments + if (name == NULL) + { + return false; + } + + for (i = 0;i < cipher_list_token->NumTokens;i++) + { + if (StrCmpi(cipher_list_token->Token[i], name) == 0) + { + return true; + } + } + + return false; +} + +// Renewing the IP address of the DHCP server +void RenewDhcp() +{ +#ifdef OS_WIN32 + Win32RenewDhcp(); +#else + UnixRenewDhcp(); +#endif +} + +// 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) +{ + bool ret = false; +#ifdef OS_WIN32 + ret = Win32GetDefaultDns(ip, NULL, 0); +#else + ret = UnixGetDefaultDns(ip); +#endif // OS_WIN32 + return ret; +} + +// Creating a socket event +SOCK_EVENT *NewSockEvent() +{ + SOCK_EVENT *e = NULL; +#ifdef OS_WIN32 + e = Win32NewSockEvent(); +#else + e = UnixNewSockEvent(); +#endif // OS_WIN32 + return e; +} + +// 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 *GetBestRouteEntryFromRouteTable(ROUTE_TABLE *table, IP *ip) +{ + return GetBestRouteEntryFromRouteTableEx(table, ip, 0); +} +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)); + } + + Hash(hash, buf->Buf, buf->Size, false); + + 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) +{ + SOCKET s; + int ret, sz; + struct sockaddr_in addr; + // Validate arguments + if (sock != NULL) + { + sock->IgnoreRecvErr = false; + } + if (sock == NULL || src_addr == NULL || src_port == NULL || data == NULL) + { + return false; + } + if (sock->Type != SOCK_UDP || sock->socket == INVALID_SOCKET) + { + return false; + } + if (size == 0) + { + return false; + } + + if (sock->IPv6) + { + return RecvFrom6(sock, src_addr, src_port, data, size); + } + + s = sock->socket; + + sz = sizeof(addr); + ret = recvfrom(s, data, size, 0, (struct sockaddr *)&addr, (int *)&sz); + if (ret > 0) + { + InAddrToIP(src_addr, &addr.sin_addr); + *src_port = (UINT)ntohs(addr.sin_port); + if (sock->IsRawSocket) + { + *src_port = sock->LocalPort; +/* + { + char tmp[MAX_SIZE]; + + IPToStr(tmp, sizeof(tmp), &sock->LocalIP); + Debug("Raw: %u from %s\n", sock->LocalPort, tmp); + }*/ + } + + Lock(sock->lock); + { + sock->RecvNum++; + sock->RecvSize += (UINT64)ret; + } + Unlock(sock->lock); + + // Debug("UDP RecvFrom: %u\n", ret); + + return (UINT)ret; + } + else + { + sock->IgnoreRecvErr = false; + +#ifdef OS_WIN32 + if (WSAGetLastError() == WSAECONNRESET || WSAGetLastError() == WSAEMSGSIZE || WSAGetLastError() == WSAENETUNREACH || + WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAEUSERS) + { + sock->IgnoreRecvErr = true; + } + else if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS) + { + return SOCK_LATER; + } + else + { + UINT e = WSAGetLastError(); +// Debug("RecvFrom Error: %u\n", e); + } +#else // OS_WIN32 + if (errno == ECONNREFUSED || errno == ECONNRESET || errno == EMSGSIZE || errno == ENOBUFS || errno == ENOMEM || errno == EINTR) + { + sock->IgnoreRecvErr = true; + } + else if (errno == EAGAIN) + { + return SOCK_LATER; + } +#endif // OS_WIN32 + return 0; + } +} +UINT RecvFrom6(SOCK *sock, IP *src_addr, UINT *src_port, void *data, UINT size) +{ + SOCKET s; + int ret, sz; + struct sockaddr_in6 addr; + // Validate arguments + if (sock != NULL) + { + sock->IgnoreRecvErr = false; + } + if (sock == NULL || src_addr == NULL || src_port == NULL || data == NULL) + { + return false; + } + if (sock->Type != SOCK_UDP || sock->socket == INVALID_SOCKET) + { + return false; + } + if (size == 0) + { + return false; + } + + s = sock->socket; + + sz = sizeof(addr); + ret = recvfrom(s, data, size, 0, (struct sockaddr *)&addr, (int *)&sz); + 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); + + // Debug("UDP RecvFrom: %u\n", ret); + + return (UINT)ret; + } + else + { + sock->IgnoreRecvErr = false; + +#ifdef OS_WIN32 + if (WSAGetLastError() == WSAECONNRESET || WSAGetLastError() == WSAEMSGSIZE || WSAGetLastError() == WSAENETUNREACH || + WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAEUSERS) + { + sock->IgnoreRecvErr = true; + } + else if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS) + { + return SOCK_LATER; + } + else + { + UINT e = WSAGetLastError(); + // Debug("RecvFrom Error: %u\n", e); + } +#else // OS_WIN32 + if (errno == ECONNREFUSED || errno == ECONNRESET || errno == EMSGSIZE || errno == ENOBUFS || errno == ENOMEM || errno == EINTR) + { + sock->IgnoreRecvErr = true; + } + else if (errno == EAGAIN) + { + return SOCK_LATER; + } +#endif // OS_WIN32 + return 0; + } +} + +// Lock the OpenSSL +void LockOpenSSL() +{ + Lock(openssl_lock); +} + +// Unlock the OpenSSL +void UnlockOpenSSL() +{ + Unlock(openssl_lock); +} + +// 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; + + 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() == WSAEMSGSIZE || WSAGetLastError() == WSAENETUNREACH || + WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAEUSERS || WSAGetLastError() == WSAEINVAL) + { + 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 SendTo6(SOCK *sock, IP *dest_addr, UINT dest_port, void *data, UINT size) +{ + return SendTo6Ex(sock, dest_addr, dest_port, data, size, false); +} +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; + + 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() == WSAEMSGSIZE || WSAGetLastError() == WSAENETUNREACH || + WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAEUSERS || WSAGetLastError() == WSAEINVAL) + { + 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; +} + +// Disable the UDP checksum +void DisableUDPChecksum(SOCK *s) +{ + bool true_flag = true; + // Validate arguments + if (s == NULL || s->Type != SOCK_UDP) + { + return; + } + + if (s->socket != INVALID_SOCKET) + { + if (s->IPv6 == false) + { +#ifdef UDP_NOCHECKSUM + setsockopt(s->socket, IPPROTO_UDP, UDP_NOCHECKSUM, (char *)&true_flag, sizeof(bool)); +#endif // UDP_NOCHECKSUM + } + } +} + +// Set the socket to asynchronous mode +void InitAsyncSocket(SOCK *sock) +{ +#ifdef OS_WIN32 + Win32InitAsyncSocket(sock); +#else // OS_WIN32 + UnixInitAsyncSocket(sock); +#endif // OS_WIN32 +} + +// 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); + + HashSha1(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); +} + +// Generate a random port number (based on the EXE path and machine key) +UINT NewRandPortByMachineAndExePath(UINT start_port, UINT end_port, UINT additional_int) +{ + 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)); + WriteBufInt(b, additional_int); + + HashSha1(hash, b->Buf, b->Size); + + FreeBuf(b); + + Free(product_id); + + return (READ_UINT(hash) % (end_port - start_port)) + start_port; +} + +// 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()); + + HashSha1(hash, b->Buf, b->Size); + + FreeBuf(b); + + Free(product_id); + + return NewUDPEx2Rand(ipv6, ip, hash, sizeof(hash), num_retry); +} + +// 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; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&true_flag, sizeof(bool)); + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0) + { + bool false_flag = false; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&false_flag, sizeof(bool)); +#ifdef SO_EXCLUSIVEADDRUSE + 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; + 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; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&true_flag, sizeof(bool)); + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) != 0) + { + bool false_flag = false; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&false_flag, sizeof(bool)); +#ifdef SO_EXCLUSIVEADDRUSE + 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 + setsockopt(sock->socket, IPPROTO_IP, IPV6_HDRINCL, (char *)&no, sizeof(no)); +#endif // IPV6_HDRINCL + 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 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; + } + 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]; + DIRLIST *dir; + + // Validate arguments + if (ctx == NULL) + { + return; + } + + GetExeDirW(exedir, sizeof(exedir)); + + CombinePathW(dirname, sizeof(dirname), exedir, L"chain_certs"); + + MakeDirExW(dirname); + + dir = EnumDirW(dirname); + + if (dir != NULL) + { + UINT i; + + 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) + { + AddChainSslCert(ctx, x); + + FreeX(x); + } + } + } + + FreeDir(dir); + } +} + +// 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) + { + 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, false, 0, NULL); +} +bool StartSSLEx(SOCK *sock, X *x, K *priv, bool client_tls, 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(); + + Lock(openssl_lock); + { + if (sock->ServerMode) + { + SSL_CTX_set_ssl_version(ssl_ctx, SSLv23_method()); + + Unlock(openssl_lock); + AddChainSslCertOnDirectory(ssl_ctx); + Lock(openssl_lock); + } + else + { + if (client_tls == false) + { + SSL_CTX_set_ssl_version(ssl_ctx, SSLv3_method()); + } + else + { + SSL_CTX_set_ssl_version(ssl_ctx, TLSv1_client_method()); + } + } + sock->ssl = SSL_new(ssl_ctx); + SSL_set_fd(sock->ssl, (int)sock->socket); + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (sock->ServerMode == false && client_tls) + { + 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); + { + SSL_set_cipher_list(sock->ssl, sock->WaitToUseCipher); + } + 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); + } + 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); + + return true; +} + +// 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) +{ + SOCKET s; + int ret, e = 0; + SSL *ssl; + +#ifdef UNIX_SOLARIS + SOCKET_TIMEOUT_PARAM *ttparam; +#endif //UNIX_SOLARIS + + s = sock->socket; + 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) + { + // 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); + 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) + { + // 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) +{ + SOCKET s; + int ret, e; + SSL *ssl; + s = sock->socket; + 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 SecureRecv() 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); + + if (ret > 0) + { + // Successful transmission + sock->SendSize += (UINT64)ret; + sock->SendNum++; + sock->WriteBlocked = false; + return (UINT)ret; + } + if (ret == 0) + { + // Disconnect + Debug("%s %u SecureRecv() 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 SecureRecv() 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 + + setsockopt(sock->socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv_timeout, sizeof(tv_timeout)); + setsockopt(sock->socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_timeout, sizeof(tv_timeout)); + } +#endif // UNIX_SOLARIS +#endif // OS_UNIX + } +} + +// Initialize the connection acceptance +void AcceptInit(SOCK *s) +{ + char tmp[MAX_SIZE]; + // Validate arguments + if (s == NULL) + { + return; + } + + Zero(tmp, sizeof(tmp)); + if (GetHostName(tmp, sizeof(tmp), &s->RemoteIP) == false || + IsEmptyStr(tmp)) + { + 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 +#ifdef UNIX_LINUX + UnixIgnoreSignalForThread(SIGUSR1); +#endif // UNIX_LINUX + 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 + 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(¤t_ip, false) == false) + { + SetCurrentGlobalIP(&sock->LocalIP, false); + } + } + + StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_NATIVE_V4); + + 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 +#ifdef UNIX_LINUX + UnixIgnoreSignalForThread(SIGUSR1); +#endif // UNIX_LINUX + 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 + 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(¤t_ip, true) == false) + { + SetCurrentGlobalIP(&sock->LocalIP, true); + } + } + + StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_NATIVE_V6); + + return ret; +} + +// Standby for TCP (IPv6) +SOCK *Listen6(UINT port) +{ + return ListenEx6(port, false); +} +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; + bool disable_conditional_accept = false; + 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 + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &true_flag, sizeof(true_flag)); +#endif // OS_UNIX + + //SetSocketSendRecvBufferSize(s, SOCKET_BUFFER_SIZE); + +#ifdef OS_UNIX + // This only have enabled for UNIX system since there is a bug + // in the implementation of REUSEADDR in Windows OS + 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); +} +SOCK *ListenEx2(UINT port, bool local_only, bool enable_ca) +{ + 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); + *((UINT *)&addr.sin_addr) = htonl(INADDR_ANY); + 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; + } + + //SetSocketSendRecvBufferSize(s, SOCKET_BUFFER_SIZE); + +#ifdef OS_UNIX + // This only have enabled for UNIX system since there is a bug + // in the implementation of REUSEADDR in Windows OS + 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 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; + +#ifdef UNIX_LINUX + { + 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 // UNIX_LINUX + +#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 + setsockopt(sock->socket, SOL_SOCKET, SO_DONTLINGER, (char *)&true_flag, sizeof(bool)); + #else // SO_DONTLINGER + 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; + // Release the certificate + if (sock->RemoteX != NULL) + { + FreeX(sock->RemoteX); + sock->RemoteX = NULL; + } + if (sock->LocalX != NULL) + { + FreeX(sock->LocalX); + sock->LocalX = NULL; + } + + // Cipher algorithm name + if (sock->CipherName != NULL) + { + Free(sock->CipherName); + sock->CipherName = NULL; + } + 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; + +// The thread to check the TCP port +void CheckTCPPortThread(THREAD *thread, void *param) +{ + TCP_PORT_CHECK *c; + SOCK *s; + // Validate arguments + if (thread == NULL || param == NULL) + { + return; + } + + c = (TCP_PORT_CHECK *)param; + AddRef(c->ref); + NoticeThreadInit(thread); + + AddWaitThread(thread); + + s = Connect(c->hostname, c->port); + if (s != NULL) + { + c->ok = true; + Disconnect(s); + ReleaseSock(s); + } + + if (Release(c->ref) == 0) + { + Free(c); + } + + DelWaitThread(thread); +} + +// 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 + 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, false, true); + + if (sock != NULL && p->Tcp_SslNoTls) + { + bool ssl_ret = false; + // 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, p->Tcp_SslNoTls, 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: + DoNothing(); + } + 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; + + 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, false, false); +} +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 ssl_no_tls, bool no_get_hostname) +{ + 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; + // 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(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(¤t_ip, sizeof(current_ip)); + + Zero(&ip4, sizeof(ip4)); + Zero(&ip6, sizeof(ip6)); + + // 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(¤t_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); + } + + 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.Tcp_SslNoTls = ssl_no_tls; + 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 three times the duration of the R-UDP, and at least 250 milliseconds from the start, + // and up to 1500 milliseconds after the R-UDP results settled + p1_wait_time = p2_spent_time * 3; + p1_wait_time = MAX(p1_wait_time, 250); + //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 + 1500ULL)); + + if (now >= tcp_giveup_tick) + { + // Result of the TCP is uncertain, but give up + 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); + } + + 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); + + 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); + + 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); + + 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(¤t_ip, &ip6, sizeof(IP)); + + is_ipv6 = true; + } + } + } + + 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)); + + // Host name resolution + if (no_get_hostname || (GetHostName(tmp, sizeof(tmp), ¤t_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 + setsockopt(sock->socket, SOL_SOCKET, SO_DONTLINGER, (char *)&true_flag, sizeof(bool)); +#else // SO_DONTLINGER + 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 + 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(¤t_ip, is_ipv6) == false) + { + SetCurrentGlobalIP(&sock->LocalIP, is_ipv6); + } + } + + sock->Connected = true; + sock->AsyncMode = false; + sock->SecureMode = false; + sock->IPv6 = is_ipv6; + + return sock; +} + +// Maximize the I/O buffer size of the socket +void SetSocketSendRecvBufferSize(SOCKET s, UINT size) +{ + int value = (int)size; + // Validate arguments + if (s == INVALID_SOCKET) + { + return; + } + + setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&value, sizeof(int)); + setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&value, sizeof(int)); +} + +// Get the buffer size of the socket +UINT GetSocketBufferSize(SOCKET s, bool send) +{ + int value = 0; + int len = sizeof(int); + // Validate arguments + if (s == INVALID_SOCKET) + { + return 0; + } + + if (getsockopt(s, SOL_SOCKET, (send ? SO_SNDBUF : SO_RCVBUF), (char *)&value, &len) != 0) + { + return 0; + } + + return value; +} + +// 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; + DWORD 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(DWORD); + 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) +{ + DWORD 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(DWORD); + + 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 + + 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 IP address of this computer +void GetMachineIp(IP *ip) +{ + char tmp[MAX_SIZE]; + // Validate arguments + if (ip == NULL) + { + return; + } + + Zero(ip, sizeof(IP)); + SetIP(ip, 127, 0, 0, 1); + + GetMachineName(tmp, sizeof(tmp)); + GetIP(ip, 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, sizeof(name), 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); +} + +// Get either of v4 and v6 results with a DNS forward lookup (The IPv4 precedes in the case of both results) +bool GetIP46Any4(IP *ip, char *hostname) +{ + IP ip4, ip6; + bool b = false; + // Validate arguments + if (ip == NULL || hostname == NULL) + { + return false; + } + + if (GetIP46(&ip4, &ip6, hostname) == false) + { + return false; + } + + if (IsZeroIp(&ip6) == false) + { + Copy(ip, &ip6, sizeof(IP)); + + b = true; + } + + if (IsZeroIp(&ip4) == false) + { + Copy(ip, &ip4, sizeof(IP)); + + b = true; + } + + return b; +} + +// Get either of v4 and v6 results with a DNS forward lookup (The IPv6 precedes in the case of both) +bool GetIP46Any6(IP *ip, char *hostname) +{ + IP ip4, ip6; + bool b = false; + // Validate arguments + if (ip == NULL || hostname == NULL) + { + return false; + } + + if (GetIP46(&ip4, &ip6, hostname) == false) + { + return false; + } + + if (IsZeroIp(&ip4) == false) + { + Copy(ip, &ip4, sizeof(IP)); + + b = true; + } + + if (IsZeroIp(&ip6) == false) + { + Copy(ip, &ip6, sizeof(IP)); + + b = true; + } + + return b; +} + +// Obtain in both v4 and v6 results with a DNS forward lookup +bool GetIP46(IP *ip4, IP *ip6, char *hostname) +{ + return GetIP46Ex(ip4, ip6, hostname, 0, NULL); +} +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); +} + +// 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; + bool use_dns_proxy = false; + char hostname[260]; + UINT i; + // 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; + } + } + } + + + 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 (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; + } + } + } + + 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 (128bit byte array) +void IPToStr128(char *str, UINT size, UCHAR *ip_bytes) +{ + IP ip_st; + // Validate arguments + if (str == NULL) + { + return; + } + + SetIP6(&ip_st, ip_bytes); + IPToStr(str, size, &ip_st); +} + +// 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); +} +bool UniStrToIP(IP *ip, wchar_t *str) +{ + char *tmp; + bool ret; + + tmp = CopyUniToStr(str); + ret = StrToIP(ip, tmp); + Free(tmp); + + return ret; +} +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 in_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); +} + +// Create the SSL_CTX +struct ssl_ctx_st *NewSSLCtx() +{ + 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 + + return ctx; +} + +// Release of the SSL_CTX +void FreeSSLCtx(struct ssl_ctx_st *ctx) +{ + // Validate arguments + if (ctx == NULL) + { + return; + } + + SSL_CTX_free(ctx); +} + +// Initialize the network communication module +void InitNetwork() +{ + + InitDynList(); + + + host_ip_address_list_cache_lock = NewLock(); + host_ip_address_list_cache_last = 0; + + num_tcp_connections = 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(); + + cipher_list_token = ParseToken(cipher_list, " "); + + 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)); +} + +// 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() +{ + return cipher_list_token; +} + +// 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, ¤t_glocal_ipv4, sizeof(IP)); + } + else + { + Copy(ip, ¤t_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; +} + +// 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(¤t_glocal_ipv4, ip, sizeof(IP)); + } + else + { + Copy(¤t_glocal_ipv6, ip, sizeof(IP)); + } + + current_global_ip_set = true; + } + Unlock(current_global_ip_lock); +} + +// Release of the network communication module +void FreeNetwork() +{ + + // Release of thread-related + FreeWaitThread(); + + FreeToken(cipher_list_token); + cipher_list_token = NULL; + + 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(); + +} + +// Add a socket to socket list +void AddSockList(SOCKLIST *sl, SOCK *s) +{ + // Validate arguments + if (sl == NULL || s == NULL) + { + return; + } + + LockList(sl->SockList); + { + if (IsInList(sl->SockList, s) == false) + { + AddRef(s->ref); + + Insert(sl->SockList, s); + } + } + UnlockList(sl->SockList); +} + +// Remove the socket from socket list +void DelSockList(SOCKLIST *sl, SOCK *s) +{ + // Validate arguments + if (sl == NULL || s == NULL) + { + return; + } + + LockList(sl->SockList); + { + if (Delete(sl->SockList, s)) + { + ReleaseSock(s); + } + } + UnlockList(sl->SockList); +} + +// 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 ParseIpAndSubnetMask6(char *src, IP *ip, IP *mask) +{ + if (ParseIpAndSubnetMask46(src, ip, mask) == false) + { + return false; + } + + if (IsIP6(ip) == false) + { + return false; + } + + return true; +} +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; +} + +// Check whether the IP address specification is correct +bool IsIpStr46(char *str) +{ + if (IsIpStr4(str) || IsIpStr6(str)) + { + return true; + } + + return false; +} + + +// Convert the string to an IPv4 mask +bool StrToMask4(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 <= 32) + { + IntToSubnetMask4(mask, n); + return true; + } + else + { + return false; + } + } + else + { + if (StrToIP(mask, str) == false) + { + return false; + } + else + { + return IsIP4(mask); + } + } +} + +// 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 string to an IPv4 / IPv6 mask +bool StrToMask46(IP *mask, char *str, bool ipv6) +{ + if (ipv6) + { + return StrToMask6(mask, str); + } + else + { + return StrToMask4(mask, str); + } +} + + +// 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 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); +} + +// Get a public port list which is known by UDP listener +void UdpListenerGetPublicPortList(UDPLISTENER *u, char *dst, UINT size) +{ + UINT k; + // Validate arguments + ClearStr(dst, size); + if (u == NULL || dst == NULL) + { + return; + } + + LockList(u->PortList); + { + for (k = 0;k < LIST_NUM(u->SockList);k++) + { + UDPLISTENER_SOCK *us = LIST_DATA(u->SockList, k); + + if (us->PublicPort != 0) + { + char tmp[64]; + ToStr(tmp, us->PublicPort); + StrCat(dst, size, tmp); + StrCat(dst, size, " "); + } + } + } + UnlockList(u->PortList); + + Trim(dst); +} + +// 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); + + 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 && IsZeroIP(&nat_t_ip) == false && IsIP4(&us->IpAddress)) + { + if (us->NextMyIpAndPortPollTick == 0 || us->NextMyIpAndPortPollTick <= now) + { + UCHAR c = 'A'; + + // 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); + + 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); + } + + 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; +} + +// 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); + } +} +void UdpListenerSendPacket(UDPLISTENER *u, UDPPACKET *packet) +{ + LIST *o; + // Validate arguments + if (u == NULL || packet == NULL) + { + return; + } + + o = NewListFast(NULL); + Add(o, packet); + + UdpListenerSendPackets(u, o); + + ReleaseList(o); +} + +// Creating a UDP listener +UDPLISTENER *NewUdpListener(UDPLISTENER_RECV_PROC *recv_proc, void *param) +{ + UDPLISTENER *u; + // Validate arguments + if (recv_proc == NULL) + { + return NULL; + } + + u = ZeroMalloc(sizeof(UDPLISTENER)); + + u->Param = param; + + u->PortList = NewList(NULL); + u->Event = NewSockEvent(); + + u->RecvProc = recv_proc; + u->SendPacketList = NewList(NULL); + + u->Interrupts = NewInterruptManager(); + + u->Thread = NewThread(UdpListenerThread, u); + + return u; +} + +// Release the UDP listener +void FreeUdpListener(UDPLISTENER *u) +{ + UINT i; + // Validate arguments + if (u == NULL) + { + return; + } + + u->Halt = true; + SetSockEvent(u->Event); + + WaitThread(u->Thread, INFINITE); + 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); +} + +// Get the port list +UINT GetUdpListenerPortList(UDPLISTENER *u, UINT **port_list) +{ + UINT num_ports; + // Validate arguments + if (u == NULL || port_list == NULL) + { + return 0; + } + + LockList(u->PortList); + { + UINT *ports; + UINT i; + + num_ports = LIST_NUM(u->PortList); + ports = ZeroMalloc(sizeof(UINT) * num_ports); + + for (i = 0;i < num_ports;i++) + { + ports[i] = *((UINT *)(LIST_DATA(u->PortList, i))); + } + + *port_list = ports; + } + UnlockList(u->PortList); + + return num_ports; +} + +// Dekete 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); + + 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); + + 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); +} + +// The server receives a PACK from the client +PACK *HttpServerRecv(SOCK *s) +{ + BUF *b; + PACK *p; + HTTP_HEADER *h; + UINT size; + UCHAR *tmp; + HTTP_VALUE *v; + UINT num_noop = 0; + // Validate arguments + if (s == NULL) + { + return NULL; + } + +START: + + h = RecvHttpHeader(s); + if (h == NULL) + { + goto BAD_REQUEST; + } + + if (StrCmpi(h->Method, "POST") != 0 || + StrCmpi(h->Target, HTTP_VPN_TARGET) != 0 || + StrCmpi(h->Version, "HTTP/1.1") != 0) + { + FreeHttpHeader(h); + goto BAD_REQUEST; + } + + v = GetHttpValue(h, "Content-Type"); + if (v == NULL || StrCmpi(v->Data, HTTP_CONTENT_TYPE2) != 0) + { + FreeHttpHeader(h); + goto BAD_REQUEST; + } + + size = GetContentLength(h); + if (size == 0 || size > HTTP_PACK_MAX_SIZE) + { + FreeHttpHeader(h); + goto BAD_REQUEST; + } + + tmp = MallocEx(size, true); + if (RecvAll(s, tmp, size, s->SecureMode) == false) + { + Free(tmp); + FreeHttpHeader(h); + return NULL; + } + + b = NewBuf(); + WriteBuf(b, tmp, size); + Free(tmp); + FreeHttpHeader(h); + + SeekBuf(b, 0, 0); + p = BufToPack(b); + FreeBuf(b); + + // Determine whether it's a NOOP + if (PackGetInt(p, "noop") != 0) + { + Debug("recv: noop\n"); + FreePack(p); + + p = PackError(0); + PackAddInt(p, "noop", 1); + if (HttpServerSend(s, p) == false) + { + FreePack(p); + return NULL; + } + + FreePack(p); + + num_noop++; + + if (num_noop > MAX_NOOP_PER_SESSION) + { + return NULL; + } + + goto START; + } + + return p; + +BAD_REQUEST: + // Return an error + + + return NULL; +} + +// 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"); +} + +// Client receives a PACK from the server +PACK *HttpClientRecv(SOCK *s) +{ + BUF *b; + PACK *p; + HTTP_HEADER *h; + UINT size; + UCHAR *tmp; + HTTP_VALUE *v; + // Validate arguments + if (s == NULL) + { + return NULL; + } + + h = RecvHttpHeader(s); + if (h == NULL) + { + return NULL; + } + + if (StrCmpi(h->Method, "HTTP/1.1") != 0 || + StrCmpi(h->Target, "200") != 0) + { + FreeHttpHeader(h); + return NULL; + } + + v = GetHttpValue(h, "Content-Type"); + if (v == NULL || StrCmpi(v->Data, HTTP_CONTENT_TYPE2) != 0) + { + FreeHttpHeader(h); + return NULL; + } + + size = GetContentLength(h); + if (size == 0 || size > MAX_PACK_SIZE) + { + FreeHttpHeader(h); + return NULL; + } + + tmp = MallocEx(size, true); + if (RecvAll(s, tmp, size, s->SecureMode) == false) + { + Free(tmp); + FreeHttpHeader(h); + return NULL; + } + + b = NewBuf(); + WriteBuf(b, tmp, size); + Free(tmp); + FreeHttpHeader(h); + + SeekBuf(b, 0, 0); + p = BufToPack(b); + FreeBuf(b); + + return p; +} + +// 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); +} + +// Client send a PACK to the server +bool HttpClientSend(SOCK *s, PACK *p) +{ + BUF *b; + bool ret; + HTTP_HEADER *h; + char date_str[MAX_SIZE]; + // Validate arguments + if (s == NULL || p == NULL) + { + return false; + } + + CreateDummyValue(p); + + b = PackToBuf(p); + if (b == NULL) + { + return false; + } + + h = NewHttpHeader("POST", HTTP_VPN_TARGET, "HTTP/1.1"); + + GetHttpDateStr(date_str, sizeof(date_str), SystemTime64()); + AddHttpValue(h, NewHttpValue("Date", date_str)); + AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE)); + AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive")); + AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE2)); + + ret = PostHttp(s, h, b->Buf, b->Size); + + FreeHttpHeader(h); + FreeBuf(b); + + return ret; +} + +// Server sends a PACK to the client +bool HttpServerSend(SOCK *s, PACK *p) +{ + BUF *b; + bool ret; + HTTP_HEADER *h; + char date_str[MAX_SIZE]; + // Validate arguments + if (s == NULL || p == NULL) + { + return false; + } + + CreateDummyValue(p); + + b = PackToBuf(p); + if (b == NULL) + { + return false; + } + + h = NewHttpHeader("HTTP/1.1", "200", "OK"); + + GetHttpDateStr(date_str, sizeof(date_str), SystemTime64()); + AddHttpValue(h, NewHttpValue("Date", date_str)); + AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE)); + AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive")); + AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE2)); + + ret = PostHttp(s, h, b->Buf, b->Size); + + FreeHttpHeader(h); + FreeBuf(b); + + return ret; +} + +// Replace unsafe characters in target +void ReplaceUnsafeCharInTarget(char *target){ + UINT i; + for(i = 0; target[i] ; i++) { + if(target[i] == '<') + target[i] = '('; + else if(target[i] == '>') + target[i] = ')'; + } +} + +// Sending the 501 Not Implemented error +bool HttpSendNotImplemented(SOCK *s, char *method, char *target, char *version) +{ + HTTP_HEADER *h; + char date_str[MAX_SIZE]; + char *str; + UINT str_size; + char port_str[MAX_SIZE]; + bool ret; + char host[MAX_SIZE]; + UINT port; + // Validate arguments + if (s == NULL || target == NULL) + { + return false; + } + + // Get the host name + //GetMachineName(host, MAX_SIZE); + Zero(host, sizeof(host)); + IPToStr(host, sizeof(host), &s->LocalIP); + // Get the port number + port = s->LocalPort; + + // Creating a header + GetHttpDateStr(date_str, sizeof(date_str), SystemTime64()); + + h = NewHttpHeader("HTTP/1.1", "501", "Method Not Implemented"); + + AddHttpValue(h, NewHttpValue("Date", date_str)); + AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE)); + AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive")); + AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE)); + + // Creating a Data + str_size = sizeof(http_501_str) * 2 + StrLen(target) + StrLen(host) + StrLen(method) + StrLen(version); + str = Malloc(str_size); + StrCpy(str, str_size, http_501_str); + + // TARGET + ReplaceUnsafeCharInTarget(target); + ReplaceStri(str, str_size, str, "$TARGET$", target); + + // HOST + ReplaceStri(str, str_size, str, "$HOST$", host); + + // PORT + ToStr(port_str, port); + ReplaceStri(str, str_size, str, "$PORT$", port_str); + + // METHOD + ReplaceStri(str, str_size, str, "$METHOD$", method); + + // VERSION + ReplaceStri(str, str_size, str, "$VERSION$", version); + + // Transmission + ret = PostHttp(s, h, str, StrLen(str)); + + FreeHttpHeader(h); + Free(str); + + return ret; +} + +// Sending a 404 Not Found error +bool HttpSendNotFound(SOCK *s, char *target) +{ + HTTP_HEADER *h; + char date_str[MAX_SIZE]; + char *str; + UINT str_size; + char port_str[MAX_SIZE]; + bool ret; + char host[MAX_SIZE]; + UINT port; + // Validate arguments + if (s == NULL || target == NULL) + { + return false; + } + + // Get the host name + //GetMachineName(host, MAX_SIZE); + Zero(host, sizeof(host)); + IPToStr(host, sizeof(host), &s->LocalIP); + // Get the port number + port = s->LocalPort; + + // Creating a header + GetHttpDateStr(date_str, sizeof(date_str), SystemTime64()); + + h = NewHttpHeader("HTTP/1.1", "404", "Not Found"); + + AddHttpValue(h, NewHttpValue("Date", date_str)); + AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE)); + AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive")); + AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE)); + + // Creating a Data + str_size = sizeof(http_404_str) * 2 + StrLen(target) + StrLen(host); + str = Malloc(str_size); + StrCpy(str, str_size, http_404_str); + + // TARGET + ReplaceUnsafeCharInTarget(target); + ReplaceStri(str, str_size, str, "$TARGET$", target); + + // HOST + ReplaceStri(str, str_size, str, "$HOST$", host); + + // PORT + ToStr(port_str, port); + ReplaceStri(str, str_size, str, "$PORT$", port_str); + + // Transmission + ret = PostHttp(s, h, str, StrLen(str)); + + FreeHttpHeader(h); + Free(str); + + return ret; +} + +// Sending a 500 Server Error +bool HttpSendServerError(SOCK *s, char *target) +{ + HTTP_HEADER *h; + char date_str[MAX_SIZE]; + char *str; + UINT str_size; + char port_str[MAX_SIZE]; + bool ret; + char host[MAX_SIZE]; + UINT port; + // Validate arguments + if (s == NULL || target == NULL) + { + return false; + } + + // Get the host name + //GetMachineName(host, MAX_SIZE); + Zero(host, sizeof(host)); + IPToStr(host, sizeof(host), &s->LocalIP); + // Get the port number + port = s->LocalPort; + + // Creating a header + GetHttpDateStr(date_str, sizeof(date_str), SystemTime64()); + + h = NewHttpHeader("HTTP/1.1", "500", "Server Error"); + + AddHttpValue(h, NewHttpValue("Date", date_str)); + AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE)); + AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive")); + AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE)); + + // Creating a Data + str_size = sizeof(http_500_str) * 2 + StrLen(target) + StrLen(host); + str = Malloc(str_size); + StrCpy(str, str_size, http_500_str); + + // TARGET + ReplaceUnsafeCharInTarget(target); + ReplaceStri(str, str_size, str, "$TARGET$", target); + + // HOST + ReplaceStri(str, str_size, str, "$HOST$", host); + + // PORT + ToStr(port_str, port); + ReplaceStri(str, str_size, str, "$PORT$", port_str); + + // Transmission + ret = PostHttp(s, h, str, StrLen(str)); + + FreeHttpHeader(h); + Free(str); + + return ret; +} + +// Sending a 403 Forbidden error +bool HttpSendForbidden(SOCK *s, char *target, char *server_id) +{ + HTTP_HEADER *h; + char date_str[MAX_SIZE]; + char *str; + UINT str_size; + char port_str[MAX_SIZE]; + bool ret; + char host[MAX_SIZE]; + UINT port; + // Validate arguments + if (s == NULL || target == NULL) + { + return false; + } + + // Get the host name + //GetMachineName(host, MAX_SIZE); + Zero(host, sizeof(host)); + IPToStr(host, sizeof(host), &s->LocalIP); + // Get the port number + port = s->LocalPort; + + // Creating a header + GetHttpDateStr(date_str, sizeof(date_str), SystemTime64()); + + h = NewHttpHeader("HTTP/1.1", "403", "Forbidden"); + + AddHttpValue(h, NewHttpValue("Date", date_str)); + AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE)); + AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive")); + AddHttpValue(h, NewHttpValue("Content-Type", HTTP_CONTENT_TYPE)); + + // Creating a Data + str_size = sizeof(http_403_str) * 2 + StrLen(target) + StrLen(host); + str = Malloc(str_size); + StrCpy(str, str_size, http_403_str); + + // TARGET + ReplaceUnsafeCharInTarget(target); + ReplaceStri(str, str_size, str, "$TARGET$", target); + + // HOST + ReplaceStri(str, str_size, str, "$HOST$", host); + + // PORT + ToStr(port_str, port); + ReplaceStri(str, str_size, str, "$PORT$", port_str); + + // Transmission + ret = PostHttp(s, h, str, StrLen(str)); + + FreeHttpHeader(h); + Free(str); + + return ret; +} + +// Get the date and time string for the HTTP header +void GetHttpDateStr(char *str, UINT size, UINT64 t) +{ + SYSTEMTIME s; + static char *wday[] = + { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", + }; + static char *month[] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec", + }; + // Validate arguments + if (str == NULL) + { + return; + } + UINT64ToSystem(&s, t); + + Format(str, size, "%s, %02u %s %04u %02u:%02u:%02u GMT", + wday[s.wDayOfWeek], s.wDay, month[s.wMonth - 1], s.wYear, + s.wHour, s.wMinute, s.wSecond); +} + +// Get the Content-Length from the HTTP header +UINT GetContentLength(HTTP_HEADER *header) +{ + UINT ret; + HTTP_VALUE *v; + // Validate arguments + if (header == NULL) + { + return 0; + } + + v = GetHttpValue(header, "Content-Length"); + if (v == NULL) + { + return 0; + } + + ret = ToInt(v->Data); + + return ret; +} + +// Send the data in the HTTP +bool PostHttp(SOCK *s, HTTP_HEADER *header, void *post_data, UINT post_size) +{ + char *header_str; + BUF *b; + bool ret; + // Validate arguments + if (s == NULL || header == NULL || (post_size != 0 && post_data == NULL)) + { + return false; + } + + // Check whether the Content-Lentgh exists? + if (GetHttpValue(header, "Content-Length") == NULL) + { + char tmp[MAX_SIZE]; + // Add because it does not exist + ToStr(tmp, post_size); + AddHttpValue(header, NewHttpValue("Content-Length", tmp)); + } + + // Convert the header to string + header_str = HttpHeaderToStr(header); + if (header_str == NULL) + { + return false; + } + b = NewBuf(); + WriteBuf(b, header_str, StrLen(header_str)); + Free(header_str); + + // Append the data + WriteBuf(b, post_data, post_size); + + // Send + ret = SendAll(s, b->Buf, b->Size, s->SecureMode); + + FreeBuf(b); + + return ret; +} + +// Convert a HTTP header to a string +char *HttpHeaderToStr(HTTP_HEADER *header) +{ + BUF *b; + char *tmp; + UINT i; + char *s; + // Validate arguments + if (header == NULL) + { + return NULL; + } + + tmp = Malloc(HTTP_HEADER_LINE_MAX_SIZE); + b = NewBuf(); + + // Header + Format(tmp, HTTP_HEADER_LINE_MAX_SIZE, + "%s %s %s\r\n", header->Method, header->Target, header->Version); + WriteBuf(b, tmp, StrLen(tmp)); + + // Value + for (i = 0;i < LIST_NUM(header->ValueList);i++) + { + HTTP_VALUE *v = (HTTP_VALUE *)LIST_DATA(header->ValueList, i); + Format(tmp, HTTP_HEADER_LINE_MAX_SIZE, + "%s: %s\r\n", v->Name, v->Data); + WriteBuf(b, tmp, StrLen(tmp)); + } + + // Trailing newline + WriteBuf(b, "\r\n", 2); + s = Malloc(b->Size + 1); + Copy(s, b->Buf, b->Size); + s[b->Size] = 0; + + FreeBuf(b); + Free(tmp); + + return s; +} + +// Send the HTTP header +bool SendHttpHeader(SOCK *s, HTTP_HEADER *header) +{ + char *str; + bool ret; + // Validate arguments + if (s == NULL || header == NULL) + { + return false; + } + + // Convert to string + str = HttpHeaderToStr(header); + + // Transmission + ret = SendAll(s, str, StrLen(str), s->SecureMode); + + Free(str); + + return ret; +} + +// Receive an HTTP header +HTTP_HEADER *RecvHttpHeader(SOCK *s) +{ + TOKEN_LIST *token = NULL; + char *str = NULL; + HTTP_HEADER *header = NULL; + // Validate arguments + if (s == NULL) + { + return NULL; + } + + // Get the first line + str = RecvLine(s, HTTP_HEADER_LINE_MAX_SIZE); + if (str == NULL) + { + goto LABEL_ERROR; + } + + // Split into tokens + token = ParseToken(str, " "); + if (token->NumTokens < 3) + { + goto LABEL_ERROR; + } + + Free(str); + str = NULL; + + // Creating a header object + header = NewHttpHeader(token->Token[0], token->Token[1], token->Token[2]); + + if (StrCmpi(header->Version, "HTTP/0.9") == 0) + { + // The header ends with this line + FreeToken(token); + return header; + } + + // Get the subsequent lines + while (true) + { + UINT pos; + HTTP_VALUE *v; + char *value_name, *value_data; + str = RecvLine(s, HTTP_HEADER_LINE_MAX_SIZE); + if (str == NULL) + { + goto LABEL_ERROR; + } + Trim(str); + + if (StrLen(str) == 0) + { + // End of header + Free(str); + str = NULL; + break; + } + + // Get the position of the colon + pos = SearchStr(str, ":", 0); + if (pos == INFINITE) + { + // The colon does not exist + goto LABEL_ERROR; + } + if ((pos + 1) >= StrLen(str)) + { + // There is no data + goto LABEL_ERROR; + } + + // Divide into the name and the data + value_name = Malloc(pos + 1); + Copy(value_name, str, pos); + value_name[pos] = 0; + value_data = &str[pos + 1]; + + v = NewHttpValue(value_name, value_data); + if (v == NULL) + { + Free(value_name); + goto LABEL_ERROR; + } + + Free(value_name); + + AddHttpValue(header, v); + Free(str); + } + + FreeToken(token); + + return header; + +LABEL_ERROR: + // Memory release + if (token) + { + FreeToken(token); + } + if (str) + { + Free(str); + } + if (header) + { + FreeHttpHeader(header); + } + return NULL; +} + +// 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; + } + } + } +} + +// Creating a new HTTP value +HTTP_VALUE *NewHttpValue(char *name, char *data) +{ + HTTP_VALUE *v; + // Validate arguments + if (name == NULL || data == NULL) + { + return NULL; + } + + v = ZeroMalloc(sizeof(HTTP_VALUE)); + + v->Name = CopyStr(name); + v->Data = CopyStr(data); + + Trim(v->Name); + Trim(v->Data); + + return v; +} + +// Look for the HTTP value from the HTTP header +HTTP_VALUE *GetHttpValue(HTTP_HEADER *header, char *name) +{ + HTTP_VALUE *v, t; + // Validate arguments + if (header == NULL || name == NULL) + { + return NULL; + } + + t.Name = name; + v = Search(header->ValueList, &t); + if (v == NULL) + { + return NULL; + } + + return v; +} + +// Add a HTTP value to the HTTP header +void AddHttpValue(HTTP_HEADER *header, HTTP_VALUE *value) +{ + // Validate arguments + if (header == NULL || value == NULL) + { + return; + } + + if (LIST_NUM(header->ValueList) < HTTP_HEADER_MAX_LINES) + { + Insert(header->ValueList, value); + } + else + { + FreeHttpValue(value); + } +} + +// Create an HTTP header +HTTP_HEADER *NewHttpHeader(char *method, char *target, char *version) +{ + return NewHttpHeaderEx(method, target, version, false); +} +HTTP_HEADER *NewHttpHeaderEx(char *method, char *target, char *version, bool no_sort) +{ + HTTP_HEADER *header; + // Validate arguments + if (method == NULL || target == NULL || version == NULL) + { + return NULL; + } + + header = ZeroMalloc(sizeof(HTTP_HEADER)); + + header->Method = CopyStr(method); + header->Target = CopyStr(target); + header->Version = CopyStr(version); + header->ValueList = NewListFast(no_sort ? NULL : CompareHttpValue); + + return header; +} + +// Comparison function of the HTTP value +int CompareHttpValue(void *p1, void *p2) +{ + HTTP_VALUE *v1, *v2; + if (p1 == NULL || p2 == NULL) + { + return 0; + } + v1 = *(HTTP_VALUE **)p1; + v2 = *(HTTP_VALUE **)p2; + if (v1 == NULL || v2 == NULL) + { + return 0; + } + return StrCmpi(v1->Name, v2->Name); +} + +// Release the HTTP value +void FreeHttpValue(HTTP_VALUE *value) +{ + // Validate arguments + if (value == NULL) + { + return; + } + + Free(value->Data); + Free(value->Name); + + Free(value); +} + +// Release the HTTP header +void FreeHttpHeader(HTTP_HEADER *header) +{ + UINT i; + HTTP_VALUE **values; + // Validate arguments + if (header == NULL) + { + return; + } + + Free(header->Method); + Free(header->Target); + Free(header->Version); + + values = ToArray(header->ValueList); + for (i = 0;i < LIST_NUM(header->ValueList);i++) + { + FreeHttpValue(values[i]); + } + Free(values); + + ReleaseList(header->ValueList); + + Free(header); +} + +// 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; + } + + HashSha1(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); + HashSha1(hash, b->Buf, b->Size); + SendAdd(s, hash, sizeof(hash)); + + FreeBuf(b); + + return SendNow(s, s->SecureMode); +} + +// Get SNI name from the data that has arrived to the TCP connection before accepting an SSL connection +bool GetSniNameFromPreSslConnection(SOCK *s, char *sni, UINT sni_size) +{ + UCHAR tmp[1500]; + UINT size; + // Validate arguments + if (s == NULL || sni == NULL) + { + return false; + } + + size = Peek(s, tmp, sizeof(tmp)); + if (size == 0) + { + return false; + } + + return GetSniNameFromSslPacket(tmp, size, sni, sni_size); +} + +// 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 == 0) + { + 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 (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) + { + 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; +} + +// Developed by SoftEther VPN Project at University of Tsukuba in Japan. +// Department of Computer Science has dozens of overly-enthusiastic geeks. +// Join us: http://www.tsukuba.ac.jp/english/admission/ -- cgit v1.2.3