1 var stream
= require('stream'),
2 util
= require('util'),
6 ipaddr
= require('ipaddr.js');
8 var SocksConnection = function (remote_options
, socks_options
) {
10 stream
.Duplex
.call(this);
12 this.remote_options
= _
.defaults(remote_options
, {
15 rejectUnauthorized
: false
17 socks_options
= _
.defaults(socks_options
, {
24 this.socksAddress
= null;
25 this.socksPort
= null;
27 this.socksSocket
= net
.connect({host
: socks_options
.host
, port
: socks_options
.port
}, socksConnected
.bind(this, !(!socks_options
.user
)));
28 this.socksSocket
.once('data', socksAuth
.bind(this, {user
: socks_options
.user
, pass
: socks_options
.pass
}));
29 this.socksSocket
.on('error', function (err
) {
30 that
.emit('error', err
);
33 this.outSocket
= this.socksSocket
;
36 util
.inherits(SocksConnection
, stream
.Duplex
);
38 SocksConnection
.connect = function (remote_options
, socks_options
, connection_listener
) {
39 var socks_connection
= new SocksConnection(remote_options
, socks_options
);
40 if (typeof connection_listener
=== 'Function') {
41 socks_connection
.on('connect', connection_listener
);
43 return socks_connection
;
46 SocksConnection
.prototype._read = function () {
47 this.outSocket
.resume();
50 SocksConnection
.prototype._write = function (chunk
, encoding
, callback
) {
51 this.outSocket
.write(chunk
, 'utf8', callback
);
54 SocksConnection
.prototype.dispose = function () {
55 this.outSocket
.destroy();
56 this.outSocket
.removeAllListeners();
57 if (this.outSocket
!== this.socksSocket
) {
58 this.socksSocket
.destroy();
59 this.socksSocket
.removeAllListeners();
61 this.removeAllListeners();
64 var socksConnected = function (auth
) {
66 this.socksSocket
.write('\x05\x02\x02\x00'); // SOCKS version 5, supporting two auth methods
67 // username/password and 'no authentication'
69 this.socksSocket
.write('\x05\x01\x00'); // SOCKS version 5, only supporting 'no auth' scheme
73 var socksAuth = function (auth
, data
) {
75 switch (data
.readUInt8(1)) {
77 this.emit('error', 'SOCKS: No acceptable authentication methods');
78 this.socksSocket
.destroy();
81 bufs
[0] = new Buffer([1]);
82 bufs
[1] = new Buffer([Buffer
.byteLength(auth
.user
)]);
83 bufs
[2] = new Buffer(auth
.user
);
84 bufs
[3] = new Buffer([Buffer
.byteLength(auth
.pass
)]);
85 bufs
[4] = new Buffer(auth
.pass
);
86 this.socksSocket
.write(Buffer
.concat(bufs
));
87 this.socksSocket
.once('data', socksAuthStatus
.bind(this));
90 socksRequest
.call(this, this.remote_options
.host
, this.remote_options
.port
);
94 var socksAuthStatus = function (data
) {
95 if (data
.readUInt8(1) === 1) {
96 socksRequest
.call(this, this.remote_options
.host
, this.remote_options
.port
);
98 this.emit('error', 'SOCKS: Authentication failed');
99 this.socksSocket
.destroy();
103 var socksRequest = function (host
, port
) {
104 var header
, type
, hostBuf
, portBuf
;
105 if (net
.isIP(host
)) {
106 if (net
.isIPv4(host
)) {
107 type
= new Buffer([1]);
108 } else if (net
.isIPv6(host
)) {
109 type
= new Buffer([4]);
111 host
= new Buffer(ipaddr
.parse(host
).toByteArray());
113 type
= new Buffer([3]);
114 hostBuf
= new Buffer(host
);
115 hostBuf
= Buffer
.concat([new Buffer([Buffer
.byteLength(host
)]), hostBuf
]);
117 header
= new Buffer([5, 1, 0]);
118 portBuf
= new Buffer(2);
119 portBuf
.writeUInt16BE(port
, 0);
120 this.socksSocket
.write(Buffer
.concat([header
, type
, hostBuf
, portBuf
]));
121 this.socksSocket
.once('data', socksReply
.bind(this));
124 var socksReply = function (data
) {
125 var err
, port
, i
, addr_len
, addr
= '';
126 var status
= data
.readUInt8(1);
128 switch (data
.readUInt8(3)) {
130 for (i
= 0; i
< 4; i
++) {
134 addr
+= data
.readUInt8(4 + i
);
136 port
= data
.readUInt16BE(8);
139 for (i
= 0; i
< 16; i
++) {
143 addr
+= data
.readUInt8(4 + i
);
145 port
= data
.readUInt16BE(20);
148 addr_len
= data
.readUInt8(4);
149 addr
= (data
.slice(5, 5 + addr_len
)).toString();
150 port
= data
.readUInt16BE(5 + addr_len
);
152 this.socksAddress
= addr
;
153 this.socksPort
= port
;
155 if (this.remote_options
.ssl
) {
158 proxyData
.call(this);
159 this.emit('connect');
165 err
= 'SOCKS: general SOCKS server failure';
168 err
= 'SOCKS: Connection not allowed by ruleset';
171 err
= 'SOCKS: Network unreachable';
174 err
= 'SOCKS: Host unreachable';
177 err
= 'SOCKS: Connection refused';
180 err
= 'SOCKS: TTL expired';
183 err
= 'SOCKS: Command not supported';
186 err
= 'SOCKS: Address type not supported';
189 err
= 'SOCKS: Unknown error';
191 this.emit('error', err
);
195 var startTLS = function () {
197 var plaintext
= tls
.connect({
198 socket
: this.socksSocket
,
199 rejectUnauthorized
: this.remote_options
.rejectUnauthorized
202 plaintext
.on('error', function (err
) {
203 that
.emit('error', err
);
206 plaintext
.on('secureConnect', function () {
207 that
.emit('connect');
209 this.outSocket
= plaintext
;
210 proxyData
.call(this);
213 var proxyData = function () {
216 this.outSocket
.on('data', function (data
) {
217 var buffer_not_full
= that
.push(data
);
218 if (!buffer_not_full
) {
223 this.outSocket
.on('end', function () {
228 module
.exports
= SocksConnection
;