#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef CONFIG #define CONFIG "config.h" #endif // CONFIG #include CONFIG #if defined(USE_MSRPC) && !defined(_WIN32) && !defined(__CYGWIN__) #error Microsoft RPC is only available on Windows and Cygwin #endif #if defined(USE_MSRPC) && defined(SIMPLE_SOCKETS) #error You can only define either USE_MSRPC or SIMPLE_SOCKETS but not both #endif #if defined(NO_SOCKETS) && defined(USE_MSRPC) #error Cannot use inetd mode with Microsoft RPC #endif #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #if _MSC_VER #include "wingetopt.h" #endif #ifndef _WIN32 #include #include #include #if !defined(NO_LIMIT) && !__minix__ #include #if !__ANDROID__ #include #endif // !__ANDROID__ #endif // !defined(NO_LIMIT) && !__minix__ #include #include #include #include #ifndef NO_LIMIT #include #endif // NO_LIMIT #endif // !_WIN32 #if __APPLE__ #include #endif // __APPLE__ #if __linux__ && defined(USE_AUXV) #include #endif #if __FreeBSD__ #include #endif #include "vlmcsd.h" // ReSharper disable CppUnusedIncludeDirective #include "endian.h" // ReSharper restore CppUnusedIncludeDirective #include "shared_globals.h" #include "output.h" #ifndef USE_MSRPC #include "network.h" #else // USE_MSRPC #include "msrpc-server.h" #endif // USE_MSRPC #include "ntservice.h" #include "helpers.h" #ifndef NO_TAP #include "wintap.h" #endif static const char* const optstring = "N:B:m:t:w:0:3:6:H:A:R:u:G:g:L:p:i:P:l:r:U:W:C:c:F:O:o:x:T:K:E:M:j:SseDdVvqkZ"; #if !defined(NO_SOCKETS) && !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) static uint_fast8_t maxsockets = 0; #endif // !defined(NO_SOCKETS) && !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) #ifdef _NTSERVICE static int_fast8_t installService = 0; static const char *restrict ServiceUser = NULL; static const char *restrict ServicePassword = ""; #endif #ifndef NO_PID_FILE static const char *fn_pid = NULL; #endif #ifndef NO_INI_FILE #ifdef INI_FILE static const char *fn_ini = INI_FILE; #else // !INI_FILE static const char *fn_ini = NULL; #endif // !INI_FILE #ifndef NO_TAP char* tapArgument = NULL; #endif // NO_TAP static const char* IniFileErrorMessage = ""; char* IniFileErrorBuffer = NULL; #define INIFILE_ERROR_BUFFERSIZE 256 static IniFileParameter_t IniFileParameterList[] = { { "Windows", INI_PARAM_WINDOWS }, { "Office2010", INI_PARAM_OFFICE2010 }, { "Office2013", INI_PARAM_OFFICE2013 }, { "Office2016", INI_PARAM_OFFICE2016 }, { "WinChinaGov", INI_PARAM_WINCHINAGOV }, # ifndef NO_SOCKETS { "ExitLevel", INI_PARAM_EXIT_LEVEL }, # endif // NO_SOCKETS # ifndef NO_TAP { "VPN", INI_PARAM_VPN }, # endif // NO_TAP # ifndef NO_EXTERNAL_DATA { "KmsData", INI_PARAM_DATA_FILE }, # endif // NO_EXTERNAL_DATA # ifndef NO_STRICT_MODES { "WhiteListingLevel", INI_PARAM_WHITELISTING_LEVEL }, { "CheckClientTime", INI_PARAM_CHECK_CLIENT_TIME }, # ifndef NO_CLIENT_LIST { "StartEmpty", INI_PARAM_START_EMPTY }, { "MaintainClients", INI_PARAM_MAINTAIN_CLIENTS }, # endif // NO_CLIENT_LIST # endif // NO_STRICT_MODES # ifndef NO_RANDOM_EPID { "RandomizationLevel", INI_PARAM_RANDOMIZATION_LEVEL }, { "LCID", INI_PARAM_LCID }, # endif // NO_RANDOM_EPID # if !defined(NO_SOCKETS) && (defined(USE_MSRPC) || defined(SIMPLE_SOCKETS) || defined(HAVE_GETIFADDR)) { "Port", INI_PARAM_PORT }, # endif // defined(USE_MSRPC) || defined(SIMPLE_SOCKETS) # if !defined(NO_SOCKETS) && !defined(USE_MSRPC) # ifndef SIMPLE_SOCKETS { "Listen", INI_PARAM_LISTEN }, # endif // SIMPLE_SOCKETS # if HAVE_FREEBIND { "FreeBind", INI_PARAM_FREEBIND }, # endif // HAVE_FREEBIND # if !defined(NO_LIMIT) && !__minix__ { "MaxWorkers", INI_PARAM_MAX_WORKERS }, # endif // !defined(NO_LIMIT) && !__minix__ # endif // !defined(NO_SOCKETS) && !defined(USE_MSRPC) # if !defined(NO_TIMEOUT) && !__minix__ && !defined(USE_MSRPC) & !defined(USE_MSRPC) { "ConnectionTimeout", INI_PARAM_CONNECTION_TIMEOUT }, # endif // !defined(NO_TIMEOUT) && !__minix__ && !defined(USE_MSRPC) & !defined(USE_MSRPC) # ifndef USE_MSRPC { "DisconnectClientsImmediately", INI_PARAM_DISCONNECT_IMMEDIATELY }, # ifndef SIMPLE_RPC { "UseNDR64", INI_PARAM_RPC_NDR64 }, { "UseBTFN", INI_PARAM_RPC_BTFN }, # endif // !SIMPLE_RPC # endif // USE_MSRPC # ifndef NO_PID_FILE { "PIDFile", INI_PARAM_PID_FILE }, # endif // NO_PID_FILE # ifndef NO_LOG { "LogDateAndTime", INI_PARAM_LOG_DATE_AND_TIME }, { "LogFile", INI_PARAM_LOG_FILE }, # ifndef NO_VERBOSE_LOG { "LogVerbose", INI_PARAM_LOG_VERBOSE }, # endif // NO_VERBOSE_LOG # endif // NO_LOG # ifndef NO_CUSTOM_INTERVALS {"ActivationInterval", INI_PARAM_ACTIVATION_INTERVAL }, {"RenewalInterval", INI_PARAM_RENEWAL_INTERVAL }, # endif // NO_CUSTOM_INTERVALS # if !defined(NO_USER_SWITCH) && !defined(_WIN32) { "user", INI_PARAM_UID }, { "group", INI_PARAM_GID}, # endif // !defined(NO_USER_SWITCH) && !defined(_WIN32) # if !defined(NO_PRIVATE_IP_DETECT) {"PublicIPProtectionLevel", INI_PARAM_PUBLIC_IP_PROTECTION_LEVEL }, # endif }; #endif // NO_INI_FILE #if !defined(NO_LIMIT) && !defined (NO_SOCKETS) && !__minix__ #if !defined(USE_THREADS) && !defined(CYGWIN) && !defined(USE_MSRPC) static int shmid = -1; #endif #endif // !defined(NO_LIMIT) && !defined (NO_SOCKETS) && !__minix__ #ifndef NO_USER_SWITCH #ifndef _WIN32 static const char *uname = NULL, *gname = NULL; static gid_t gid = INVALID_GID; static uid_t uid = INVALID_UID; // Get Numeric id of user/group static char GetNumericId(gid_t *restrict id, const char *const c) { char* endptr; gid_t temp; temp = (gid_t)strtoll(c, &endptr, 10); if (!*endptr) *id = temp; if (*endptr || temp == (gid_t)-1) errno = EINVAL; return *endptr || *id == (gid_t)-1; } // Get group id from option argument static char GetGid() { struct group *g; if ((g = getgrnam(optarg))) gid = g->gr_gid; else return GetNumericId(&gid, optarg); return 0; } // Get user id from option argument static char GetUid() { struct passwd *u; ////PORTABILITY: Assumes uid_t and gid_t are of same size (shouldn't be a problem) if ((u = getpwnam(optarg))) uid = u->pw_uid; else return GetNumericId((gid_t*)&uid, optarg); return 0; } #endif // _WIN32 #endif //NO_USER_SWITCH #ifdef NO_HELP static __noreturn void usage() { printerrorf("Incorrect parameters\n\n"); exit(VLMCSD_EINVAL); } #else // HELP static __noreturn void usage() { printerrorf("vlmcsd %s\n" "\nUsage:\n" " %s [ options ]\n\n" "Where:\n" # ifndef NO_CL_PIDS " -w \t\talways use for Windows\n" " -0 \t\talways use for Office2010\n" " -3 \t\talways use for Office2013\n" " -6 \t\talways use for Office2016\n" " -G \t\talways use for Win China Gov\n" " -H \t\talways use hardware Id \n" # endif // NO_CL_PIDS # if !defined(_WIN32) && !defined(NO_USER_SWITCH) " -u \t\tset uid to \n" " -g \t\tset gid to \n" # endif // !defined(_WIN32) && !defined(NO_USER_SWITCH) # ifndef NO_RANDOM_EPID " -r 0|1|2\t\tset ePID randomization level (default 1)\n" " -C \t\tuse fixed in random ePIDs\n" # endif // NO_RANDOM_EPID # if !defined(NO_PRIVATE_IP_DETECT) # if HAVE_GETIFADDR " -o 0|1|2|3\t\tset protection level against clients with public IP addresses (default 0)\n" # else // !HAVE_GETIFADDR # ifndef USE_MSRPC " -o 0|2\t\tset protection level against clients with public IP addresses (default 0)\n" # else // USE_MSRPC " -o 0|2\t\tset protection level against clients with public IP addresses (default 0). Limited use with MS RPC\n" # endif // USE_MSRPC # endif // !HAVE_GETIFADDR # endif // !defined(NO_PRIVATE_IP_DETECT) # ifndef NO_TAP " -O [=][/]\tuse VPN adapter with IPv4 address and CIDR \n" # endif # ifndef NO_SOCKETS " -x \t\texit if warning reached (default 0)\n" # if !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) " -L
[:]\tlisten on IP address
with optional \n" " -P \t\tset TCP port for subsequent -L statements (default 1688)\n" # if HAVE_FREEBIND " -F0, -F1\t\tdisable/enable binding to foreign IP addresses\n" # endif // HAVE_FREEBIND # else // defined(USE_MSRPC) || defined(SIMPLE_SOCKETS) " -P \t\tuse TCP port (default 1688)\n" # endif // defined(USE_MSRPC) || defined(SIMPLE_SOCKETS) # if !defined(NO_LIMIT) && !__minix__ " -m \t\tHandle max. simultaneously (default no limit)\n" # endif // !defined(NO_LIMIT) && !__minix__ # ifdef _NTSERVICE " -s\t\t\tinstall vlmcsd as an NT service. Ignores -e" # ifndef _WIN32 ", -f and -D" # endif // _WIN32 "\n" " -S\t\t\tremove vlmcsd service. Ignores all other options\n" " -U \t\trun NT service as . Must be used with -s\n" " -W \t\toptional for -U. Must be used with -s\n" # endif // _NTSERVICE # ifndef NO_LOG " -e\t\t\tlog to stdout\n" # endif // NO_LOG # ifndef _WIN32 // " -D\t\t\trun in foreground\n" # else // _WIN32 " -D\t\t\tdoes nothing. Provided for compatibility with POSIX versions only\n" # endif // _WIN32 # endif // NO_SOCKETS # ifndef NO_STRICT_MODES " -K 0|1|2|3\t\tset whitelisting level for KMS IDs (default -K0)\n" " -c0, -c1\t\tdisable/enable client time checking (default -c0)\n" # ifndef NO_CLIENT_LIST " -M0, -M1\t\tdisable/enable maintaining clients (default -M0)\n" " -E0, -E1\t\tdisable/enable start with empty client list (default -E0, ignored if -M0)\n" # endif // !NO_CLIENT_LIST # endif // !NO_STRICT_MODES # ifndef USE_MSRPC # if !defined(NO_TIMEOUT) && !__minix__ " -t \t\tdisconnect clients after of inactivity (default 30)\n" # endif // !defined(NO_TIMEOUT) && !__minix__ " -d\t\t\tdisconnect clients after each request\n" " -k\t\t\tdon't disconnect clients after each request (default)\n" # ifndef SIMPLE_RPC " -N0, -N1\t\tdisable/enable NDR64\n" " -B0, -B1\t\tdisable/enable bind time feature negotiation\n" # endif // !SIMPLE_RPC # endif // USE_MSRPC # ifndef NO_PID_FILE " -p \t\twrite pid to \n" # endif // NO_PID_FILE # ifndef NO_INI_FILE " -i \t\tuse config file \n" # endif // NO_INI_FILE # ifndef NO_EXTERNAL_DATA " -j \t\tuse KMS data file \n" # endif // !NO_EXTERNAL_DATA # ifndef NO_CUSTOM_INTERVALS " -R \t\trenew activation every (default 1w)\n" " -A \t\tretry activation every (default 2h)\n" # endif // NO_CUSTOM_INTERVALS # ifndef NO_LOG # ifndef _WIN32 " -l syslog log to syslog\n" # endif // _WIN32 " -l \t\tlog to \n" " -T0, -T1\t\tdisable/enable logging with time and date (default -T1)\n" # ifndef NO_VERBOSE_LOG " -v\t\t\tlog verbose\n" " -q\t\t\tdon't log verbose (default)\n" # endif // NO_VERBOSE_LOG # endif // NO_LOG # ifndef NO_VERSION_INFORMATION " -V\t\t\tdisplay version information and exit\n" # endif // NO_VERSION_INFORMATION , Version, global_argv[0]); exit(VLMCSD_EINVAL); } #endif // HELP #ifndef NO_CUSTOM_INTERVALS #ifndef NO_INI_FILE __pure static BOOL getTimeSpanFromIniFile(DWORD* result, const char *const restrict argument) { DWORD val = timeSpanString2Minutes(argument); if (!val) { IniFileErrorMessage = "Incorrect time span."; return FALSE; } *result = val; return TRUE; } #endif // NO_INI_FILE __pure static DWORD getTimeSpanFromCommandLine(const char *const restrict arg, const char optchar) { DWORD val = timeSpanString2Minutes(arg); if (!val) { printerrorf("Fatal: No valid time span specified in option -%c.\n", optchar); exit(VLMCSD_EINVAL); } return val; } #endif // NO_CUSTOM_INTERVALS #ifndef NO_INI_FILE static void ignoreIniFileParameter(uint_fast8_t iniFileParameterId) { uint_fast8_t i; for (i = 0; i < vlmcsd_countof(IniFileParameterList); i++) { if (IniFileParameterList[i].Id != iniFileParameterId) continue; IniFileParameterList[i].Id = 0; break; } } #else // NO_INI_FILE #define ignoreIniFileParameter(x) #endif // NO_INI_FILE #ifndef NO_INI_FILE static BOOL getIniFileArgumentBool(int_fast8_t *result, const char *const argument) { IniFileErrorMessage = "Argument must be true/on/yes/1 or false/off/no/0"; return getArgumentBool(result, argument); } static BOOL getIniFileArgumentInt(unsigned int *result, const char *const argument, const unsigned int min, const unsigned int max) { unsigned int tempResult; if (!stringToInt(argument, min, max, &tempResult)) { vlmcsd_snprintf(IniFileErrorBuffer, INIFILE_ERROR_BUFFERSIZE, "Must be integer between %u and %u", min, max); IniFileErrorMessage = IniFileErrorBuffer; return FALSE; } *result = tempResult; return TRUE; } static __pure int isControlCharOrSlash(const char c) { if ((unsigned char)c < '!') return TRUE; if (c == '/') return TRUE; return FALSE; } static void iniFileLineNextWord(const char **s) { while (**s && isspace((int)**s)) (*s)++; } static BOOL setHwIdFromIniFileLine(const char **s, const uint32_t index) { iniFileLineNextWord(s); if (**s == '/') { if (KmsResponseParameters[index].HwId) return TRUE; BYTE* HwId = (BYTE*)vlmcsd_malloc(sizeof(((RESPONSE_V6 *)0)->HwId)); hex2bin(HwId, *s + 1, sizeof(((RESPONSE_V6 *)0)->HwId)); KmsResponseParameters[index].HwId = HwId; } return TRUE; } static BOOL setEpidFromIniFileLine(const char **s, const uint32_t index) { iniFileLineNextWord(s); const char *savedPosition = *s; uint_fast16_t i; for (i = 0; !isControlCharOrSlash(**s); i++) { if (utf8_to_ucs2_char((const unsigned char*)*s, (const unsigned char**)s) == (WCHAR)~0) { return FALSE; } } if (i < 1 || i >= PID_BUFFER_SIZE) return FALSE; if (KmsResponseParameters[index].Epid) return TRUE; size_t size = *s - savedPosition + 1; char* epidbuffer = (char*)vlmcsd_malloc(size); memcpy(epidbuffer, savedPosition, size - 1); epidbuffer[size - 1] = 0; KmsResponseParameters[index].Epid = epidbuffer; #ifndef NO_LOG KmsResponseParameters[index].EpidSource = fn_ini; #endif //NO_LOG return TRUE; } static BOOL setIniFileParameter(uint_fast8_t id, const char *const iniarg) { unsigned int result; BOOL success = TRUE; const char *s = (const char*)iniarg; switch (id) { case INI_PARAM_WINDOWS: setEpidFromIniFileLine(&s, EPID_INDEX_WINDOWS); setHwIdFromIniFileLine(&s, EPID_INDEX_WINDOWS); break; case INI_PARAM_OFFICE2010: setEpidFromIniFileLine(&s, EPID_INDEX_OFFICE2010); setHwIdFromIniFileLine(&s, EPID_INDEX_OFFICE2010); break; case INI_PARAM_OFFICE2013: setEpidFromIniFileLine(&s, EPID_INDEX_OFFICE2013); setHwIdFromIniFileLine(&s, EPID_INDEX_OFFICE2013); break; case INI_PARAM_OFFICE2016: setEpidFromIniFileLine(&s, EPID_INDEX_OFFICE2016); setHwIdFromIniFileLine(&s, EPID_INDEX_OFFICE2016); break; case INI_PARAM_WINCHINAGOV: setEpidFromIniFileLine(&s, EPID_INDEX_WINCHINAGOV); setHwIdFromIniFileLine(&s, EPID_INDEX_WINCHINAGOV); break; # ifndef NO_TAP case INI_PARAM_VPN: tapArgument = (char*)vlmcsd_strdup(iniarg); break; # endif // NO_TAP # if !defined(NO_USER_SWITCH) && !_WIN32 case INI_PARAM_GID: { struct group *g; IniFileErrorMessage = "Invalid group id or name"; if (!(gname = vlmcsd_strdup(iniarg))) return FALSE; if ((g = getgrnam(iniarg))) gid = g->gr_gid; else success = !GetNumericId(&gid, iniarg); break; } case INI_PARAM_UID: { struct passwd *p; IniFileErrorMessage = "Invalid user id or name"; if (!(uname = vlmcsd_strdup(iniarg))) return FALSE; if ((p = getpwnam(iniarg))) uid = p->pw_uid; else success = !GetNumericId(&uid, iniarg); break; } # endif // !defined(NO_USER_SWITCH) && !defined(_WIN32) # ifndef NO_RANDOM_EPID case INI_PARAM_LCID: success = getIniFileArgumentInt(&result, iniarg, 0, 32767); if (success) Lcid = (uint16_t)result; break; case INI_PARAM_RANDOMIZATION_LEVEL: success = getIniFileArgumentInt(&result, iniarg, 0, 2); if (success) RandomizationLevel = (int_fast8_t)result; break; # endif // NO_RANDOM_EPID # if (defined(USE_MSRPC) || defined(SIMPLE_SOCKETS) || defined(HAVE_GETIFADDR)) && !defined(NO_SOCKETS) case INI_PARAM_PORT: defaultport = vlmcsd_strdup(iniarg); break; # endif // (defined(USE_MSRPC) || defined(SIMPLE_SOCKETS) || defined(HAVE_GETIFADDR)) && !defined(NO_SOCKETS) # if !defined(NO_SOCKETS) && !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) case INI_PARAM_LISTEN: maxsockets++; return TRUE; # endif // !defined(NO_SOCKETS) && !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) # if !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !__minix__ case INI_PARAM_MAX_WORKERS: # ifdef USE_MSRPC success = getIniFileArgumentInt(&MaxTasks, iniarg, 1, RPC_C_LISTEN_MAX_CALLS_DEFAULT); # else // !USE_MSRPC success = getIniFileArgumentInt(&MaxTasks, iniarg, 1, SEM_VALUE_MAX); # endif // !USE_MSRPC break; # endif // !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !__minix__ # ifndef NO_PID_FILE case INI_PARAM_PID_FILE: fn_pid = vlmcsd_strdup(iniarg); break; # endif // NO_PID_FILE # ifndef NO_EXTERNAL_DATA case INI_PARAM_DATA_FILE: fn_data = vlmcsd_strdup(iniarg); # ifndef NO_INTERNAL_DATA ExplicitDataLoad = TRUE; # endif // NO_INTERNAL_DATA break; # endif // NO_EXTERNAL_DATA # ifndef NO_STRICT_MODES case INI_PARAM_WHITELISTING_LEVEL: success = getIniFileArgumentInt(&WhitelistingLevel, iniarg, 0, 3); break; case INI_PARAM_CHECK_CLIENT_TIME: success = getIniFileArgumentBool(&CheckClientTime, iniarg); break; # ifndef NO_CLIENT_LIST case INI_PARAM_MAINTAIN_CLIENTS: success = getIniFileArgumentBool(&MaintainClients, iniarg); break; case INI_PARAM_START_EMPTY: success = getIniFileArgumentBool(&StartEmpty, iniarg); break; # endif // NO_CLIENT_LIST # endif // !NO_STRICT_MODES # ifndef NO_LOG case INI_PARAM_LOG_FILE: fn_log = vlmcsd_strdup(iniarg); break; case INI_PARAM_LOG_DATE_AND_TIME: success = getIniFileArgumentBool(&LogDateAndTime, iniarg); break; # ifndef NO_VERBOSE_LOG case INI_PARAM_LOG_VERBOSE: success = getIniFileArgumentBool(&logverbose, iniarg); break; # endif // NO_VERBOSE_LOG # endif // NO_LOG # ifndef NO_CUSTOM_INTERVALS case INI_PARAM_ACTIVATION_INTERVAL: success = getTimeSpanFromIniFile(&VLActivationInterval, iniarg); break; case INI_PARAM_RENEWAL_INTERVAL: success = getTimeSpanFromIniFile(&VLRenewalInterval, iniarg); break; # endif // NO_CUSTOM_INTERVALS # ifndef USE_MSRPC # if !defined(NO_TIMEOUT) && !__minix__ case INI_PARAM_CONNECTION_TIMEOUT: success = getIniFileArgumentInt(&result, iniarg, 1, 600); if (success) ServerTimeout = (DWORD)result; break; # endif // !defined(NO_TIMEOUT) && !__minix__ case INI_PARAM_DISCONNECT_IMMEDIATELY: success = getIniFileArgumentBool(&DisconnectImmediately, iniarg); break; case INI_PARAM_RPC_NDR64: success = getIniFileArgumentBool(&UseServerRpcNDR64, iniarg); break; case INI_PARAM_RPC_BTFN: success = getIniFileArgumentBool(&UseServerRpcBTFN, iniarg); break; # endif // USE_MSRPC # ifndef NO_SOCKETS case INI_PARAM_EXIT_LEVEL: success = getIniFileArgumentInt(&result, iniarg, 0, 1); if (success) ExitLevel = (int_fast8_t)result; break; # endif // NO_SOCKETS # if HAVE_FREEBIND case INI_PARAM_FREEBIND: success = getIniFileArgumentBool(&freebind, iniarg); break; # endif // HAVE_FREEBIND # if !defined(NO_PRIVATE_IP_DETECT) case INI_PARAM_PUBLIC_IP_PROTECTION_LEVEL: success = getIniFileArgumentInt(&PublicIPProtectionLevel, iniarg, 0, 3); # if !HAVE_GETIFADDR if (PublicIPProtectionLevel & 1) { IniFileErrorMessage = "Must be 0 or 2"; success = FALSE; } # endif // !HAVE_GETIFADDR break; # endif // !defined(NO_PRIVATE_IP_DETECT) default: return FALSE; } return success; } static BOOL getIniFileArgument(const char **s) { while (!isspace((int)**s) && **s != '=' && **s) (*s)++; iniFileLineNextWord(s); if (*((*s)++) != '=') { IniFileErrorMessage = "'=' required after keyword."; return FALSE; } iniFileLineNextWord(s); if (!**s) { IniFileErrorMessage = "missing argument after '='."; return FALSE; } return TRUE; } static BOOL handleIniFileParameter(const char *s) { uint_fast8_t i; for (i = 0; i < vlmcsd_countof(IniFileParameterList); i++) { if (strncasecmp(IniFileParameterList[i].Name, s, strlen(IniFileParameterList[i].Name))) continue; if (!IniFileParameterList[i].Id) return TRUE; if (!getIniFileArgument(&s)) return FALSE; return setIniFileParameter(IniFileParameterList[i].Id, s); } IniFileErrorMessage = "Unknown keyword."; return FALSE; } #if !defined(NO_SOCKETS) && !defined(SIMPLE_SOCKETS) && !defined(USE_MSRPC) static BOOL setupListeningSocketsFromIniFile(const char *s) { if (!maxsockets) return TRUE; if (strncasecmp("Listen", s, 6)) return TRUE; if (!getIniFileArgument(&s)) return TRUE; vlmcsd_snprintf(IniFileErrorBuffer, INIFILE_ERROR_BUFFERSIZE, "Cannot listen on %s.", s); IniFileErrorMessage = IniFileErrorBuffer; return addListeningSocket(s); } #endif // !defined(NO_SOCKETS) && !defined(SIMPLE_SOCKETS) && !defined(USE_MSRPC) static BOOL readIniFile(const uint_fast8_t pass) { char line[256]; const char *s; unsigned int lineNumber; uint_fast8_t lineParseError; FILE *restrict f; BOOL result = TRUE; IniFileErrorBuffer = (char*)vlmcsd_malloc(INIFILE_ERROR_BUFFERSIZE); if (!((f = fopen(fn_ini, "r")))) return FALSE; for (lineNumber = 1; (s = fgets(line, sizeof(line), f)); lineNumber++) { line[strlen(line) - 1] = 0; iniFileLineNextWord(&s); if (*s == ';' || *s == '#' || !*s) continue; # ifndef NO_SOCKETS if (pass == INI_FILE_PASS_1) # endif // NO_SOCKETS { if (handleIniFileParameter(s)) continue; lineParseError = TRUE;/*!checkGuidInIniFileLine(&s, &appIndex) || !setEpidFromIniFileLine(&s, appIndex) || !setHwIdFromIniFileLine(&s, appIndex);*/ } # if !defined(NO_SOCKETS) && !defined(SIMPLE_SOCKETS) && !defined(USE_MSRPC) else if (pass == INI_FILE_PASS_2) { lineParseError = !setupListeningSocketsFromIniFile(s); } else { return FALSE; } # endif // !defined(NO_SOCKETS) && && !defined(SIMPLE_SOCKETS) && !defined(USE_MSRPC) if (lineParseError) { printerrorf("Warning: %s line %u: \"%s\". %s\n", fn_ini, lineNumber, line, IniFileErrorMessage); continue; } } if (ferror(f)) result = FALSE; free(IniFileErrorBuffer); fclose(f); # if !defined(NO_SOCKETS) && !defined(NO_LOG) if (pass == INI_FILE_PASS_1 && !InetdMode && result) { # ifdef _NTSERVICE if (!installService) # endif // _NTSERVICE logger("Read ini file %s\n", fn_ini); } # endif // !defined(NO_SOCKETS) && !defined(NO_LOG) return result; } #endif // NO_INI_FILE #if !defined(NO_SOCKETS) #if !defined(_WIN32) #if !defined(NO_SIGHUP) static void exec_self(char** argv) { getExeName(); if (fn_exe != NULL) { execv(fn_exe, argv); } else { execvp(argv[0], argv); } } __noreturn static void HangupHandler(const int signal_unused) { int i; int_fast8_t daemonize_protection = TRUE; CARGV argv_in = multi_argv == NULL ? global_argv : multi_argv; int argc_in = multi_argc == 0 ? global_argc : multi_argc; const char** argv_out = (const char**)vlmcsd_malloc((argc_in + 2) * sizeof(char**)); for (i = 0; i < argc_in; i++) { if (!strcmp(argv_in[i], "-Z")) daemonize_protection = FALSE; argv_out[i] = argv_in[i]; } argv_out[argc_in] = argv_out[argc_in + 1] = NULL; if (daemonize_protection) argv_out[argc_in] = (char*) "-Z"; exec_self((char**)argv_out); int error = errno; # ifndef NO_LOG logger("Fatal: Unable to restart on SIGHUP: %s\n", strerror(error)); # endif # ifndef NO_PID_FILE if (fn_pid) unlink(fn_pid); # endif // NO_PID_FILE exit(error); } #endif // NO_SIGHUP __noreturn static void terminationHandler(const int signal_unused) { cleanup(); exit(0); } #if defined(CHILD_HANDLER) || __minix__ static void childHandler(const int signal) { waitpid(-1, NULL, WNOHANG); } #endif // defined(CHILD_HANDLER) || __minix__ static int daemonizeAndSetSignalAction() { struct sigaction sa; sigemptyset(&sa.sa_mask); # ifndef NO_LOG if (!nodaemon) if (daemon(!0, logstdout)) # else // NO_LOG if (!nodaemon) if (daemon(!0, 0)) # endif // NO_LOG { printerrorf("Fatal: Could not daemonize to background.\n"); return(errno); } if (!InetdMode) { # ifndef USE_THREADS # if defined(CHILD_HANDLER) || __minix__ sa.sa_handler = childHandler; # else // !(defined(CHILD_HANDLER) || __minix__) sa.sa_handler = SIG_IGN; # endif // !(defined(CHILD_HANDLER) || __minix__) sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL)) return(errno); # endif // !USE_THREADS sa.sa_handler = terminationHandler; sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); # ifndef NO_SIGHUP sa.sa_handler = HangupHandler; sa.sa_flags = SA_NODEFER; sigaction(SIGHUP, &sa, NULL); # endif // NO_SIGHUP } return 0; } #else // _WIN32 static BOOL terminationHandler(const DWORD fdwCtrlType) { // What a lame substitute for Unix signal handling switch (fdwCtrlType) { case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: cleanup(); exit(0); default: return FALSE; } } static DWORD daemonizeAndSetSignalAction() { if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminationHandler, TRUE)) { #ifndef NO_LOG DWORD rc = GetLastError(); logger("Warning: Could not register Windows signal handler: Error %u\n", rc); #endif // NO_LOG } return ERROR_SUCCESS; } #endif // _WIN32 #endif // !defined(NO_SOCKETS) // Workaround for Cygwin fork problem (only affects cygwin processes that are Windows services) // Best is to compile for Cygwin with threads. fork() is slow and unreliable on Cygwin #if !defined(NO_INI_FILE) || !defined(NO_LOG) || !defined(NO_CL_PIDS) || !defined(NO_EXTERNAL_DATA) __pure static char* getCommandLineArg(char *const restrict arg) { # if !__CYGWIN__ || defined(USE_THREADS) || defined(NO_SOCKETS) return arg; # else if (!IsNTService) return arg; return vlmcsd_strdup(arg); # endif } #endif // !defined(NO_INI_FILE) || !defined(NO_LOG) || !defined(NO_CL_PIDS) || !defined(NO_EXTERNAL_DATA) static void parseGeneralArguments() { int o; #ifndef NO_CL_PIDS BYTE* HwId; #endif // NO_CL_PIDS for (opterr = 0; (o = getopt(global_argc, (char* const*)global_argv, (const char*)optstring)) > 0; ) switch (o) { # if !defined(NO_SOCKETS) && !defined(NO_SIGHUP) && !defined(_WIN32) case 'Z': IsRestarted = TRUE; nodaemon = TRUE; break; # endif // !defined(NO_SOCKETS) && !defined(NO_SIGHUP) && !defined(_WIN32) # ifndef NO_TAP case 'O': ignoreIniFileParameter(INI_PARAM_VPN); tapArgument = getCommandLineArg(optarg); break; # endif // NO_TAP # ifndef NO_CL_PIDS case 'w': KmsResponseParameters[EPID_INDEX_WINDOWS].Epid = getCommandLineArg(optarg); # ifndef NO_LOG KmsResponseParameters[EPID_INDEX_WINDOWS].EpidSource = "command line"; # endif // NO_LOG break; case '0': KmsResponseParameters[EPID_INDEX_OFFICE2010].Epid = getCommandLineArg(optarg); # ifndef NO_LOG KmsResponseParameters[EPID_INDEX_OFFICE2010].EpidSource = "command line"; # endif // NO_LOG break; case '3': KmsResponseParameters[EPID_INDEX_OFFICE2013].Epid = getCommandLineArg(optarg); # ifndef NO_LOG KmsResponseParameters[EPID_INDEX_OFFICE2013].EpidSource = "command line"; # endif // NO_LOG break; case '6': KmsResponseParameters[EPID_INDEX_OFFICE2016].Epid = getCommandLineArg(optarg); # ifndef NO_LOG KmsResponseParameters[EPID_INDEX_OFFICE2016].EpidSource = "command line"; # endif // NO_LOG break; case 'G': KmsResponseParameters[EPID_INDEX_WINCHINAGOV].Epid = getCommandLineArg(optarg); # ifndef NO_LOG KmsResponseParameters[EPID_INDEX_WINCHINAGOV].EpidSource = "command line"; # endif // NO_LOG break; case 'H': HwId = (BYTE*)vlmcsd_malloc(sizeof(((RESPONSE_V6 *)0)->HwId)); hex2bin(HwId, optarg, sizeof(((RESPONSE_V6 *)0)->HwId)); KmsResponseParameters[EPID_INDEX_WINDOWS].HwId = KmsResponseParameters[EPID_INDEX_OFFICE2010].HwId = KmsResponseParameters[EPID_INDEX_OFFICE2013].HwId = KmsResponseParameters[EPID_INDEX_WINCHINAGOV].HwId = KmsResponseParameters[EPID_INDEX_OFFICE2016].HwId = HwId; break; # endif // NO_CL_PIDS # ifndef NO_EXTERNAL_DATA case 'j': ignoreIniFileParameter(INI_PARAM_DATA_FILE); fn_data = getCommandLineArg(optarg); # ifndef NO_INTERNAL_DATA ExplicitDataLoad = TRUE; # endif // NO_INTERNAL_DATA break; # endif // NO_EXTERNAL_DATA # ifndef NO_SOCKETS case 'x': ignoreIniFileParameter(INI_PARAM_EXIT_LEVEL); ExitLevel = (int_fast8_t)getOptionArgumentInt((char)o, 0, 1); break; case 'P': ignoreIniFileParameter(INI_PARAM_PORT); # if !defined(SIMPLE_SOCKETS) && !defined(USE_MSRPC) ignoreIniFileParameter(INI_PARAM_LISTEN); # else defaultport = optarg; # endif // !SIMPLE_SOCKETS break; # if !defined(NO_LIMIT) && !__minix__ case 'm': # ifdef USE_MSRPC MaxTasks = getOptionArgumentInt(o, 1, RPC_C_LISTEN_MAX_CALLS_DEFAULT); # else // !USE_MSRPC MaxTasks = getOptionArgumentInt((char)o, 1, SEM_VALUE_MAX); # endif // !USE_MSRPC ignoreIniFileParameter(INI_PARAM_MAX_WORKERS); break; # endif // !defined(NO_LIMIT) && !__minix__ # endif // NO_SOCKETS # if !defined(NO_TIMEOUT) && !__minix__ && !defined(USE_MSRPC) case 't': ServerTimeout = getOptionArgumentInt((char)o, 1, 600); ignoreIniFileParameter(INI_PARAM_CONNECTION_TIMEOUT); break; # endif // !defined(NO_TIMEOUT) && !__minix__ && !defined(USE_MSRPC) # ifndef NO_PID_FILE case 'p': fn_pid = getCommandLineArg(optarg); ignoreIniFileParameter(INI_PARAM_PID_FILE); break; # endif # ifndef NO_INI_FILE case 'i': fn_ini = getCommandLineArg(optarg); if (!strcmp(fn_ini, "-")) fn_ini = NULL; break; # endif # ifndef NO_LOG case 'T': if (!getArgumentBool(&LogDateAndTime, optarg)) usage(); ignoreIniFileParameter(INI_PARAM_LOG_DATE_AND_TIME); break; case 'l': fn_log = getCommandLineArg(optarg); ignoreIniFileParameter(INI_PARAM_LOG_FILE); break; # ifndef NO_VERBOSE_LOG case 'v': case 'q': logverbose = o == 'v'; ignoreIniFileParameter(INI_PARAM_LOG_VERBOSE); break; # endif // NO_VERBOSE_LOG # endif // NO_LOG # if !defined(NO_PRIVATE_IP_DETECT) case 'o': ignoreIniFileParameter(INI_PARAM_PUBLIC_IP_PROTECTION_LEVEL); PublicIPProtectionLevel = getOptionArgumentInt((char)o, 0, 3); # if !HAVE_GETIFADDR if (PublicIPProtectionLevel & 1) usage(); # endif // !HAVE_GETIFADDR break; # endif // !defined(NO_PRIVATE_IP_DETECT) # ifndef NO_SOCKETS # if !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) case 'L': maxsockets++; ignoreIniFileParameter(INI_PARAM_LISTEN); break; # if HAVE_FREEBIND case 'F': if (!getArgumentBool(&freebind, optarg)) usage(); ignoreIniFileParameter(INI_PARAM_FREEBIND); break; # endif // HAVE_FREEBIND # endif // !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) # ifdef _NTSERVICE case 'U': ServiceUser = optarg; break; case 'W': ServicePassword = optarg; break; case 's': # ifndef USE_MSRPC if (InetdMode) usage(); # endif // USE_MSRPC if (!IsNTService) installService = 1; // Install break; case 'S': if (!IsNTService) installService = 2; // Remove break; # endif // _NTSERVICE # ifndef NO_STRICT_MODES case 'K': WhitelistingLevel = (int_fast8_t)getOptionArgumentInt((char)o, 0, 3); ignoreIniFileParameter(INI_PARAM_WHITELISTING_LEVEL); break; case 'c': if (!getArgumentBool(&CheckClientTime, optarg)) usage(); ignoreIniFileParameter(INI_PARAM_CHECK_CLIENT_TIME); break; # ifndef NO_CLIENT_LIST case 'E': if (!getArgumentBool(&StartEmpty, optarg)) usage(); ignoreIniFileParameter(INI_PARAM_START_EMPTY); break; case 'M': if (!getArgumentBool(&MaintainClients, optarg)) usage(); ignoreIniFileParameter(INI_PARAM_MAINTAIN_CLIENTS); break; # endif // !NO_CLIENT_LIST # endif // !NO_STRICT_MODES case 'D': # ifndef _WIN32 nodaemon = 1; # else // _WIN32 # ifdef _PEDANTIC printerrorf("Warning: Option -D has no effect in the Windows version of vlmcsd.\n"); # endif // _PEDANTIC # endif // _WIN32 break; # ifndef NO_LOG case 'e': logstdout = 1; break; # endif // NO_LOG # endif // NO_SOCKETS # ifndef NO_RANDOM_EPID case 'r': RandomizationLevel = (int_fast8_t)getOptionArgumentInt((char)o, 0, 2); ignoreIniFileParameter(INI_PARAM_RANDOMIZATION_LEVEL); break; case 'C': Lcid = (uint16_t)getOptionArgumentInt((char)o, 0, 32767); ignoreIniFileParameter(INI_PARAM_LCID); # ifdef _PEDANTIC if (!IsValidLcid(Lcid)) { printerrorf("Warning: %s is not a valid LCID.\n", optarg); } # endif // _PEDANTIC break; # endif // NO_RANDOM_PID # if !defined(NO_USER_SWITCH) && !defined(_WIN32) case 'g': gname = optarg; ignoreIniFileParameter(INI_PARAM_GID); # ifndef NO_SIGHUP if (!IsRestarted) # endif // NO_SIGHUP if (GetGid()) { printerrorf("Fatal: %s for %s failed: %s\n", "setgid", gname, strerror(errno)); exit(errno); } break; case 'u': uname = optarg; ignoreIniFileParameter(INI_PARAM_UID); # ifndef NO_SIGHUP if (!IsRestarted) # endif // NO_SIGHUP if (GetUid()) { printerrorf("Fatal: %s for %s failed: %s\n", "setuid", uname, strerror(errno)); exit(errno); } break; # endif // NO_USER_SWITCH && !_WIN32 # ifndef NO_CUSTOM_INTERVALS case 'R': VLRenewalInterval = getTimeSpanFromCommandLine(optarg, (char)o); ignoreIniFileParameter(INI_PARAM_RENEWAL_INTERVAL); break; case 'A': VLActivationInterval = getTimeSpanFromCommandLine(optarg, (char)o); ignoreIniFileParameter(INI_PARAM_ACTIVATION_INTERVAL); break; # endif // NO_CUSTOM_INTERVALS # ifndef USE_MSRPC case 'd': case 'k': DisconnectImmediately = o == 'd'; ignoreIniFileParameter(INI_PARAM_DISCONNECT_IMMEDIATELY); break; # ifndef SIMPLE_RPC case 'N': if (!getArgumentBool(&UseServerRpcNDR64, optarg)) usage(); ignoreIniFileParameter(INI_PARAM_RPC_NDR64); break; case 'B': if (!getArgumentBool(&UseServerRpcBTFN, optarg)) usage(); ignoreIniFileParameter(INI_PARAM_RPC_BTFN); break; # endif // !SIMPLE_RPC # endif // !USE_MSRPC # ifndef NO_VERSION_INFORMATION case 'V': # ifdef _NTSERVICE if (IsNTService) break; # endif # if defined(__s390__) && !defined(__zarch__) && !defined(__s390x__) printf("vlmcsd %s %i-bit\n", Version, sizeof(void*) == 4 ? 31 : (int)sizeof(void*) << 3); # else printf("vlmcsd %s %i-bit\n", Version, (int)sizeof(void*) << 3); # endif // defined(__s390__) && !defined(__zarch__) && !defined(__s390x__) printPlatform(); printCommonFlags(); printServerFlags(); exit(0); # endif // NO_VERSION_INFORMATION default: usage(); } // Do not allow non-option arguments if (optind != global_argc) usage(); # ifdef _NTSERVICE // -U and -W must be used with -s if ((ServiceUser || *ServicePassword) && installService != 1) usage(); # endif // _NTSERVICE } #if !defined(NO_PID_FILE) static void writePidFile() { # ifndef NO_SIGHUP if (IsRestarted) return; # endif // NO_SIGHUP if (fn_pid && !InetdMode) { FILE *file = fopen(fn_pid, "w"); if (file) { # if _MSC_VER fprintf(file, "%u", (unsigned int)GetCurrentProcessId()); # else fprintf(file, "%u", (unsigned int)getpid()); # endif fclose(file); } # ifndef NO_LOG else { logger("Warning: Cannot write pid file '%s'. %s.\n", fn_pid, strerror(errno)); } # endif // NO_LOG } } #else #define writePidFile() #endif // !defined(NO_PID_FILE) #if !defined(NO_SOCKETS) && !defined(USE_MSRPC) void cleanup() { if (!InetdMode) { # ifndef NO_CLIENT_LIST if (MaintainClients) CleanUpClientLists(); # endif // !NO_CLIENT_LIST # ifndef NO_PID_FILE if (fn_pid) vlmcsd_unlink(fn_pid); # endif // NO_PID_FILE closeAllListeningSockets(); # if !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !defined(_WIN32) && !__minix__ sem_unlink("/vlmcsd"); # if !defined(USE_THREADS) && !defined(CYGWIN) if (shmid >= 0) { if (MaxTaskSemaphore != (sem_t*)-1) shmdt(MaxTaskSemaphore); shmctl(shmid, IPC_RMID, NULL); } # endif // !defined(USE_THREADS) && !defined(CYGWIN) # endif // !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !defined(_WIN32) && !__minix__ # ifndef NO_LOG logger("vlmcsd %s was shutdown\n", Version); # endif // NO_LOG } } #elif defined(USE_MSRPC) void cleanup() { # ifndef NO_PID_FILE if (fn_pid) unlink(fn_pid); # endif // NO_PID_FILE # ifndef NO_LOG logger("vlmcsd %s was shutdown\n", Version); # endif // NO_LOG } #else // Neither Sockets nor RPC __pure void cleanup() {} #endif // Neither Sockets nor RPC #if !defined(USE_MSRPC) && !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !__minix__ // Get a semaphore for limiting the maximum concurrent tasks static void allocateSemaphore(void) { # ifdef USE_THREADS # define sharemode 0 # else # define sharemode 1 # endif # ifndef _WIN32 sem_unlink("/vlmcsd"); # endif if (MaxTasks < SEM_VALUE_MAX && !InetdMode) { # ifndef _WIN32 # if !defined(USE_THREADS) && !defined(CYGWIN) if ((MaxTaskSemaphore = sem_open("/vlmcsd", O_CREAT /*| O_EXCL*/, 0700, MaxTasks)) == SEM_FAILED) // fails on many systems { // We didn't get a named Semaphore (/dev/shm on Linux) so let's try our own shared page if ( (shmid = shmget(IPC_PRIVATE, sizeof(sem_t), IPC_CREAT | 0600)) < 0 || (MaxTaskSemaphore = (sem_t*)shmat(shmid, NULL, 0)) == (sem_t*)-1 || sem_init(MaxTaskSemaphore, 1, MaxTasks) < 0 ) { int errno_save = errno; if (MaxTaskSemaphore != (sem_t*)-1) shmdt(MaxTaskSemaphore); if (shmid >= 0) shmctl(shmid, IPC_RMID, NULL); printerrorf("Warning: Could not create semaphore: %s\n", vlmcsd_strerror(errno_save)); MaxTasks = SEM_VALUE_MAX; } } # else // THREADS or CYGWIN MaxTaskSemaphore = (sem_t*)vlmcsd_malloc(sizeof(sem_t)); if (sem_init(MaxTaskSemaphore, sharemode, MaxTasks) < 0) // sem_init is not implemented on Darwin (returns ENOSYS) { free(MaxTaskSemaphore); if ((MaxTaskSemaphore = sem_open("/vlmcsd", O_CREAT /*| O_EXCL*/, 0700, MaxTasks)) == SEM_FAILED) { printerrorf("Warning: Could not create semaphore: %s\n", vlmcsd_strerror(errno)); MaxTasks = SEM_VALUE_MAX; } } # endif // THREADS or CYGWIN # else // _WIN32 if (!((MaxTaskSemaphore = CreateSemaphoreA(NULL, MaxTasks, MaxTasks, NULL)))) { printerrorf("Warning: Could not create semaphore: %s\n", vlmcsd_strerror(GetLastError())); MaxTasks = SEM_VALUE_MAX; } # endif // _WIN32 } } #endif // !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !__minix__ #if !defined(NO_SOCKETS) && !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) int setupListeningSockets() { int o; # if HAVE_GETIFADDR char** privateIPList = NULL; int numPrivateIPs = 0; if (PublicIPProtectionLevel & 1) getPrivateIPAddresses(&numPrivateIPs, &privateIPList); uint_fast8_t allocsockets = (uint_fast8_t)(maxsockets ? (maxsockets + numPrivateIPs) : ((PublicIPProtectionLevel & 1) ? numPrivateIPs : 2)); # else // !HAVE_GETIFADDR uint_fast8_t allocsockets = maxsockets ? maxsockets : 2; # endif // !HAVE_GETIFADDR SocketList = (SOCKET*)vlmcsd_malloc((size_t)allocsockets * sizeof(SOCKET)); int_fast8_t haveIPv4Stack = checkProtocolStack(AF_INET); int_fast8_t haveIPv6Stack = checkProtocolStack(AF_INET6); // Reset getopt since we've alread used it optReset(); for (opterr = 0; (o = getopt(global_argc, (char* const*)global_argv, (const char*)optstring)) > 0; ) switch (o) { case 'P': defaultport = optarg; break; case 'L': addListeningSocket(optarg); break; default: break; } # ifndef NO_INI_FILE if (maxsockets && !numsockets) { if (fn_ini && !readIniFile(INI_FILE_PASS_2)) { # ifdef INI_FILE if (strcmp(fn_ini, INI_FILE)) # endif // INI_FILE printerrorf("Warning: Can't read %s: %s\n", fn_ini, strerror(errno)); } } # endif # if HAVE_GETIFADDR if (PublicIPProtectionLevel & 1) { int i; for (i = 0; i < numPrivateIPs; i++) { addListeningSocket(privateIPList[i]); free(privateIPList[i]); } free(privateIPList); } # endif // HAVE_GETIFADDR // if -L hasn't been specified on the command line, use default sockets (all IP addresses) // maxsocket results from first pass parsing the arguments if (!maxsockets) { # if HAVE_GETIFADDR if (!(PublicIPProtectionLevel & 1) && haveIPv6Stack) addListeningSocket("::"); if (!(PublicIPProtectionLevel & 1) && haveIPv4Stack) addListeningSocket("0.0.0.0"); # else // !HAVE_GETIFADDR if (haveIPv6Stack) addListeningSocket("::"); if (haveIPv4Stack) addListeningSocket("0.0.0.0"); # endif // !HAVE_GETIFADDR } if (!numsockets) { printerrorf("Fatal: Could not listen on any socket.\n"); return(!0); } return 0; } #endif // !defined(NO_SOCKETS) && !defined(USE_MSRPC) && !defined(SIMPLE_SOCKETS) int server_main(int argc, CARGV argv) { global_argc = argc; global_argv = argv; # ifdef _NTSERVICE DWORD lasterror = ERROR_SUCCESS; if (!StartServiceCtrlDispatcher(NTServiceDispatchTable) && (lasterror = GetLastError()) == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { IsNTService = FALSE; return newmain(); } return lasterror; # else // !_NTSERVICE return newmain(); # endif // !_NTSERVICE } int newmain() { # if !defined(NO_RANDOM_EPID) || !defined(NO_CL_PIDS) || !defined(NO_INI_FILE) KmsResponseParameters = (KmsResponseParam_t*)vlmcsd_malloc(sizeof(KmsResponseParam_t) * MIN_CSVLK); memset(KmsResponseParameters, 0, sizeof(KmsResponseParam_t) * MIN_CSVLK); # endif // !defined(NO_RANDOM_EPID) || !defined(NO_CL_PIDS) || !defined(NO_INI_FILE) // Initialize thread synchronization objects for Windows and Cygwin # ifdef USE_THREADS # ifndef NO_LOG // Initialize the Critical Section for proper logging # if _WIN32 || __CYGWIN__ InitializeCriticalSection(&logmutex); # endif // _WIN32 || __CYGWIN__ # endif // NO_LOG # endif // USE_THREADS # ifdef _WIN32 # ifndef USE_MSRPC WSADATA wsadata; { // Windows Sockets must be initialized int error; if ((error = WSAStartup(0x0202, &wsadata))) { printerrorf("Fatal: Could not initialize Windows sockets (Error: %d).\n", error); return error; } } # endif // USE_MSRPC // Windows can never daemonize //nodaemon = 1; # else // __CYGWIN__ // Do not daemonize if we are a Windows service # ifdef _NTSERVICE if (IsNTService) nodaemon = 1; # endif # endif // _WIN32 / __CYGWIN__ parseGeneralArguments(); // Does not return if an error occurs # if !defined(_WIN32) && !defined(NO_SOCKETS) && !defined(USE_MSRPC) struct stat statbuf; fstat(STDIN_FILENO, &statbuf); if (S_ISSOCK(statbuf.st_mode)) { InetdMode = 1; # ifndef NO_CLIENT_LIST MaintainClients = FALSE; # endif // !NO_CLIENT_LIST nodaemon = 1; # ifndef SIMPLE_SOCKETS maxsockets = 0; # endif // !SIMPLE_SOCKETS # ifndef NO_LOG logstdout = 0; # endif // !NO_LOG } # endif // !defined(_WIN32) && !defined(NO_SOCKETS) && !defined(USE_MSRPC) # ifndef NO_INI_FILE if (fn_ini && !readIniFile(INI_FILE_PASS_1)) { # ifdef INI_FILE if (strcmp(fn_ini, INI_FILE)) # endif // INI_FILE printerrorf("Warning: Can't read %s: %s\n", fn_ini, strerror(errno)); } # endif // NO_INI_FILE loadKmsData(); # ifndef NO_CLIENT_LIST if (MaintainClients) InitializeClientLists(); # endif // !NO_CLIENT_LIST # if defined(USE_MSRPC) && !defined(NO_PRIVATE_IP_DETECT) if (PublicIPProtectionLevel) { printerrorf("Warning: Public IP address protection using MS RPC is poor. See vlmcsd.8\n"); } # endif // defined(USE_MSRPC) && !defined(NO_PRIVATE_IP_DETECT) # if !defined(NO_LIMIT) && !defined(NO_SOCKETS) && !__minix__ && !defined(USE_MSRPC) allocateSemaphore(); # endif // !defined(NO_LIMIT) && !defined(NO_SOCKETS) && __minix__ # ifdef _NTSERVICE if (installService) return NtServiceInstallation(installService, ServiceUser, ServicePassword); # endif // _NTSERVICE # ifndef NO_TAP if (tapArgument && !InetdMode) startTap(tapArgument); # endif // NO_TAP # if !defined(NO_SOCKETS) && !defined(USE_MSRPC) if (!InetdMode) { int error; # ifdef SIMPLE_SOCKETS if ((error = listenOnAllAddresses())) return error; # else // !SIMPLE_SOCKETS if ((error = setupListeningSockets())) return error; # endif // !SIMPLE_SOCKETS } # endif // !defined(NO_SOCKETS) && !defined(USE_MSRPC) // After sockets have been set up, we may switch to a lower privileged user # if !defined(_WIN32) && !defined(NO_USER_SWITCH) # ifndef NO_SIGHUP if (!IsRestarted) { # endif // NO_SIGHUP if (gid != INVALID_GID) { if (setgid(gid)) { printerrorf("Fatal: %s for %s failed: %s\n", "setgid", gname, strerror(errno)); return errno; } if (setgroups(1, &gid)) { printerrorf("Fatal: %s for %s failed: %s\n", "setgroups", gname, strerror(errno)); return errno; } } if (uid != INVALID_UID && setuid(uid)) { printerrorf("Fatal: %s for %s failed: %s\n", "setuid", uname, strerror(errno)); return errno; } # ifndef NO_SIGHUP } # endif // NO_SIGHUP # endif // !defined(_WIN32) && !defined(NO_USER_SWITCH) randomNumberInit(); // Randomization Level 1 means generate ePIDs at startup and use them during // the lifetime of the process. So we generate them now # ifndef NO_RANDOM_EPID if (RandomizationLevel == 1) randomPidInit(); # endif # if !defined(NO_SOCKETS) # ifdef _WIN32 if (!IsNTService) { # endif // _WIN32 int error; if ((error = daemonizeAndSetSignalAction())) return error; # ifdef _WIN32 } # endif // _WIN32 # endif // !defined(NO_SOCKETS) writePidFile(); # if !defined(NO_LOG) && !defined(NO_SOCKETS) && !defined(USE_MSRPC) if (!InetdMode) logger("vlmcsd %s started successfully\n", Version); # endif // !defined(NO_LOG) && !defined(NO_SOCKETS) && !defined(USE_MSRPC) # if defined(_NTSERVICE) && !defined(USE_MSRPC) if (IsNTService) ReportServiceStatus(SERVICE_RUNNING, NO_ERROR, 200); # endif // defined(_NTSERVICE) && !defined(USE_MSRPC) int rc; rc = runServer(); // Clean up things and exit # ifdef _NTSERVICE if (!ServiceShutdown) # endif cleanup(); # ifdef _NTSERVICE else ReportServiceStatus(SERVICE_STOPPED, NO_ERROR, 0); # endif return rc; } #if _MSC_VER && !defined(_DEBUG)&& !MULTI_CALL_BINARY int __stdcall WinStartUp(void) { WCHAR **szArgList; int argc; szArgList = CommandLineToArgvW(GetCommandLineW(), &argc); int i; char **argv = (char**)vlmcsd_malloc(sizeof(char*)*argc); for (i = 0; i < argc; i++) { int size = WideCharToMultiByte(CP_UTF8, 0, szArgList[i], -1, argv[i], 0, NULL, NULL); argv[i] = (char*)vlmcsd_malloc(size); WideCharToMultiByte(CP_UTF8, 0, szArgList[i], -1, argv[i], size, NULL, NULL); } exit(server_main(argc, argv)); } #endif // _MSC_VER && !defined(_DEBUG)&& !MULTI_CALL_BINARY