Skip to content

Commit

Permalink
WIP: feat: SOCK4 Proxy 対応
Browse files Browse the repository at this point in the history
  • Loading branch information
tksugimoto committed Oct 9, 2022
1 parent c53f259 commit 048eaa2
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,10 @@ Host github.com gist.github.com
# node <filepath> 形式での実行も可能
# ProxyCommand node ~/code/connect-to-http-proxy/index.js -H proxy.intra.example.co.jp:8080 %h %p
```

## SOCKS4 の使用
`-S``proxyHost:Port` の前に入れる

```
connect-to-http-proxy -S proxy.intra.example.co.jp:1080 example.com 80
```
39 changes: 39 additions & 0 deletions connect.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const http = require('http');
const net = require('net');
const assert = require('assert');

/**
Expand All @@ -10,13 +11,15 @@ const assert = require('assert');
* @param {NodeJS.WriteStream} outputStream stream that accepts output like process.stdout
* @param {object} options
* @param {number?} options.timeoutMs timeout to CONNECT(HTTP method)
* @param {Boolean} options.isSocks4 use SOCKS4. default: false = HTTP Proxy mode
*/
function connect(proxyServerHosts, destHostname, destPort, inputStream, outputStream, options = {}) {
assert(proxyServerHosts, 'http-proxy-server arg ("hostname:port") required.');
assert(destHostname, 'destination-server hostname arg required.');
assert(destPort, 'destination-server port arg required.');
const timeoutMs = options.timeoutMs || 500;
assert(Number.isInteger(timeoutMs), 'timeoutMs must be Integer.');
const isSocks4 = options.isSocks4 || false;

proxyServerHosts.split(',')
.reduce((previousPromise, proxyServerHost) => {
Expand All @@ -37,6 +40,42 @@ function connect(proxyServerHosts, destHostname, destPort, inputStream, outputSt
port: proxyPort,
} = new URL(`http://${proxyServerHost}`);

if (isSocks4) {
assert(/^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/.test(destHostname), `destination-server(${destHostname}) must be IPv4.`);
const proxyRequest = net.createConnection(proxyPort, proxyHostname);

const uint8 = new Uint8Array(9);
uint8[0] = 4; // version number
uint8[1] = 1; // command code: CONNECT
const portView = new DataView(uint8.buffer, 2, 2); // destination port
portView.setInt16(0, Number(destPort), /* littleEndian = */ false);
uint8.set(destHostname.split('.').map(Number), 4); // destination IP
uint8[8] = 0; // USERID and NULL: null
proxyRequest.write(uint8);

setTimeout(() => {
promise.catch(() => {
proxyRequest.destroy();
});
reject(`Connection timeout to ${proxyHostname}:${proxyPort} (${timeoutMs} ms)`);
}, timeoutMs);
proxyRequest.once('readable', () => {
const data = proxyRequest.read(8); // Socks Response: 8 Bytes
const response = new DataView(data.buffer);
const resultCode = response.getInt8(1);
if (resultCode !== 90) { // request granted
reject(`Socks server result code: ${resultCode}`);
return;
}
resolve(proxyRequest);
});
proxyRequest.on('error', err => {
reject(err);
});
// TODO: 共通化
return;
}

const proxyRequestOptions = {
hostname: proxyHostname,
port: proxyPort,
Expand Down
14 changes: 13 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
const showUsageAndExit = () => {
console.error(`
connect-to-http-proxy: simple relaying command via proxy.
usage: connect-to-http-proxy [-H] proxy-server-hostname:proxy-server-port target-hostname target-port
usage: connect-to-http-proxy
[[-H] http-proxy-server-hostname:proxy-server-port]
[-S socks4-proxy-server-hostname:proxy-server-port]
target-hostname target-port
example:
connect-to-http-proxy -H proxy.intra.example.co.jp:8080 example.com 80
connect-to-http-proxy -H proxy.intra.example.co.jp:8080,proxy.intra.example.co.jp:8081 example.com 80
connect-to-http-proxy -S proxy.intra.example.co.jp:1080 example.com 80
connect-to-http-proxy -S proxy.intra.example.co.jp:1080,proxy.intra.example.co.jp:1081 example.com 80
environment variable:
SSH_CONNECT_TIMEOUT_MS: timeout to CONNECT(HTTP method). default: 500.
`.replace(/^ {8}/mg, '').trim());
Expand All @@ -31,6 +36,13 @@ if (process.argv[2] === '-H') { // HTTP Proxy
const destHostname = process.argv[4];
const destPort = process.argv[5];

connect(proxyServerHosts, destHostname, destPort, process.stdin, process.stdout, options);
} else if (process.argv[2] === '-S') { // SOCKS4 Proxy
const proxyServerHosts = process.argv[3];
const destHostname = process.argv[4];
const destPort = process.argv[5];
options.isSocks4 = true;

connect(proxyServerHosts, destHostname, destPort, process.stdin, process.stdout, options);
} else {
// 互換性のため `-` オプション無しの場合は HTTP Proxy とする
Expand Down

0 comments on commit 048eaa2

Please sign in to comment.