Socket
Socket
Sign inDemoInstall

proxying-agent

Package Overview
Dependencies
0
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.3 to 0.1.4

lib/ntlm.js

189

lib/proxying-agent.js

@@ -8,3 +8,3 @@ 'use strict';

var util = require('util');
var ntlm = require('ntlm');
var ntlm = require('./ntlm');

@@ -19,10 +19,31 @@ function ProxyingAgent(options) {

this.options.port = this.options.proxy.port || (this.options.ssl ? 443 : 80);
this.options.authType = this.options.authType || 'basic';
if (this.options.authType == 'ntlm') {
if (!this.options.proxy.auth) {
throw new Error('NTLM authentication credentials must be provided');
}
if (!this.options.ntlm || !this.options.ntlm.domain) {
throw new Error('NTLM domain must be provided');
}
}
// base64 decode proxy auth if necessary
var auth = this.options.proxy.auth;
if (auth && auth.indexOf(':') == -1) {
auth = new Buffer(auth, 'base64').toString('ascii');
// if after decoding there still isn't a colon, then revert back to the original value
if (auth.indexOf(':') == -1) {
auth = this.options.proxy.auth;
}
this.options.proxy.auth = auth;
}
// select the Agent type to use based on the proxy protocol
if (this.options.ssl) {
this.agent = https.Agent;
this.options.agent = https.globalAgent;
this.options.agent = new https.Agent();
} else {
this.agent = http.Agent;
this.options.agent = http.globalAgent;
this.options.agent = new http.Agent();
}

@@ -33,12 +54,9 @@ this.agent.call(this, this.options);

/**
* Overrides the 'addRequest' Agent method for establishing a socket with the proxy
* that will e used to issue the actual request
* @param req
* @param host
* @param port
* @param localAddress
*/
ProxyingAgent.prototype.addRequest = function(req, host, port, localAddress) {
if (this.options.ntlm) {
if (this.options.authType == 'ntlm') {
this.startNtlm(req, host, port, localAddress);

@@ -54,23 +72,11 @@ } else {

* or just issues a regular request for the proxy to transfer
* @param req
* @param host
* @param port
* @param localAddress
*/
ProxyingAgent.prototype.startProxying = function(req, host, port, localAddress) {
// setup the authorization header for the proxy
if (this.options.proxy.auth) {
var auth = this.options.proxy.auth;
// if there is no colon then assume that the auth is a base64 encoded username:password
if (auth.indexOf(':') == -1) {
auth = new Buffer(auth, 'base64').toString('ascii');
// if after decoding there still isn't a colon, then revert back to the original value
if (auth.indexOf(':') == -1) {
auth = this.options.proxy.auth;
}
}
// setup the basic authentication header for the proxy.
// we do this only if we haven't already authenticated through NTLM
if (this.options.authType == 'basic' && this.options.proxy.auth) {
this.authHeader = {
header: 'Proxy-Authorization',
value: 'Basic ' + new Buffer(auth).toString('base64')
value: 'Basic ' + new Buffer(this.options.proxy.auth).toString('base64')
}

@@ -84,2 +90,3 @@ }

tunnelOptions.path = host+':'+port;
tunnelOptions.headers = tunnelOptions.headers || {};

@@ -92,13 +99,29 @@ // if we already have a socket open then execute the CONNECT method on it

// add the authentication header
if (this.authHeader) {
tunnelOptions.headers[this.authHeader.header] = this.authHeader.value;
}
// create a new CONNECT request to the proxy to create the tunnel
// to the server
var newReq = this.createNewRequest(tunnelOptions);
if (this.authHeader) {
newReq.setHeader(this.authHeader.header, this.authHeader.value);
}
newReq.once('close', function() {
this.emitError(req, 'Tunnel creation failed. Socket closed prematurely');
}.bind(this));
newReq.once('error', function(error) {
this.emitError(req, 'Tunnel creation failed. Socket error: ' + error);
}.bind(this));
// listen for the CONNECT event to complete and execute the original request
// on the TLSed socket
newReq.on('connect', function(response, socket, head) {
newReq.once('connect', function(response, socket, head) {
newReq.removeAllListeners();
if (response.statusCode != 200) {
this.emitError(req, 'Tunnel creation failed. Received status code ' + response.statusCode);
return;
}
var tlsOptions = {
socket: socket,
socket: response.socket,
servername: host

@@ -112,2 +135,3 @@ }

}.bind(this));
// execute the CONNECT method to create the tunnel

@@ -129,7 +153,2 @@ newReq.end();

* to issue the actual request or open a tunnel on
*
* @param req
* @param host
* @param port
* @param localAddress
*/

@@ -139,5 +158,12 @@ ProxyingAgent.prototype.startNtlm = function(req, host, port, localAddress) {

ntlmOptions.method = ntlmOptions.method || 'GET'; // just for the NTLM handshake
ntlmOptions.path = (this.options.ssl ? 'https://' : 'http://')+host+':'+port+req.path
ntlmOptions.ntlm.workstation = ntlmOptions.ntlm.workstation || require('os').hostname();
var creds = this.options.proxy.auth.split(':');
ntlmOptions.ntlm.username = creds[0];
ntlmOptions.ntlm.password = creds[1];
// set the NTLM type 1 message header
ntlmOptions.headers['Authorization'] = ntlm.challengeHeader(ntlmOptions.ntlm.hostname, ntlmOptions.ntlm.domain);
ntlmOptions.headers = ntlmOptions.headers || {};
ntlmOptions.headers['Proxy-Authorization'] = ntlm.createType1Message(ntlmOptions.ntlm);

@@ -147,24 +173,48 @@ // create the NTLM type 1 request

// capture the socket
newReq.on('socket', function(socket) {
this.setSocket(req, socket);
});
// capture the response and set the NTLM type 3 authorization header
// that will be used when issuing the actual request
newReq.on('response', function(response) {
if (!response.statusCode == 401 || !response.getHeader('WWW-Authenticate')) {
newReq.once('response', function(response) {
if (!response.statusCode == 407 || !response.headers['proxy-authenticate']) {
this.emitError(req, 'did not receive NTLM type 2 message');
return;
}
var type2msg = ntlm.parseType2Message(response.headers['proxy-authenticate'], function(error) {
this.emitError(req, error);
return null;
}.bind(this));
if (!type2msg) {
return;
}
this.authHeader = {
header: 'Authorization',
value: ntlm.responseHeader(response, req.path,
ntlmOptions.ntlm.domain, ntlmOptions.ntlm.username, ntlmOptions.ntlm.password)
header: 'Proxy-Authorization',
value: ntlm.createType3Message(type2msg, ntlmOptions.ntlm)
}
// start proxying the actual request.
// te socket should have already been captured and associated with the request
response.on('socket', function(socket) {
// capture the socket
this.setSocket(req, socket);
}.bind(this));
// read all the data from the socket as it may contain a body that should be discarded
response.on('data', function() {
// just consume the body
}.bind(this));
// start proxying
this.startProxying(req, host, port, localAddress);
});
}.bind(this));
// start proxying the actual request only when there is not more body to read.
// the socket should have already been captured and associated with the request
newReq.once('close', function() {
this.emitError(req, 'NTLM failed. Socket closed prematurely');
}.bind(this));
newReq.once('error', function(error) {
this.emitError(req, 'NTLM failed. Socket error: ' + error);
}.bind(this));
// issue the NTLM type 1 request

@@ -176,4 +226,2 @@ newReq.end();

* Create a new request instance according the needed security
* @param options
* @returns {*}
*/

@@ -185,2 +233,3 @@ ProxyingAgent.prototype.createNewRequest = function(options) {

return new http.request(options);
}

@@ -192,7 +241,2 @@

* will be used for issuing the request (via the 'createSocket' method)
*
* @param req
* @param host
* @param port
* @param localAddress
*/

@@ -208,17 +252,16 @@ ProxyingAgent.prototype.execRequest = function(req, host, port, localAddress) {

}
/**
* Remember a socket and associate it with a specific request.
* When the 'createSocket' method will be called to execute the actual request
* then the already existing socket will be used
* @param req
* @param socket
*/
* Remember a socket and associate it with a specific request.
* When the 'createSocket' method will be called to execute the actual request
* then the already existing socket will be used
*/
ProxyingAgent.prototype.setSocket = function(req, socket) {
var self = this;
this.openSockets[req] = socket;
var onClose = function() {
if (self.openSockets[req]) {
delete self.openSockets[req];
if (this.openSockets[req]) {
delete this.openSockets[req];
}
};
}.bind(this);
this.openSockets[req].on('close', onClose);

@@ -232,12 +275,6 @@ }

/**
* This is called during the 'addRequest' call of the original Agent to return a
* new socket for executing the request. If a socket already exists then it is used
* instead of creating a new one.
* @param name
* @param host
* @param port
* @param localAddress
* @param req
* @returns {*}
*/
* This is called during the 'addRequest' call of the original Agent to return a
* new socket for executing the request. If a socket already exists then it is used
* instead of creating a new one.
*/
ProxyingAgent.prototype.createSocket = function(name, host, port, localAddress, req) {

@@ -259,4 +296,2 @@ if (this.openSockets[req]) {

* A simple agent to execute a request on a given socket
* @param socket
* @constructor
*/

@@ -263,0 +298,0 @@ function SocketAgent(socket) {

{
"name": "proxying-agent",
"version": "0.1.3",
"version": "0.1.4",
"description": "Node HTTP/HTTPS Forward Proxy Agent",

@@ -10,3 +10,4 @@ "keywords": [

"proxy",
"tunnel"
"tunnel",
"ntlm"
],

@@ -26,6 +27,3 @@ "homepage": "https://github.com/capriza/node-proxying-agent/",

},
"dependencies": {
"ntlm": "~0.1.1"
},
"readmeFilename": "README.md"
}

@@ -6,6 +6,6 @@ # Node HTTP/HTTPS Forward Proxy Agent

It supports the following:
* Connect to a proxy with a regular socket or SSL/TLS socket
* Connect to a proxy with either HTTP or HTTPS
* Proxying to a remote server using SSL tunneling (via the http CONNECT method)
* Authenticate with a proxy with Basic authentication
* Authenticate with a proxy with NTLM authentication (experimental). Depends on ``node-ntlm``
* Authenticate with a proxy with NTLM authentication (beta)

@@ -21,5 +21,50 @@ The agent inherits directly from the ``http.Agent`` Node object so it benefits from all

The following options are supported:
* ``proxy`` - Specifies the proxy url. The supported format is ``http[s]://[auth@]host:port`` where ``auth``
is the authentication information in the form of ``username:password``. The authentication information can also be
in the form of a Base64 encoded ``user:password``, e.g. ``http://dXNlcm5hbWU6cGFzc3dvcmQ=@proxy.example.com:8080``
* ``tunnel`` - If ``true`` then the proxy will become a tunnel to the server.
This should usually be ``true`` only if the target server protocol is ``https``
* ``authType`` - Proxy authentication type. Possible values are ``basic`` and ``ntlm`` (default is ``basic``).
* ``ntlm`` - (beta) applicable only if ``authType`` is ``ntlm``. Supported fields:
* ``domain`` (required) - the NTLM domain
* ``hostname`` (optional) - the local machine hostname
### HTTP Server
```javascript
var proxying = require('proxying-agent');
var proxyingOptions = {
proxy: 'http://proxy.example.com:8080'
};
var proxyingAgent = new proxying.ProxyingAgent(proxyingOptions);
var req = http.request({
host: 'example.com',
port: 80,
agent: proxyingAgent
});
```
### HTTPS Server
```javascript
var proxying = require('proxying-agent');
var proxyingOptions = {
proxy: 'http://proxy.example.com:8080',
tunnel: true
};
var proxyingAgent = new proxying.ProxyingAgent(proxyingOptions);
var req = https.request({
host: 'example.com',
port: 443,
agent: proxyingAgent
});
```
### Basic Authentication
```javascript
var proxying = require('proxying-agent');
var proxyingOptions = {
proxy: 'http://username:password@proxy.example.com:8080',

@@ -36,14 +81,33 @@ tunnel: true

The following options are supported:
### NTLM Authentication
* ``proxy`` - Specifies the proxy url. The supported format is ``http[s]://[auth@]host:port`` where ``auth``
is the authentication information in the form of ``username:password``. The authentication information can also be
in the form of a Base64 encoded ``user:password``, e.g. ``http://dXNlcm5hbWU6cGFzc3dvcmQ=@proxy.example.com:8080``
* ``tunnel`` - If ``true`` then the proxy will become a tunnel to the server. This should only be ``true`` if the target server protocol is https
* ``ntlm`` - (experimental) connect to the proxy using NTLM authentication. ``ntlm`` is expected to contain the
following fields:
* ``hostname`` - the local machine hostname
* ``domain`` - the NTLM domain
* ``username`` - the NTLM username
* ``password`` - the NTLM password
When authenticating using NTLM it is important to delay sending the request data until the socket is assigned to the request.
Failing to do so will result in the socket being prematurely closed, preventing the NTLM handshake from completing.
```javascript
var proxying = require('proxying-agent');
var proxyingOptions = {
proxy: 'http://username:password@proxy.example.com:8080',
tunnel: true,
authType: 'ntlm',
ntlm: {
domain: 'MYDOMAIN'
}
};
var proxyingAgent = new proxying.ProxyingAgent(proxyingOptions);
var req = https.request({
host: 'example.com',
port: 443,
agent: proxyingAgent
});
req.on('socket', function(socket) {
req.write('DATA');
req.end();
});
```
## References
* NTLM code was forked from https://github.com/SamDecrock/node-http-ntlm.git
* NTLM Authentication Scheme for HTTP - http://www.innovation.ch/personal/ronald/ntlm.html
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc