"use strict" ;
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
var CC = Components.Constructor;
const ServerSocket = CC(
"@mozilla.org/network/server-socket;1" ,
"nsIServerSocket" ,
"init"
);
const BinaryInputStream = CC(
"@mozilla.org/binaryinputstream;1" ,
"nsIBinaryInputStream" ,
"setInputStream"
);
const DirectoryService = CC(
"@mozilla.org/file/directory_service;1" ,
"nsIProperties"
);
const Process = CC("@mozilla.org/process/util;1" , "nsIProcess" , "init" );
const currentThread =
Cc["@mozilla.org/thread-manager;1" ].getService().currentThread;
var socks_test_server = null ;
var socks_listen_port = -1 ;
function getAvailableBytes(input) {
var len = 0 ;
try {
len = input.available();
} catch (e) {}
return len;
}
function runScriptSubprocess(script, args) {
var ds = new DirectoryService();
var bin = ds.get("XREExeF" , Ci.nsIFile);
if (!bin.exists()) {
do_throw("Can't find xpcshell binary" );
}
var file = do_get_file(script);
var proc = new Process(bin);
var procArgs = [];
if (AppConstants.platform != "macosx" ) {
var grebind = ds.get("GreBinD" , Ci.nsIFile);
if (!grebind.exists()) {
do_throw("Could not find binary dir" );
}
procArgs.push("-g" , grebind.path);
}
procArgs.push(file.path);
procArgs = procArgs.concat(args);
proc.run(false , procArgs, procArgs.length);
return proc;
}
function buf2ip(buf) {
if (buf.length == 16 ) {
var ip =
((buf[0 ] << 4 ) | buf[1 ]).toString(16 ) +
":" +
((buf[2 ] << 4 ) | buf[3 ]).toString(16 ) +
":" +
((buf[4 ] << 4 ) | buf[5 ]).toString(16 ) +
":" +
((buf[6 ] << 4 ) | buf[7 ]).toString(16 ) +
":" +
((buf[8 ] << 4 ) | buf[9 ]).toString(16 ) +
":" +
((buf[10 ] << 4 ) | buf[11 ]).toString(16 ) +
":" +
((buf[12 ] << 4 ) | buf[13 ]).toString(16 ) +
":" +
((buf[14 ] << 4 ) | buf[15 ]).toString(16 );
for (var i = 8 ; i >= 2 ; i--) {
var re = new RegExp("(^|:)(0(:|$)){" + i + "}" );
var shortip = ip.replace(re, "::" );
if (shortip != ip) {
return shortip;
}
}
return ip;
}
return buf.join("." );
}
function buf2int(buf) {
var n = 0 ;
for (var i in buf) {
n |= buf[i] << ((buf.length - i - 1 ) * 8 );
}
return n;
}
function buf2str(buf) {
return String.fromCharCode.apply(null , buf);
}
const STATE_WAIT_GREETING = 1 ;
const STATE_WAIT_SOCKS4_REQUEST = 2 ;
const STATE_WAIT_SOCKS4_USERNAME = 3 ;
const STATE_WAIT_SOCKS4_HOSTNAME = 4 ;
const STATE_WAIT_SOCKS5_GREETING = 5 ;
const STATE_WAIT_SOCKS5_REQUEST = 6 ;
const STATE_WAIT_PONG = 7 ;
const STATE_GOT_PONG = 8 ;
function SocksClient(server, client_in, client_out) {
this .server = server;
this .type = "" ;
this .username = "" ;
this .dest_name = "" ;
this .dest_addr = [];
this .dest_port = [];
this .client_in = client_in;
this .client_out = client_out;
this .inbuf = [];
this .outbuf = String();
this .state = STATE_WAIT_GREETING;
this .waitRead(this .client_in);
}
SocksClient.prototype = {
onInputStreamReady(input) {
var len = getAvailableBytes(input);
if (len == 0 ) {
print("server: client closed!" );
Assert .equal(this .state, STATE_GOT_PONG);
this .close();
this .server.testCompleted(this );
return ;
}
var bin = new BinaryInputStream(input);
var data = bin.readByteArray(len);
this .inbuf = this .inbuf.concat(data);
switch (this .state) {
case STATE_WAIT_GREETING:
this .checkSocksGreeting();
break ;
case STATE_WAIT_SOCKS4_REQUEST:
this .checkSocks4Request();
break ;
case STATE_WAIT_SOCKS4_USERNAME:
this .checkSocks4Username();
break ;
case STATE_WAIT_SOCKS4_HOSTNAME:
this .checkSocks4Hostname();
break ;
case STATE_WAIT_SOCKS5_GREETING:
this .checkSocks5Greeting();
break ;
case STATE_WAIT_SOCKS5_REQUEST:
this .checkSocks5Request();
break ;
case STATE_WAIT_PONG:
this .checkPong();
break ;
default :
do_throw("server: read in invalid state!" );
}
this .waitRead(input);
},
onOutputStreamReady(output) {
var len = output.write(this .outbuf, this .outbuf.length);
if (len != this .outbuf.length) {
this .outbuf = this .outbuf.substring(len);
this .waitWrite(output);
} else {
this .outbuf = String();
}
},
waitRead(input) {
input.asyncWait(this , 0 , 0 , currentThread);
},
waitWrite(output) {
output.asyncWait(this , 0 , 0 , currentThread);
},
write(buf) {
this .outbuf += buf;
this .waitWrite(this .client_out);
},
checkSocksGreeting() {
if (!this .inbuf.length) {
return ;
}
if (this .inbuf[0 ] == 4 ) {
print("server: got socks 4" );
this .type = "socks4" ;
this .state = STATE_WAIT_SOCKS4_REQUEST;
this .checkSocks4Request();
} else if (this .inbuf[0 ] == 5 ) {
print("server: got socks 5" );
this .type = "socks" ;
this .state = STATE_WAIT_SOCKS5_GREETING;
this .checkSocks5Greeting();
} else {
do_throw("Unknown socks protocol!" );
}
},
checkSocks4Request() {
if (this .inbuf.length < 8 ) {
return ;
}
Assert .equal(this .inbuf[1 ], 0 x01);
this .dest_port = this .inbuf.slice(2 , 4 );
this .dest_addr = this .inbuf.slice(4 , 8 );
this .inbuf = this .inbuf.slice(8 );
this .state = STATE_WAIT_SOCKS4_USERNAME;
this .checkSocks4Username();
},
readString() {
var i = this .inbuf.indexOf(0 );
var str = null ;
if (i >= 0 ) {
var buf = this .inbuf.slice(0 , i);
str = buf2str(buf);
this .inbuf = this .inbuf.slice(i + 1 );
}
return str;
},
checkSocks4Username() {
var str = this .readString();
if (str == null ) {
return ;
}
this .username = str;
if (
this .dest_addr[0 ] == 0 &&
this .dest_addr[1 ] == 0 &&
this .dest_addr[2 ] == 0 &&
this .dest_addr[3 ] != 0
) {
this .state = STATE_WAIT_SOCKS4_HOSTNAME;
this .checkSocks4Hostname();
} else {
this .sendSocks4Response();
}
},
checkSocks4Hostname() {
var str = this .readString();
if (str == null ) {
return ;
}
this .dest_name = str;
this .sendSocks4Response();
},
sendSocks4Response() {
this .outbuf = "\x00\x5a\x00\x00\x00\x00\x00\x00" ;
this .sendPing();
},
checkSocks5Greeting() {
if (this .inbuf.length < 2 ) {
return ;
}
var nmethods = this .inbuf[1 ];
if (this .inbuf.length < 2 + nmethods) {
return ;
}
Assert .greaterOrEqual(nmethods, 1 );
var methods = this .inbuf.slice(2 , 2 + nmethods);
Assert .ok(0 in methods);
this .inbuf = [];
this .state = STATE_WAIT_SOCKS5_REQUEST;
this .write("\x05\x00" );
},
checkSocks5Request() {
if (this .inbuf.length < 4 ) {
return ;
}
Assert .equal(this .inbuf[0 ], 0 x05);
Assert .equal(this .inbuf[1 ], 0 x01);
Assert .equal(this .inbuf[2 ], 0 x00);
var atype = this .inbuf[3 ];
var len;
var name = false ;
switch (atype) {
case 0 x01:
len = 4 ;
break ;
case 0 x03:
len = this .inbuf[4 ];
name = true ;
break ;
case 0 x04:
len = 16 ;
break ;
default :
do_throw("Unknown address type " + atype);
}
if (name) {
if (this .inbuf.length < 4 + len + 1 + 2 ) {
return ;
}
let buf = this .inbuf.slice(5 , 5 + len);
this .dest_name = buf2str(buf);
len += 1 ;
} else {
if (this .inbuf.length < 4 + len + 2 ) {
return ;
}
this .dest_addr = this .inbuf.slice(4 , 4 + len);
}
len += 4 ;
this .dest_port = this .inbuf.slice(len, len + 2 );
this .inbuf = this .inbuf.slice(len + 2 );
this .sendSocks5Response();
},
sendSocks5Response() {
if (this .dest_addr.length == 16 ) {
// send a successful response with the address, [::1]:80
this .outbuf +=
"\x05\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x80" ;
} else {
// send a successful response with the address, 127.0.0.1:80
this .outbuf += "\x05\x00\x00\x01\x7f\x00\x00\x01\x00\x80" ;
}
this .sendPing();
},
sendPing() {
print("server: sending ping" );
this .state = STATE_WAIT_PONG;
this .outbuf += "PING!" ;
this .inbuf = [];
this .waitWrite(this .client_out);
},
checkPong() {
var pong = buf2str(this .inbuf);
Assert .equal(pong, "PONG!" );
this .state = STATE_GOT_PONG;
},
close() {
this .client_in.close();
this .client_out.close();
},
};
function SocksTestServer() {
this .listener = ServerSocket(-1 , true , -1 );
socks_listen_port = this .listener.port;
print("server: listening on" , socks_listen_port);
this .listener.asyncListen(this );
this .test_cases = [];
this .client_connections = [];
this .client_subprocess = null ;
// port is used as the ID for test cases
this .test_port_id = 8000 ;
this .tests_completed = 0 ;
}
SocksTestServer.prototype = {
addTestCase(test) {
test.finished = false ;
test.port = this .test_port_id++;
this .test_cases.push(test);
},
pickTest(id) {
for (var i in this .test_cases) {
var test = this .test_cases[i];
if (test.port == id) {
this .tests_completed++;
return test;
}
}
do_throw("No test case with id " + id);
return null ;
},
testCompleted(client) {
var port_id = buf2int(client.dest_port);
var test = this .pickTest(port_id);
print("server: test finished" , test.port);
Assert .notEqual(test, null );
Assert .equal(test.expectedType || test.type, client.type);
Assert .equal(test.port, port_id);
if (test.remote_dns) {
Assert .equal(test.host, client.dest_name);
} else {
Assert .equal(test.host, buf2ip(client.dest_addr));
}
if (this .test_cases.length == this .tests_completed) {
print("server: all tests completed" );
this .close();
do_test_finished();
}
},
runClientSubprocess() {
var argv = [];
// marshaled: socks_ver|server_port|dest_host|dest_port|<remote|local>
for (var test of this .test_cases) {
var arg =
test.type +
"|" +
String(socks_listen_port) +
"|" +
test.host +
"|" +
test.port +
"|" ;
if (test.remote_dns) {
arg += "remote" ;
} else {
arg += "local" ;
}
print("server: using test case" , arg);
argv.push(arg);
}
this .client_subprocess = runScriptSubprocess(
"socks_client_subprocess.js" ,
argv
);
},
onSocketAccepted(socket, trans) {
print("server: got client connection" );
var input = trans.openInputStream(0 , 0 , 0 );
var output = trans.openOutputStream(0 , 0 , 0 );
var client = new SocksClient(this , input, output);
this .client_connections.push(client);
},
onStopListening() {},
close() {
if (this .client_subprocess) {
try {
this .client_subprocess.kill();
} catch (x) {
do_note_exception(x, "Killing subprocess failed" );
}
this .client_subprocess = null ;
}
this .client_connections = [];
if (this .listener) {
this .listener.close();
this .listener = null ;
}
},
};
function run_test() {
socks_test_server = new SocksTestServer();
socks_test_server.addTestCase({
type: "socks4" ,
host: "127.0.0.1" ,
remote_dns: false ,
});
socks_test_server.addTestCase({
type: "socks4" ,
host: "12345.xxx" ,
remote_dns: true ,
});
socks_test_server.addTestCase({
type: "socks4" ,
expectedType: "socks" ,
host: "::1" ,
remote_dns: false ,
});
socks_test_server.addTestCase({
type: "socks" ,
host: "127.0.0.1" ,
remote_dns: false ,
});
socks_test_server.addTestCase({
type: "socks" ,
host: "abcdefg.xxx" ,
remote_dns: true ,
});
socks_test_server.addTestCase({
type: "socks" ,
host: "::1" ,
remote_dns: false ,
});
socks_test_server.runClientSubprocess();
do_test_pending();
}
Messung V0.5 in Prozent C=100 H=92 G=95
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland