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 Sep 28, 2022
1 parent 7a158e5 commit 2da8bd2
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 5 deletions.
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 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
```
41 changes: 40 additions & 1 deletion 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 @@ -8,8 +9,9 @@ const assert = require('assert');
* @param {string} destPort destination-server port (numeric string)
* @param {NodeJS.ReadStream} inputStream stream supplying input like process.stdin
* @param {NodeJS.WriteStream} outputStream stream that accepts output like process.stdout
* @param {Boolean} isSocks4 use SOCKS4. default: false = HTTP Proxy mode
*/
function connect(proxyServerHosts, destHostname, destPort, inputStream, outputStream) {
function connect(proxyServerHosts, destHostname, destPort, inputStream, outputStream, isSocks4 = false) {
assert(proxyServerHosts, 'http-proxy-server arg ("hostname:port") required.');
assert(destHostname, 'destination-server hostname arg required.');
assert(destPort, 'destination-server port arg required.');
Expand All @@ -33,6 +35,43 @@ 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);

const timeoutMs = 500;
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
16 changes: 12 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@

const connect = require('./connect');

const proxyServerHosts = process.argv[2];
const destHostname = process.argv[3];
const destPort = process.argv[4];
if (process.argv[2] === '-S') {
const proxyServerHosts = process.argv[3];
const destHostname = process.argv[4];
const destPort = process.argv[5];

connect(proxyServerHosts, destHostname, destPort, process.stdin, process.stdout);
connect(proxyServerHosts, destHostname, destPort, process.stdin, process.stdout, /* isSocks4 = */ true);
} else {
const proxyServerHosts = process.argv[2];
const destHostname = process.argv[3];
const destPort = process.argv[4];

connect(proxyServerHosts, destHostname, destPort, process.stdin, process.stdout);
}

0 comments on commit 2da8bd2

Please sign in to comment.