// Execution of registration UINT DCRegister(DDNS_CLIENT *c, bool ipv6, DDNS_REGISTER_PARAM *p, char *replace_v6) { char *url; char url2[MAX_SIZE]; char url3[MAX_SIZE]; PACK *req, *ret; char key_str[MAX_SIZE]; UCHAR machine_key[SHA1_SIZE]; char machine_key_str[MAX_SIZE]; char machine_name[MAX_SIZE]; BUF *cert_hash; UINT err = ERR_INTERNAL_ERROR; UCHAR key_hash[SHA1_SIZE]; char key_hash_str[MAX_SIZE]; bool use_azure = false; char current_azure_ip[MAX_SIZE]; INTERNET_SETTING t; UINT build = 0; bool use_https = false; bool use_vgs = false; // Validate arguments if (c == NULL) { return ERR_INTERNAL_ERROR; } Zero(current_azure_ip, sizeof(current_azure_ip)); GetCurrentMachineIpProcessHash(machine_key); BinToStr(machine_key_str, sizeof(machine_key_str), machine_key, sizeof(machine_key)); GetMachineHostName(machine_name, sizeof(machine_name)); StrLower(machine_name); if (ipv6 == false) { url = DDNS_URL_V4_GLOBAL; if (IsUseAlternativeHostname()) { url = DDNS_URL_V4_ALT; } } else { url = DDNS_URL_V6_GLOBAL; if (IsUseAlternativeHostname()) { url = DDNS_URL_V6_ALT; } if (replace_v6) { url = replace_v6; } } Zero(&t, sizeof(t)); if (ipv6 == false) { // Proxy Setting Copy(&t, &c->InternetSetting, sizeof(INTERNET_SETTING)); } if (ipv6 == false) { // Get the current status of the VPN Azure Client if (c->Cedar->Server != NULL) { AZURE_CLIENT *ac = c->Cedar->Server->AzureClient; if (ac != NULL) { use_azure = SiIsAzureEnabled(c->Cedar->Server); if (use_azure) { Lock(ac->Lock); { StrCpy(current_azure_ip, sizeof(current_azure_ip), ac->ConnectingAzureIp); } Unlock(ac->Lock); } } } } req = NewPack(); BinToStr(key_str, sizeof(key_str), c->Key, sizeof(c->Key)); StrUpper(key_str); PackAddStr(req, "key", key_str); // Build Number build = c->Cedar->Build; PackAddInt(req, "build", build); PackAddInt(req, "osinfo", GetOsInfo()->OsType); PackAddInt(req, "is_64bit", Is64()); #ifdef OS_WIN32 PackAddInt(req, "is_windows_64bit", MsIs64BitWindows()); #endif // OS_WIN32 PackAddBool(req, "is_softether", true); PackAddBool(req, "is_packetix", false); PackAddStr(req, "machine_key", machine_key_str); PackAddStr(req, "machine_name", machine_name); PackAddInt(req, "lasterror_ipv4", c->Err_IPv4_GetMyIp); PackAddInt(req, "lasterror_ipv6", c->Err_IPv6_GetMyIp); PackAddBool(req, "use_azure", use_azure); PackAddStr(req, "product_str", CEDAR_PRODUCT_STR); PackAddInt(req, "ddns_protocol_version", DDNS_VERSION); if (use_azure) { Debug("current_azure_ip = %s\n", current_azure_ip); PackAddStr(req, "current_azure_ip", current_azure_ip); } HashSha1(key_hash, key_str, StrLen(key_str)); BinToStr(key_hash_str, sizeof(key_hash_str), key_hash, sizeof(key_hash)); StrLower(key_hash_str); if (p != NULL) { if (IsEmptyStr(p->NewHostname) == false) { PackAddStr(req, "new_hostname", p->NewHostname); } } cert_hash = StrToBin(DDNS_CERT_HASH); Format(url2, sizeof(url2), "%s?v=%I64u", url, Rand64()); Format(url3, sizeof(url3), url2, key_hash_str[0], key_hash_str[1], key_hash_str[2], key_hash_str[3]); if (use_https == false) { ReplaceStr(url3, sizeof(url3), url3, "https://", "http://"); } ReplaceStr(url3, sizeof(url3), url3, ".servers", ".open.servers"); Debug("WpcCall: %s\n", url3); ret = WpcCallEx(url3, &t, DDNS_CONNECT_TIMEOUT, DDNS_COMM_TIMEOUT, "register", req, NULL, NULL, ((cert_hash != NULL && cert_hash->Size == SHA1_SIZE) ? cert_hash->Buf : NULL), NULL, DDNS_RPC_MAX_RECV_SIZE); Debug("WpcCall Ret: %u\n", ret); FreeBuf(cert_hash); FreePack(req); err = GetErrorFromPack(ret); ExtractAndApplyDynList(ret); // Status update Lock(c->Lock); { if (err == ERR_NO_ERROR) { char snat_t[MAX_SIZE]; char current_region[128]; // Current host name PackGetStr(ret, "current_hostname", c->CurrentHostName, sizeof(c->CurrentHostName)); PackGetStr(ret, "current_fqdn", c->CurrentFqdn, sizeof(c->CurrentFqdn)); PackGetStr(ret, "current_ipv4", c->CurrentIPv4, sizeof(c->CurrentIPv4)); PackGetStr(ret, "current_ipv6", c->CurrentIPv6, sizeof(c->CurrentIPv6)); PackGetStr(ret, "dns_suffix", c->DnsSuffix, sizeof(c->DnsSuffix)); PackGetStr(ret, "current_region", current_region, sizeof(current_region)); // SecureNAT connectivity check parameters Zero(snat_t, sizeof(snat_t)); PackGetStr(ret, "snat_t", snat_t, sizeof(snat_t)); NnSetSecureNatTargetHostname(snat_t); if (ipv6 == false) { char cert_hash[MAX_SIZE]; PackGetStr(ret, "current_azure_ip", c->CurrentAzureIp, sizeof(c->CurrentAzureIp)); c->CurrentAzureTimestamp = PackGetInt64(ret, "current_azure_timestamp"); PackGetStr(ret, "current_azure_signature", c->CurrentAzureSignature, sizeof(c->CurrentAzureSignature)); Zero(cert_hash, sizeof(cert_hash)); PackGetStr(ret, "azure_cert_hash", cert_hash, sizeof(cert_hash)); if (IsEmptyStr(cert_hash) == false) { StrCpy(c->AzureCertHash, sizeof(c->AzureCertHash), cert_hash); } } StrCpy(c->Cedar->CurrentDDnsFqdn, sizeof(c->Cedar->CurrentDDnsFqdn), c->CurrentFqdn); Debug("current_hostname=%s, current_fqdn=%s, current_ipv4=%s, current_ipv6=%s, current_azure_ip=%s, CurrentAzureTimestamp=%I64u, CurrentAzureSignature=%s, CertHash=%s\n", c->CurrentHostName, c->CurrentFqdn, c->CurrentIPv4, c->CurrentIPv6, c->CurrentAzureIp, c->CurrentAzureTimestamp, c->CurrentAzureSignature, c->AzureCertHash); if (IsEmptyStr(current_region) == false) { // Update the current region SiUpdateCurrentRegion(c->Cedar, current_region, false); } } } Unlock(c->Lock); if (IsEmptyStr(c->CurrentFqdn) == false) { SetCurrentDDnsFqdn(c->CurrentFqdn); } FreePack(ret); UniDebug(L"DCRegister Error: %s\n", _E(err)); if (err == ERR_DUPLICATE_DDNS_KEY) { // Key duplication DCGenNewKey(c->Key); c->KeyChanged = true; } if (err == ERR_DISCONNECTED) { err = ERR_DDNS_DISCONNECTED; } if (IsUseAlternativeHostname() == false) { if (err == ERR_CONNECT_FAILED) { if (ipv6 && replace_v6 == NULL) { UINT type = DetectFletsType(); if (type & FLETS_DETECT_TYPE_EAST_BFLETS_PRIVATE && err != ERR_NO_ERROR) { err = DCRegister(c, ipv6, p, DDNS_REPLACE_URL_FOR_EAST_BFLETS); } if (type & FLETS_DETECT_TYPE_EAST_NGN_PRIVATE && err != ERR_NO_ERROR) { err = DCRegister(c, ipv6, p, DDNS_REPLACE_URL_FOR_EAST_NGN); } if (type & FLETS_DETECT_TYPE_WEST_NGN_PRIVATE && err != ERR_NO_ERROR) { err = DCRegister(c, ipv6, p, DDNS_REPLACE_URL_FOR_WEST_NGN); } } } } return err; }
// RPC call PACK *RpcCall(RPC *r, char *function_name, PACK *p) { PACK *ret; UINT num_retry = 0; UINT err = 0; // Validate arguments if (r == NULL || function_name == NULL) { return NULL; } // Debug("RpcCall: %s\n", function_name); Lock(r->Lock); { if (p == NULL) { p = NewPack(); } PackAddStr(p, "function_name", function_name); RETRY: err = 0; ret = RpcCallInternal(r, p); if (ret == NULL) { if (r->IsVpnServer && r->Sock != NULL) { if (num_retry < 1) { num_retry++; // Attempt to reconnect the RPC to the VPN Server err = AdminReconnect(r); if (err == ERR_NO_ERROR) { goto RETRY; } } } } FreePack(p); if (ret == NULL) { if (err == 0) { err = ERR_DISCONNECTED; } ret = PackError(err); PackAddInt(ret, "error_code", err); } } Unlock(r->Lock); return ret; }
// Wait for connection request void AcWaitForRequest(AZURE_CLIENT *ac, SOCK *s, AZURE_PARAM *param) { // Validate arguments if (ac == NULL || s == NULL || param == NULL) { return; } while (ac->Halt == false) { UCHAR uc; // Receive 1 byte if (RecvAll(s, &uc, 1, false) == 0) { break; } if (uc != 0) { // Receive a Pack PACK *p = RecvPackWithHash(s); if (p == NULL) { break; } else { // Verify contents of Pack char opcode[MAX_SIZE]; char cipher_name[MAX_SIZE]; char hostname[MAX_SIZE]; PackGetStr(p, "opcode", opcode, sizeof(opcode)); PackGetStr(p, "cipher_name", cipher_name, sizeof(cipher_name)); PackGetStr(p, "hostname", hostname, sizeof(hostname)); if (StrCmpi(opcode, "relay") == 0) { IP client_ip, server_ip; UINT client_port; UINT server_port; UCHAR session_id[SHA1_SIZE]; if (PackGetIp(p, "client_ip", &client_ip) && PackGetIp(p, "server_ip", &server_ip) && PackGetData2(p, "session_id", session_id, sizeof(session_id))) { client_port = PackGetInt(p, "client_port"); server_port = PackGetInt(p, "server_port"); if (client_port != 0 && server_port != 0) { SOCK *ns; Debug("Connect Request from %r:%u\n", &client_ip, client_port); // Create new socket and connect VPN Azure Server if (ac->DDnsStatusCopy.InternetSetting.ProxyType == PROXY_DIRECT) { ns = ConnectEx2(ac->DDnsStatusCopy.CurrentAzureIp, AZURE_SERVER_PORT, 0, (bool *)&ac->Halt); } else { ns = WpcSockConnect2(ac->DDnsStatusCopy.CurrentAzureIp, AZURE_SERVER_PORT, &ac->DDnsStatusCopy.InternetSetting, NULL, AZURE_VIA_PROXY_TIMEOUT); } if (ns == NULL) { Debug("Connect Error.\n"); } else { Debug("Connected to the relay server.\n"); SetTimeout(ns, param->DataTimeout); if (StartSSLEx(ns, NULL, NULL, true, 0, NULL)) { // Check certification char server_cert_hash_str[MAX_SIZE]; UCHAR server_cert_hash[SHA1_SIZE]; Zero(server_cert_hash, sizeof(server_cert_hash)); GetXDigest(ns->RemoteX, server_cert_hash, true); BinToStr(server_cert_hash_str, sizeof(server_cert_hash_str), server_cert_hash, SHA1_SIZE); if (IsEmptyStr(ac->DDnsStatusCopy.AzureCertHash) || StrCmpi(server_cert_hash_str, ac->DDnsStatusCopy.AzureCertHash) == 0) { if (SendAll(ns, AZURE_PROTOCOL_DATA_SIANGTURE, 24, true)) { PACK *p2 = NewPack(); PackAddStr(p2, "hostname", hostname); PackAddData(p2, "session_id", session_id, sizeof(session_id)); if (SendPackWithHash(ns, p2)) { UCHAR uc; if (RecvAll(ns, &uc, 1, true) != false) { if (uc != 0) { SOCK *accept_sock = GetReverseListeningSock(ac->Cedar); if (accept_sock != NULL) { AddRef(ns->ref); SetTimeout(ns, INFINITE); Copy(&ns->Reverse_MyServerGlobalIp, &server_ip, sizeof(IP)); ns->Reverse_MyServerPort = server_port; InjectNewReverseSocketToAccept(accept_sock, ns, &client_ip, client_port); ReleaseSock(accept_sock); } } } } FreePack(p2); } } } ReleaseSock(ns); } } } } FreePack(p); } } // Send 1 byte uc = 0; if (SendAll(s, &uc, 1, false) == 0) { break; } } }
// VPN Azure client main thread void AcMainThread(THREAD *thread, void *param) { AZURE_CLIENT *ac = (AZURE_CLIENT *)param; UINT last_ip_revision = INFINITE; UINT64 last_reconnect_tick = 0; UINT64 next_reconnect_interval = AZURE_CONNECT_INITIAL_RETRY_INTERVAL; UINT num_reconnect_retry = 0; UINT64 next_ddns_retry_tick = 0; bool last_connect_ok = false; // Validate arguments if (ac == NULL || thread == NULL) { return; } while (ac->Halt == false) { UINT64 now = Tick64(); bool connect_was_ok = false; // Wait for enabling VPN Azure function if (ac->IsEnabled) { // VPN Azure is enabled DDNS_CLIENT_STATUS st; bool connect_now = false; bool azure_ip_changed = false; Lock(ac->Lock); { Copy(&st, &ac->DDnsStatus, sizeof(DDNS_CLIENT_STATUS)); if (StrCmpi(st.CurrentAzureIp, ac->DDnsStatusCopy.CurrentAzureIp) != 0) { if (IsEmptyStr(st.CurrentAzureIp) == false) { // Destination IP address is changed connect_now = true; num_reconnect_retry = 0; } } if (StrCmpi(st.CurrentHostName, ac->DDnsStatusCopy.CurrentHostName) != 0) { // DDNS host name is changed connect_now = true; num_reconnect_retry = 0; } Copy(&ac->DDnsStatusCopy, &st, sizeof(DDNS_CLIENT_STATUS)); } Unlock(ac->Lock); if (last_ip_revision != ac->IpStatusRevision) { last_ip_revision = ac->IpStatusRevision; connect_now = true; num_reconnect_retry = 0; } if (last_reconnect_tick == 0 || (now >= (last_reconnect_tick + next_reconnect_interval))) { UINT r; last_reconnect_tick = now; num_reconnect_retry++; next_reconnect_interval = (UINT64)num_reconnect_retry * AZURE_CONNECT_INITIAL_RETRY_INTERVAL; next_reconnect_interval = MIN(next_reconnect_interval, AZURE_CONNECT_MAX_RETRY_INTERVAL); r = (UINT)next_reconnect_interval; r = GenRandInterval(r / 2, r); next_reconnect_interval = r; connect_now = true; } if (IsEmptyStr(st.CurrentAzureIp) == false && IsEmptyStr(st.CurrentHostName) == false) { if (connect_now) { SOCK *s; char *host = NULL; UINT port = AZURE_SERVER_PORT; Debug("VPN Azure: Connecting to %s...\n", st.CurrentAzureIp); if (ParseHostPort(st.CurrentAzureIp, &host, &port, AZURE_SERVER_PORT)) { if (st.InternetSetting.ProxyType == PROXY_DIRECT) { s = ConnectEx2(host, port, 0, (bool *)&ac->Halt); } else { s = WpcSockConnect2(host, port, &st.InternetSetting, NULL, AZURE_VIA_PROXY_TIMEOUT); } if (s != NULL) { PACK *p; UINT64 established_tick = 0; Debug("VPN Azure: Connected.\n"); SetTimeout(s, AZURE_PROTOCOL_CONTROL_TIMEOUT_DEFAULT); Lock(ac->Lock); { ac->CurrentSock = s; ac->IsConnected = true; StrCpy(ac->ConnectingAzureIp, sizeof(ac->ConnectingAzureIp), st.CurrentAzureIp); } Unlock(ac->Lock); SendAll(s, AZURE_PROTOCOL_CONTROL_SIGNATURE, StrLen(AZURE_PROTOCOL_CONTROL_SIGNATURE), false); // Receive parameter p = RecvPackWithHash(s); if (p != NULL) { UCHAR c; AZURE_PARAM param; bool hostname_changed = false; Zero(¶m, sizeof(param)); param.ControlKeepAlive = PackGetInt(p, "ControlKeepAlive"); param.ControlTimeout = PackGetInt(p, "ControlTimeout"); param.DataTimeout = PackGetInt(p, "DataTimeout"); param.SslTimeout = PackGetInt(p, "SslTimeout"); FreePack(p); param.ControlKeepAlive = MAKESURE(param.ControlKeepAlive, 1000, AZURE_SERVER_MAX_KEEPALIVE); param.ControlTimeout = MAKESURE(param.ControlTimeout, 1000, AZURE_SERVER_MAX_TIMEOUT); param.DataTimeout = MAKESURE(param.DataTimeout, 1000, AZURE_SERVER_MAX_TIMEOUT); param.SslTimeout = MAKESURE(param.SslTimeout, 1000, AZURE_SERVER_MAX_TIMEOUT); Lock(ac->Lock); { Copy(&ac->AzureParam, ¶m, sizeof(AZURE_PARAM)); } Unlock(ac->Lock); SetTimeout(s, param.ControlTimeout); // Send parameter p = NewPack(); PackAddStr(p, "CurrentHostName", st.CurrentHostName); PackAddStr(p, "CurrentAzureIp", st.CurrentAzureIp); PackAddInt64(p, "CurrentAzureTimestamp", st.CurrentAzureTimestamp); PackAddStr(p, "CurrentAzureSignature", st.CurrentAzureSignature); Lock(ac->Lock); { if (StrCmpi(st.CurrentHostName, ac->DDnsStatus.CurrentHostName) != 0) { hostname_changed = true; } } Unlock(ac->Lock); if (hostname_changed == false) { if (SendPackWithHash(s, p)) { // Receive result if (RecvAll(s, &c, 1, false)) { if (c && ac->Halt == false) { connect_was_ok = true; established_tick = Tick64(); AcWaitForRequest(ac, s, ¶m); } } } } FreePack(p); } else { WHERE; } Debug("VPN Azure: Disconnected.\n"); Lock(ac->Lock); { ac->IsConnected = false; ac->CurrentSock = NULL; ClearStr(ac->ConnectingAzureIp, sizeof(ac->ConnectingAzureIp)); } Unlock(ac->Lock); if (established_tick != 0) { if ((established_tick + (UINT64)AZURE_CONNECT_MAX_RETRY_INTERVAL) <= Tick64()) { // If the connected time exceeds the AZURE_CONNECT_MAX_RETRY_INTERVAL, reset the retry counter. last_reconnect_tick = 0; num_reconnect_retry = 0; next_reconnect_interval = AZURE_CONNECT_INITIAL_RETRY_INTERVAL; } } Disconnect(s); ReleaseSock(s); } else { Debug("VPN Azure: Error: Connect Failed.\n"); } Free(host); } } } } else { last_reconnect_tick = 0; num_reconnect_retry = 0; next_reconnect_interval = AZURE_CONNECT_INITIAL_RETRY_INTERVAL; } if (ac->Halt) { break; } if (connect_was_ok) { // If connection goes out after connected, increment connection success count to urge DDNS client query next_ddns_retry_tick = Tick64() + MIN((UINT64)DDNS_VPN_AZURE_CONNECT_ERROR_DDNS_RETRY_TIME_DIFF * (UINT64)(num_reconnect_retry + 1), (UINT64)DDNS_VPN_AZURE_CONNECT_ERROR_DDNS_RETRY_TIME_DIFF_MAX); } if ((next_ddns_retry_tick != 0) && (Tick64() >= next_ddns_retry_tick)) { next_ddns_retry_tick = 0; ac->DDnsTriggerInt++; } Wait(ac->Event, rand() % 1000); } }
PACK *WpcCallEx2(char *url, INTERNET_SETTING *setting, UINT timeout_connect, UINT timeout_comm, char *function_name, PACK *pack, X *cert, K *key, void *sha1_cert_hash, UINT num_hashes, bool *cancel, UINT max_recv_size, char *additional_header_name, char *additional_header_value, char *sni_string) { URL_DATA data; BUF *b, *recv; UINT error; WPC_PACKET packet; // Validate arguments if (function_name == NULL || pack == NULL) { return PackError(ERR_INTERNAL_ERROR); } if (ParseUrl(&data, url, true, NULL) == false) { return PackError(ERR_INTERNAL_ERROR); } PackAddStr(pack, "function", function_name); b = WpcGeneratePacket(pack, cert, key); if (b == NULL) { return PackError(ERR_INTERNAL_ERROR); } SeekBuf(b, b->Size, 0); WriteBufInt(b, 0); SeekBuf(b, 0, 0); if (IsEmptyStr(additional_header_name) == false && IsEmptyStr(additional_header_value) == false) { StrCpy(data.AdditionalHeaderName, sizeof(data.AdditionalHeaderName), additional_header_name); StrCpy(data.AdditionalHeaderValue, sizeof(data.AdditionalHeaderValue), additional_header_value); } if (sni_string != NULL && IsEmptyStr(sni_string) == false) { StrCpy(data.SniString, sizeof(data.SniString), sni_string); } recv = HttpRequestEx3(&data, setting, timeout_connect, timeout_comm, &error, false, b->Buf, NULL, NULL, sha1_cert_hash, num_hashes, cancel, max_recv_size, NULL, NULL); FreeBuf(b); if (recv == NULL) { return PackError(error); } if (WpcParsePacket(&packet, recv) == false) { FreeBuf(recv); return PackError(ERR_PROTOCOL_ERROR); } FreeBuf(recv); FreeX(packet.Cert); return packet.Pack; }