vpn-split 21.0.13 → 21.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/package.json +1 -1
- package/browser-prod/fesm2022/vpn-split-browser.mjs +19 -20
- package/browser-prod/fesm2022/vpn-split-browser.mjs.map +1 -1
- package/browser-prod/package.json +1 -1
- package/browser-prod.re-export.json +1 -0
- package/lib/build-info._auto-generated_.d.ts +1 -1
- package/lib/build-info._auto-generated_.js +1 -1
- package/lib/package.json +1 -1
- package/lib/vpn-split.backend.js +3 -2
- package/lib/vpn-split.backend.js.map +1 -1
- package/lib-prod/build-info._auto-generated_.ts +27 -0
- package/lib-prod/env/env.angular-node-app.ts +66 -0
- package/lib-prod/env/env.docs-webapp.ts +66 -0
- package/lib-prod/env/env.electron-app.ts +66 -0
- package/lib-prod/env/env.mobile-app.ts +66 -0
- package/lib-prod/env/env.npm-lib-and-cli-tool.ts +66 -0
- package/lib-prod/env/env.vscode-plugin.ts +66 -0
- package/lib-prod/hostile.backend.ts +123 -0
- package/lib-prod/{index._auto-generated_.js → index._auto-generated_.ts} +0 -1
- package/lib-prod/{index.d.ts → index.ts} +3 -0
- package/lib-prod/lib-info.md +8 -0
- package/lib-prod/migrations/index.ts +2 -0
- package/lib-prod/migrations/migrations-info.md +6 -0
- package/lib-prod/migrations/{migrations_index._auto-generated_.js → migrations_index._auto-generated_.ts} +2 -1
- package/lib-prod/models.ts +250 -0
- package/lib-prod/package.json +1 -1
- package/lib-prod/{start.backend.js → start.backend.ts} +6 -2
- package/lib-prod/vpn-split.backend.ts +1039 -0
- package/lib-prod.re-export.json +1 -0
- package/package.json +2 -1
- package/websql/package.json +1 -1
- package/websql-prod/fesm2022/vpn-split-websql.mjs +19 -20
- package/websql-prod/fesm2022/vpn-split-websql.mjs.map +1 -1
- package/websql-prod/package.json +1 -1
- package/websql-prod.re-export.json +1 -0
- package/lib-prod/build-info._auto-generated_.d.ts +0 -24
- package/lib-prod/build-info._auto-generated_.js +0 -30
- package/lib-prod/build-info._auto-generated_.js.map +0 -1
- package/lib-prod/env/env.angular-node-app.d.ts +0 -64
- package/lib-prod/env/env.angular-node-app.js +0 -71
- package/lib-prod/env/env.angular-node-app.js.map +0 -1
- package/lib-prod/env/env.docs-webapp.d.ts +0 -64
- package/lib-prod/env/env.docs-webapp.js +0 -71
- package/lib-prod/env/env.docs-webapp.js.map +0 -1
- package/lib-prod/env/env.electron-app.d.ts +0 -64
- package/lib-prod/env/env.electron-app.js +0 -71
- package/lib-prod/env/env.electron-app.js.map +0 -1
- package/lib-prod/env/env.mobile-app.d.ts +0 -64
- package/lib-prod/env/env.mobile-app.js +0 -71
- package/lib-prod/env/env.mobile-app.js.map +0 -1
- package/lib-prod/env/env.npm-lib-and-cli-tool.d.ts +0 -64
- package/lib-prod/env/env.npm-lib-and-cli-tool.js +0 -71
- package/lib-prod/env/env.npm-lib-and-cli-tool.js.map +0 -1
- package/lib-prod/env/env.vscode-plugin.d.ts +0 -64
- package/lib-prod/env/env.vscode-plugin.js +0 -71
- package/lib-prod/env/env.vscode-plugin.js.map +0 -1
- package/lib-prod/env/index.js +0 -23
- package/lib-prod/env/index.js.map +0 -1
- package/lib-prod/hostile.backend.d.ts +0 -37
- package/lib-prod/hostile.backend.js +0 -122
- package/lib-prod/hostile.backend.js.map +0 -1
- package/lib-prod/index._auto-generated_.d.ts +0 -0
- package/lib-prod/index._auto-generated_.js.map +0 -1
- package/lib-prod/index.js +0 -21
- package/lib-prod/index.js.map +0 -1
- package/lib-prod/migrations/index.d.ts +0 -1
- package/lib-prod/migrations/index.js +0 -19
- package/lib-prod/migrations/index.js.map +0 -1
- package/lib-prod/migrations/migrations_index._auto-generated_.d.ts +0 -0
- package/lib-prod/migrations/migrations_index._auto-generated_.js.map +0 -1
- package/lib-prod/models.d.ts +0 -61
- package/lib-prod/models.js +0 -182
- package/lib-prod/models.js.map +0 -1
- package/lib-prod/start.backend.d.ts +0 -0
- package/lib-prod/start.backend.js.map +0 -1
- package/lib-prod/vpn-split.backend.d.ts +0 -56
- package/lib-prod/vpn-split.backend.js +0 -742
- package/lib-prod/vpn-split.backend.js.map +0 -1
- /package/lib-prod/env/{index.d.ts → index.ts} +0 -0
|
@@ -0,0 +1,1039 @@
|
|
|
1
|
+
//#region imports
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import * as crypto from 'crypto';
|
|
4
|
+
// TODO I am using TCP ugrade not UDP!
|
|
5
|
+
import * as dgram from 'dgram'; // <-- For UDP sockets
|
|
6
|
+
import * as http from 'http';
|
|
7
|
+
import { URL } from 'url';
|
|
8
|
+
|
|
9
|
+
import axios from 'axios';
|
|
10
|
+
import * as express from 'express';
|
|
11
|
+
import * as httpProxy from 'http-proxy';
|
|
12
|
+
import { Log, Level } from 'ng2-logger/lib-prod';
|
|
13
|
+
import { config, Utils__NS__binary__NS__base64toBlob, Utils__NS__binary__NS__base64toBuffer, Utils__NS__binary__NS__base64toDbBinaryFormat, Utils__NS__binary__NS__blobToArrayBuffer, Utils__NS__binary__NS__blobToBase64, Utils__NS__binary__NS__blobToBuffer, Utils__NS__binary__NS__blobToFile, Utils__NS__binary__NS__blobToJson, Utils__NS__binary__NS__blobToText, Utils__NS__binary__NS__bufferToBase64, Utils__NS__binary__NS__bufferToBlob, Utils__NS__binary__NS__bufferToText, Utils__NS__binary__NS__dbBinaryFormatToBase64, Utils__NS__binary__NS__dbBinaryFormatToText, Utils__NS__binary__NS__fileToBlob, Utils__NS__binary__NS__fileToText, Utils__NS__binary__NS__getBlobFrom, Utils__NS__binary__NS__jsonToBlob, Utils__NS__binary__NS__textToBlob, Utils__NS__binary__NS__textToBuffer, Utils__NS__binary__NS__textToDbBinaryFormat, Utils__NS__binary__NS__textToFile, Utils__NS__camelize, Utils__NS__css__NS__numValue, Utils__NS__DbBinaryFormat, Utils__NS__DbBinaryFormatEnum, Utils__NS__DbBinaryFormatForBackend, Utils__NS__DbBinaryFormatForBrowser, Utils__NS__escapeStringForRegEx, Utils__NS__fullDate, Utils__NS__fullDateTime, Utils__NS__getFreePort, Utils__NS__removeChalkSpecialChars, Utils__NS__requireUncached, Utils__NS__sortKeys, Utils__NS__uniqArray, Utils__NS__wait, Utils__NS__waitMilliseconds } from 'tnp-core/lib-prod';
|
|
14
|
+
import { path, fse, https, isElevated, crossPlatformPath, os, ___NS__add, ___NS__after, ___NS__ary, ___NS__assign, ___NS__assignIn, ___NS__assignInWith, ___NS__assignWith, ___NS__at, ___NS__attempt, ___NS__before, ___NS__bind, ___NS__bindAll, ___NS__bindKey, ___NS__camelCase, ___NS__capitalize, ___NS__castArray, ___NS__ceil, ___NS__chain, ___NS__chunk, ___NS__clamp, ___NS__clone, ___NS__cloneDeep, ___NS__cloneDeepWith, ___NS__cloneWith, ___NS__compact, ___NS__concat, ___NS__cond, ___NS__conforms, ___NS__conformsTo, ___NS__constant, ___NS__countBy, ___NS__create, ___NS__curry, ___NS__curryRight, ___NS__debounce, ___NS__deburr, ___NS__defaults, ___NS__defaultsDeep, ___NS__defaultTo, ___NS__defer, ___NS__delay, ___NS__difference, ___NS__differenceBy, ___NS__differenceWith, ___NS__divide, ___NS__drop, ___NS__dropRight, ___NS__dropRightWhile, ___NS__dropWhile, ___NS__each, ___NS__eachRight, ___NS__endsWith, ___NS__entries, ___NS__entriesIn, ___NS__eq, ___NS__escape, ___NS__escapeRegExp, ___NS__every, ___NS__extend, ___NS__extendWith, ___NS__fill, ___NS__filter, ___NS__find, ___NS__findIndex, ___NS__findKey, ___NS__findLast, ___NS__findLastIndex, ___NS__findLastKey, ___NS__first, ___NS__flatMap, ___NS__flatMapDeep, ___NS__flatMapDepth, ___NS__flatten, ___NS__flattenDeep, ___NS__flattenDepth, ___NS__flip, ___NS__floor, ___NS__flow, ___NS__flowRight, ___NS__forEach, ___NS__forEachRight, ___NS__forIn, ___NS__forInRight, ___NS__forOwn, ___NS__forOwnRight, ___NS__fromPairs, ___NS__functions, ___NS__functionsIn, ___NS__get, ___NS__groupBy, ___NS__gt, ___NS__gte, ___NS__has, ___NS__hasIn, ___NS__head, ___NS__identity, ___NS__includes, ___NS__indexOf, ___NS__initial, ___NS__inRange, ___NS__intersection, ___NS__intersectionBy, ___NS__intersectionWith, ___NS__invert, ___NS__invertBy, ___NS__invoke, ___NS__invokeMap, ___NS__isArguments, ___NS__isArray, ___NS__isArrayBuffer, ___NS__isArrayLike, ___NS__isArrayLikeObject, ___NS__isBoolean, ___NS__isBuffer, ___NS__isDate, ___NS__isElement, ___NS__isEmpty, ___NS__isEqual, ___NS__isEqualWith, ___NS__isError, ___NS__isFinite, ___NS__isFunction, ___NS__isInteger, ___NS__isLength, ___NS__isMap, ___NS__isMatch, ___NS__isMatchWith, ___NS__isNaN, ___NS__isNative, ___NS__isNil, ___NS__isNull, ___NS__isNumber, ___NS__isObject, ___NS__isObjectLike, ___NS__isPlainObject, ___NS__isRegExp, ___NS__isSafeInteger, ___NS__isSet, ___NS__isString, ___NS__isSymbol, ___NS__isTypedArray, ___NS__isUndefined, ___NS__isWeakMap, ___NS__isWeakSet, ___NS__iteratee, ___NS__join, ___NS__kebabCase, ___NS__keyBy, ___NS__keys, ___NS__keysIn, ___NS__last, ___NS__lastIndexOf, ___NS__lowerCase, ___NS__lowerFirst, ___NS__lt, ___NS__lte, ___NS__map, ___NS__mapKeys, ___NS__mapValues, ___NS__matches, ___NS__matchesProperty, ___NS__max, ___NS__maxBy, ___NS__mean, ___NS__meanBy, ___NS__memoize, ___NS__merge, ___NS__mergeWith, ___NS__method, ___NS__methodOf, ___NS__min, ___NS__minBy, ___NS__mixin, ___NS__multiply, ___NS__negate, ___NS__noop, ___NS__now, ___NS__nth, ___NS__nthArg, ___NS__omit, ___NS__omitBy, ___NS__once, ___NS__orderBy, ___NS__over, ___NS__overArgs, ___NS__overEvery, ___NS__overSome, ___NS__pad, ___NS__padEnd, ___NS__padStart, ___NS__parseInt, ___NS__partial, ___NS__partialRight, ___NS__partition, ___NS__pick, ___NS__pickBy, ___NS__property, ___NS__propertyOf, ___NS__pull, ___NS__pullAll, ___NS__pullAllBy, ___NS__pullAllWith, ___NS__pullAt, ___NS__random, ___NS__range, ___NS__rangeRight, ___NS__rearg, ___NS__reduce, ___NS__reduceRight, ___NS__reject, ___NS__remove, ___NS__repeat, ___NS__replace, ___NS__rest, ___NS__result, ___NS__reverse, ___NS__round, ___NS__sample, ___NS__sampleSize, ___NS__set, ___NS__setWith, ___NS__shuffle, ___NS__size, ___NS__slice, ___NS__snakeCase, ___NS__some, ___NS__sortBy, ___NS__sortedIndex, ___NS__sortedIndexBy, ___NS__sortedIndexOf, ___NS__sortedLastIndex, ___NS__sortedLastIndexBy, ___NS__sortedLastIndexOf, ___NS__sortedUniq, ___NS__sortedUniqBy, ___NS__split, ___NS__spread, ___NS__startCase, ___NS__startsWith, ___NS__stubArray, ___NS__stubFalse, ___NS__stubObject, ___NS__stubString, ___NS__stubTrue, ___NS__subtract, ___NS__sum, ___NS__sumBy, ___NS__tail, ___NS__take, ___NS__takeRight, ___NS__takeRightWhile, ___NS__takeWhile, ___NS__tap, ___NS__template, ___NS__templateSettings, ___NS__throttle, ___NS__thru, ___NS__times, ___NS__toArray, ___NS__toFinite, ___NS__toInteger, ___NS__toLength, ___NS__toLower, ___NS__toNumber, ___NS__toPairs, ___NS__toPairsIn, ___NS__toPath, ___NS__toPlainObject, ___NS__toSafeInteger, ___NS__toString, ___NS__toUpper, ___NS__transform, ___NS__trim, ___NS__trimEnd, ___NS__trimStart, ___NS__truncate, ___NS__unary, ___NS__unescape, ___NS__union, ___NS__unionBy, ___NS__unionWith, ___NS__uniq, ___NS__uniqBy, ___NS__uniqueId, ___NS__uniqWith, ___NS__unset, ___NS__unzip, ___NS__unzipWith, ___NS__update, ___NS__updateWith, ___NS__upperCase, ___NS__upperFirst, ___NS__values, ___NS__valuesIn, ___NS__without, ___NS__words, ___NS__wrap, ___NS__xor, ___NS__xorBy, ___NS__xorWith, ___NS__zip, ___NS__zipObject, ___NS__zipObjectDeep, ___NS__zipWith, UtilsNetwork__NS__checkIfServerOnline, UtilsNetwork__NS__checkIfServerPings, UtilsNetwork__NS__checkPing, UtilsNetwork__NS__etcHostHasProperLocalhostIp4Entry, UtilsNetwork__NS__etcHostHasProperLocalhostIp6Entry, UtilsNetwork__NS__getCurrentPublicIpAddress, UtilsNetwork__NS__getEtcHostEntriesByDomain, UtilsNetwork__NS__getEtcHostEntryByComment, UtilsNetwork__NS__getEtcHostEntryByIp, UtilsNetwork__NS__getEtcHostsPath, UtilsNetwork__NS__getFirstIpV4LocalActiveIpAddress, UtilsNetwork__NS__getLocalIpAddresses, UtilsNetwork__NS__isValidDomain, UtilsNetwork__NS__isValidIp, UtilsNetwork__NS__LocalIpInfo, UtilsNetwork__NS__PingResult, UtilsNetwork__NS__removeEtcHost, UtilsNetwork__NS__setEtcHost, UtilsNetwork__NS__SIMULATE_DOMAIN_TAG, UtilsNetwork__NS__simulateDomain, UtilsNetwork__NS__urlParse, UtilsOs__NS__commandExistsAsync, UtilsOs__NS__commandExistsSync, UtilsOs__NS__detectEditor, UtilsOs__NS__Editor, UtilsOs__NS__EDITOR_PROCESSES, UtilsOs__NS__EditorArr, UtilsOs__NS__EditorProcess, UtilsOs__NS__getEditorSettingsJsonPath, UtilsOs__NS__getRealHomeDir, UtilsOs__NS__isBrowser, UtilsOs__NS__isDockerAvailable, UtilsOs__NS__isElectron, UtilsOs__NS__isNode, UtilsOs__NS__isNodeVersionOk, UtilsOs__NS__isPortInUse, UtilsOs__NS__isRunningInBrowser, UtilsOs__NS__isRunningInCliMode, UtilsOs__NS__isRunningInDocker, UtilsOs__NS__isRunningInElectron, UtilsOs__NS__isRunningInLinuxGraphicsCapableEnvironment, UtilsOs__NS__isRunningInMochaTest, UtilsOs__NS__isRunningInNode, UtilsOs__NS__isRunningInOsWithGraphicsCapableEnvironment, UtilsOs__NS__isRunningInSSRMode, UtilsOs__NS__isRunningInVscodeExtension, UtilsOs__NS__isRunningInWebSQL, UtilsOs__NS__isRunningInWindows, UtilsOs__NS__isRunningInWindowsCmd, UtilsOs__NS__isRunningInWindowsPowerShell, UtilsOs__NS__isRunningInWsl, UtilsOs__NS__isRunningNodeDebugger, UtilsOs__NS__isSSRMode, UtilsOs__NS__isVscodeExtension, UtilsOs__NS__isWebSQL, UtilsOs__NS__killAllEditor, UtilsOs__NS__openFolderInFileExplorer, UtilsOs__NS__openFolderInVSCode, UtilsOs__NS__pipxNestedPackageExists, UtilsOs__NS__pipxPackageExists, UtilsOs__NS__pythonModuleExists, UtilsOs__NS__UnknownEditor } from 'tnp-core/lib-prod';
|
|
15
|
+
import { CoreModels__NS__BaseProjectType, CoreModels__NS__BaseProjectTypeArr, CoreModels__NS__CfontAlign, CoreModels__NS__CfontStyle, CoreModels__NS__ClassNameStaticProperty, CoreModels__NS__ContentType, CoreModels__NS__ContentTypeKeys, CoreModels__NS__CoreLibCategory, CoreModels__NS__CutableFileExt, CoreModels__NS__DatabaseType, CoreModels__NS__EnvironmentName, CoreModels__NS__EnvironmentNameTaon, CoreModels__NS__ExecuteOptions, CoreModels__NS__FileEvent, CoreModels__NS__FileExtension, CoreModels__NS__FrameworkVersion, CoreModels__NS__GlobalDependencies, CoreModels__NS__HttpMethod, CoreModels__NS__ImageFileExtension, CoreModels__NS__ImageFileExtensionArr, CoreModels__NS__InstalationType, CoreModels__NS__InstalationTypeArr, CoreModels__NS__LibType, CoreModels__NS__localhostDomain, CoreModels__NS__localhostIp127, CoreModels__NS__ManifestIcon, CoreModels__NS__MediaType, CoreModels__NS__MediaTypeAllArr, CoreModels__NS__MimeType, CoreModels__NS__mimeTypes, CoreModels__NS__MimeTypesObj, CoreModels__NS__NewFactoryType, CoreModels__NS__NpmInstallOptions, CoreModels__NS__NpmSpecialVersions, CoreModels__NS__OrignalClassKey, CoreModels__NS__OutFolder, CoreModels__NS__Package, CoreModels__NS__ParamType, CoreModels__NS__parentLocation, CoreModels__NS__pathToChildren, CoreModels__NS__Position, CoreModels__NS__PreReleaseVersionTag, CoreModels__NS__PROGRESS_DATA_TYPE, CoreModels__NS__PUSHTYPE, CoreModels__NS__PwaManifest, CoreModels__NS__ReleaseVersionType, CoreModels__NS__ReleaseVersionTypeEnum, CoreModels__NS__RunOptions, CoreModels__NS__Size, CoreModels__NS__SPECIAL_APP_READY_MESSAGE, CoreModels__NS__SPECIAL_WORKER_READY_MESSAGE, CoreModels__NS__tagForTaskName, CoreModels__NS__TaonHttpErrorCustomProp, CoreModels__NS__TsUsage, CoreModels__NS__UIFramework, CoreModels__NS__UploadedBackendFile, CoreModels__NS__VSCodeSettings } from 'tnp-core/lib-prod';
|
|
16
|
+
import { Helpers__NS___fixCommand, Helpers__NS__bigMaxBuffer, Helpers__NS__checkProcess, Helpers__NS__cleanExit, Helpers__NS__clearConsole, Helpers__NS__command, Helpers__NS__commandOutputAsString, Helpers__NS__commandOutputAsStringAsync, Helpers__NS__compilationWrapper, Helpers__NS__contain, Helpers__NS__createFolder, Helpers__NS__createSymLink, Helpers__NS__error, Helpers__NS__execute, Helpers__NS__exists, Helpers__NS__filesFrom, Helpers__NS__foldersFrom, Helpers__NS__getFilesFrom, Helpers__NS__getFoldersFrom, Helpers__NS__getIsBrowser, Helpers__NS__getIsElectron, Helpers__NS__getIsNode, Helpers__NS__getIsRunningInGitBash, Helpers__NS__getIsSupportedTaonTerminal, Helpers__NS__getIsVerboseMode, Helpers__NS__getIsWebSQL, Helpers__NS__getIsWsl, Helpers__NS__getStdio, Helpers__NS__hideNodeWarnings, Helpers__NS__info, Helpers__NS__isBlob, Helpers__NS__isBuffer, Helpers__NS__isClass, Helpers__NS__isExistedSymlink, Helpers__NS__isFile, Helpers__NS__isFolder, Helpers__NS__isRunningInDocker, Helpers__NS__isRunningInLinuxGraphicsCapableEnvironment, Helpers__NS__isSymlinkFileExitedOrUnexisted, Helpers__NS__isSymlinkThatMatchesUrl, Helpers__NS__isUnexistedLink, Helpers__NS__killOnPort, Helpers__NS__killProcess, Helpers__NS__killProcessByPort, Helpers__NS__linksToFolderFrom, Helpers__NS__linksToFoldersFrom, Helpers__NS__log, Helpers__NS__logError, Helpers__NS__logInfo, Helpers__NS__logProc, Helpers__NS__logSuccess, Helpers__NS__logWarn, Helpers__NS__mediaTypeFromSrc, Helpers__NS__mkdirp, Helpers__NS__modifyLineByLine, Helpers__NS__msgCacheClear, Helpers__NS__openFolderInFileExplorer, Helpers__NS__parse, Helpers__NS__pathContainLink, Helpers__NS__questionYesNo, Helpers__NS__readFile, Helpers__NS__readJson, Helpers__NS__readJson5, Helpers__NS__readJsonC, Helpers__NS__relative, Helpers__NS__remove, Helpers__NS__removeEmptyLineFromString, Helpers__NS__removeFileIfExists, Helpers__NS__removeFolderIfExists, Helpers__NS__removeIfExists, Helpers__NS__removeSlashAtBegin, Helpers__NS__removeSlashAtEnd, Helpers__NS__removeSymlinks, Helpers__NS__renderError, Helpers__NS__replaceLinesInFile, Helpers__NS__run, Helpers__NS__runAsyncIn, Helpers__NS__runSyncIn, Helpers__NS__runSyncOrAsync, Helpers__NS__sleep, Helpers__NS__stopApplication, Helpers__NS__stringify, Helpers__NS__success, Helpers__NS__taskDone, Helpers__NS__taskStarted, Helpers__NS__throwError, Helpers__NS__timeout, Helpers__NS__tryCatchError, Helpers__NS__tryReadFile, Helpers__NS__tryRemoveDir, Helpers__NS__values, Helpers__NS__wait, Helpers__NS__warn, Helpers__NS__writeFile, Helpers__NS__writeJson, Helpers__NS__writeJson5, Helpers__NS__writeJsonC, HelpersTaon__NS__actionWrapper, HelpersTaon__NS__applyMixins, HelpersTaon__NS__arrays__NS__arrayMoveElementAfterB, HelpersTaon__NS__arrays__NS__arrayMoveElementBefore, HelpersTaon__NS__arrays__NS__from, HelpersTaon__NS__arrays__NS__fuzzy, HelpersTaon__NS__arrays__NS__moveObjectAfter, HelpersTaon__NS__arrays__NS__moveObjectBefore, HelpersTaon__NS__arrays__NS__second, HelpersTaon__NS__arrays__NS__sortKeys, HelpersTaon__NS__arrays__NS__uniqArray, HelpersTaon__NS__autocompleteAsk, HelpersTaon__NS__bundleCodeIntoSingleFile, HelpersTaon__NS__changeCwd, HelpersTaon__NS__changeCwdWrapper, HelpersTaon__NS__checkEnvironment, HelpersTaon__NS__checkIfNameAllowedForTaonProj, HelpersTaon__NS__checksum, HelpersTaon__NS__cliTool__NS__cleanCommand, HelpersTaon__NS__cliTool__NS__fixUnexpectedCommandCharacters, HelpersTaon__NS__cliTool__NS__getPramsFromArgs, HelpersTaon__NS__cliTool__NS__globalArgumentsParserTnp, HelpersTaon__NS__cliTool__NS__match, HelpersTaon__NS__cliTool__NS__removeArg, HelpersTaon__NS__cliTool__NS__removeArgsFromCommand, HelpersTaon__NS__cliTool__NS__resolveItemFromArgsBegin, HelpersTaon__NS__cliTool__NS__resolveItemsFromArgsBegin, HelpersTaon__NS__CLIWRAP, HelpersTaon__NS__consoleGui__NS__multiselect, HelpersTaon__NS__consoleGui__NS__pressAnyKey, HelpersTaon__NS__consoleGui__NS__question__NS__yesNo, HelpersTaon__NS__consoleGui__NS__select, HelpersTaon__NS__consoleGui__NS__wait, HelpersTaon__NS__copy, HelpersTaon__NS__copyFile, HelpersTaon__NS__copyFolderOsNative, HelpersTaon__NS__filterDontCopy, HelpersTaon__NS__filterOnlyCopy, HelpersTaon__NS__findChildren, HelpersTaon__NS__findChildrenNavi, HelpersTaon__NS__fixWebpackEnv, HelpersTaon__NS__formatPath, HelpersTaon__NS__generatedFileWrap, HelpersTaon__NS__getLinesFromFiles, HelpersTaon__NS__getMethodName, HelpersTaon__NS__getMostRecentFileName, HelpersTaon__NS__getMostRecentFilesNames, HelpersTaon__NS__getRecrusiveFilesFrom, HelpersTaon__NS__getStringFrom, HelpersTaon__NS__getTempFolder, HelpersTaon__NS__getValueFromJSON, HelpersTaon__NS__getValueFromJSONC, HelpersTaon__NS__getWorkingDirOfProcess, HelpersTaon__NS__git__NS___pull, HelpersTaon__NS__git__NS__allOrigins, HelpersTaon__NS__git__NS__backupBranch, HelpersTaon__NS__git__NS__changeRemoteFromHttpsToSSh, HelpersTaon__NS__git__NS__changeRemoveFromSshToHttps, HelpersTaon__NS__git__NS__changesSummary, HelpersTaon__NS__git__NS__checkIfthereAreSomeUncommitedChange, HelpersTaon__NS__git__NS__checkout, HelpersTaon__NS__git__NS__checkoutDefaultBranch, HelpersTaon__NS__git__NS__checkoutFromTo, HelpersTaon__NS__git__NS__checkTagExists, HelpersTaon__NS__git__NS__cleanRepoFromAnyFilesExceptDotGitFolder, HelpersTaon__NS__git__NS__clone, HelpersTaon__NS__git__NS__commit, HelpersTaon__NS__git__NS__countCommits, HelpersTaon__NS__git__NS__currentBranchName, HelpersTaon__NS__git__NS__defaultRepoBranch, HelpersTaon__NS__git__NS__fetch, HelpersTaon__NS__git__NS__findGitRoot, HelpersTaon__NS__git__NS__getACTION_MSG_RESET_GIT_HARD_COMMIT, HelpersTaon__NS__git__NS__getAllTags, HelpersTaon__NS__git__NS__getBranchesNames, HelpersTaon__NS__git__NS__getChangedFiles, HelpersTaon__NS__git__NS__getChangedFilesInCommitByHash, HelpersTaon__NS__git__NS__getChangedFilesInCommitByIndex, HelpersTaon__NS__git__NS__getCommitHashByIndex, HelpersTaon__NS__git__NS__getCommitMessageByHash, HelpersTaon__NS__git__NS__getCommitMessageByIndex, HelpersTaon__NS__git__NS__getListOfCurrentGitChanges, HelpersTaon__NS__git__NS__getOriginURL, HelpersTaon__NS__git__NS__getRemoteProvider, HelpersTaon__NS__git__NS__getUserInfo, HelpersTaon__NS__git__NS__hasAnyCommits, HelpersTaon__NS__git__NS__isGitRoot, HelpersTaon__NS__git__NS__isInMergeProcess, HelpersTaon__NS__git__NS__isInsideGitRepo, HelpersTaon__NS__git__NS__isValidRepoUrl, HelpersTaon__NS__git__NS__lastCommitDate, HelpersTaon__NS__git__NS__lastCommitHash, HelpersTaon__NS__git__NS__lastCommitMessage, HelpersTaon__NS__git__NS__lastTagHash, HelpersTaon__NS__git__NS__lastTagNameForMajorVersion, HelpersTaon__NS__git__NS__lastTagVersionName, HelpersTaon__NS__git__NS__meltActionCommits, HelpersTaon__NS__git__NS__originHttpToSsh, HelpersTaon__NS__git__NS__originSshToHttp, HelpersTaon__NS__git__NS__penultimateCommitHash, HelpersTaon__NS__git__NS__penultimateCommitMessage, HelpersTaon__NS__git__NS__pullCurrentBranch, HelpersTaon__NS__git__NS__pushCurrentBranch, HelpersTaon__NS__git__NS__rebase, HelpersTaon__NS__git__NS__removeTag, HelpersTaon__NS__git__NS__resetFiles, HelpersTaon__NS__git__NS__resetHard, HelpersTaon__NS__git__NS__resetSoftHEAD, HelpersTaon__NS__git__NS__restoreLastVersion, HelpersTaon__NS__git__NS__revertFileChanges, HelpersTaon__NS__git__NS__setUserInfos, HelpersTaon__NS__git__NS__stageAllAndCommit, HelpersTaon__NS__git__NS__stageAllFiles, HelpersTaon__NS__git__NS__stagedFiles, HelpersTaon__NS__git__NS__stageFile, HelpersTaon__NS__git__NS__stash, HelpersTaon__NS__git__NS__stashApply, HelpersTaon__NS__git__NS__tagAndPushToGitRepo, HelpersTaon__NS__git__NS__thereAreSomeUncommitedChangeExcept, HelpersTaon__NS__git__NS__uncommitedFiles, HelpersTaon__NS__git__NS__unstageAllFiles, HelpersTaon__NS__goToDir, HelpersTaon__NS__HelpersNumber, HelpersTaon__NS__input, HelpersTaon__NS__isElevated, HelpersTaon__NS__isPlainFileOrFolder, HelpersTaon__NS__killAllNode, HelpersTaon__NS__killAllNodeExceptCurrentProcess, HelpersTaon__NS__list, HelpersTaon__NS__mesureExectionInMs, HelpersTaon__NS__mesureExectionInMsSync, HelpersTaon__NS__move, HelpersTaon__NS__multipleChoicesAsk, HelpersTaon__NS__osIsMacOs, HelpersTaon__NS__outputToVScode, HelpersTaon__NS__pathFromLink, HelpersTaon__NS__paths__NS__create, HelpersTaon__NS__paths__NS__PREFIX, HelpersTaon__NS__paths__NS__removeExt, HelpersTaon__NS__paths__NS__removeExtension, HelpersTaon__NS__paths__NS__removeRootFolder, HelpersTaon__NS__prepareWatchCommand, HelpersTaon__NS__pressKeyAndContinue, HelpersTaon__NS__pressKeyOrWait, HelpersTaon__NS__randomInteger, HelpersTaon__NS__readValueFromJson, HelpersTaon__NS__readValueFromJsonC, HelpersTaon__NS__removeExcept, HelpersTaon__NS__renameFiles, HelpersTaon__NS__renameFolder, HelpersTaon__NS__requireJs, HelpersTaon__NS__requireUncached, HelpersTaon__NS__resolve, HelpersTaon__NS__restartApplicationItself, HelpersTaon__NS__selectChoicesAsk, HelpersTaon__NS__setValueToJSON, HelpersTaon__NS__setValueToJSONC, HelpersTaon__NS__size, HelpersTaon__NS__slash, HelpersTaon__NS__strings__NS__interpolateString, HelpersTaon__NS__strings__NS__numValue, HelpersTaon__NS__strings__NS__plural, HelpersTaon__NS__strings__NS__removeDoubleOrMoreEmptyLines, HelpersTaon__NS__strings__NS__singular, HelpersTaon__NS__strings__NS__splitIfNeed, HelpersTaon__NS__terminalLine, HelpersTaon__NS__tryCopyFrom, HelpersTaon__NS__tryRecreateDir, HelpersTaon__NS__uniqArray, HelpersTaon__NS__waitForCondition, HelpersTaon__NS__waitForMessegeInStdout, HelpersTaon__NS__workerCalculateArray } from 'tnp-helpers/lib-prod';
|
|
17
|
+
|
|
18
|
+
import { Hostile } from './hostile.backend';
|
|
19
|
+
import { EtcHosts, HostForServer, OptHostForServer } from './models';
|
|
20
|
+
|
|
21
|
+
const HOST_FILE_PATH = UtilsNetwork__NS__getEtcHostsPath();
|
|
22
|
+
|
|
23
|
+
const log = Log.create('vpn-split', Level.INFO);
|
|
24
|
+
//#endregion
|
|
25
|
+
|
|
26
|
+
//#region consts
|
|
27
|
+
|
|
28
|
+
const GENERATED = '#GENERATED_BY_CLI#';
|
|
29
|
+
|
|
30
|
+
const EOL = process.platform === 'win32' ? '\r\n' : '\n';
|
|
31
|
+
|
|
32
|
+
const SERVERS_PATH = '/$$$$servers$$$$';
|
|
33
|
+
|
|
34
|
+
const HOST_FILE_PATHUSER = crossPlatformPath([
|
|
35
|
+
UtilsOs__NS__getRealHomeDir(),
|
|
36
|
+
'hosts-file__vpn-split',
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const from = HostForServer.From;
|
|
40
|
+
const DefaultEtcHosts = {
|
|
41
|
+
'localhost alias': from({
|
|
42
|
+
ipOrDomain: '127.0.0.1',
|
|
43
|
+
aliases: 'localhost' as any,
|
|
44
|
+
isDefault: true,
|
|
45
|
+
} as OptHostForServer),
|
|
46
|
+
broadcasthost: from({
|
|
47
|
+
ipOrDomain: '255.255.255.255',
|
|
48
|
+
aliases: 'broadcasthost' as any,
|
|
49
|
+
isDefault: true,
|
|
50
|
+
} as OptHostForServer),
|
|
51
|
+
'localhost alias ipv6': from({
|
|
52
|
+
ipOrDomain: '::1',
|
|
53
|
+
aliases: 'localhost' as any,
|
|
54
|
+
isDefault: true,
|
|
55
|
+
} as OptHostForServer),
|
|
56
|
+
} as EtcHosts;
|
|
57
|
+
//#endregion
|
|
58
|
+
|
|
59
|
+
export class VpnSplit {
|
|
60
|
+
//#region getters
|
|
61
|
+
get hostsArr() {
|
|
62
|
+
const hosts = this.hosts;
|
|
63
|
+
return ___NS__keys(hosts).map(hostName => {
|
|
64
|
+
const v = hosts[hostName] as HostForServer;
|
|
65
|
+
v.name = hostName;
|
|
66
|
+
return v;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get hostsArrWithoutDefault() {
|
|
71
|
+
return this.hostsArr.filter(f => !f.isDefault);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private get serveKeyName() {
|
|
75
|
+
return 'tmp-' + config.file.server_key;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private get serveKeyPath() {
|
|
79
|
+
return path.join(this.cwd, this.serveKeyName);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private get serveCertName() {
|
|
83
|
+
return 'tmp-' + config.file.server_cert;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private get serveCertPath() {
|
|
87
|
+
return path.join(this.cwd, this.serveCertName);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private get serveCertChainName() {
|
|
91
|
+
return 'tmp-' + config.file.server_chain_cert;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private get serveCertChainPath() {
|
|
95
|
+
return path.join(this.cwd, this.serveCertChainName);
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
|
|
99
|
+
//#region fields
|
|
100
|
+
private readonly __hostile: Hostile;
|
|
101
|
+
//#endregion
|
|
102
|
+
|
|
103
|
+
//#region singleton
|
|
104
|
+
private static _instances = {};
|
|
105
|
+
|
|
106
|
+
private constructor(
|
|
107
|
+
private portsToPass: number[],
|
|
108
|
+
private hosts: EtcHosts,
|
|
109
|
+
private cwd: string,
|
|
110
|
+
) {
|
|
111
|
+
this.__hostile = new Hostile();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public static async Instance({
|
|
115
|
+
ports = [80, 443, 4443, 22, 2222, 8180, 8080, 4407, 7999, 9443],
|
|
116
|
+
additionalDefaultHosts = {},
|
|
117
|
+
cwd = process.cwd(),
|
|
118
|
+
allowNotSudo = false,
|
|
119
|
+
}: {
|
|
120
|
+
ports?: number[];
|
|
121
|
+
additionalDefaultHosts?: EtcHosts;
|
|
122
|
+
cwd?: string;
|
|
123
|
+
allowNotSudo?: boolean;
|
|
124
|
+
} = {}) {
|
|
125
|
+
if (!(await isElevated()) && !allowNotSudo) {
|
|
126
|
+
Helpers__NS__error(
|
|
127
|
+
`[vpn-split] Please run this program as sudo (or admin on windows)`,
|
|
128
|
+
false,
|
|
129
|
+
true,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!VpnSplit._instances[cwd]) {
|
|
134
|
+
VpnSplit._instances[cwd] = new VpnSplit(
|
|
135
|
+
ports,
|
|
136
|
+
___NS__merge(DefaultEtcHosts, additionalDefaultHosts),
|
|
137
|
+
cwd,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return VpnSplit._instances[cwd] as VpnSplit;
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
|
|
144
|
+
//#region start server
|
|
145
|
+
async startServer(saveHostInUserFolder = false) {
|
|
146
|
+
this.createCertificateIfNotExists();
|
|
147
|
+
|
|
148
|
+
const hostsForCert = this.hosts;
|
|
149
|
+
|
|
150
|
+
// const domainsForCert: string[] = Utils__NS__uniqArray(
|
|
151
|
+
// Object.keys(hostsForCert).reduce((prev, curr) => {
|
|
152
|
+
// const host = hostsForCert[curr] as HostForServer;
|
|
153
|
+
// host.aliases.forEach(alias => {
|
|
154
|
+
// if (!host.isDefault) {
|
|
155
|
+
// prev.push(alias);
|
|
156
|
+
// }
|
|
157
|
+
// });
|
|
158
|
+
// return prev;
|
|
159
|
+
// }, [] as string[]),
|
|
160
|
+
// );
|
|
161
|
+
|
|
162
|
+
// this.ensureMkcertCertificate(domainsForCert);
|
|
163
|
+
|
|
164
|
+
//#region modify /etc/host to direct traffic appropriately
|
|
165
|
+
saveHosts(hostsForCert, { saveHostInUserFolder });
|
|
166
|
+
//#endregion
|
|
167
|
+
|
|
168
|
+
// Start TCP/HTTPS passthrough
|
|
169
|
+
for (const portToPassthrough of this.portsToPass) {
|
|
170
|
+
await this.serverPassthrough(portToPassthrough);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Start UDP passthrough
|
|
174
|
+
for (const portToPassthrough of this.portsToPass) {
|
|
175
|
+
await this.serverUdpPassthrough(portToPassthrough);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
Helpers__NS__info(`Activated (server).`);
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
|
|
182
|
+
//#region apply hosts
|
|
183
|
+
public applyHosts(hosts: EtcHosts) {
|
|
184
|
+
// console.log(hosts);
|
|
185
|
+
saveHosts(hosts);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public applyHostsLocal(hosts: EtcHosts) {
|
|
189
|
+
// console.log(hosts);
|
|
190
|
+
saveHostsLocal(hosts);
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
193
|
+
|
|
194
|
+
//#region start client
|
|
195
|
+
public async startClient(
|
|
196
|
+
vpnServerTargets: URL[] | URL,
|
|
197
|
+
options: {
|
|
198
|
+
saveHostInUserFolder?: boolean;
|
|
199
|
+
useHost?: EtcHosts;
|
|
200
|
+
},
|
|
201
|
+
) {
|
|
202
|
+
options = options || {};
|
|
203
|
+
const { saveHostInUserFolder } = options;
|
|
204
|
+
if (!Array.isArray(vpnServerTargets)) {
|
|
205
|
+
vpnServerTargets = [vpnServerTargets];
|
|
206
|
+
}
|
|
207
|
+
for (const vpnServerTarget of vpnServerTargets) {
|
|
208
|
+
await this.preventBadTargetForClient(vpnServerTarget);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.createCertificateIfNotExists();
|
|
212
|
+
|
|
213
|
+
// Get remote host definitions from remote server
|
|
214
|
+
const hosts: Array<{
|
|
215
|
+
ip: string;
|
|
216
|
+
alias: string;
|
|
217
|
+
originHostname?: string;
|
|
218
|
+
}> = [];
|
|
219
|
+
|
|
220
|
+
for (const vpnServerTarget of vpnServerTargets) {
|
|
221
|
+
const newHosts = await this.getRemoteHosts(vpnServerTarget);
|
|
222
|
+
for (const host of newHosts) {
|
|
223
|
+
// Mark the original VPN server domain
|
|
224
|
+
host.originHostname = vpnServerTarget.hostname;
|
|
225
|
+
hosts.push(host);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Merge with original, redirecting all non-default entries to 127.0.0.1
|
|
230
|
+
const originalHosts = this.hostsArr;
|
|
231
|
+
const combinedHostsObj = ___NS__values(
|
|
232
|
+
[
|
|
233
|
+
...originalHosts,
|
|
234
|
+
...hosts.map(h =>
|
|
235
|
+
HostForServer.From(
|
|
236
|
+
{
|
|
237
|
+
aliases: h.alias as any,
|
|
238
|
+
ipOrDomain: h.ip,
|
|
239
|
+
originHostname: h.originHostname,
|
|
240
|
+
},
|
|
241
|
+
`external host ${h.alias} ${h.ip}`,
|
|
242
|
+
),
|
|
243
|
+
),
|
|
244
|
+
]
|
|
245
|
+
.map(c => {
|
|
246
|
+
let copy = c.clone();
|
|
247
|
+
if (!copy.isDefault) {
|
|
248
|
+
copy.ip = `127.0.0.1`;
|
|
249
|
+
}
|
|
250
|
+
return copy;
|
|
251
|
+
})
|
|
252
|
+
.reduce((prev, curr) => {
|
|
253
|
+
return ___NS__merge(prev, {
|
|
254
|
+
[curr.aliases.join(' ')]: curr,
|
|
255
|
+
});
|
|
256
|
+
}, {}),
|
|
257
|
+
) as HostForServer[];
|
|
258
|
+
|
|
259
|
+
const hostsForCert = options.useHost ? options.useHost : combinedHostsObj;
|
|
260
|
+
|
|
261
|
+
// const domainsForCert: string[] = Utils__NS__uniqArray(
|
|
262
|
+
// Object.keys(hostsForCert).reduce((prev, curr) => {
|
|
263
|
+
// const host = hostsForCert[curr] as HostForServer;
|
|
264
|
+
// host.aliases.forEach(alias => {
|
|
265
|
+
// if (!host.isDefault) {
|
|
266
|
+
// prev.push(alias);
|
|
267
|
+
// }
|
|
268
|
+
// });
|
|
269
|
+
// return prev;
|
|
270
|
+
// }, [] as string[]),
|
|
271
|
+
// );
|
|
272
|
+
|
|
273
|
+
// this.ensureMkcertCertificate(domainsForCert);
|
|
274
|
+
|
|
275
|
+
saveHosts(hostsForCert, {
|
|
276
|
+
saveHostInUserFolder,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Start TCP/HTTPS passthrough
|
|
280
|
+
for (const portToPassthrough of this.portsToPass) {
|
|
281
|
+
await this.clientPassthrough(
|
|
282
|
+
portToPassthrough,
|
|
283
|
+
vpnServerTargets,
|
|
284
|
+
combinedHostsObj,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Start UDP passthrough
|
|
289
|
+
for (const portToPassthrough of this.portsToPass) {
|
|
290
|
+
await this.clientUdpPassthrough(
|
|
291
|
+
portToPassthrough,
|
|
292
|
+
vpnServerTargets,
|
|
293
|
+
combinedHostsObj,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
Helpers__NS__info(`Client activated`);
|
|
298
|
+
}
|
|
299
|
+
//#endregion
|
|
300
|
+
|
|
301
|
+
//#region private methods / get remote hosts
|
|
302
|
+
private async getRemoteHosts(vpnServerTarget: URL) {
|
|
303
|
+
try {
|
|
304
|
+
const url = `http://${vpnServerTarget.hostname}${SERVERS_PATH}`;
|
|
305
|
+
const response = await axios({ url, method: 'GET' });
|
|
306
|
+
return response.data as {
|
|
307
|
+
ip: string;
|
|
308
|
+
alias: string;
|
|
309
|
+
originHostname?: string;
|
|
310
|
+
}[];
|
|
311
|
+
} catch (err) {
|
|
312
|
+
Helpers__NS__error(
|
|
313
|
+
`Remote server: ${vpnServerTarget.hostname} may be inactive...`,
|
|
314
|
+
true,
|
|
315
|
+
true,
|
|
316
|
+
);
|
|
317
|
+
return [];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
//#endregion
|
|
321
|
+
|
|
322
|
+
//#region private methods / create certificate
|
|
323
|
+
private createCertificateIfNotExists() {
|
|
324
|
+
if (
|
|
325
|
+
!Helpers__NS__exists(this.serveKeyPath) ||
|
|
326
|
+
!Helpers__NS__exists(this.serveCertPath)
|
|
327
|
+
) {
|
|
328
|
+
Helpers__NS__info(
|
|
329
|
+
`[vpn-split] Generating new self-signed certificate for localhost...`,
|
|
330
|
+
);
|
|
331
|
+
const commandGen = UtilsOs__NS__isRunningInWindowsPowerShell()
|
|
332
|
+
? `powershell -Command "& 'C:\\Program Files\\Git\\usr\\bin\\openssl.exe' req -nodes -new -x509 -keyout ${this.serveKeyName} -out ${this.serveCertName}"`
|
|
333
|
+
: `openssl req -nodes -new -x509 -keyout ${this.serveKeyName} -out ${this.serveCertName}`;
|
|
334
|
+
Helpers__NS__run(commandGen, { cwd: this.cwd, output: true }).sync();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
//#endregion
|
|
338
|
+
|
|
339
|
+
//#region private methods / TCP & HTTPS passthrough
|
|
340
|
+
private getTarget({
|
|
341
|
+
req,
|
|
342
|
+
res,
|
|
343
|
+
port,
|
|
344
|
+
hostname,
|
|
345
|
+
}: {
|
|
346
|
+
req: express.Request;
|
|
347
|
+
res: express.Response;
|
|
348
|
+
port: number;
|
|
349
|
+
hostname: string;
|
|
350
|
+
}): string {
|
|
351
|
+
return `${req.protocol}://${hostname}:${port}`;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private getProxyConfig({
|
|
355
|
+
req,
|
|
356
|
+
res,
|
|
357
|
+
port,
|
|
358
|
+
hostname,
|
|
359
|
+
isHttps,
|
|
360
|
+
}: {
|
|
361
|
+
req: express.Request;
|
|
362
|
+
res: express.Response;
|
|
363
|
+
port: number;
|
|
364
|
+
hostname?: string;
|
|
365
|
+
isHttps: boolean;
|
|
366
|
+
}): httpProxy.ServerOptions {
|
|
367
|
+
const serverPassthrough = !!hostname;
|
|
368
|
+
const target = this.getTarget({
|
|
369
|
+
req,
|
|
370
|
+
res,
|
|
371
|
+
port,
|
|
372
|
+
hostname: serverPassthrough ? hostname : req.hostname,
|
|
373
|
+
});
|
|
374
|
+
// console.log({
|
|
375
|
+
// target,
|
|
376
|
+
// port,
|
|
377
|
+
// hostname,
|
|
378
|
+
// reqHostname: req.hostname,
|
|
379
|
+
// serverPassthrough,
|
|
380
|
+
// });
|
|
381
|
+
return isHttps
|
|
382
|
+
? {
|
|
383
|
+
target,
|
|
384
|
+
ssl: {
|
|
385
|
+
key: fse.readFileSync(this.serveKeyPath),
|
|
386
|
+
cert: fse.readFileSync(this.serveCertPath),
|
|
387
|
+
},
|
|
388
|
+
// agent: new https.Agent({
|
|
389
|
+
// secureOptions: crypto.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION,
|
|
390
|
+
// }),
|
|
391
|
+
// changeOrigin: true,
|
|
392
|
+
secure: true,
|
|
393
|
+
}
|
|
394
|
+
: ({ target } as httpProxy.ServerOptions);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private getNotFoundMsg(
|
|
398
|
+
req: express.Request,
|
|
399
|
+
res: express.Response,
|
|
400
|
+
port: number,
|
|
401
|
+
type: 'client' | 'server',
|
|
402
|
+
): string {
|
|
403
|
+
return `[vpn-split] You are requesting a URL that is not in proxy reach [${type}]
|
|
404
|
+
Protocol: ${req.protocol}
|
|
405
|
+
Hostname: ${req.hostname}
|
|
406
|
+
OriginalUrl: ${req.originalUrl}
|
|
407
|
+
Req.method: ${req.method}
|
|
408
|
+
Port: ${port}
|
|
409
|
+
SERVERS_PATH: ${SERVERS_PATH}`;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private getMaybeChangeOriginTrueMsg(
|
|
413
|
+
req: express.Request,
|
|
414
|
+
res: express.Response,
|
|
415
|
+
port: number,
|
|
416
|
+
type: 'client' | 'server',
|
|
417
|
+
): string {
|
|
418
|
+
return `[vpn-split] Possibly need changeOrigin: true in your proxy config
|
|
419
|
+
Protocol: ${req.protocol}
|
|
420
|
+
Hostname: ${req.hostname}
|
|
421
|
+
OriginalUrl: ${req.originalUrl}
|
|
422
|
+
Req.method: ${req.method}
|
|
423
|
+
Port: ${port}`;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private filterHeaders(
|
|
427
|
+
req: http.IncomingMessage & express.Request,
|
|
428
|
+
res: http.ServerResponse & express.Response,
|
|
429
|
+
): void {
|
|
430
|
+
// If you have any headers to remove, do it here:
|
|
431
|
+
const headersToRemove = [
|
|
432
|
+
// 'Strict-Transport-Security',
|
|
433
|
+
// 'Content-Security-Policy',
|
|
434
|
+
// ...
|
|
435
|
+
];
|
|
436
|
+
headersToRemove.forEach(headerName => {
|
|
437
|
+
delete req.headers[headerName];
|
|
438
|
+
res.setHeader(headerName, '');
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private isHttpsPort(port: number): boolean {
|
|
443
|
+
// Decide your logic for “is HTTPS” here
|
|
444
|
+
return [443, 4443, 9443].includes(port);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
//#region server passthrough (TCP/HTTPS)
|
|
448
|
+
private async serverPassthrough(portToPassthrough: number): Promise<void> {
|
|
449
|
+
const isHttps = this.isHttpsPort(portToPassthrough);
|
|
450
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
451
|
+
const app = express();
|
|
452
|
+
const proxy = httpProxy.createProxyServer({});
|
|
453
|
+
|
|
454
|
+
const localIp = await UtilsNetwork__NS__getLocalIpAddresses();
|
|
455
|
+
const currentLocalIps = [
|
|
456
|
+
CoreModels__NS__localhostDomain,
|
|
457
|
+
CoreModels__NS__localhostIp127,
|
|
458
|
+
...localIp.map(a => a.address),
|
|
459
|
+
];
|
|
460
|
+
|
|
461
|
+
app.use(
|
|
462
|
+
(
|
|
463
|
+
req: http.IncomingMessage & express.Request,
|
|
464
|
+
res: http.ServerResponse & express.Response,
|
|
465
|
+
next,
|
|
466
|
+
) => {
|
|
467
|
+
this.filterHeaders(req, res);
|
|
468
|
+
|
|
469
|
+
if (currentLocalIps.includes(req.hostname)) {
|
|
470
|
+
if (req.method === 'GET' && req.originalUrl === SERVERS_PATH) {
|
|
471
|
+
res.send(
|
|
472
|
+
JSON.stringify(
|
|
473
|
+
this.hostsArrWithoutDefault.map(h => ({
|
|
474
|
+
ip: h.ip,
|
|
475
|
+
alias: HelpersTaon__NS__arrays__NS__from(h.aliases).join(' '),
|
|
476
|
+
})),
|
|
477
|
+
),
|
|
478
|
+
);
|
|
479
|
+
} else {
|
|
480
|
+
const msg = this.getNotFoundMsg(
|
|
481
|
+
req,
|
|
482
|
+
res,
|
|
483
|
+
portToPassthrough,
|
|
484
|
+
'server',
|
|
485
|
+
);
|
|
486
|
+
log.d(msg);
|
|
487
|
+
res.send(msg);
|
|
488
|
+
}
|
|
489
|
+
next();
|
|
490
|
+
} else {
|
|
491
|
+
proxy.web(
|
|
492
|
+
req,
|
|
493
|
+
res,
|
|
494
|
+
this.getProxyConfig({ req, res, port: portToPassthrough, isHttps }),
|
|
495
|
+
next,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
const server = isHttps
|
|
502
|
+
? https.createServer(
|
|
503
|
+
{
|
|
504
|
+
key: fse.readFileSync(this.serveKeyPath),
|
|
505
|
+
cert: fse.readFileSync(this.serveCertPath),
|
|
506
|
+
},
|
|
507
|
+
app,
|
|
508
|
+
)
|
|
509
|
+
: http.createServer(app);
|
|
510
|
+
|
|
511
|
+
await Helpers__NS__killProcessByPort(portToPassthrough, { silent: true });
|
|
512
|
+
await new Promise<void>((resolve, reject) => {
|
|
513
|
+
server.listen(portToPassthrough, () => {
|
|
514
|
+
console.log(
|
|
515
|
+
`TCP/HTTPS server listening on port ${portToPassthrough} (secure=${isHttps})`,
|
|
516
|
+
);
|
|
517
|
+
resolve();
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
//#endregion
|
|
522
|
+
|
|
523
|
+
//#region client passthrough (TCP/HTTPS)
|
|
524
|
+
private async clientPassthrough(
|
|
525
|
+
portToPassthrough: number,
|
|
526
|
+
vpnServerTargets: URL[],
|
|
527
|
+
hostsArr: HostForServer[],
|
|
528
|
+
) {
|
|
529
|
+
// Map from an alias => the “real” origin hostname (the server domain)
|
|
530
|
+
const aliasToOriginHostname: { [alias: string]: string | undefined } = {};
|
|
531
|
+
for (const h of hostsArr) {
|
|
532
|
+
for (const alias of h.aliases) {
|
|
533
|
+
aliasToOriginHostname[alias] = h.originHostname;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// Remove defaults from the map so we don't cause collisions
|
|
537
|
+
delete aliasToOriginHostname['localhost'];
|
|
538
|
+
delete aliasToOriginHostname['broadcasthost'];
|
|
539
|
+
|
|
540
|
+
// Build a dictionary from originHostname => URL (for quick lookup)
|
|
541
|
+
const originToUrlMap: { [origin: string]: URL } = {};
|
|
542
|
+
for (const url of vpnServerTargets) {
|
|
543
|
+
originToUrlMap[url.hostname] = url;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const isHttps = this.isHttpsPort(portToPassthrough);
|
|
547
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
548
|
+
const app = express();
|
|
549
|
+
const proxy = httpProxy.createProxyServer({});
|
|
550
|
+
|
|
551
|
+
app.use(
|
|
552
|
+
(
|
|
553
|
+
req: http.IncomingMessage & express.Request,
|
|
554
|
+
res: http.ServerResponse & express.Response,
|
|
555
|
+
next,
|
|
556
|
+
) => {
|
|
557
|
+
this.filterHeaders(req, res);
|
|
558
|
+
|
|
559
|
+
// Identify the real origin server based on alias
|
|
560
|
+
const originHostname = aliasToOriginHostname[req.hostname];
|
|
561
|
+
if (!originHostname) {
|
|
562
|
+
// Not one of our known aliases
|
|
563
|
+
const msg = this.getMaybeChangeOriginTrueMsg(
|
|
564
|
+
req,
|
|
565
|
+
res,
|
|
566
|
+
portToPassthrough,
|
|
567
|
+
'client',
|
|
568
|
+
);
|
|
569
|
+
res.send(msg);
|
|
570
|
+
next();
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Proxy onward to the real server domain at the same port
|
|
575
|
+
const targetUrlObj = originToUrlMap[originHostname];
|
|
576
|
+
if (!targetUrlObj) {
|
|
577
|
+
const notFoundMsg = this.getNotFoundMsg(
|
|
578
|
+
req,
|
|
579
|
+
res,
|
|
580
|
+
portToPassthrough,
|
|
581
|
+
'client',
|
|
582
|
+
);
|
|
583
|
+
res.send(notFoundMsg);
|
|
584
|
+
next();
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Forward
|
|
589
|
+
proxy.web(
|
|
590
|
+
req,
|
|
591
|
+
res,
|
|
592
|
+
this.getProxyConfig({
|
|
593
|
+
req,
|
|
594
|
+
res,
|
|
595
|
+
port: portToPassthrough,
|
|
596
|
+
isHttps,
|
|
597
|
+
hostname: targetUrlObj.hostname,
|
|
598
|
+
}),
|
|
599
|
+
next,
|
|
600
|
+
);
|
|
601
|
+
},
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
const server = isHttps
|
|
605
|
+
? https.createServer(
|
|
606
|
+
{
|
|
607
|
+
key: fse.readFileSync(this.serveKeyPath),
|
|
608
|
+
cert: fse.readFileSync(this.serveCertPath),
|
|
609
|
+
},
|
|
610
|
+
app,
|
|
611
|
+
)
|
|
612
|
+
: http.createServer(app);
|
|
613
|
+
|
|
614
|
+
await Helpers__NS__killProcessByPort(portToPassthrough, { silent: true });
|
|
615
|
+
await new Promise<void>((resolve, reject) => {
|
|
616
|
+
server.listen(portToPassthrough, () => {
|
|
617
|
+
log.i(
|
|
618
|
+
`TCP/HTTPS client listening on port ${portToPassthrough} (secure=${isHttps})`,
|
|
619
|
+
);
|
|
620
|
+
resolve();
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
//#endregion
|
|
625
|
+
|
|
626
|
+
//#region UDP passthrough
|
|
627
|
+
/**
|
|
628
|
+
* Start a UDP socket for “server” mode on a given port.
|
|
629
|
+
* This example forwards inbound messages right back to the sender,
|
|
630
|
+
* or you can forward them to an external IP:port if desired.
|
|
631
|
+
*/
|
|
632
|
+
private async serverUdpPassthrough(port: number): Promise<void> {
|
|
633
|
+
return;
|
|
634
|
+
// Example of a simple “echo-style” server or forwarder
|
|
635
|
+
const socket = dgram.createSocket('udp4');
|
|
636
|
+
|
|
637
|
+
// (Optionally) kill existing processes on that port – though for UDP
|
|
638
|
+
// we might not have a direct “listener process.” Adjust as needed.
|
|
639
|
+
// await Helpers__NS__killProcessByPort(port, { silent: true }).catch(() => {});
|
|
640
|
+
|
|
641
|
+
socket.on('message', (msg, rinfo) => {
|
|
642
|
+
// rinfo contains { address, port } of the sender
|
|
643
|
+
// In a typical “server” scenario, you might:
|
|
644
|
+
// 1. Inspect msg
|
|
645
|
+
// 2. Possibly forward to some real backend if needed
|
|
646
|
+
// 3. Or just echo it back
|
|
647
|
+
|
|
648
|
+
// For a real forward, do something like:
|
|
649
|
+
// const backendHost = 'some-other-host-or-ip';
|
|
650
|
+
// const backendPort = 9999;
|
|
651
|
+
// socket.send(msg, 0, msg.length, backendPort, backendHost);
|
|
652
|
+
|
|
653
|
+
// For a basic echo server:
|
|
654
|
+
socket.send(msg, 0, msg.length, rinfo.port, rinfo.address, err => {
|
|
655
|
+
if (err) {
|
|
656
|
+
log.er(`UDP server send error: ${err}`);
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
socket.on('listening', () => {
|
|
662
|
+
const address = socket.address();
|
|
663
|
+
log.i(`UDP server listening at ${address.address}:${address.port}`);
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
socket.bind(port);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Start a UDP socket for “client” mode on a given port.
|
|
671
|
+
* This example also just does a trivial pass-through or echo,
|
|
672
|
+
* but you can adapt to forward to a remote server.
|
|
673
|
+
*/
|
|
674
|
+
private async clientUdpPassthrough(
|
|
675
|
+
port: number,
|
|
676
|
+
vpnServerTargets: URL[],
|
|
677
|
+
hostsArr: HostForServer[],
|
|
678
|
+
): Promise<void> {
|
|
679
|
+
return;
|
|
680
|
+
// console.log(`Client UDP passthrough, port ${port}` );
|
|
681
|
+
// In client mode, we typically intercept local UDP traffic on “port”
|
|
682
|
+
// and forward it to the remote server. Then we forward the remote
|
|
683
|
+
// server's response back to the local client.
|
|
684
|
+
|
|
685
|
+
const socket = dgram.createSocket('udp4');
|
|
686
|
+
// await Helpers__NS__killProcessByPort(port, { silent: true }).catch(() => {});
|
|
687
|
+
|
|
688
|
+
// For simplicity, pick the first server from the array
|
|
689
|
+
// Or add your own logic to choose among multiple.
|
|
690
|
+
const primaryTarget = vpnServerTargets[0];
|
|
691
|
+
const targetHost = primaryTarget.hostname;
|
|
692
|
+
// Choose the same port or a custom port
|
|
693
|
+
const targetPort = port;
|
|
694
|
+
|
|
695
|
+
// A map to remember who originally sent us a packet,
|
|
696
|
+
// so we can route the response back properly.
|
|
697
|
+
// Key could be “targetHost:targetPort => { address, port }”
|
|
698
|
+
// or “clientAddress:clientPort” => { remoteAddress, remotePort }.
|
|
699
|
+
// Adapt to your scenario.
|
|
700
|
+
const clientMap = new Map<string, dgram.RemoteInfo>();
|
|
701
|
+
|
|
702
|
+
socket.on('message', (msg, rinfo) => {
|
|
703
|
+
//
|
|
704
|
+
// If the message is from a local client, forward to server.
|
|
705
|
+
// If the message is from the server, forward back to the correct client.
|
|
706
|
+
//
|
|
707
|
+
|
|
708
|
+
// Check if it’s from local or remote by address. This is simplistic:
|
|
709
|
+
const isFromLocal = !___NS__includes(
|
|
710
|
+
hostsArr.map(h => h.ipOrDomain),
|
|
711
|
+
rinfo.address,
|
|
712
|
+
);
|
|
713
|
+
|
|
714
|
+
if (isFromLocal) {
|
|
715
|
+
// Received from local => forward to “server” (the VPN server)
|
|
716
|
+
// Keep track of who we got this from (the local client)
|
|
717
|
+
const key = `local-${rinfo.address}:${rinfo.port}`;
|
|
718
|
+
clientMap.set(key, rinfo);
|
|
719
|
+
|
|
720
|
+
// Forward to remote
|
|
721
|
+
socket.send(msg, 0, msg.length, targetPort, targetHost, err => {
|
|
722
|
+
if (err) {
|
|
723
|
+
log.er(`UDP client forward error: ${err}`);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
} else {
|
|
727
|
+
// Probably from remote => forward back to whichever local client sent it
|
|
728
|
+
// In a more advanced scenario, parse the payload or maintain a bigger table
|
|
729
|
+
// with NAT-like sessions.
|
|
730
|
+
//
|
|
731
|
+
// For now, we guess it’s from the “VPN server”
|
|
732
|
+
// We'll just route it back to the single local client that we stored
|
|
733
|
+
// or do multiple if we had a better NAT map.
|
|
734
|
+
|
|
735
|
+
// If you have multi-target or multi-client logic, adapt here.
|
|
736
|
+
// For example, search clientMap by something in the msg or a NAT key.
|
|
737
|
+
clientMap.forEach((localRinfo, key) => {
|
|
738
|
+
socket.send(
|
|
739
|
+
msg,
|
|
740
|
+
0,
|
|
741
|
+
msg.length,
|
|
742
|
+
localRinfo.port,
|
|
743
|
+
localRinfo.address,
|
|
744
|
+
err => {
|
|
745
|
+
if (err) {
|
|
746
|
+
log.er(`UDP client re-send error: ${err}`);
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
);
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
socket.on('listening', () => {
|
|
755
|
+
const address = socket.address();
|
|
756
|
+
log.i(
|
|
757
|
+
`UDP client listening at ${address.address}:${address.port}, forwarding to ${targetHost}:${targetPort}`,
|
|
758
|
+
);
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
socket.bind(port);
|
|
762
|
+
}
|
|
763
|
+
//#endregion
|
|
764
|
+
|
|
765
|
+
//#region private methods / get port from request
|
|
766
|
+
private getPortFromRequest(req: express.Request): number {
|
|
767
|
+
const host = req.headers.host;
|
|
768
|
+
const protocol = req.protocol;
|
|
769
|
+
if (host) {
|
|
770
|
+
const hostParts = host.split(':');
|
|
771
|
+
if (hostParts.length === 2) {
|
|
772
|
+
return Number(hostParts[1]);
|
|
773
|
+
} else {
|
|
774
|
+
return protocol === 'https' ? 443 : 80;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return 80;
|
|
778
|
+
}
|
|
779
|
+
//#endregion
|
|
780
|
+
|
|
781
|
+
// Put this method in your VpnSplit class (or outside as a static helper)
|
|
782
|
+
private ensureMkcertCertificate(domains: string[]) {
|
|
783
|
+
//#region @backendFunc
|
|
784
|
+
|
|
785
|
+
// console.log('Domains for cert:', domains);
|
|
786
|
+
// Helpers__NS__error(``,false,true);
|
|
787
|
+
|
|
788
|
+
if (!UtilsOs__NS__commandExistsSync('mkcert')) {
|
|
789
|
+
Helpers__NS__error('[vpn-split] mkcert is not installed.', false, true);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const certPath = this.serveCertPath; // e.g. .../tmp-server_cert
|
|
793
|
+
const keyPath = this.serveKeyPath; // e.g. .../tmp-server_key
|
|
794
|
+
|
|
795
|
+
const allNames = [
|
|
796
|
+
'localhost',
|
|
797
|
+
'127.0.0.1',
|
|
798
|
+
// '::1',
|
|
799
|
+
// optional: common local networks so the cert also works for raw IP access
|
|
800
|
+
// '10.0.0.0/8',
|
|
801
|
+
// '172.16.0.0/12',
|
|
802
|
+
// '192.168.0.0/16',
|
|
803
|
+
...domains, // <-- your 120+ domains here
|
|
804
|
+
];
|
|
805
|
+
|
|
806
|
+
// Helper: run mkcert and capture output
|
|
807
|
+
const runMkcert = (args: string) => {
|
|
808
|
+
try {
|
|
809
|
+
return execSync(`mkcert ${args}`, { stdio: 'pipe' }).toString().trim();
|
|
810
|
+
} catch (err: any) {
|
|
811
|
+
const stderr = err.stderr?.toString() || '';
|
|
812
|
+
if (stderr.includes('command not found')) {
|
|
813
|
+
Helpers__NS__error(
|
|
814
|
+
'[vpn-split] mkcert is not installed. Install it first: https://github.com/FiloSottile/mkcert',
|
|
815
|
+
false,
|
|
816
|
+
true,
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
throw err;
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
// 1. Check if the root CA is already installed
|
|
824
|
+
let caInstalled = false;
|
|
825
|
+
try {
|
|
826
|
+
const output = runMkcert('-CAROOT');
|
|
827
|
+
// If -CAROOT succeeds and the root files exist → CA is installed
|
|
828
|
+
const rootDir = crossPlatformPath(output.trim());
|
|
829
|
+
if (
|
|
830
|
+
rootDir &&
|
|
831
|
+
fse.pathExistsSync(crossPlatformPath([rootDir, 'rootCA.pem']))
|
|
832
|
+
) {
|
|
833
|
+
Helpers__NS__info('[vpn-split] mkcert local CA is already installed');
|
|
834
|
+
caInstalled = true;
|
|
835
|
+
} else {
|
|
836
|
+
Helpers__NS__info('[vpn-split] mkcert local CA is NOT installed');
|
|
837
|
+
}
|
|
838
|
+
} catch {
|
|
839
|
+
Helpers__NS__info('[vpn-split] mkcert error checking local CA installation');
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// 2. Install the local CA if needed (requires sudo once)
|
|
843
|
+
if (!caInstalled) {
|
|
844
|
+
Helpers__NS__info(
|
|
845
|
+
'[vpn-split] Installing mkcert local CA (requires sudo once)...',
|
|
846
|
+
);
|
|
847
|
+
try {
|
|
848
|
+
execSync('mkcert -install', { stdio: 'inherit' });
|
|
849
|
+
Helpers__NS__info('[vpn-split] Local CA installed and trusted system-wide');
|
|
850
|
+
} catch (err) {
|
|
851
|
+
Helpers__NS__error(
|
|
852
|
+
'[vpn-split] Failed to run "mkcert -install". Run it manually with sudo.',
|
|
853
|
+
false,
|
|
854
|
+
true,
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
const isWindows = process.platform === 'win32';
|
|
860
|
+
|
|
861
|
+
// const maxArgLength = isWindows ? 2000 : 8000; // Conservative limits
|
|
862
|
+
let tempCertFiles: string[] = [];
|
|
863
|
+
let tempKeyFiles: string[] = [];
|
|
864
|
+
|
|
865
|
+
if (allNames.length <= 20 || !isWindows) {
|
|
866
|
+
// Small list or non-Windows: one shot
|
|
867
|
+
const namesArg = allNames.map(d => `"${d}"`).join(' ');
|
|
868
|
+
const mkcertCmd = `-key-file "${keyPath}" -cert-file "${certPath}" ${namesArg}`;
|
|
869
|
+
fse.ensureDirSync(path.dirname(certPath));
|
|
870
|
+
runMkcert(mkcertCmd);
|
|
871
|
+
} else {
|
|
872
|
+
// Big list on Windows: Batch into chunks of ~20 domains
|
|
873
|
+
const chunkSize = 20;
|
|
874
|
+
fse.ensureDirSync(path.dirname(certPath));
|
|
875
|
+
|
|
876
|
+
for (let i = 0; i < allNames.length; i += chunkSize) {
|
|
877
|
+
const chunk = allNames.slice(i, i + chunkSize);
|
|
878
|
+
const tempCert = `${certPath}.${i}.pem`;
|
|
879
|
+
const tempKey = `${keyPath}.${i}.pem`;
|
|
880
|
+
const namesArg = chunk.map(d => `"${d}"`).join(' ');
|
|
881
|
+
const mkcertCmd = `-key-file "${tempKey}" -cert-file "${tempCert}" ${namesArg}`;
|
|
882
|
+
|
|
883
|
+
Helpers__NS__info(
|
|
884
|
+
`[vpn-split] Generating batch ${Math.floor(i / chunkSize) + 1}/${Math.ceil(allNames.length / chunkSize)}...`,
|
|
885
|
+
);
|
|
886
|
+
runMkcert(mkcertCmd);
|
|
887
|
+
|
|
888
|
+
tempCertFiles.push(tempCert);
|
|
889
|
+
tempKeyFiles.push(tempKey);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// 3. Merge all temp certs into one (cert chain + leaves) and pick first key
|
|
893
|
+
Helpers__NS__info('[vpn-split] Merging batches into final cert...');
|
|
894
|
+
const firstKey = tempKeyFiles[0];
|
|
895
|
+
fse.copySync(firstKey, keyPath); // Use the first key (all are identical)
|
|
896
|
+
|
|
897
|
+
// Concat all certs (mkcert output is already PEM-formatted)
|
|
898
|
+
const mergedCertContent = tempCertFiles
|
|
899
|
+
.map(f => fse.readFileSync(f, 'utf8'))
|
|
900
|
+
.join('\n'); // PEM certs stack nicely
|
|
901
|
+
fse.writeFileSync(certPath, mergedCertContent);
|
|
902
|
+
|
|
903
|
+
// Clean up temps
|
|
904
|
+
tempCertFiles.forEach(f => fse.removeSync(f));
|
|
905
|
+
tempKeyFiles.slice(1).forEach(f => fse.removeSync(f)); // Keep first key as backup if needed
|
|
906
|
+
|
|
907
|
+
Helpers__NS__info('[vpn-split] Merged cert ready!');
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Verify
|
|
911
|
+
if (!fse.existsSync(certPath) || !fse.existsSync(keyPath)) {
|
|
912
|
+
Helpers__NS__error('[vpn-split] Files missing after generation!', false, true);
|
|
913
|
+
} else {
|
|
914
|
+
Helpers__NS__info(
|
|
915
|
+
`[vpn-split] Trusted cert ready: ${allNames.length} hostnames covered`,
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
//#endregion
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
//#region private methods / prevent bad target for client
|
|
922
|
+
private async preventBadTargetForClient(vpnServerTarget: URL) {
|
|
923
|
+
if (!vpnServerTarget) {
|
|
924
|
+
const currentLocalIp =
|
|
925
|
+
await UtilsNetwork__NS__getFirstIpV4LocalActiveIpAddress();
|
|
926
|
+
Helpers__NS__error(
|
|
927
|
+
`[vpn-server] Please provide a correct target server.\n` +
|
|
928
|
+
`Example:\n` +
|
|
929
|
+
`vpn-server ${currentLocalIp}\n\n` +
|
|
930
|
+
`Your local IP is: ${currentLocalIp}`,
|
|
931
|
+
false,
|
|
932
|
+
true,
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
//#endregion
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
//#region helper / gen msg
|
|
940
|
+
const genMsg =
|
|
941
|
+
`
|
|
942
|
+
################################################
|
|
943
|
+
## This file is generated #####################
|
|
944
|
+
################################################
|
|
945
|
+
`.trim() + EOL;
|
|
946
|
+
//#endregion
|
|
947
|
+
|
|
948
|
+
//#region helpers / save hosts
|
|
949
|
+
function saveHosts(
|
|
950
|
+
hosts: EtcHosts | HostForServer[],
|
|
951
|
+
options?: {
|
|
952
|
+
saveHostInUserFolder?: boolean;
|
|
953
|
+
},
|
|
954
|
+
) {
|
|
955
|
+
const { saveHostInUserFolder } = options || {};
|
|
956
|
+
if (___NS__isArray(hosts)) {
|
|
957
|
+
hosts = hosts.reduce((prev, curr) => {
|
|
958
|
+
return ___NS__merge(prev, {
|
|
959
|
+
[curr.name]: curr,
|
|
960
|
+
});
|
|
961
|
+
}, {} as EtcHosts);
|
|
962
|
+
}
|
|
963
|
+
const toSave = parseHost(hosts, !!saveHostInUserFolder);
|
|
964
|
+
if (saveHostInUserFolder) {
|
|
965
|
+
Helpers__NS__writeFile(HOST_FILE_PATHUSER, toSave);
|
|
966
|
+
} else {
|
|
967
|
+
Helpers__NS__writeFile(HOST_FILE_PATH, toSave);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function saveHostsLocal(
|
|
972
|
+
hosts: EtcHosts | HostForServer[],
|
|
973
|
+
options?: {
|
|
974
|
+
saveHostInUserFolder?: boolean;
|
|
975
|
+
},
|
|
976
|
+
) {
|
|
977
|
+
const { saveHostInUserFolder } = options || {};
|
|
978
|
+
if (___NS__isArray(hosts)) {
|
|
979
|
+
hosts = hosts.reduce((prev, curr) => {
|
|
980
|
+
return ___NS__merge(prev, {
|
|
981
|
+
[curr.name]: curr,
|
|
982
|
+
});
|
|
983
|
+
}, {} as EtcHosts);
|
|
984
|
+
}
|
|
985
|
+
const toSave = parseHost(hosts, !!saveHostInUserFolder, true);
|
|
986
|
+
if (saveHostInUserFolder) {
|
|
987
|
+
Helpers__NS__writeFile(HOST_FILE_PATHUSER, toSave);
|
|
988
|
+
} else {
|
|
989
|
+
Helpers__NS__writeFile(HOST_FILE_PATH, toSave);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
//#endregion
|
|
993
|
+
|
|
994
|
+
//#region helpers / parse hosts
|
|
995
|
+
function parseHost(
|
|
996
|
+
hosts: EtcHosts,
|
|
997
|
+
saveHostInUserFolder: boolean,
|
|
998
|
+
useLocal = false,
|
|
999
|
+
) {
|
|
1000
|
+
// hosts = ___NS__merge(hosts, DefaultEtcHosts);
|
|
1001
|
+
hosts = {
|
|
1002
|
+
...DefaultEtcHosts,
|
|
1003
|
+
...hosts,
|
|
1004
|
+
};
|
|
1005
|
+
___NS__keys(hosts).forEach(hostName => {
|
|
1006
|
+
const v = hosts[hostName] as HostForServer;
|
|
1007
|
+
v.name = hostName;
|
|
1008
|
+
});
|
|
1009
|
+
return (
|
|
1010
|
+
genMsg +
|
|
1011
|
+
EOL +
|
|
1012
|
+
___NS__keys(hosts)
|
|
1013
|
+
.map(hostName => {
|
|
1014
|
+
const v = hosts[hostName] as HostForServer;
|
|
1015
|
+
|
|
1016
|
+
if (v.skipUpdateOfServerEtcHosts) {
|
|
1017
|
+
console.warn(
|
|
1018
|
+
`[vpn-split] Skip saving host: ${v.name} (${v.ipOrDomain})`,
|
|
1019
|
+
);
|
|
1020
|
+
return `# SKIPPING HOST ${v.ipOrDomain} ${v.aliases.join(' ')} ${GENERATED}`; // Skip saving this host
|
|
1021
|
+
}
|
|
1022
|
+
const aliasesStr = (v.aliases as string[]).join(' ');
|
|
1023
|
+
if (saveHostInUserFolder) {
|
|
1024
|
+
// For a user-specific hosts file:
|
|
1025
|
+
return useLocal
|
|
1026
|
+
? `127.0.0.1 ${aliasesStr}`
|
|
1027
|
+
: `${v.disabled ? '#' : ''}${v.ipOrDomain} ${aliasesStr}`;
|
|
1028
|
+
}
|
|
1029
|
+
return useLocal
|
|
1030
|
+
? `127.0.0.1 ${aliasesStr}`
|
|
1031
|
+
: `${v.disabled ? '#' : ''}${v.ipOrDomain} ${aliasesStr} # ${v.name} ${GENERATED}`;
|
|
1032
|
+
})
|
|
1033
|
+
.join(EOL) +
|
|
1034
|
+
EOL +
|
|
1035
|
+
EOL +
|
|
1036
|
+
genMsg
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
//#endregion
|