initial commit

This commit is contained in:
Micooz 2018-04-04 11:29:45 +08:00
commit c4b259dae3
176 changed files with 20101 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea/

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "core/vendor/pac-cmd"]
path = core/vendor/pac-cmd
url = https://github.com/getlantern/pac-cmd
[submodule "core/vendor/sysproxy-cmd"]
path = core/vendor/sysproxy-cmd
url = https://github.com/getlantern/sysproxy-cmd

12
CHANGELOG.md Normal file
View File

@ -0,0 +1,12 @@
# 更新日志
## 0.1.0 (2018-04-03)
### :rocket: 特性
- 三大平台支持Windows、Linux、macOS
- 双端图形化界面
- 单机服务多开
- 远程服务配置、启动/停止
- 实时监控图表CPU、内存、上下行速度、网络连接数、网络流量
- 日志查看和搜索

201
LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Micooz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

148
README.md Normal file
View File

@ -0,0 +1,148 @@
# blinksocks-gui
为 [blinksocks](https://github.com/blinksocks/blinksocks) 封装的 WEB 图形化界面。
![](screenshot.png)
## 在线体验(只读模式)
https://gui.blinksocks.org/landing?password=preview
## 特性
- 三大平台支持Windows、Linux、macOS
- 双端图形化界面
- 单机服务多开
- 远程服务配置、启动/停止
- 实时监控图表CPU、内存、上下行速度、网络连接数、网络流量
- 日志查看和搜索
## 安装
### 使用 npm 安装或升级(推荐)
在此之前,请先安装 [Node.js](https://nodejs.org/en/)Node.js 自带 npm 包管理器。
> Tips: 如果你是在服务端(一般是 Linux上使用可以使用官方提供的安装脚本
> https://nodejs.org/en/download/package-manager/#installing-node-js-via-package-manager
然后执行下面的命令安装 blinksocks-gui
```
$ npm install -g blinksocks-gui
```
需要升级时重新执行上面的命令即可。
### 使用预编译版本
> 使用预编译版本无需安装 Node.js 和其他依赖软件,但升级时必须重新下载、解压和替换整个软件包。
下载地址https://github.com/blinksocks/blinksocks-gui/releases
## 启动
### 交互式启动
桌面环境双击直接运行,服务器环境从命令行启动:
```
$ blinksocks-gui
```
根据提示选择启动类型(客户端或者服务端):
```
? Please choose run type - Use arrow-keys. Return to submit.
Client
Server
```
选择一个端口号用于远程访问图形界面:
```
✔ Please choose run type Client
? Please choose a port(1 ~ 65535) for web ui: 3000
```
完成后在**浏览器**中打开提示链接即可:
```
✔ Please choose run type Client
✔ Please choose a port(1 ~ 65535) for web ui: … 3000
info: blinksocks gui client is running at 3000.
info: You can now open blinksocks-gui in browser:
http://localhost:3000/
```
## 命令行启动
```
$ blinksocks-gui --client --port 3000
```
> Tips: 第一次启动时,程序会自动创建一个 `root` 用户,初始密码为 `root`。在 `/landing` 页面输入初始密码后登录系统。
## 修改初始登录密码
转到 `/settings` 页面或点击左侧 `Settings` 菜单进入系统配置面板修改相关配置并保存。
## 开发指引
### 拉取仓库并初始化
```
$ git clone https://github.com/blinksocks/blinksocks-gui
$ cd blinksocks-gui
$ git submodule update --init
```
### 安装依赖
```
$ cd core && npm install
$ cd ui && npm install
```
### 启动调试
启动本地 HTTP/WebSocket 服务:
```
$ cd core && npm run start:client
```
启动前端开发服务器:
```
$ cd ui && npm start
```
根据提示打开链接开始调试。
### 编译和打包
只需要编译打包前端代码,完成后会自动替换 `core/public` 里的内容:
```
$ cd ui && npm run build
```
### 发布
只需发布 `core/` 里的内容到 npm registry 即可:
```
$ cd core
$ npm publish
```
## 更新日志
[CHANGELOG.md](CHANGELOG.md)
## License
Apache License 2.0

26
core/.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# pkg
pkg/blinksocks-*.gz
pkg/sha256sum.txt
# runtime
/runtime
*.log*

1
core/bin/bootstrap.js vendored Normal file
View File

@ -0,0 +1 @@
module.exports = require('../src/main');

108
core/bin/start.js Normal file
View File

@ -0,0 +1,108 @@
#!/usr/bin/env node
const os = require('os');
const prompts = require('prompts');
const chalk = require('chalk');
const bootstrap = require('./bootstrap');
const version = require('../package.json').version;
const RUN_TYPE_CLIENT = 0;
const RUN_TYPE_SERVER = 1;
const examples = [
['Start web ui interactively', '$ blinksocks-gui'],
['Start web ui at 3000 as client', '$ blinksocks-gui --client --port 3000'],
];
const usage = `
${chalk.bold.underline(`blinksocks-gui v${version}`)}
Usage: blinksocks-gui [options] ...
Options:
-h, --help output usage information
-v, --version output blinksocks-gui version
-c, --client start web ui as client
-s, --server start web ui as server
-p, --port web ui listening port
Examples:
${examples.map(([description, example]) => ` ${chalk.gray('-')} ${description}${os.EOL} ${chalk.blue(example)}`).join(os.EOL)}
About & Help: ${chalk.underline('https://github.com/blinksocks/blinksocks-gui')}
`;
const argv = process.argv;
const options = argv.slice(2);
function hasOption(opt) {
return options.indexOf(opt) !== -1;
}
function getOptionValue(opt) {
const index = options.indexOf(opt);
if (index !== -1) {
return options[index + 1];
}
}
async function main() {
if (argv.length < 2) {
return console.log(usage);
}
// parse options
if (hasOption('-h') || hasOption('--help')) {
return console.log(usage);
}
if (hasOption('-v') || hasOption('--version')) {
return console.log(version);
}
let runType, port;
// ask for runType when necessary
if (hasOption('-c') || hasOption('--client')) {
runType = RUN_TYPE_CLIENT;
}
if (hasOption('-s') || hasOption('--server')) {
runType = RUN_TYPE_SERVER;
}
if (typeof runType === 'undefined') {
const answer = await prompts({
type: 'select',
name: 'value',
message: 'Please choose run type',
choices: [
{ title: 'Client', value: RUN_TYPE_CLIENT },
{ title: 'Server', value: RUN_TYPE_SERVER },
],
initial: 0,
});
runType = answer.value;
}
// ask for port when necessary
port = getOptionValue('-p') || getOptionValue('--port');
if (!port || port === '0') {
const answer = await prompts({
type: 'number',
name: 'value',
message: 'Please choose a port(1 ~ 65535) for web ui:',
initial: 3000,
style: 'default',
min: 1,
max: 65535,
});
port = answer.value;
}
bootstrap({ runType, port });
}
main();

71
core/package.json Normal file
View File

@ -0,0 +1,71 @@
{
"name": "blinksocks-gui",
"version": "0.1.0",
"description": "A web based GUI wrapper for blinksocks",
"author": "Micooz",
"files": [
"bin",
"src",
"public"
],
"bin": {
"blinksocks-gui": "bin/start.js"
},
"scripts": {
"start:client": "cross-env NODE_ENV=development nodemon bin/start.js --client --port 3000",
"start:server": "cross-env NODE_ENV=development nodemon bin/start.js --server --port 3000",
"debug:client": "cross-env NODE_ENV=development node --inspect --inspect-port=9400 bin/start.js --client --port 3000",
"debug:server": "cross-env NODE_ENV=development node --inspect --inspect-port=9401 bin/start.js --server --port 3000",
"prepkg": "rimraf pkg/blinksocks-gui-* pkg/sha256sum.txt",
"pkg": "pkg --out-path pkg/ --targets node8.9.0-linux-x64,node8.9.0-macos-x64,node8.9.0-win-x64 .",
"postpkg": "node pkg/postpkg.js"
},
"dependencies": {
"blinksocks": "^3.1.0",
"chalk": "^2.3.2",
"date-fns": "^1.29.0",
"fs-extra": "^5.0.0",
"jssha": "^2.3.1",
"koa": "^2.5.0",
"koa-bodyparser": "^4.2.0",
"koa-favicon": "^2.0.1",
"koa-router": "^7.4.0",
"koa-static-cache": "^5.1.2",
"lodash": "^4.17.5",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
"pidusage": "^2.0.6",
"prompts": "^0.1.8",
"socket.io": "^2.1.0",
"sudo-prompt": "^8.2.0",
"winston": "^2.4.1"
},
"devDependencies": {
"cross-env": "^5.1.4",
"nodemon": "^1.17.3",
"pkg": "^4.3.1",
"rimraf": "^2.6.2"
},
"nodemonConfig": {
"ignore": [
"test/*",
"docs/*",
"vendor/*",
"public/*",
"runtime/*"
]
},
"pkg": {
"assets": [
"public/*",
"vendor/pac-cmd/binaries/**/*",
"vendor/sysproxy-cmd/binaries/**/*"
],
"scripts": [
"src/utils/_fork.js",
"src/lives/*",
"src/methods/*"
]
},
"license": "MIT"
}

46
core/pkg/postpkg.js Executable file
View File

@ -0,0 +1,46 @@
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const utils = require('util');
const zlib = require('zlib');
const { version } = require('../package.json');
const readdir = utils.promisify(fs.readdir);
const remove = utils.promisify(fs.unlink);
const appendFile = utils.promisify(fs.appendFile);
const readFile = utils.promisify(fs.readFile);
async function sha256sum(file) {
const sha256 = crypto.createHash('sha256');
const input = await readFile(file);
return sha256.update(input).digest('hex');
}
(async function main() {
try {
let files = await readdir(path.resolve(__dirname));
files = files.filter((f) => f.startsWith('blinksocks-gui-'));
for (const file of files) {
const name = path.basename(file, '.exe');
const ext = path.extname(file);
const newName = `${name}-x64-v${version}${ext}.gz`;
const input = path.join(__dirname, file);
const output = path.join(__dirname, newName);
const hashFile = path.join(__dirname, 'sha256sum.txt');
// compress into .gz
const stream = fs.createReadStream(input).pipe(zlib.createGzip()).pipe(fs.createWriteStream(output));
stream.on('finish', async () => {
// calc sha256sum
await appendFile(hashFile, `${path.basename(output)} ${await sha256sum(output)}\n`);
// remove original
await remove(input);
});
}
} catch (err) {
console.error(err.message);
}
})();

View File

@ -0,0 +1,10 @@
{
"main.css": "static/css/main.6ff87aea.css",
"main.js": "static/js/main.d43ae99d.js",
"static/media/icons-16.eot": "static/media/icons-16.2c8962a6.eot",
"static/media/icons-16.ttf": "static/media/icons-16.717d9aac.ttf",
"static/media/icons-16.woff": "static/media/icons-16.d4e66df1.woff",
"static/media/icons-20.eot": "static/media/icons-20.8eb14aee.eot",
"static/media/icons-20.ttf": "static/media/icons-20.f9eec517.ttf",
"static/media/icons-20.woff": "static/media/icons-20.5d6d0525.woff"
}

BIN
core/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 KiB

1
core/public/index.html Normal file
View File

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><title>blinksocks-gui</title><link href="/static/css/main.6ff87aea.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script type="text/javascript" src="/static/js/main.d43ae99d.js"></script></body></html>

View File

@ -0,0 +1,3 @@
{
}

View File

@ -0,0 +1,3 @@
{
}

View File

@ -0,0 +1,3 @@
{
}

15
core/public/manifest.json Normal file
View File

@ -0,0 +1,15 @@
{
"short_name": "blinksocks gui",
"name": "friendly gui management tools for blinksocks",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1 @@
"use strict";var precacheConfig=[["/index.html","1836fa0b8441b5341481371e418ce0ef"],["/static/css/main.6ff87aea.css","6ff87aea65bc52b9faaf9ffd641e2cb0"],["/static/js/main.d43ae99d.js","fb1c108bf08f1b01237f028c44e0db3c"],["/static/media/icons-16.2c8962a6.eot","2c8962a6b93ca1f31585c107526f724b"],["/static/media/icons-16.717d9aac.ttf","717d9aacb221362a32e46507340c673e"],["/static/media/icons-16.d4e66df1.woff","d4e66df1333255a372f320437481b2e0"],["/static/media/icons-20.5d6d0525.woff","5d6d0525f61413e1c5b34cf379f234d4"],["/static/media/icons-20.8eb14aee.eot","8eb14aee4b53af31571001acc9161483"],["/static/media/icons-20.f9eec517.ttf","f9eec51722361fb5ef0faec14405391f"]],cacheName="sw-precache-v3-sw-precache-webpack-plugin-"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var n=new URL(e);return"/"===n.pathname.slice(-1)&&(n.pathname+=t),n.toString()},cleanResponse=function(t){return t.redirected?("body"in t?Promise.resolve(t.body):t.blob()).then(function(e){return new Response(e,{headers:t.headers,status:t.status,statusText:t.statusText})}):Promise.resolve(t)},createCacheKey=function(e,t,n,r){var a=new URL(e);return r&&a.pathname.match(r)||(a.search+=(a.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(n)),a.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var n=new URL(t).pathname;return e.some(function(e){return n.match(e)})},stripIgnoredUrlParameters=function(e,n){var t=new URL(e);return t.hash="",t.search=t.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(t){return n.every(function(e){return!e.test(t[0])})}).map(function(e){return e.join("=")}).join("&"),t.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],n=e[1],r=new URL(t,self.location),a=createCacheKey(r,hashParamName,n,/\.\w{8}\./);return[r.toString(),a]}));function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(r){return setOfCachedUrls(r).then(function(n){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(t){if(!n.has(t)){var e=new Request(t,{credentials:"same-origin"});return fetch(e).then(function(e){if(!e.ok)throw new Error("Request for "+t+" returned a response with status "+e.status);return cleanResponse(e).then(function(e){return r.put(t,e)})})}}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var n=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(t){return t.keys().then(function(e){return Promise.all(e.map(function(e){if(!n.has(e.url))return t.delete(e)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(t){if("GET"===t.request.method){var e,n=stripIgnoredUrlParameters(t.request.url,ignoreUrlParametersMatching),r="index.html";(e=urlsToCacheKeys.has(n))||(n=addDirectoryIndex(n,r),e=urlsToCacheKeys.has(n));0,e&&t.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(n)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(e){return console.warn('Couldn\'t serve response for "%s" from cache: %O',t.request.url,e),fetch(t.request)}))}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

20
core/src/constants.js Normal file
View File

@ -0,0 +1,20 @@
const path = require('path');
const cwd = process.cwd();
exports.DATABASE_PATH = path.join(cwd, 'runtime/db.json');
exports.RUNTIME_HELPERS_PAC_PATH = path.join(cwd, 'runtime/helpers/pac');
exports.RUNTIME_HELPERS_SYSPROXY_PATH = path.join(cwd, 'runtime/helpers/sysproxy');
exports.HASH_SALT = 'blinksocks';
exports.DESENSITIZE_PLACEHOLDER = '********';
exports.RUN_TYPE_CLIENT = 0;
exports.RUN_TYPE_SERVER = 1;
exports.SERVER_PUSH_REGISTER_SUCCESS = 0;
exports.SERVER_PUSH_REGISTER_ERROR = 1;
exports.SERVER_PUSH_DISPOSE_TIMEOUT = 6e4; // 1min
exports.SERVICE_STATUS_INIT = -1;
exports.SERVICE_STATUS_RUNNING = 0;
exports.SERVICE_STATUS_STOPPED = 1;

28
core/src/core/router.js Normal file
View File

@ -0,0 +1,28 @@
const path = require('path');
const { import_dir } = require('../utils');
const methods = import_dir(path.join(__dirname, '..', 'methods'));
async function dispatch(method, args, extra) {
const func = methods[method];
if (typeof func === 'function') {
// check methods
if (this.getDisallowedMethods().includes(method)) {
throw Error(`you don't have privileges to call: "${method}"`);
}
const result = await func.call(this, args || {}, extra || {});
if (typeof result === 'undefined') {
return null;
} else if (result instanceof Promise) {
return await result;
} else {
return result;
}
} else {
throw Error(`method "${method}" is not implemented or not registered`);
}
}
module.exports = {
dispatch,
};

158
core/src/core/server.js Normal file
View File

@ -0,0 +1,158 @@
const path = require('path');
const http = require('http');
const Koa = require('koa');
const KoaRouter = require('koa-router');
const staticCache = require('koa-static-cache');
const favicon = require('koa-favicon');
const bodyParser = require('koa-bodyparser');
const _ = require('lodash');
const {
RUN_TYPE_CLIENT,
RUN_TYPE_SERVER,
HASH_SALT,
} = require('../constants');
const Router = require('./router');
const { hash, logger, db, import_dir } = require('../utils');
const ALL_METHODS = Object.assign(
{},
import_dir(path.resolve(__dirname, '../methods')),
import_dir(path.resolve(__dirname, '../lives')),
);
function onConnection(socket, { runType }) {
const { handshake } = socket;
const { user } = handshake;
logger.verbose(`[${handshake.address}] connected`);
function extendDB(db) {
db.getConfigs = () => {
const key = {
[RUN_TYPE_CLIENT]: 'client_configs',
[RUN_TYPE_SERVER]: 'server_configs',
}[runType];
return db.get(key);
};
return db;
}
const thisArg = {
ctx: {
runType,
// push_handlers is used for _xxx_server_push().
push_handlers: {},
},
user: user || null,
db: extendDB(db),
getConfigurableMethods() {
const methods = _.transform(ALL_METHODS, (result, _, key) => result.push(key), []);
return methods.filter((name) => name[0] !== '_');
},
getDisallowedMethods() {
return user['disallowed_methods'] || [];
},
push(event, data) {
logger.info(`[${handshake.address}] [PUSH] ${JSON.stringify(data)}`);
socket.emit(event, data);
},
invoke(method, args, extra) {
return Router.dispatch.call(thisArg, method, args, extra);
},
};
// handle client requests
socket.on('request', async function (req, send) {
const reqStr = JSON.stringify(req);
logger.info(`[${handshake.address}] request => ${reqStr}`);
const { method, args } = req;
try {
const result = await Router.dispatch.call(thisArg, method, args);
const response = { code: 0 };
if (result !== null) {
response.data = result;
}
logger.info(`[${handshake.address}] response => ${JSON.stringify(response)}`);
send(response);
} catch (err) {
logger.error(`[${handshake.address}] cannot process the request: ${reqStr}`, err);
send({ code: -1, message: err.message });
}
});
socket.on('disconnect', async function () {
logger.verbose(`[${handshake.address}] disconnected`);
try {
const { push_handlers } = thisArg.ctx;
for (const key of Object.keys(push_handlers)) {
await push_handlers[key].dispose();
}
} catch (err) {
// ignore any errors
// console.log(err);
}
});
}
function createWrappedUsers() {
return db.get('users').value()
.map((user) => Object.assign({
token: hash('SHA-256', user.password + HASH_SALT),
}, user));
}
module.exports = async function startServer(args) {
const { runType, port } = args;
// start koa server
const app = new Koa();
const router = new KoaRouter();
const server = http.createServer(app.callback());
const io = require('socket.io')(server);
// ws authentication middleware
io.use((socket, next) => {
const { query: { token } } = socket.handshake;
const user = createWrappedUsers().find((user) => user.token === token);
if (user) {
// NOTE: put user to socket.handshake so that
// we can access it again in onConnection().
socket.handshake.user = user;
return next();
}
return next(new Error('authentication error'));
});
// handle ws connections
io.on('connection', (socket) => onConnection(socket, args));
// standalone http interface
router.post('/verify', async (ctx) => {
const { token } = ctx.request.body;
if (!createWrappedUsers().find((user) => user.token === token)) {
return ctx.throw(403, 'authentication error');
}
ctx.status = 200;
});
app.use(favicon(path.join(__dirname, '../../public/favicon.ico')));
app.use(staticCache(path.join(__dirname, '../../public'), {
alias: {
'/': '/index.html',
'/landing': '/index.html',
},
}));
app.use(bodyParser());
app.use(router.routes());
app.use(router.allowedMethods());
const _port = port || 3000;
server.listen(_port, () => {
logger.info(`blinksocks gui ${runType === RUN_TYPE_SERVER ? 'server' : 'client'} is running at ${_port}.`);
logger.info('You can now open blinksocks-gui in browser:');
console.log('');
console.log(` http://localhost:${_port}/`);
console.log('');
});
};

View File

@ -0,0 +1,72 @@
const fs = require('fs');
const path = require('path');
const utils = require('util');
const readline = require('readline');
const { tailFile: tail } = require('winston/lib/winston/common');
const readdir = utils.promisify(fs.readdir);
const TAIL_FROM = 100;
async function getTotalLines(file) {
let totalLines = 0;
const reader = readline.createInterface({
input: fs.createReadStream(file),
});
return new Promise((resolve) => {
reader.on('line', () => totalLines += 1);
reader.on('close', () => resolve(totalLines));
});
}
module.exports = async function live_log({ id }) {
const { log_path } = await this.invoke('get_config', { id });
const logFilePath = path.resolve(process.cwd(), log_path || 'blinksocks');
const logFileName = path.basename(logFilePath);
const logFileDir = path.dirname(logFilePath);
// find the most recently created log file
const files = await readdir(logFileDir);
const logFiles = files
.filter(name => name.startsWith(logFileName))
.sort()
.map(name => path.join(logFileDir, name));
const logFile = logFiles[0] || '';
// count total lines
let totalLines = 0;
if (logFile) {
totalLines = await getTotalLines(logFile);
}
let firstTime = true;
let counter = 0;
let lines = [];
const start = totalLines > TAIL_FROM ? totalLines - TAIL_FROM - 1 : -1;
const destroy = tail({ file: logFile, start: start }, (err, line) => {
if (err) {
return;
}
// instead of push many times at the first time,
// we can make a cache here and do an one-time push.
if (firstTime) {
const end = start === -1 ? totalLines : TAIL_FROM;
if (counter < end - 1) {
lines.push(line);
counter++;
} else {
firstTime = false;
lines.push(line);
this.push(lines);
lines = null;
}
} else {
this.push(line);
}
});
return async function unregister() {
destroy();
};
};

View File

@ -0,0 +1,7 @@
const { ServiceManager } = require('../utils');
module.exports = async function live_services() {
this.pushInterval(async () => ({
services: await ServiceManager.getServices()
}), 5e3);
};

View File

@ -0,0 +1,12 @@
const os = require('os');
const pidusage = require('pidusage');
module.exports = async function live_usage() {
this.pushInterval(async () => {
const stats = await pidusage(process.pid);
return {
cpuUsage: stats.cpu / os.cpus().length,
memoryUsage: stats.memory,
};
}, 5e3);
};

110
core/src/main.js Normal file
View File

@ -0,0 +1,110 @@
const os = require('os');
const fs = require('fs');
const utils = require('util');
const path = require('path');
const fsExtra = require('fs-extra');
const bsInit = require('blinksocks/bin/init');
const runServer = require('./core/server');
const {
RUN_TYPE_CLIENT,
RUN_TYPE_SERVER,
RUNTIME_HELPERS_PAC_PATH,
RUNTIME_HELPERS_SYSPROXY_PATH,
} = require('./constants');
const { db } = require('./utils');
const chmod = utils.promisify(fs.chmod);
function getSysArch() {
const arch = os.arch();
switch (arch) {
case'x32':
return '386';
case 'x64':
return 'amd64';
default:
throw Error('unsupported architecture: ' + arch);
}
}
async function copy(source, target) {
if (process.pkg) {
// use stream pipe to reduce memory usage
// when load a large file into memory.
fs.createReadStream(source).pipe(fs.createWriteStream(target));
} else {
await fsExtra.copy(source, target);
}
}
async function extractHelpers() {
// copy system-related helper tools to runtime/helpers
const platform = os.platform();
const arch = getSysArch();
const pac_cmd_binaries_path = path.resolve(__dirname, '../vendor/pac-cmd/binaries');
const sysproxy_binaries_path = path.resolve(__dirname, '../vendor/sysproxy-cmd/binaries');
switch (platform) {
case 'darwin':
await copy(path.join(pac_cmd_binaries_path, 'darwin/pac'), RUNTIME_HELPERS_PAC_PATH);
await copy(path.join(sysproxy_binaries_path, 'darwin/sysproxy'), RUNTIME_HELPERS_SYSPROXY_PATH);
break;
case 'linux':
await copy(path.join(pac_cmd_binaries_path, `linux_${arch}/pac`), RUNTIME_HELPERS_PAC_PATH);
await copy(path.join(sysproxy_binaries_path, `linux_${arch}/sysproxy`), RUNTIME_HELPERS_SYSPROXY_PATH);
break;
case 'win32':
await copy(path.join(pac_cmd_binaries_path, `windows/pac_${arch}`), RUNTIME_HELPERS_PAC_PATH);
await copy(path.join(sysproxy_binaries_path, `windows/sysproxy_${arch}`), RUNTIME_HELPERS_SYSPROXY_PATH);
break;
default:
throw Error('unsupported platform: ' + platform);
}
// grant execute permission
await chmod(RUNTIME_HELPERS_PAC_PATH, 0o774);
await chmod(RUNTIME_HELPERS_SYSPROXY_PATH, 0o774);
}
module.exports = async function main(args) {
const { runType } = args;
try {
// create runtime directory
await fsExtra.mkdirp('runtime');
// keep at least one config in database
const { clientJson, serverJson } = bsInit({ isMinimal: false, isDryRun: true });
if (runType === RUN_TYPE_CLIENT) {
const configs = db.get('client_configs');
if (configs.size().value() < 1) {
clientJson.remarks = 'Default';
configs.insert(clientJson).write();
}
// await extractHelpers();
}
if (runType === RUN_TYPE_SERVER) {
const configs = db.get('server_configs');
if (configs.size().value() < 1) {
serverJson.remarks = 'Default';
configs.insert(serverJson).write();
}
}
// add a default user if no users set
const users = db.get('users');
if (users.value().length < 1) {
users.insert({ 'name': 'root', 'password': 'root', 'disallowed_methods': [] }).write();
}
// start server
await runServer(args);
} catch (err) {
console.error(err);
process.exit(1);
}
};

View File

@ -0,0 +1,8 @@
module.exports = async function keepalive_server_push({ method }) {
const handler = this.ctx.push_handlers[method];
if (handler) {
handler.keepalive();
} else {
throw Error(`method "${method}" is not found or was unregistered`);
}
};

View File

@ -0,0 +1,85 @@
const path = require('path');
const { import_dir } = require('../utils');
const {
SERVER_PUSH_REGISTER_SUCCESS,
SERVER_PUSH_REGISTER_ERROR,
SERVER_PUSH_DISPOSE_TIMEOUT,
} = require('../constants');
const lives = import_dir(path.join(__dirname, '..', 'lives'));
module.exports = async function register_server_push({ method, args }) {
const { push_handlers } = this.ctx;
if (push_handlers[method]) {
return { code: SERVER_PUSH_REGISTER_ERROR, message: `method "${method}" is already registered` };
}
const func = lives[method];
if (typeof func !== 'function') {
throw Error(`live method "${method}" is not implemented or not registered`);
}
// keepalive timer
let timer = null;
// interval timers
let interval_timers = [];
// prepare a new thisArgs for live methods
const push = this.push;
const thisArgs = Object.assign({}, this, {
push(data) {
// keepalive();
push(method, data);
},
pushInterval(getData, interval) {
if (typeof getData !== 'function') {
throw Error('"getData" must be a function');
}
const tick = async () => {
try {
this.push(await getData());
} catch (err) {
console.error(err);
}
};
tick();
const tm = setInterval(tick, interval);
interval_timers.push(tm);
},
});
const unregister = await func.call(thisArgs, args, {});
// set a timeout here in case the client doesn't call _unregister_server_push()
timer = setTimeout(dispose, SERVER_PUSH_DISPOSE_TIMEOUT);
// remember to remove this handler from ctx.push_handlers after unregister()
async function dispose() {
clearTimeout(timer);
interval_timers.forEach(clearTimeout);
if (typeof unregister === 'function') {
await unregister();
}
delete push_handlers[method];
}
// reset timeout timer
function keepalive() {
if (timer) {
clearTimeout(timer);
timer = setTimeout(dispose, SERVER_PUSH_DISPOSE_TIMEOUT);
}
}
// put it to ctx so that it can be accessed from
// _keepalive_server_push() and _unregister_server_push()
push_handlers[method] = {
// this function should be called in _keepalive_server_push()
keepalive,
// this function should be called in _unregister_server_push()
dispose,
};
return { code: SERVER_PUSH_REGISTER_SUCCESS };
};

View File

@ -0,0 +1,6 @@
module.exports = async function unregister_server_push({ method }) {
const handler = this.ctx.push_handlers[method];
if (handler) {
await handler.dispose();
}
};

View File

@ -0,0 +1,14 @@
const bsInit = require('blinksocks/bin/init');
const { RUN_TYPE_CLIENT, RUN_TYPE_SERVER } = require('../constants');
module.exports = async function add_setting({ remarks }) {
const { runType } = this.ctx;
const { clientJson, serverJson } = bsInit({ isMinimal: false, isDryRun: true });
const configs = this.db.getConfigs();
if (runType === RUN_TYPE_CLIENT) {
return configs.insert(Object.assign({}, clientJson, { remarks })).write().id;
}
if (runType === RUN_TYPE_SERVER) {
return configs.insert(Object.assign({}, serverJson, { remarks })).write().id;
}
};

View File

@ -0,0 +1,23 @@
module.exports = async function add_user({ user }) {
if (typeof user !== 'object') {
throw Error('invalid parameter');
}
const { name, password } = user;
if (typeof name !== 'string' || name.length < 1) {
throw Error('username is invalid');
}
if (typeof password !== 'string' || password.length < 1) {
throw Error('password is invalid');
}
if (this.db.get('users').find({ name }).value()) {
throw Error('user is already exists');
}
this.db.get('users').insert({
name: name,
password: password,
disallowed_methods: [],
}).write();
};

View File

@ -0,0 +1,6 @@
module.exports = async function copy_setting({ id }) {
const configs = this.db.getConfigs();
const config = configs.find({ id }).cloneDeep().value();
delete config.id;
return configs.insert(config).write();
};

View File

@ -0,0 +1,3 @@
module.exports = async function delete_setting({ id }) {
return this.db.getConfigs().remove({ id }).write();
};

View File

@ -0,0 +1,3 @@
module.exports = async function delete_user({ id }) {
this.db.get('users').remove({ id }).write();
};

View File

@ -0,0 +1,18 @@
const { RUN_TYPE_CLIENT, RUN_TYPE_SERVER, DESENSITIZE_PLACEHOLDER } = require('../constants');
module.exports = async function get_config({ id }, { desensitize = true }) {
const { runType } = this.ctx;
const config = this.db.getConfigs().find({ id }).cloneDeep().value();
if (!config) {
throw Error('config is not found');
}
if (desensitize) {
if (runType === RUN_TYPE_CLIENT) {
config.server.key = DESENSITIZE_PLACEHOLDER;
}
if (runType === RUN_TYPE_SERVER) {
config.key = DESENSITIZE_PLACEHOLDER;
}
}
return config;
};

View File

@ -0,0 +1,27 @@
const os = require('os');
/**
* return os information and Node.js versions
*/
module.exports = async function get_env() {
const osParams = [
['cpu', os.cpus()[0].model],
['cores', os.cpus().length],
['memory', os.totalmem()],
['type', os.type()],
['platform', os.platform()],
['arch', os.arch()],
['release', os.release()]
];
const nodeVersions = [];
for (const [key, value] of Object.entries(process.versions)) {
nodeVersions.push([key, value]);
}
return {
version: require('../../package').version,
blinksocksVersion: require('blinksocks/package').version,
runType: this.ctx.runType,
os: osParams,
node: nodeVersions,
};
};

View File

@ -0,0 +1,159 @@
module.exports = async function get_preset_defs() {
return [
{
"name": "ss-base",
"params": [],
"isAddressing": true
},
{
"name": "ss-stream-cipher",
"params": [{
"key": "method",
"type": "enum",
"values": [
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"rc4-md5",
"rc4-md5-6",
"none"
],
"defaultValue": "aes-128-ctr",
"description": "encryption/decryption algorithm",
"optional": true
}]
},
{
"name": "ss-aead-cipher",
"params": [{
"key": "method",
"type": "enum",
"values": [
"aes-128-gcm",
"aes-192-gcm",
"aes-256-gcm",
"chacha20-poly1305",
"chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305"
],
"defaultValue": "aes-128-gcm",
"description": "encryption/decryption algorithm",
"optional": true
}]
},
{
"name": "ssr-auth-aes128-md5",
"params": []
},
{
"name": "ssr-auth-aes128-sha1",
"params": []
},
{
"name": "ssr-auth-chain-a",
"params": []
},
{
"name": "ssr-auth-chain-b",
"params": []
},
{
"name": "v2ray-vmess",
"params": [{
"key": "id",
"type": "string",
"defaultValue": "",
"description": "uuid",
"optional": false
}, {
"key": "security",
"type": "enum",
"values": [
"aes-128-gcm",
"chacha20-poly1305",
"none"
],
"defaultValue": "aes-128-gcm",
"description": "encryption/decryption algorithm",
"optional": true
}]
},
{
"name": "obfs-random-padding",
"params": []
},
// {
// "name": "obfs-http",
// "params": [
// {
// "key": "file",
// "type": "string",
// "defaultValue": ""
// }
// ]
// },
{
"name": "obfs-tls1.2-ticket",
"params": [{
"key": "sni",
"type": "array",
"defaultValue": [],
"description": "server name indication",
"optional": false
}]
},
{
"name": "base-auth",
"params": [{
"key": "method",
"type": "enum",
"values": [
"md5",
"sha1",
"sha256"
],
"defaultValue": "sha1",
"description": "hash algorithm",
"optional": true
}],
"isAddressing": true
},
{
"name": "aead-random-cipher",
"params": [{
"key": "method",
"type": "enum",
"values": [
"aes-128-gcm",
"aes-192-gcm",
"aes-256-gcm"
],
"defaultValue": "aes-128-gcm",
"description": "encryption/decryption algorithm",
"optional": true
}, {
"key": "info",
"type": "string",
"defaultValue": "bs-subkey",
"description": "",
"optional": true
}, {
"key": "factor",
"type": "number",
"defaultValue": 2,
"description": "",
"optional": true
}]
},
// {
// "name": "auto-conf",
// "params": []
// },
];
};

View File

@ -0,0 +1,3 @@
module.exports = async function get_service({ id }) {
return this.db.getConfigs().find({ id }).value();
};

View File

@ -0,0 +1,18 @@
const { ServiceManager } = require('../utils');
module.exports = async function get_service_metrics({ id }) {
const [cpu_metrics, memory_metrics, speed_metrics, connections_metrics, traffic_metrics] = await Promise.all([
ServiceManager.getMetrics(id, 'cpu'),
ServiceManager.getMetrics(id, 'memory'),
ServiceManager.getMetrics(id, 'speed'),
ServiceManager.getMetrics(id, 'connections'),
ServiceManager.getMetrics(id, 'traffic'),
]);
return {
cpu_metrics,
memory_metrics,
speed_metrics,
connections_metrics,
traffic_metrics,
};
};

View File

@ -0,0 +1,19 @@
const url = require('url');
module.exports = async function get_services() {
return this.db
.getConfigs()
.map(({ id, service, log_path, remarks }) => {
const { protocol, hostname, port } = url.parse(service);
return {
id: id,
status: '-', // TODO: make a query
protocol: protocol ? protocol.slice(0, -1) : '-',
address: `${hostname}:${port}`,
remarks: remarks || '',
log_path: log_path || '-',
};
})
.sortBy('updatedAt')
.value();
};

View File

@ -0,0 +1,14 @@
const child_process = require('child_process');
const { RUNTIME_HELPERS_PAC_PATH } = require('../constants');
module.exports = async function get_system_pac() {
return new Promise((resolve, reject) => {
child_process.exec(RUNTIME_HELPERS_PAC_PATH + ' show', { encoding: 'utf-8' }, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};

View File

@ -0,0 +1,26 @@
const child_process = require('child_process');
const sudo = require('sudo-prompt');
const { RUNTIME_HELPERS_SYSPROXY_PATH } = require('../constants');
module.exports = async function get_system_proxy() {
const command = RUNTIME_HELPERS_SYSPROXY_PATH + ' show';
return new Promise((resolve, reject) => {
if (process.platform === 'darwin') {
sudo.exec(command, { name: 'blinksocksGUI' }, (err, stdout) => {
if (err) {
reject(err);
} else {
resolve(stdout);
}
});
} else {
child_process.exec(command, { encoding: 'utf-8' }, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
}
});
};

View File

@ -0,0 +1,28 @@
const _ = require('lodash');
const { DESENSITIZE_PLACEHOLDER } = require('../constants');
function merge(method_mapping, disallowed_methods) {
for (const name of disallowed_methods) {
method_mapping[name] = false;
}
return _.transform(method_mapping, (result, value, key) => result.push({
name: key,
active: !!value,
}), []);
}
module.exports = async function get_users() {
return this.db
.get('users')
.map(({ id, name, password, disallowed_methods }) => {
const method_mapping = _.transform(this.getConfigurableMethods(), (result, name) => result[name] = true, {});
return {
id,
name,
password: DESENSITIZE_PLACEHOLDER,
methods: merge(method_mapping, disallowed_methods || [])
.sort((a, b) => a.name.localeCompare(b.name)),
};
})
.value();
};

View File

@ -0,0 +1,7 @@
const { ServiceManager } = require('../utils');
module.exports = async function restart_service({ id }) {
await this.invoke('stop_service', { id });
await this.invoke('start_service', { id });
return ServiceManager.getServiceInfo(id);
};

View File

@ -0,0 +1,23 @@
const { Config } = require('blinksocks');
const { RUN_TYPE_CLIENT, RUN_TYPE_SERVER, DESENSITIZE_PLACEHOLDER } = require('../constants');
module.exports = async function save_setting({ id, config }) {
const { runType } = this.ctx;
const configs = this.db.getConfigs();
const prevConfig = configs.find({ id }).value();
switch (runType) {
case RUN_TYPE_CLIENT:
Config.testOnClient(config);
if (config.server.key === DESENSITIZE_PLACEHOLDER) {
config.server.key = prevConfig.server.key;
}
break;
case RUN_TYPE_SERVER:
Config.testOnServer(config);
if (config.key === DESENSITIZE_PLACEHOLDER) {
config.key = prevConfig.key;
}
break;
}
configs.find({ id }).assign(config).write();
};

View File

@ -0,0 +1,42 @@
const { DESENSITIZE_PLACEHOLDER } = require('../constants');
module.exports = async function save_user({ user }) {
if (!user || typeof user !== 'object') {
throw Error('invalid parameter');
}
// strict check
const { id, name, password, methods } = user;
if (typeof name !== 'string' || name.length < 1) {
throw Error('name is invalid');
}
if (typeof password !== 'string' || password.length < 1) {
throw Error('password is invalid');
}
if (!Array.isArray(methods)) {
throw Error('methods is invalid');
}
// all exist method names
const method_names = this.getConfigurableMethods();
for (const method of methods) {
const { name, active } = method;
if (!method_names.includes(name)) {
throw Error(`method: "${name}" is not allowed here`);
}
if (typeof active !== 'boolean') {
throw Error('active is invalid');
}
}
const _user = this.db.get('users').find({ id }).value();
if (!_user) {
throw Error(`user "${name}" is not exist`);
}
this.db.get('users').find({ id }).assign({
name: name,
password: password === DESENSITIZE_PLACEHOLDER ? _user.password : password,
disallowed_methods: methods.filter(({ active }) => !active).map(({ name }) => name),
}).write();
};

View File

@ -0,0 +1,3 @@
module.exports = async function set_system_pac({ sysPac }) {
};

View File

@ -0,0 +1,3 @@
module.exports = async function set_system_proxy({ sysProxy }) {
};

View File

@ -0,0 +1,9 @@
const { Config } = require('blinksocks');
const { ServiceManager } = require('../utils');
module.exports = async function start_service({ id }) {
const config = await this.invoke('get_config', { id }, { desensitize: false });
Config.test(config);
await ServiceManager.start(id, config);
return ServiceManager.getServiceInfo(id);
};

View File

@ -0,0 +1,6 @@
const { ServiceManager } = require('../utils');
module.exports = async function stop_service({ id }) {
await ServiceManager.stop(id);
return ServiceManager.getServiceInfo(id);
};

View File

@ -0,0 +1,3 @@
module.exports = async function update_remarks({ id, remarks }) {
this.db.getConfigs().find({ id }).assign({ remarks }).write();
};

190
core/src/utils/_fork.js Normal file
View File

@ -0,0 +1,190 @@
const pidusage = require('pidusage');
const dateFns = require('date-fns');
const { Hub } = require('blinksocks');
let hub = null;
async function getUsage() {
const stats = await pidusage(process.pid);
return {
cpuUsage: stats.cpu,
memoryUsage: stats.memory,
};
}
const QUEUE_SIZE = 300; // 5min
// metrics collector
const Monitor = {
_timer: null,
_cpu_metrics: [
// timestamp, cpu_percentage
],
_memory_metrics: [
// timestamp, memory_usage
],
_upload_speed_metrics: [
// timestamp, memory_usage
],
_download_speed_metrics: [
// timestamp, memory_usage
],
_connections_metrics: [
// timestamp, connections
],
_upload_traffic_metrics: [
// timestamp, total_bytes
],
_download_traffic_metrics: [
// timestamp, total_bytes
],
getCPUMetrics() {
return this._cpu_metrics;
},
getMemoryMetrics() {
return this._memory_metrics;
},
getUploadSpeedMetrics() {
return this._upload_speed_metrics;
},
getDownloadSpeedMetrics() {
return this._download_speed_metrics;
},
getConnectionsMetrics() {
return this._connections_metrics;
},
getUploadTrafficMetrics() {
return this._upload_traffic_metrics;
},
getDownloadTrafficMetrics() {
return this._download_traffic_metrics;
},
start() {
this._timer = setInterval(this._sample.bind(this), 1e3);
},
stop() {
clearInterval(this._timer);
},
async _sample() {
const { _cpu_metrics, _memory_metrics } = this;
const { _connections_metrics, _upload_speed_metrics, _download_speed_metrics } = this;
const { _upload_traffic_metrics, _download_traffic_metrics } = this;
try {
const { cpuUsage, memoryUsage } = await getUsage();
const dateStr = dateFns.format(Date.now(), 'HH:mm:ss');
_cpu_metrics.push([dateStr, cpuUsage > 1 ? 1 : cpuUsage]);
_memory_metrics.push([dateStr, memoryUsage]);
if (hub) {
const performance = hub.getPerformance();
const connections = await hub.getConnections();
_upload_speed_metrics.push([dateStr, performance.getUploadSpeed()]);
_download_speed_metrics.push([dateStr, performance.getDownloadSpeed()]);
_connections_metrics.push([dateStr, connections]);
_upload_traffic_metrics.push([dateStr, hub.getTotalWritten()]);
_download_traffic_metrics.push([dateStr, hub.getTotalRead()]);
}
const metricsCollection = [
_cpu_metrics,
_memory_metrics,
_upload_speed_metrics,
_download_speed_metrics,
_connections_metrics,
_upload_traffic_metrics,
_download_traffic_metrics,
];
metricsCollection.forEach((metrics) => {
if (metrics.length > QUEUE_SIZE) {
metrics.shift();
}
});
} catch (err) {
console.error(err);
}
}
};
// process methods mapping
const methods = {
// start hub
'start': async function start(config) {
if (!hub) {
hub = new Hub(config);
Monitor.start();
return hub.run();
}
},
// stop hub
'stop': async function stop() {
if (hub) {
Monitor.stop();
return hub.terminate();
}
},
// get status from hub
'getStatus': async function getStatus() {
if (hub) {
const performance = hub.getPerformance();
return {
connections: await hub.getConnections(),
// total_download_bytes: instance.getTotalRead(),
// total_upload_bytes: instance.getTotalWritten(),
download_speed: performance.getDownloadSpeed(),
upload_speed: performance.getUploadSpeed(),
};
}
},
// get current process cpu metrics
'getCPUMetrics': () => Monitor.getCPUMetrics(),
// get current process memory metrics
'getMemoryMetrics': () => Monitor.getMemoryMetrics(),
// get current process upload speed and download speed metrics
'getSpeedMetrics': () => [Monitor.getUploadSpeedMetrics(), Monitor.getDownloadSpeedMetrics()],
// get current process connections number
'getConnectionsMetrics': () => Monitor.getConnectionsMetrics(),
// get current process upload traffic and download traffic
'getTrafficMetrics': () => [Monitor.getUploadTrafficMetrics(), Monitor.getDownloadTrafficMetrics()],
};
process.on('message', async (action) => {
if (typeof action !== 'object') {
return;
}
const { type, payload } = action;
const func = methods[type];
if (typeof func !== 'function') {
return;
}
try {
const result = await func(payload);
process.send({ type: type + '/done', payload: result });
} catch (err) {
process.send({ type: type + '/error', payload: err.message });
}
});

26
core/src/utils/db.js Normal file
View File

@ -0,0 +1,26 @@
const path = require('path');
const fsExtra = require('fs-extra');
const lodashId = require('lodash-id');
const lowdb = require('lowdb');
const FileSync = require('lowdb/adapters/FileSync');
const { DATABASE_PATH } = require('../constants');
const DATABASE_SCHEMA = {
"client_configs": [],
"server_configs": [],
"users": [],
};
fsExtra.mkdirpSync(path.dirname(DATABASE_PATH));
const db = lowdb(new FileSync(DATABASE_PATH, {
defaultValue: DATABASE_SCHEMA,
serialize: (data) => JSON.stringify(data, null, 2),
deserialize: JSON.parse,
}));
db.defaults(DATABASE_SCHEMA).write();
db._.mixin(lodashId);
module.exports = db;

7
core/src/utils/hash.js Normal file
View File

@ -0,0 +1,7 @@
const jsSHA = require('jssha');
module.exports = function hash(algorithm, message) {
const shaObj = new jsSHA(algorithm, 'TEXT');
shaObj.update(message);
return shaObj.getHash('HEX');
};

View File

@ -0,0 +1,19 @@
const fs = require('fs');
const path = require('path');
const cache = {};
module.exports = function import_dir(dir) {
if (cache[dir]) {
return cache[dir];
}
const files = fs.readdirSync(dir);
const modules = {};
for (const file of files) {
const name = path.basename(file, '.js');
if (name && name[0] !== '.') {
modules[name] = require(path.resolve(dir, name));
}
}
return cache[dir] = modules;
};

7
core/src/utils/index.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
db: require('./db'),
hash: require('./hash'),
import_dir: require('./import_dir'),
logger: require('./logger'),
ServiceManager: require('./service_manager'),
};

19
core/src/utils/logger.js Normal file
View File

@ -0,0 +1,19 @@
// const os = require('os');
const winston = require('winston');
module.exports = new (winston.Logger)({
level: 'silly',
transports: [
new (winston.transports.Console)({
colorize: true,
prettyPrint: true
}),
// new (require('winston-daily-rotate-file'))({
// json: false,
// eol: os.EOL,
// filename: __LOG_PATH__,
// level: __LOG_LEVEL__,
// maxDays: __LOG_MAX_DAYS__
// }),
]
});

View File

@ -0,0 +1,156 @@
const path = require('path');
const child_process = require('child_process');
const {
SERVICE_STATUS_INIT,
SERVICE_STATUS_RUNNING,
SERVICE_STATUS_STOPPED,
} = require('../constants');
const FORK_SCRIPT = path.resolve(__dirname, '_fork.js');
const subprocesses = new Map(
// <id>: <ChildProcess>,
);
// spawn a new sub process
function fork() {
const subprocess = child_process.fork(FORK_SCRIPT, { silent: true });
const messageQueue = [];
subprocess.on('message', (message) => {
if (typeof message !== 'object') {
// drop non-object message
return;
}
messageQueue.push(message);
});
async function send(action) {
return new Promise((resolve, reject) => {
// send message to sub process immediately
subprocess.send(action);
function scanQueue() {
for (let i = 0; i < messageQueue.length; i++) {
const message = messageQueue[i];
// find related message from sub process
if (message.type.indexOf(action.type) === 0) {
const { type, payload } = message;
if (type === action.type + '/error') {
reject(Error(payload));
return i;
}
if (type === action.type + '/done') {
resolve(payload);
return i;
}
}
}
return -1;
}
// consume messageQueue
setImmediate(function consume() {
const index = scanQueue();
// if not found, continue to consume,
if (index < 0) {
setImmediate(consume);
}
// if message found, remove it from queue.
else {
messageQueue.splice(index, 1);
}
});
});
}
return {
// return original <ChildProcess>
getProcess() {
return subprocess;
},
// call any methods of forked process
async invoke(method, args) {
return send({ type: method, payload: args });
},
};
}
module.exports = {
async start(id, config) {
let sub = subprocesses.get(id);
if (!sub) {
sub = fork();
subprocesses.set(id, sub);
}
try {
await sub.invoke('start', config);
} catch (err) {
subprocesses.delete(id);
throw err;
}
},
async stop(id) {
const sub = subprocesses.get(id);
if (sub) {
await sub.invoke('stop');
sub.getProcess().kill();
subprocesses.set(id, null);
} else {
throw Error(`service(${id}) is not found`);
}
},
async getServices() {
const services = {};
for (const [id] of subprocesses) {
services[id] = await this.getServiceInfo(id);
}
return services;
},
async getServiceInfo(id) {
const sub = subprocesses.get(id);
if (sub) {
return {
status: SERVICE_STATUS_RUNNING,
...(await sub.invoke('getStatus')),
};
} else {
let status;
if (sub === undefined) {
status = SERVICE_STATUS_INIT;
}
if (sub === null) {
status = SERVICE_STATUS_STOPPED;
}
return { status };
}
},
async getMetrics(id, type) {
const sub = subprocesses.get(id);
if (sub) {
switch (type) {
case 'cpu':
return await sub.invoke('getCPUMetrics');
case 'memory':
return await sub.invoke('getMemoryMetrics');
case 'speed':
return await sub.invoke('getSpeedMetrics');
case 'connections':
return await sub.invoke('getConnectionsMetrics');
case 'traffic':
return await sub.invoke('getTrafficMetrics');
default:
break;
}
} else {
return [];
}
},
};

3
core/vendor/pac-cmd/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
*~
*.gch

202
core/vendor/pac-cmd/LICENSE vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Brave New Software Project, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

63
core/vendor/pac-cmd/Makefile vendored Normal file
View File

@ -0,0 +1,63 @@
# This Makefile is GNU make compatible. You can get GNU Make from
# http://gnuwin32.sourceforge.net/packages/make.htm
CCFLAGS = -Wall -c
ifeq ($(OS),Windows_NT)
os = windows
CCFLAGS += -D WIN32
# 32 bit `make` utility over 64 bit OS
ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
CCFLAGS += -D AMD64
BIN = binaries/windows/pac_amd64
else
ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
CCFLAGS += -D AMD64
BIN = binaries/windows/pac_amd64
endif
ifeq ($(PROCESSOR_ARCHITECTURE),x86)
CCFLAGS += -D IA32
BIN = binaries/windows/pac_386
endif
endif
LDFLAGS += -l rasapi32 -l wininet -Wl,--subsystem,windows
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
os = linux
CCFLAGS += -D LINUX $(shell pkg-config --cflags gio-2.0)
LDFLAGS += $(shell pkg-config --libs gio-2.0)
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_P),x86_64)
CCFLAGS += -D AMD64
BIN = binaries/linux_amd64/pac
endif
ifneq ($(filter %86,$(UNAME_P)),)
CCFLAGS += -D IA32
BIN = binaries/linux_386/pac
endif
ifneq ($(filter arm%,$(UNAME_P)),)
CCFLAGS += -D ARM
BIN = binaries/linux_arm/pac
endif
endif
ifeq ($(UNAME_S),Darwin)
os = darwin
CCFLAGS += -D DARWIN -D AMD64 -x objective-c
LDFLAGS += -framework Cocoa -framework SystemConfiguration -framework Security
BIN = binaries/darwin/pac
endif
endif
CC=gcc
all: $(BIN)
main.o: main.c common.h
$(CC) $(CCFLAGS) $^
$(os).o: $(os).c common.h
$(CC) $(CCFLAGS) $^
$(BIN): $(os).o main.o
$(CC) -o $@ $^ $(LDFLAGS)
clean:
rm *.o

42
core/vendor/pac-cmd/README.md vendored Normal file
View File

@ -0,0 +1,42 @@
# pac-cmd
A command line tool to change proxy auto-config settings of operation system.
Binaries included in repo. Simply `make` to build it again.
Note - you will need to run make separately on each platform.
# Usage
```sh
pac show
pac on <pac-url>
pac off [old-pac-url-prefix]
```
`pac off` with `old-pac-url` will turn off pac setting only if the existing pac url is prefixed with `old-pac-url-prefix`.
#Notes
* **Mac**
Setting pac is an privileged action on Mac OS. `sudo` or elevate it as below.
There's an additional option to chown itself to root:wheel and add setuid bit.
```sh
pac setuid
```
* **Windows**
Install [MinGW-W64](http://sourceforge.net/projects/mingw-w64) to build pac, as it has up to date SDK headers we require.
To avoid bringing up console window, it doesn't show anything directly to console. Piping the result to other utilities should work.
```
pac show | cat
```
* **Linux**
`sudo apt-get install libgtk2.0-dev`

BIN
core/vendor/pac-cmd/binaries/darwin/pac vendored Executable file

Binary file not shown.

BIN
core/vendor/pac-cmd/binaries/linux_386/pac vendored Executable file

Binary file not shown.

BIN
core/vendor/pac-cmd/binaries/linux_amd64/pac vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

19
core/vendor/pac-cmd/common.h vendored Normal file
View File

@ -0,0 +1,19 @@
#include <errno.h>
#include <stdbool.h>
#ifdef DARWIN
int setUid();
int elevate(char *path, char *prompt, char *iconPath);
#endif
int show();
int togglePac(bool turnOn, const char* pacUrl);
enum RET_ERRORS {
RET_NO_ERROR = 0,
INVALID_FORMAT = 1,
NO_PERMISSION = 2,
SYSCALL_FAILED = 3,
NO_MEMORY = 4,
PAC_URL_CONVERSION_ERROR = 5,
};

169
core/vendor/pac-cmd/darwin.c vendored Normal file
View File

@ -0,0 +1,169 @@
#import <Foundation/NSArray.h>
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SCPreferences.h>
#import <SystemConfiguration/SCNetworkConfiguration.h>
#include <sys/syslimits.h>
#include <sys/stat.h>
#include <mach-o/dyld.h>
#include "common.h"
/* === implement details === */
typedef Boolean (*visitor) (SCNetworkProtocolRef proxyProtocolRef, NSDictionary* oldPreferences, bool turnOn, const char* pacUrl);
Boolean showAction(SCNetworkProtocolRef proxyProtocolRef/*unused*/, NSDictionary* oldPreferences, bool turnOn/*unused*/, const char* pacUrl/*unused*/)
{
NSNumber* on = [oldPreferences valueForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigEnable];
NSString* nsOldPacUrl = [oldPreferences valueForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString];
if ([on intValue] == 1) {
printf("%s\n", [nsOldPacUrl UTF8String]);
}
return TRUE;
}
Boolean toggleAction(SCNetworkProtocolRef proxyProtocolRef, NSDictionary* oldPreferences, bool turnOn, const char* pacUrl)
{
NSString* nsPacUrl = [[NSString alloc] initWithCString: pacUrl encoding:NSUTF8StringEncoding];
NSString* nsOldPacUrl;
NSMutableDictionary *newPreferences = [NSMutableDictionary dictionaryWithDictionary: oldPreferences];
Boolean success;
if(turnOn == true) {
[newPreferences setValue:[NSNumber numberWithInt:1] forKey:(NSString*)kSCPropNetProxiesProxyAutoConfigEnable];
[newPreferences setValue:nsPacUrl forKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString];
} else {
nsOldPacUrl = [oldPreferences valueForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString];
// we turn pac off only if the option is set and pac url has the provided
// prefix.
if (nsPacUrl.length == 0 || [nsOldPacUrl hasPrefix:nsPacUrl]) {
[newPreferences setValue:[NSNumber numberWithInt:0] forKey:(NSString*)kSCPropNetProxiesProxyAutoConfigEnable];
[newPreferences setValue:@"" forKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString];
}
}
success = SCNetworkProtocolSetConfiguration(proxyProtocolRef, (__bridge CFDictionaryRef)newPreferences);
if(!success) {
NSLog(@"Failed to set Protocol Configuration");
}
return success;
}
int visit(visitor v, bool persist, bool turnOn, const char* pacUrl)
{
int ret = RET_NO_ERROR;
Boolean success;
SCNetworkSetRef networkSetRef;
CFArrayRef networkServicesArrayRef;
SCNetworkServiceRef networkServiceRef;
SCNetworkProtocolRef proxyProtocolRef;
NSDictionary *oldPreferences;
// Get System Preferences Lock
SCPreferencesRef prefsRef = SCPreferencesCreate(NULL, CFSTR("org.getlantern.lantern"), NULL);
if(prefsRef==NULL) {
NSLog(@"Fail to obtain Preferences Ref");
ret = NO_PERMISSION;
goto freePrefsRef;
}
success = SCPreferencesLock(prefsRef, true);
if (!success) {
NSLog(@"Fail to obtain PreferencesLock");
ret = NO_PERMISSION;
goto freePrefsRef;
}
// Get available network services
networkSetRef = SCNetworkSetCopyCurrent(prefsRef);
if(networkSetRef == NULL) {
NSLog(@"Fail to get available network services");
ret = SYSCALL_FAILED;
goto freeNetworkSetRef;
}
//Look up interface entry
networkServicesArrayRef = SCNetworkSetCopyServices(networkSetRef);
networkServiceRef = NULL;
for (long i = 0; i < CFArrayGetCount(networkServicesArrayRef); i++) {
networkServiceRef = CFArrayGetValueAtIndex(networkServicesArrayRef, i);
// Get proxy protocol
proxyProtocolRef = SCNetworkServiceCopyProtocol(networkServiceRef, kSCNetworkProtocolTypeProxies);
if(proxyProtocolRef == NULL) {
NSLog(@"Couldn't acquire copy of proxyProtocol");
ret = SYSCALL_FAILED;
goto freeProxyProtocolRef;
}
oldPreferences = (__bridge NSDictionary*)SCNetworkProtocolGetConfiguration(proxyProtocolRef);
if (!v(proxyProtocolRef, oldPreferences, turnOn, pacUrl)) {
ret = SYSCALL_FAILED;
}
freeProxyProtocolRef:
CFRelease(proxyProtocolRef);
}
if (persist) {
success = SCPreferencesCommitChanges(prefsRef);
if(!success) {
NSLog(@"Failed to Commit Changes");
ret = SYSCALL_FAILED;
goto freeNetworkServicesArrayRef;
}
success = SCPreferencesApplyChanges(prefsRef);
if(!success) {
NSLog(@"Failed to Apply Changes");
ret = SYSCALL_FAILED;
goto freeNetworkServicesArrayRef;
}
}
//Free Resources
freeNetworkServicesArrayRef:
CFRelease(networkServicesArrayRef);
freeNetworkSetRef:
CFRelease(networkSetRef);
freePrefsRef:
SCPreferencesUnlock(prefsRef);
CFRelease(prefsRef);
return ret;
}
/* === public functions === */
int setUid()
{
char exeFullPath [PATH_MAX];
uint32_t size = PATH_MAX;
if (_NSGetExecutablePath(exeFullPath, &size) != 0)
{
printf("Path longer than %d, should not occur!!!!!", size);
return SYSCALL_FAILED;
}
if (chown(exeFullPath, 0, 0) != 0) // root:wheel
{
puts("Error chown");
return NO_PERMISSION;
}
if (chmod(exeFullPath, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISUID) != 0)
{
puts("Error chmod");
return NO_PERMISSION;
}
return RET_NO_ERROR;
}
int show()
{
return visit(&showAction, false, false /*unused*/, "" /*unused*/);
}
int togglePac(bool turnOn, const char* pacUrl)
{
return visit(&toggleAction, true, turnOn, pacUrl);
}

69
core/vendor/pac-cmd/linux.c vendored Normal file
View File

@ -0,0 +1,69 @@
#include <gio/gio.h>
#include <stdio.h>
#include <string.h>
#include "common.h"
void init() {
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// deprecated since version 2.36, must leave here or prior glib will crash
g_type_init();
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
}
int show()
{
init();
GSettings* setting = g_settings_new("org.gnome.system.proxy");
char* old_mode = g_settings_get_string(setting, "mode");
char* old_pac_url = g_settings_get_string(setting, "autoconfig-url");
if (strcmp(old_mode, "auto") == 0) {
printf("%s\n", old_pac_url);
}
return RET_NO_ERROR;
}
int togglePac(bool turnOn, const char* pacUrl)
{
int ret = RET_NO_ERROR;
init();
GSettings* setting = g_settings_new("org.gnome.system.proxy");
if (turnOn == true) {
gboolean success = g_settings_set_string(setting, "mode", "auto");
if (!success) {
fprintf(stderr, "error setting mode to auto\n");
ret = SYSCALL_FAILED;
goto cleanup;
}
success = g_settings_set_string(setting, "autoconfig-url", pacUrl);
if (!success) {
fprintf(stderr, "error setting autoconfig-url to %s\n", pacUrl);
ret = SYSCALL_FAILED;
goto cleanup;
}
}
else {
if (strlen(pacUrl) != 0) {
char* old_mode = g_settings_get_string(setting, "mode");
char* old_pac_url = g_settings_get_string(setting, "autoconfig-url");
// we turn pac off only if the option is set and pac url has the provided
// prefix.
if (strcmp(old_mode, "auto") != 0
|| strncmp(old_pac_url, pacUrl, strlen(pacUrl)) != 0 ) {
fprintf(stderr, "current pac url setting is not %s, skipping\n", pacUrl);
goto cleanup;
}
}
g_settings_reset(setting, "autoconfig-url");
gboolean success = g_settings_set_string(setting, "mode", "none");
if (!success) {
fprintf(stderr, "error setting mode to none\n");
ret = SYSCALL_FAILED;
goto cleanup;
}
}
cleanup:
g_settings_sync();
g_object_unref(setting);
return ret;
}

37
core/vendor/pac-cmd/main.c vendored Normal file
View File

@ -0,0 +1,37 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
void usage(const char* binName)
{
printf("Usage: %s [show | on <pac url> | off [prefix-of-old-pac-url]]\n", binName);
exit(INVALID_FORMAT);
}
int main(int argc, char* argv[]) {
if (argc < 2) {
usage(argv[0]);
}
#ifdef DARWIN
if (strcmp(argv[1], "setuid") == 0) {
return setUid();
}
#endif
if (strcmp(argv[1], "show") == 0) {
return show();
} else if (strcmp(argv[1], "on") == 0) {
if (argc < 3) {
usage(argv[0]);
}
return togglePac(true, argv[2]);
} else if (strcmp(argv[1], "off") == 0) {
return togglePac(false, argc < 3 ? "" : argv[2]);
} else {
usage(argv[0]);
}
// code never reaches here, just avoids compiler from complaining.
return RET_NO_ERROR;
}

182
core/vendor/pac-cmd/windows.c vendored Normal file
View File

@ -0,0 +1,182 @@
#include <stdlib.h>
#include <windows.h>
#include <Wininet.h>
#include <ras.h>
#include <tchar.h>
#include <stdio.h>
#include "common.h"
void reportWindowsError(const char* action) {
LPTSTR pErrMsg = NULL;
DWORD errCode = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL,
errCode,
LANG_NEUTRAL,
pErrMsg,
0,
NULL);
fprintf(stderr, "Error %s: %lu %s\n", action, errCode, pErrMsg);
}
// Stolen from https://github.com/getlantern/winproxy
// Figure out which Dial-Up or VPN connection is active; in a normal LAN connection, this should
// return NULL. NOTE: For some reason this method fails when compiled in Debug mode but works
// every time in Release mode.
LPTSTR findActiveConnection() {
DWORD dwCb = sizeof(RASCONN);
DWORD dwErr = ERROR_SUCCESS;
DWORD dwRetries = 5;
DWORD dwConnections = 0;
RASCONN* lpRasConn = NULL;
RASCONNSTATUS rasconnstatus;
rasconnstatus.dwSize = sizeof(RASCONNSTATUS);
//
// Loop through in case the information from RAS changes between calls.
//
while (dwRetries--) {
// If the memory is allocated, free it.
if (NULL != lpRasConn) {
HeapFree(GetProcessHeap(), 0, lpRasConn);
lpRasConn = NULL;
}
// Allocate the size needed for the RAS structure.
lpRasConn = (RASCONN*)HeapAlloc(GetProcessHeap(), 0, dwCb);
if (NULL == lpRasConn) {
dwErr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Set the structure size for version checking purposes.
lpRasConn->dwSize = sizeof(RASCONN);
// Call the RAS API then exit the loop if we are successful or an unknown
// error occurs.
dwErr = RasEnumConnections(lpRasConn, &dwCb, &dwConnections);
if (ERROR_INSUFFICIENT_BUFFER != dwErr) {
break;
}
}
//
// In the success case, print the names of the connections.
//
if (ERROR_SUCCESS == dwErr) {
DWORD i;
for (i = 0; i < dwConnections; i++) {
RasGetConnectStatus(lpRasConn[i].hrasconn, &rasconnstatus);
if (rasconnstatus.rasconnstate == RASCS_Connected){
return lpRasConn[i].szEntryName;
}
}
}
return NULL; // Couldn't find an active dial-up/VPN connection; return NULL
}
int initialize(INTERNET_PER_CONN_OPTION_LIST* options) {
DWORD dwBufferSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
options->dwSize = dwBufferSize;
options->pszConnection = findActiveConnection();
options->dwOptionCount = 2;
options->dwOptionError = 0;
options->pOptions = (INTERNET_PER_CONN_OPTION*)calloc(2, sizeof(INTERNET_PER_CONN_OPTION));
if(!options->pOptions) {
return NO_MEMORY;
}
options->pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
options->pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
return RET_NO_ERROR;
}
int query(INTERNET_PER_CONN_OPTION_LIST* options) {
DWORD dwBufferSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
if(!InternetQueryOption(NULL, INTERNET_OPTION_PER_CONNECTION_OPTION, options, &dwBufferSize)) {
reportWindowsError("Querying options");
return SYSCALL_FAILED;
}
return RET_NO_ERROR;
}
int show()
{
INTERNET_PER_CONN_OPTION_LIST options;
int ret = initialize(&options);
if (ret != RET_NO_ERROR) {
return ret;
}
ret = query(&options);
if (ret != RET_NO_ERROR) {
return ret;
}
if ((options.pOptions[0].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) > 0) {
if (options.pOptions[1].Value.pszValue != NULL) {
printf("%s\n", options.pOptions[1].Value.pszValue);
}
}
return ret;
}
int togglePac(bool turnOn, const char* pacUrl)
{
INTERNET_PER_CONN_OPTION_LIST options;
int ret = initialize(&options);
if (ret != RET_NO_ERROR) {
return ret;
}
if (turnOn) {
options.pOptions[0].Value.dwValue = PROXY_TYPE_AUTO_PROXY_URL;
options.pOptions[1].Value.pszValue = (char*)pacUrl;
}
else {
if (strlen(pacUrl) == 0) {
goto turnOff;
}
ret = query(&options);
if (ret != RET_NO_ERROR) {
goto cleanup;
}
// we turn pac off only if the option is set and pac url has the provided
// prefix.
if ((options.pOptions[0].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == 0
|| options.pOptions[1].Value.pszValue == NULL
|| strncmp(pacUrl, options.pOptions[1].Value.pszValue, strlen(pacUrl)) != 0) {
goto cleanup;
}
// fall through
turnOff:
options.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT;
options.pOptions[1].Value.pszValue = "";
}
DWORD dwBufferSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
BOOL result = InternetSetOption(NULL,
INTERNET_OPTION_PER_CONNECTION_OPTION,
&options,
dwBufferSize);
if (!result) {
reportWindowsError("setting options");
ret = SYSCALL_FAILED;
goto cleanup;
}
result = InternetSetOption(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
if (!result) {
reportWindowsError("propagating changes");
ret = SYSCALL_FAILED;
goto cleanup;
}
result = InternetSetOption(NULL, INTERNET_OPTION_REFRESH , NULL, 0);
if (!result) {
reportWindowsError("refreshing");
ret = SYSCALL_FAILED;
goto cleanup;
}
cleanup:
free(options.pOptions);
return ret;
}

3
core/vendor/sysproxy-cmd/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
*~
*.gch

202
core/vendor/sysproxy-cmd/LICENSE vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Brave New Software Project, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

63
core/vendor/sysproxy-cmd/Makefile vendored Normal file
View File

@ -0,0 +1,63 @@
# This Makefile is GNU make compatible. You can get GNU Make from
# http://gnuwin32.sourceforge.net/packages/make.htm
CCFLAGS = -Wall -c
ifeq ($(OS),Windows_NT)
os = windows
CCFLAGS += -D WIN32
# 32 bit `make` utility over 64 bit OS
ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
CCFLAGS += -D AMD64
BIN = binaries/windows/sysproxy_amd64
else
ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
CCFLAGS += -D AMD64
BIN = binaries/windows/sysproxy_amd64
endif
ifeq ($(PROCESSOR_ARCHITECTURE),x86)
CCFLAGS += -D IA32
BIN = binaries/windows/sysproxy_386
endif
endif
LDFLAGS += -l rasapi32 -l wininet -Wl,--subsystem,windows
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
os = linux
CCFLAGS += -D LINUX $(shell pkg-config --cflags gio-2.0)
LDFLAGS += $(shell pkg-config --libs gio-2.0)
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_P),x86_64)
CCFLAGS += -D AMD64
BIN = binaries/linux_amd64/sysproxy
endif
ifneq ($(filter %86,$(UNAME_P)),)
CCFLAGS += -D IA32
BIN = binaries/linux_386/sysproxy
endif
ifneq ($(filter arm%,$(UNAME_P)),)
CCFLAGS += -D ARM
BIN = binaries/linux_arm/sysproxy
endif
endif
ifeq ($(UNAME_S),Darwin)
os = darwin
CCFLAGS += -D DARWIN -D AMD64 -x objective-c
LDFLAGS += -framework Cocoa -framework SystemConfiguration -framework Security
BIN = binaries/darwin/sysproxy
endif
endif
CC=gcc
all: $(BIN)
main.o: main.c common.h
$(CC) $(CCFLAGS) $^
$(os).o: $(os).c common.h
$(CC) $(CCFLAGS) $^
$(BIN): $(os).o main.o
$(CC) -o $@ $^ $(LDFLAGS)
clean:
rm *.o

53
core/vendor/sysproxy-cmd/README.md vendored Normal file
View File

@ -0,0 +1,53 @@
# sysproxy-cmd
A command line tool to change HTTP(s) proxy settings of the operating system.
Binaries included in repo. Simply `make` to build it again.
Note - you will need to run make separately on each platform.
# Usage
```sh
sysproxy show
sysproxy on <proxy host> <proxy port>
sysproxy off <proxy host> <proxy port>
sysproxy wait-and-cleanup <proxy host> <proxy port>
```
`sysproxy off` and `sysproxy wait-and-cleanup` turns off proxy setting only if the
existing host and port equal <proxy host> <proxy port>.
`sysproxy wait-and-cleanup` differs from `sysproxy off` in that it waits for input
from stdin (or close) before turning off proxy setting. Any signal or Windows
system shutdown message triggers the cleanup too.
# Notes
* **Mac**
Setting the system proxy is a privileged action on Mac OS. `sudo` or elevate it
as below.
There's an additional option to chown itself to root:wheel and add setuid bit.
```sh
sysproxy setuid
```
* **Windows**
Install [MinGW-W64](http://sourceforge.net/projects/mingw-w64) to build sysproxy
as it has up to date SDK headers we require. The make command is `mingw32-make`.
To avoid bringing up console window, it doesn't show anything directly to
console. Piping the result to other utilities should work.
```
sysproxy show | cat
```
* **Linux**
`sudo apt-get install libgtk2.0-dev`

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

25
core/vendor/sysproxy-cmd/common.h vendored Normal file
View File

@ -0,0 +1,25 @@
#include <errno.h>
#include <stdbool.h>
#ifdef DARWIN
int setUid();
int elevate(char *path, char *prompt, char *iconPath);
#endif
const char* proxyHost;
const char* proxyPort;
#ifdef _WIN32
void setupSystemShutdownHandler();
#endif
int show();
int toggleProxy(bool turnOn);
enum RET_ERRORS {
RET_NO_ERROR = 0,
INVALID_FORMAT = 1,
NO_PERMISSION = 2,
SYSCALL_FAILED = 3,
NO_MEMORY = 4
};

183
core/vendor/sysproxy-cmd/darwin.c vendored Normal file
View File

@ -0,0 +1,183 @@
#import <Foundation/NSArray.h>
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SCPreferences.h>
#import <SystemConfiguration/SCNetworkConfiguration.h>
#include <sys/syslimits.h>
#include <sys/stat.h>
#include <mach-o/dyld.h>
#include "common.h"
/* === implement details === */
typedef Boolean (*visitor) (SCNetworkProtocolRef proxyProtocolRef, NSDictionary* oldPreferences, bool turnOn);
Boolean showAction(SCNetworkProtocolRef proxyProtocolRef /*unused*/, NSDictionary* oldPreferences, bool turnOn /*unused*/)
{
NSNumber* on = [oldPreferences valueForKey:(NSString*)kSCPropNetProxiesHTTPEnable];
NSString* nsOldProxyHost = [oldPreferences valueForKey:(NSString*)kSCPropNetProxiesHTTPProxy];
NSNumber* nsOldProxyPort = [oldPreferences valueForKey:(NSString*)kSCPropNetProxiesHTTPPort];
if ([on intValue] == 1) {
printf("%s:%d\n", [nsOldProxyHost UTF8String], [nsOldProxyPort intValue]);
}
return TRUE;
}
Boolean toggleAction(SCNetworkProtocolRef proxyProtocolRef, NSDictionary* oldPreferences, bool turnOn)
{
NSString* nsProxyHost = [[NSString alloc] initWithCString: proxyHost encoding:NSUTF8StringEncoding];
NSNumber* nsProxyPort = [[NSNumber alloc] initWithLong: [[[NSString alloc] initWithCString: proxyPort encoding:NSUTF8StringEncoding] integerValue]];
NSString* nsOldProxyHost;
NSNumber* nsOldProxyPort;
NSMutableDictionary *newPreferences = [NSMutableDictionary dictionaryWithDictionary: oldPreferences];
Boolean success;
if (turnOn == true) {
[newPreferences setValue: nsProxyHost forKey:(NSString*)kSCPropNetProxiesHTTPProxy];
[newPreferences setValue: nsProxyHost forKey:(NSString*)kSCPropNetProxiesHTTPSProxy];
[newPreferences setValue: nsProxyPort forKey:(NSString*)kSCPropNetProxiesHTTPPort];
[newPreferences setValue: nsProxyPort forKey:(NSString*)kSCPropNetProxiesHTTPSPort];
[newPreferences setValue:[NSNumber numberWithInt:1] forKey:(NSString*)kSCPropNetProxiesHTTPEnable];
[newPreferences setValue:[NSNumber numberWithInt:1] forKey:(NSString*)kSCPropNetProxiesHTTPSEnable];
} else {
nsOldProxyHost = [newPreferences valueForKey:(NSString*)kSCPropNetProxiesHTTPProxy];
nsOldProxyPort = [newPreferences valueForKey:(NSString*)kSCPropNetProxiesHTTPPort];
if ([nsProxyHost isEqualToString:nsOldProxyHost] && [nsProxyPort intValue] == [nsOldProxyPort intValue]) {
[newPreferences setValue:[NSNumber numberWithInt:0] forKey:(NSString*)kSCPropNetProxiesHTTPEnable];
[newPreferences setValue: @"" forKey:(NSString*)kSCPropNetProxiesHTTPProxy];
[newPreferences setValue: @"" forKey:(NSString*)kSCPropNetProxiesHTTPPort];
}
nsOldProxyHost = [newPreferences valueForKey:(NSString*)kSCPropNetProxiesHTTPSProxy];
nsOldProxyPort = [newPreferences valueForKey:(NSString*)kSCPropNetProxiesHTTPSPort];
if ([nsProxyHost isEqualToString:nsOldProxyHost] && [nsProxyPort intValue] == [nsOldProxyPort intValue]) {
[newPreferences setValue:[NSNumber numberWithInt:0] forKey:(NSString*)kSCPropNetProxiesHTTPSEnable];
[newPreferences setValue: @"" forKey:(NSString*)kSCPropNetProxiesHTTPSProxy];
[newPreferences setValue: @"" forKey:(NSString*)kSCPropNetProxiesHTTPSPort];
}
}
success = SCNetworkProtocolSetConfiguration(proxyProtocolRef, (__bridge CFDictionaryRef)newPreferences);
if(!success) {
NSLog(@"Failed to set Protocol Configuration");
}
return success;
}
int visit(visitor v, bool persist, bool turnOn)
{
int ret = RET_NO_ERROR;
Boolean success;
SCNetworkSetRef networkSetRef;
CFArrayRef networkServicesArrayRef;
SCNetworkServiceRef networkServiceRef;
SCNetworkProtocolRef proxyProtocolRef;
NSDictionary *oldPreferences;
// Get System Preferences Lock
SCPreferencesRef prefsRef = SCPreferencesCreate(NULL, CFSTR("org.getlantern.lantern"), NULL);
if(prefsRef==NULL) {
NSLog(@"Fail to obtain Preferences Ref");
ret = NO_PERMISSION;
goto freePrefsRef;
}
success = SCPreferencesLock(prefsRef, true);
if (!success) {
NSLog(@"Fail to obtain PreferencesLock");
ret = NO_PERMISSION;
goto freePrefsRef;
}
// Get available network services
networkSetRef = SCNetworkSetCopyCurrent(prefsRef);
if(networkSetRef == NULL) {
NSLog(@"Fail to get available network services");
ret = SYSCALL_FAILED;
goto freeNetworkSetRef;
}
//Look up interface entry
networkServicesArrayRef = SCNetworkSetCopyServices(networkSetRef);
networkServiceRef = NULL;
for (long i = 0; i < CFArrayGetCount(networkServicesArrayRef); i++) {
networkServiceRef = CFArrayGetValueAtIndex(networkServicesArrayRef, i);
// Get proxy protocol
proxyProtocolRef = SCNetworkServiceCopyProtocol(networkServiceRef, kSCNetworkProtocolTypeProxies);
if(proxyProtocolRef == NULL) {
NSLog(@"Couldn't acquire copy of proxyProtocol");
ret = SYSCALL_FAILED;
goto freeProxyProtocolRef;
}
oldPreferences = (__bridge NSDictionary*)SCNetworkProtocolGetConfiguration(proxyProtocolRef);
if (!v(proxyProtocolRef, oldPreferences, turnOn)) {
ret = SYSCALL_FAILED;
}
freeProxyProtocolRef:
CFRelease(proxyProtocolRef);
}
if (persist) {
success = SCPreferencesCommitChanges(prefsRef);
if(!success) {
NSLog(@"Failed to Commit Changes");
ret = SYSCALL_FAILED;
goto freeNetworkServicesArrayRef;
}
success = SCPreferencesApplyChanges(prefsRef);
if(!success) {
NSLog(@"Failed to Apply Changes");
ret = SYSCALL_FAILED;
goto freeNetworkServicesArrayRef;
}
}
//Free Resources
freeNetworkServicesArrayRef:
CFRelease(networkServicesArrayRef);
freeNetworkSetRef:
CFRelease(networkSetRef);
freePrefsRef:
SCPreferencesUnlock(prefsRef);
CFRelease(prefsRef);
return ret;
}
/* === public functions === */
int setUid()
{
char exeFullPath [PATH_MAX];
uint32_t size = PATH_MAX;
if (_NSGetExecutablePath(exeFullPath, &size) != 0)
{
printf("Path longer than %d, should not occur!!!!!", size);
return SYSCALL_FAILED;
}
if (chown(exeFullPath, 0, 0) != 0) // root:wheel
{
puts("Error chown");
return NO_PERMISSION;
}
if (chmod(exeFullPath, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISUID) != 0)
{
puts("Error chmod");
return NO_PERMISSION;
}
return RET_NO_ERROR;
}
int show()
{
return visit(&showAction, false, false /*unused*/);
}
int toggleProxy(bool turnOn)
{
return visit(&toggleAction, true, turnOn);
}

110
core/vendor/sysproxy-cmd/linux.c vendored Normal file
View File

@ -0,0 +1,110 @@
#include <gio/gio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
void init() {
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// deprecated since version 2.36, must leave here or prior glib will crash
g_type_init();
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
}
int show()
{
init();
GSettings* setting = g_settings_new("org.gnome.system.proxy");
GSettings* httpSetting = g_settings_new("org.gnome.system.proxy.http");
char* oldMode = g_settings_get_string(setting, "mode");
gboolean oldEnabled = g_settings_get_boolean(httpSetting, "enabled");
char* oldHost = g_settings_get_string(httpSetting, "host");
gint oldPort = g_settings_get_int(httpSetting, "port");
if (oldEnabled && strcmp(oldMode, "manual") == 0) {
printf("%s:%d\n", oldHost, oldPort);
}
return RET_NO_ERROR;
}
int toggleProxy(bool turnOn)
{
long port = strtol(proxyPort, NULL, 10);
if (port == 0) {
fprintf(stderr, "unable to parse port '%s'\n", proxyPort);
return INVALID_FORMAT;
}
int ret = RET_NO_ERROR;
init();
GSettings* setting = g_settings_new("org.gnome.system.proxy");
GSettings* httpSetting = g_settings_new("org.gnome.system.proxy.http");
GSettings* httpsSetting = g_settings_new("org.gnome.system.proxy.https");
if (turnOn == true) {
gboolean success = g_settings_set_string(httpSetting, "host", proxyHost);
if (!success) {
fprintf(stderr, "error setting http host to %s\n", proxyHost);
ret = SYSCALL_FAILED;
goto cleanup;
}
success = g_settings_set_int(httpSetting, "port", port);
if (!success) {
fprintf(stderr, "error setting http port %s\n", proxyPort);
ret = SYSCALL_FAILED;
goto cleanup;
}
success = g_settings_set_string(httpsSetting, "host", proxyHost);
if (!success) {
fprintf(stderr, "error setting https host to %s\n", proxyHost);
ret = SYSCALL_FAILED;
goto cleanup;
}
success = g_settings_set_int(httpsSetting, "port", port);
if (!success) {
fprintf(stderr, "error setting https port %s\n", proxyPort);
ret = SYSCALL_FAILED;
goto cleanup;
}
success = g_settings_set_boolean(httpSetting, "enabled", TRUE);
if (!success) {
fprintf(stderr, "error enabling http %s\n", proxyPort);
ret = SYSCALL_FAILED;
goto cleanup;
}
success = g_settings_set_string(setting, "mode", "manual");
if (!success) {
fprintf(stderr, "error setting mode to manual\n");
ret = SYSCALL_FAILED;
goto cleanup;
}
}
else {
if (strlen(proxyHost) != 0) {
// clear proxy setting only if it's equal to the original setting
char* oldMode = g_settings_get_string(setting, "mode");
char* oldHTTPHost = g_settings_get_string(httpSetting, "host");
long oldHTTPPort = g_settings_get_int(httpSetting, "port");
char* oldHTTPSHost = g_settings_get_string(httpsSetting, "host");
long oldHTTPSPort = g_settings_get_int(httpsSetting, "port");
if (strcmp(oldMode, "manual") != 0 ||
strcmp(oldHTTPHost, proxyHost) != 0 ||
oldHTTPPort != port ||
strcmp(oldHTTPSHost, proxyHost) != 0 ||
oldHTTPSPort != port) {
fprintf(stderr, "current http or https setting is not %s:%s, skipping\n", proxyHost, proxyPort);
goto cleanup;
}
}
g_settings_reset(httpSetting, "host");
g_settings_reset(httpSetting, "port");
g_settings_reset(httpsSetting, "host");
g_settings_reset(httpsSetting, "port");
g_settings_reset(httpSetting, "enabled");
g_settings_reset(setting, "mode");
}
cleanup:
g_settings_sync();
g_object_unref(setting);
return ret;
}

68
core/vendor/sysproxy-cmd/main.c vendored Normal file
View File

@ -0,0 +1,68 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include "common.h"
void usage(const char* binName)
{
printf("Usage: %s [show | on | off | wait-and-cleanup <proxy host> <proxy port>]\n", binName);
exit(INVALID_FORMAT);
}
void turnOffProxyOnSignal(int signal)
{
toggleProxy(false);
exit(0);
}
void setupSignals()
{
// Register signal handlers to make sure we turn proxy off no matter what
signal(SIGABRT, turnOffProxyOnSignal);
signal(SIGFPE, turnOffProxyOnSignal);
signal(SIGILL, turnOffProxyOnSignal);
signal(SIGINT, turnOffProxyOnSignal);
signal(SIGSEGV, turnOffProxyOnSignal);
signal(SIGTERM, turnOffProxyOnSignal);
signal(SIGSEGV, turnOffProxyOnSignal);
}
int main(int argc, char* argv[]) {
if (argc < 2) {
usage(argv[0]);
}
#ifdef DARWIN
if (strcmp(argv[1], "setuid") == 0) {
return setUid();
}
#endif
if (strcmp(argv[1], "show") == 0) {
return show();
} else {
if (argc < 4) {
usage(argv[0]);
}
proxyHost = argv[2];
proxyPort = argv[3];
if (strcmp(argv[1], "on") == 0) {
return toggleProxy(true);
} else if (strcmp(argv[1], "off") == 0) {
return toggleProxy(false);
} else if (strcmp(argv[1], "wait-and-cleanup") == 0) {
setupSignals();
#ifdef _WIN32
setupSystemShutdownHandler();
#endif
// wait for input from stdin (or close), then toggle off
getchar();
return toggleProxy(false);
} else {
usage(argv[0]);
}
}
// code never reaches here, just avoids compiler from complaining.
return RET_NO_ERROR;
}

278
core/vendor/sysproxy-cmd/windows.c vendored Normal file
View File

@ -0,0 +1,278 @@
#include <stdlib.h>
#include <windows.h>
#include <Wininet.h>
#include <ras.h>
#include <tchar.h>
#include <stdio.h>
#include "common.h"
void reportWindowsError(const char* action, const char* connName) {
LPTSTR pErrMsg = NULL;
DWORD errCode = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_HMODULE|
FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
GetModuleHandle(_T("wininet.dll")),
errCode,
LANG_NEUTRAL,
pErrMsg,
0,
NULL);
if (NULL != connName) {
fprintf(stderr, "Error %s for connection '%s': %lu %s\n",
action, connName, errCode, pErrMsg);
} else {
fprintf(stderr, "Error %s: %lu %s\n", action, errCode, pErrMsg);
}
}
// Stolen from https://github.com/getlantern/winproxy Figure out which Dial-Up
// or VPN connection is active; in a normal LAN connection, this should return
// NULL. NOTE: For some reason this method fails when compiled in Debug mode
// but works every time in Release mode.
// TODO: we may want to find all active connections instead of the first one.
LPTSTR findActiveConnection() {
DWORD dwCb = sizeof(RASCONN);
DWORD dwErr = ERROR_SUCCESS;
DWORD dwRetries = 5;
DWORD dwConnections = 0;
RASCONN* lpRasConn = NULL;
RASCONNSTATUS rasconnstatus;
rasconnstatus.dwSize = sizeof(RASCONNSTATUS);
// Loop through in case the information from RAS changes between calls.
while (dwRetries--) {
// If the memory is allocated, free it.
if (NULL != lpRasConn) {
HeapFree(GetProcessHeap(), 0, lpRasConn);
lpRasConn = NULL;
}
// Allocate the size needed for the RAS structure.
lpRasConn = (RASCONN*)HeapAlloc(GetProcessHeap(), 0, dwCb);
if (NULL == lpRasConn) {
dwErr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Set the structure size for version checking purposes.
lpRasConn->dwSize = sizeof(RASCONN);
// Call the RAS API then exit the loop if we are successful or an unknown
// error occurs.
dwErr = RasEnumConnections(lpRasConn, &dwCb, &dwConnections);
if (ERROR_INSUFFICIENT_BUFFER != dwErr) {
break;
}
}
// In the success case, return the first active connection.
if (ERROR_SUCCESS == dwErr) {
DWORD i;
for (i = 0; i < dwConnections; i++) {
RasGetConnectStatus(lpRasConn[i].hrasconn, &rasconnstatus);
if (rasconnstatus.rasconnstate == RASCS_Connected){
return lpRasConn[i].szEntryName;
}
}
}
return NULL; // Couldn't find an active dial-up/VPN connection; return NULL
}
int initialize(INTERNET_PER_CONN_OPTION_LIST* options) {
DWORD dwBufferSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
options->dwSize = dwBufferSize;
// NULL for LAN, connection name otherwise.
options->pszConnection = findActiveConnection();
options->dwOptionCount = 3;
options->dwOptionError = 0;
options->pOptions = (INTERNET_PER_CONN_OPTION*)calloc(3, sizeof(INTERNET_PER_CONN_OPTION));
if(NULL == options->pOptions) {
return NO_MEMORY;
}
options->pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
options->pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
options->pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
return RET_NO_ERROR;
}
int query(INTERNET_PER_CONN_OPTION_LIST* options) {
DWORD dwBufferSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
if(!InternetQueryOption(NULL, INTERNET_OPTION_PER_CONNECTION_OPTION, options, &dwBufferSize)) {
reportWindowsError("querying options", options->pszConnection ? options->pszConnection : "LAN");
return SYSCALL_FAILED;
}
return RET_NO_ERROR;
}
int show()
{
INTERNET_PER_CONN_OPTION_LIST options;
int ret = initialize(&options);
if (ret != RET_NO_ERROR) {
return ret;
}
ret = query(&options);
if (ret != RET_NO_ERROR) {
return ret;
}
if ((options.pOptions[0].Value.dwValue & PROXY_TYPE_PROXY) > 0) {
if (options.pOptions[1].Value.pszValue != NULL) {
printf("%s\n", options.pOptions[1].Value.pszValue);
}
}
return ret;
}
int doToggleProxy(bool turnOn)
{
INTERNET_PER_CONN_OPTION_LIST options;
int ret = initialize(&options);
if (ret != RET_NO_ERROR) {
return ret;
}
char *proxy = malloc(256);
snprintf(proxy, 256, "%s:%s", proxyHost, proxyPort);
if (turnOn) {
options.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;
options.pOptions[1].Value.pszValue = proxy;
options.pOptions[2].Value.pszValue = TEXT("<local>");
}
else {
if (strlen(proxyHost) == 0) {
goto turnOff;
}
ret = query(&options);
if (ret != RET_NO_ERROR) {
goto cleanup;
}
// we turn proxy off only if the option is set and proxy address has the
// provided prefix.
if ((options.pOptions[0].Value.dwValue & PROXY_TYPE_PROXY) == 0
|| options.pOptions[1].Value.pszValue == NULL
|| strncmp(proxy, options.pOptions[1].Value.pszValue, strlen(proxy)) != 0) {
goto cleanup;
}
// fall through
turnOff:
options.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT;
options.pOptions[1].Value.pszValue = "";
options.pOptions[2].Value.pszValue = "";
}
DWORD dwBufferSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
BOOL result = InternetSetOption(NULL,
INTERNET_OPTION_PER_CONNECTION_OPTION,
&options,
dwBufferSize);
if (!result) {
reportWindowsError("setting options", options.pszConnection ? options.pszConnection : "LAN");
ret = SYSCALL_FAILED;
goto cleanup;
}
result = InternetSetOption(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
if (!result) {
reportWindowsError("propagating changes", NULL);
ret = SYSCALL_FAILED;
goto cleanup;
}
result = InternetSetOption(NULL, INTERNET_OPTION_REFRESH , NULL, 0);
if (!result) {
reportWindowsError("refreshing", NULL);
ret = SYSCALL_FAILED;
goto cleanup;
}
cleanup:
free(options.pOptions);
free(proxy);
return ret;
}
HWND hWnd = NULL;
HANDLE hInvisibleThread = NULL;
int toggleProxy(bool turnOn)
{
if (hInvisibleThread == NULL) {
return doToggleProxy(turnOn);
}
// in this case, we're running in wait-and-cleanup mode, close the Window and
// let that trigger the toggling on the event loop thread.
PostMessage(hWnd, WM_CLOSE, 0, 0);
WaitForSingleObject(hInvisibleThread, INFINITE);
DWORD exitCode = 0;
GetExitCodeThread(hInvisibleThread, &exitCode);
return exitCode;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_DESTROY:
PostQuitMessage(doToggleProxy(false));
break;
case WM_ENDSESSION:
doToggleProxy(false);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// courtesy of https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/abf09824-4e4c-4f2c-ae1e-5981f06c9c6e/windows-7-console-application-has-no-way-of-trapping-logoffshutdown-event?forum=windowscompatibility
void createInvisibleWindow()
{
WNDCLASS wc={0};
wc.lpfnWndProc=(WNDPROC)WndProc;
wc.hInstance=GetModuleHandle(NULL);
wc.hIcon=LoadIcon(GetModuleHandle(NULL), "SysproxyWindow");
wc.lpszClassName="SysproxyWindow";
RegisterClass(&wc);
hWnd=CreateWindowEx(0,"SysproxyWindow","SysproxyWindow",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,(HWND) NULL, (HMENU) NULL, GetModuleHandle(NULL), (LPVOID) NULL);
if(!hWnd)
printf("FAILED to create window!!! %Iu\n",GetLastError());
}
DWORD WINAPI runInvisibleWindowThread(LPVOID lpParam)
{
MSG msg;
createInvisibleWindow();
while (GetMessage(&msg,(HWND) NULL , 0 , 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void setupSystemShutdownHandler()
{
// Create an invisible window so that we can respond to system shutdown and
// make sure that we finish setting the system proxy to off.
DWORD tid;
hInvisibleThread=CreateThread(NULL, 0, runInvisibleWindowThread, NULL, 0, &tid);
if (hInvisibleThread == NULL)
{
printf("FAILED to create thread for invisible window!!! %Iu\n",GetLastError());
}
// Make this process shut down very early so that in system logoff case, we
// successfully disable system proxy before parent process tries to do so and
// fails, and also do it early enough that required system services have not
// yet shut down.
//
// See https://stackoverflow.com/questions/8760509/graceful-application-shutdown-when-windows-shuts-down
BOOL fOkay = SetProcessShutdownParameters(0x3FF, SHUTDOWN_NORETRY);
if (!fOkay) {
printf("Failed to prioritize sysproxy-cmd for shutdown\n");
}
}

2709
core/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

4
ui/.env.development Normal file
View File

@ -0,0 +1,4 @@
BROWSER=none
HOST=localhost
PORT=3030
CI=false

2
ui/.env.production Normal file
View File

@ -0,0 +1,2 @@
GENERATE_SOURCEMAP=false
GOOGLE_ANALYTICS_TRACKING_ID=UA-72182315-3

23
ui/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.css

8
ui/config-overrides.js Normal file
View File

@ -0,0 +1,8 @@
const rewireReactHotLoader = require('react-app-rewire-hot-loader');
const rewireMobX = require('react-app-rewire-mobx');
module.exports = function override(config, env) {
config = rewireReactHotLoader(config, env);
config = rewireMobX(config, env);
return config;
};

Some files were not shown because too many files have changed in this diff Show More