3 var CC = Components.Constructor;
5 const ServerSocket = CC(
6 "@mozilla.org/network/server-socket;1",
10 const BinaryInputStream = CC(
11 "@mozilla.org/binaryinputstream;1",
12 "nsIBinaryInputStream",
15 const DirectoryService = CC(
16 "@mozilla.org/file/directory_service;1",
19 const Process = CC("@mozilla.org/process/util;1", "nsIProcess", "init");
22 Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
24 var socks_test_server = null;
25 var socks_listen_port = -1;
27 function getAvailableBytes(input) {
31 len = input.available();
37 function runScriptSubprocess(script, args) {
38 var ds = new DirectoryService();
39 var bin = ds.get("XREExeF", Ci.nsIFile);
41 do_throw("Can't find xpcshell binary");
44 var file = do_get_file(script);
45 var proc = new Process(bin);
46 var procArgs = [file.path].concat(args);
48 proc.run(false, procArgs, procArgs.length);
53 function buf2ip(buf) {
54 if (buf.length == 16) {
56 ((buf[0] << 4) | buf[1]).toString(16) +
58 ((buf[2] << 4) | buf[3]).toString(16) +
60 ((buf[4] << 4) | buf[5]).toString(16) +
62 ((buf[6] << 4) | buf[7]).toString(16) +
64 ((buf[8] << 4) | buf[9]).toString(16) +
66 ((buf[10] << 4) | buf[11]).toString(16) +
68 ((buf[12] << 4) | buf[13]).toString(16) +
70 ((buf[14] << 4) | buf[15]).toString(16);
71 for (var i = 8; i >= 2; i--) {
72 var re = new RegExp("(^|:)(0(:|$)){" + i + "}");
73 var shortip = ip.replace(re, "::");
83 function buf2int(buf) {
87 n |= buf[i] << ((buf.length - i - 1) * 8);
93 function buf2str(buf) {
94 return String.fromCharCode.apply(null, buf);
97 const STATE_WAIT_GREETING = 1;
98 const STATE_WAIT_SOCKS4_REQUEST = 2;
99 const STATE_WAIT_SOCKS4_USERNAME = 3;
100 const STATE_WAIT_SOCKS4_HOSTNAME = 4;
101 const STATE_WAIT_SOCKS5_GREETING = 5;
102 const STATE_WAIT_SOCKS5_REQUEST = 6;
103 const STATE_WAIT_PONG = 7;
104 const STATE_GOT_PONG = 8;
106 function SocksClient(server, client_in, client_out) {
107 this.server = server;
114 this.client_in = client_in;
115 this.client_out = client_out;
117 this.outbuf = String();
118 this.state = STATE_WAIT_GREETING;
119 this.waitRead(this.client_in);
121 SocksClient.prototype = {
122 onInputStreamReady(input) {
123 var len = getAvailableBytes(input);
126 print("server: client closed!");
127 Assert.equal(this.state, STATE_GOT_PONG);
129 this.server.testCompleted(this);
133 var bin = new BinaryInputStream(input);
134 var data = bin.readByteArray(len);
135 this.inbuf = this.inbuf.concat(data);
137 switch (this.state) {
138 case STATE_WAIT_GREETING:
139 this.checkSocksGreeting();
141 case STATE_WAIT_SOCKS4_REQUEST:
142 this.checkSocks4Request();
144 case STATE_WAIT_SOCKS4_USERNAME:
145 this.checkSocks4Username();
147 case STATE_WAIT_SOCKS4_HOSTNAME:
148 this.checkSocks4Hostname();
150 case STATE_WAIT_SOCKS5_GREETING:
151 this.checkSocks5Greeting();
153 case STATE_WAIT_SOCKS5_REQUEST:
154 this.checkSocks5Request();
156 case STATE_WAIT_PONG:
160 do_throw("server: read in invalid state!");
163 this.waitRead(input);
166 onOutputStreamReady(output) {
167 var len = output.write(this.outbuf, this.outbuf.length);
168 if (len != this.outbuf.length) {
169 this.outbuf = this.outbuf.substring(len);
170 this.waitWrite(output);
172 this.outbuf = String();
177 input.asyncWait(this, 0, 0, currentThread);
181 output.asyncWait(this, 0, 0, currentThread);
186 this.waitWrite(this.client_out);
189 checkSocksGreeting() {
190 if (!this.inbuf.length) {
194 if (this.inbuf[0] == 4) {
195 print("server: got socks 4");
196 this.type = "socks4";
197 this.state = STATE_WAIT_SOCKS4_REQUEST;
198 this.checkSocks4Request();
199 } else if (this.inbuf[0] == 5) {
200 print("server: got socks 5");
202 this.state = STATE_WAIT_SOCKS5_GREETING;
203 this.checkSocks5Greeting();
205 do_throw("Unknown socks protocol!");
209 checkSocks4Request() {
210 if (this.inbuf.length < 8) {
214 Assert.equal(this.inbuf[1], 0x01);
216 this.dest_port = this.inbuf.slice(2, 4);
217 this.dest_addr = this.inbuf.slice(4, 8);
219 this.inbuf = this.inbuf.slice(8);
220 this.state = STATE_WAIT_SOCKS4_USERNAME;
221 this.checkSocks4Username();
225 var i = this.inbuf.indexOf(0);
229 var buf = this.inbuf.slice(0, i);
231 this.inbuf = this.inbuf.slice(i + 1);
237 checkSocks4Username() {
238 var str = this.readString();
246 this.dest_addr[0] == 0 &&
247 this.dest_addr[1] == 0 &&
248 this.dest_addr[2] == 0 &&
249 this.dest_addr[3] != 0
251 this.state = STATE_WAIT_SOCKS4_HOSTNAME;
252 this.checkSocks4Hostname();
254 this.sendSocks4Response();
258 checkSocks4Hostname() {
259 var str = this.readString();
265 this.dest_name = str;
266 this.sendSocks4Response();
269 sendSocks4Response() {
270 this.outbuf = "\x00\x5a\x00\x00\x00\x00\x00\x00";
274 checkSocks5Greeting() {
275 if (this.inbuf.length < 2) {
278 var nmethods = this.inbuf[1];
279 if (this.inbuf.length < 2 + nmethods) {
283 Assert.ok(nmethods >= 1);
284 var methods = this.inbuf.slice(2, 2 + nmethods);
285 Assert.ok(0 in methods);
288 this.state = STATE_WAIT_SOCKS5_REQUEST;
289 this.write("\x05\x00");
292 checkSocks5Request() {
293 if (this.inbuf.length < 4) {
297 Assert.equal(this.inbuf[0], 0x05);
298 Assert.equal(this.inbuf[1], 0x01);
299 Assert.equal(this.inbuf[2], 0x00);
301 var atype = this.inbuf[3];
317 do_throw("Unknown address type " + atype);
321 if (this.inbuf.length < 4 + len + 1 + 2) {
325 let buf = this.inbuf.slice(5, 5 + len);
326 this.dest_name = buf2str(buf);
329 if (this.inbuf.length < 4 + len + 2) {
333 this.dest_addr = this.inbuf.slice(4, 4 + len);
337 this.dest_port = this.inbuf.slice(len, len + 2);
338 this.inbuf = this.inbuf.slice(len + 2);
339 this.sendSocks5Response();
342 sendSocks5Response() {
343 if (this.dest_addr.length == 16) {
344 // send a successful response with the address, [::1]:80
346 "\x05\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x80";
348 // send a successful response with the address, 127.0.0.1:80
349 this.outbuf += "\x05\x00\x00\x01\x7f\x00\x00\x01\x00\x80";
355 print("server: sending ping");
356 this.state = STATE_WAIT_PONG;
357 this.outbuf += "PING!";
359 this.waitWrite(this.client_out);
363 var pong = buf2str(this.inbuf);
364 Assert.equal(pong, "PONG!");
365 this.state = STATE_GOT_PONG;
369 this.client_in.close();
370 this.client_out.close();
374 function SocksTestServer() {
375 this.listener = ServerSocket(-1, true, -1);
376 socks_listen_port = this.listener.port;
377 print("server: listening on", socks_listen_port);
378 this.listener.asyncListen(this);
379 this.test_cases = [];
380 this.client_connections = [];
381 this.client_subprocess = null;
382 // port is used as the ID for test cases
383 this.test_port_id = 8000;
384 this.tests_completed = 0;
386 SocksTestServer.prototype = {
388 test.finished = false;
389 test.port = this.test_port_id++;
390 this.test_cases.push(test);
394 for (var i in this.test_cases) {
395 var test = this.test_cases[i];
396 if (test.port == id) {
397 this.tests_completed++;
401 do_throw("No test case with id " + id);
405 testCompleted(client) {
406 var port_id = buf2int(client.dest_port);
407 var test = this.pickTest(port_id);
409 print("server: test finished", test.port);
410 Assert.ok(test != null);
411 Assert.equal(test.expectedType || test.type, client.type);
412 Assert.equal(test.port, port_id);
414 if (test.remote_dns) {
415 Assert.equal(test.host, client.dest_name);
417 Assert.equal(test.host, buf2ip(client.dest_addr));
420 if (this.test_cases.length == this.tests_completed) {
421 print("server: all tests completed");
427 runClientSubprocess() {
430 // marshaled: socks_ver|server_port|dest_host|dest_port|<remote|local>
431 for (var test of this.test_cases) {
435 String(socks_listen_port) +
441 if (test.remote_dns) {
446 print("server: using test case", arg);
450 this.client_subprocess = runScriptSubprocess(
451 "socks_client_subprocess.js",
456 onSocketAccepted(socket, trans) {
457 print("server: got client connection");
458 var input = trans.openInputStream(0, 0, 0);
459 var output = trans.openOutputStream(0, 0, 0);
460 var client = new SocksClient(this, input, output);
461 this.client_connections.push(client);
464 onStopListening(socket) {},
467 if (this.client_subprocess) {
469 this.client_subprocess.kill();
471 do_note_exception(x, "Killing subprocess failed");
473 this.client_subprocess = null;
475 this.client_connections = [];
477 this.listener.close();
478 this.listener = null;
483 function run_test() {
484 socks_test_server = new SocksTestServer();
486 socks_test_server.addTestCase({
491 socks_test_server.addTestCase({
496 socks_test_server.addTestCase({
498 expectedType: "socks",
502 socks_test_server.addTestCase({
507 socks_test_server.addTestCase({
512 socks_test_server.addTestCase({
517 socks_test_server.runClientSubprocess();