blinksocks/src/presets/obfs-http.js

139 lines
3.0 KiB
JavaScript
Raw Normal View History

2017-02-06 15:35:19 +00:00
import fs from 'fs';
import crypto from 'crypto';
2018-02-17 04:19:31 +00:00
import { IPreset } from './defs';
2017-11-06 06:01:07 +00:00
/**
* parse text file into {request: response} pairs
* @param file
* @returns {Array}
*/
function parseFile(file) {
2018-02-17 04:19:31 +00:00
const txt = fs.readFileSync(file, { encoding: 'utf-8' });
2017-11-06 06:01:07 +00:00
const lines = txt.split(/\r\n|\n|\r/);
const parts = [];
let part = '';
for (const line of lines) {
switch (line[0]) {
case '=':
case '-':
if (part !== '') {
part += '\r\n';
2017-11-06 06:01:07 +00:00
parts.push(part);
part = '';
}
break;
default:
part += line;
part += '\r\n';
break;
}
2017-02-06 15:35:19 +00:00
}
2017-11-06 06:01:07 +00:00
const pairs = [];
for (let i = 0; i < parts.length; i += 2) {
const prev = parts[i];
const next = parts[i + 1];
pairs.push({
request: Buffer.from(prev),
response: Buffer.from(next)
});
2017-02-06 15:35:19 +00:00
}
2017-11-06 06:01:07 +00:00
return pairs;
2017-02-06 15:35:19 +00:00
}
/**
* @description
2017-11-06 06:01:07 +00:00
* Wrap packet with pre-shared HTTP header in the first request/response.
*
* @params
* file: A text file which contains several HTTP header paris.
*
* @examples
* {
* "name": "obfs-http",
* "params": {
* "file": "http-fake.txt"
* }
* }
*
* @protocol
*
2017-11-06 06:01:07 +00:00
* C ---- [http header][data] ---> S
* C <---------- [data] ---------> S
*
*/
export default class ObfsHttpPreset extends IPreset {
2017-11-06 06:01:07 +00:00
_isHeaderSent = false;
_isHeaderRecv = false;
2017-02-06 15:35:19 +00:00
_response = null;
2018-02-17 04:19:31 +00:00
static onCheckParams({ file }) {
2017-08-31 07:31:44 +00:00
if (typeof file !== 'string' || file === '') {
throw Error('\'file\' must be a non-empty string');
}
}
2018-02-17 04:19:31 +00:00
static onCache({ file }) {
2018-02-15 03:01:24 +00:00
return {
pairs: parseFile(file),
};
2017-11-06 06:01:07 +00:00
}
onDestroy() {
this._response = null;
2017-02-06 15:35:19 +00:00
}
2018-02-17 04:19:31 +00:00
clientOut({ buffer }) {
2017-11-06 06:01:07 +00:00
if (!this._isHeaderSent) {
2018-02-17 04:19:31 +00:00
const { pairs } = this.getStore();
2017-11-06 06:01:07 +00:00
this._isHeaderSent = true;
const index = crypto.randomBytes(1)[0] % pairs.length;
2018-02-17 04:19:31 +00:00
const { request } = pairs[index];
2017-11-06 06:01:07 +00:00
return Buffer.concat([request, buffer]);
} else {
2017-11-06 06:01:07 +00:00
return buffer;
}
}
2018-02-17 04:19:31 +00:00
serverIn({ buffer, fail }) {
2017-11-06 06:01:07 +00:00
if (!this._isHeaderRecv) {
2018-02-17 04:19:31 +00:00
const found = this.getStore().pairs.find(({ request }) => buffer.indexOf(request) === 0);
2017-11-06 06:01:07 +00:00
if (found !== undefined) {
this._isHeaderRecv = true;
this._response = found.response;
return buffer.slice(found.request.length);
} else {
return fail('http header mismatch');
}
} else {
2017-11-06 06:01:07 +00:00
return buffer;
}
}
2018-02-17 04:19:31 +00:00
serverOut({ buffer }) {
2017-11-06 06:01:07 +00:00
if (!this._isHeaderSent) {
this._isHeaderSent = true;
return Buffer.concat([this._response, buffer]);
2017-11-06 06:01:07 +00:00
} else {
return buffer;
}
}
2018-02-17 04:19:31 +00:00
clientIn({ buffer, fail }) {
2017-11-06 06:01:07 +00:00
if (!this._isHeaderRecv) {
2018-02-17 04:19:31 +00:00
const found = this.getStore().pairs.find(({ response }) => buffer.indexOf(response) === 0);
2017-11-06 06:01:07 +00:00
if (found !== undefined) {
this._isHeaderRecv = true;
return buffer.slice(found.response.length);
} else {
return fail('http header mismatch');
}
} else {
2017-11-06 06:01:07 +00:00
return buffer;
}
}
}