Skip to content
This repository has been archived by the owner on Apr 24, 2020. It is now read-only.

Add socket opts for zeromq 4.x security mechanisms #278

Merged
merged 4 commits into from Feb 14, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 10 additions & 1 deletion .travis.yml
Expand Up @@ -2,9 +2,18 @@ env:
- ZMQ="git://github.com/zeromq/zeromq2-x.git"
- ZMQ="git://github.com/zeromq/zeromq3-x.git -b v3.1.0"
- ZMQ="git://github.com/zeromq/zeromq3-x.git -b v3.2.4"
- ZMQ="git://github.com/zeromq/zeromq4-x.git -b v4.0.3"
- ZMQ="git://github.com/zeromq/zeromq4-x.git -b v4.0.3" SODIUM="git://github.com/jedisct1/libsodium.git -b 0.4.5"
before_install:
- sudo apt-get install uuid-dev
- if [ -n "$SODIUM" ]; then
- git clone --depth 1 $SODIUM libsodium
- cd libsodium
- ./autogen.sh
- ./configure
- make
- sudo make install
- cd ../
- fi
- git clone --depth 1 $ZMQ zmqlib
- cd zmqlib
- ./autogen.sh
Expand Down
12 changes: 12 additions & 0 deletions binding.cc
Expand Up @@ -1133,6 +1133,18 @@ namespace zmq {
opts_uint64.insert(12); // ZMQ_RCVBUF 2.x uint64_t
#endif

#if ZMQ_VERSION_MAJOR >= 4
opts_int.insert(43); // ZMQ_MECHANISM
opts_int.insert(44); // ZMQ_PLAIN_SERVER
opts_binary.insert(45); // ZMQ_PLAIN_USERNAME
opts_binary.insert(46); // ZMQ_PLAIN_PASSWORD
opts_int.insert(47); // ZMQ_CURVE_SERVER
opts_binary.insert(48); // ZMQ_CURVE_PUBLICKEY
opts_binary.insert(49); // ZMQ_CURVE_SECRETKEY
opts_binary.insert(50); // ZMQ_CURVE_SERVERKEY
opts_binary.insert(55); // ZMQ_ZAP_DOMAIN
#endif

NODE_DEFINE_CONSTANT(target, ZMQ_CAN_DISCONNECT);
NODE_DEFINE_CONSTANT(target, ZMQ_CAN_UNBIND);
NODE_DEFINE_CONSTANT(target, ZMQ_CAN_MONITOR);
Expand Down
18 changes: 18 additions & 0 deletions lib/index.js
Expand Up @@ -79,6 +79,15 @@ var longOptions = {
, ZMQ_XPUB_VERBOSE: 40
, ZMQ_ROUTER_RAW: 41
, ZMQ_IPV6: 42
, ZMQ_MECHANISM: 43
, ZMQ_PLAIN_SERVER: 44
, ZMQ_PLAIN_USERNAME: 45
, ZMQ_PLAIN_PASSWORD: 46
, ZMQ_CURVE_SERVER: 47
, ZMQ_CURVE_PUBLICKEY: 48
, ZMQ_CURVE_SECRETKEY: 49
, ZMQ_CURVE_SERVERKEY: 50
, ZMQ_ZAP_DOMAIN: 55
};

Object.keys(longOptions).forEach(function(name){
Expand Down Expand Up @@ -113,6 +122,15 @@ var opts = exports.options = {
, recovery_ivl: zmq.ZMQ_RECOVERY_IVL
, sndbuf: zmq.ZMQ_SNDBUF
, swap: zmq.ZMQ_SWAP
, mechanism: zmq.ZMQ_MECHANISM
, plain_server: zmq.ZMQ_PLAIN_SERVER
, plain_username: zmq.ZMQ_PLAIN_USERNAME
, plain_password: zmq.ZMQ_PLAIN_PASSWORD
, curve_server: zmq.ZMQ_CURVE_SERVER
, curve_publickey: zmq.ZMQ_CURVE_PUBLICKEY
, curve_secretkey: zmq.ZMQ_CURVE_SECRETKEY
, curve_serverkey: zmq.ZMQ_CURVE_SERVERKEY
, zap_domain: zmq.ZMQ_ZAP_DOMAIN
};

/**
Expand Down
59 changes: 59 additions & 0 deletions test/test.socket.zap.curve.js
@@ -0,0 +1,59 @@
var should = require('should')
, semver = require('semver')
, zmq = require('../');

if (semver.gte(zmq.version, '4.0.0')) {
var zap = require('./zap')
, port = 'tcp://127.0.0.1:12347'
, zapSocket = zap.start()
, rep = zmq.socket('rep')
, req = zmq.socket('req');

try {
rep.curve_server = 0;
} catch(e) {
console.log("libsoduim seems to be missing; skipping curve test");
process.exit(0);
}

var serverPublicKey = new Buffer('7f188e5244b02bf497b86de417515cf4d4053ce4eb977aee91a55354655ec33a', 'hex')
, serverPrivateKey = new Buffer('1f5d3873472f95e11f4723d858aaf0919ab1fb402cb3097742c606e61dd0d7d8', 'hex')
, clientPublicKey = new Buffer('ea1cc8bd7c8af65497d43fc21dbec6560c5e7b61bcfdcbd2b0dfacf0b4c38d45', 'hex')
, clientPrivateKey = new Buffer('83f99afacfab052406e5f421612568034e85f4c8182a1c92671e83dca669d31d', 'hex');

rep.on('message', function(msg){
msg.should.be.an.instanceof(Buffer);
msg.toString().should.equal('hello');
rep.send('world');
});

rep.zap_domain = "test";
rep.curve_server = 1;
rep.curve_secretkey = serverPrivateKey;
rep.mechanism.should.eql(2);

var timeout = setTimeout(function() {
req.close();
rep.close();
zapSocket.close();
throw new Error("Request timed out");
}, 1000);

rep.bind(port, function(){
req.curve_serverkey = serverPublicKey;
req.curve_publickey = clientPublicKey;
req.curve_secretkey = clientPrivateKey;
req.mechanism.should.eql(2);

req.connect(port);
req.send('hello');
req.on('message', function(msg){
msg.should.be.an.instanceof(Buffer);
msg.toString().should.equal('world');
clearTimeout(timeout);
req.close();
rep.close();
zapSocket.close();
});
});
}
41 changes: 41 additions & 0 deletions test/test.socket.zap.null.js
@@ -0,0 +1,41 @@
var should = require('should')
, semver = require('semver')
, zmq = require('../');

if (semver.gte(zmq.version, '4.0.0')) {
var zap = require('./zap')
, port = 'tcp://127.0.0.1:12345'
, zapSocket = zap.start()
, rep = zmq.socket('rep')
, req = zmq.socket('req');

rep.on('message', function(msg){
msg.should.be.an.instanceof(Buffer);
msg.toString().should.equal('hello');
rep.send('world');
});

rep.zap_domain = "test";
rep.mechanism.should.eql(0);

var timeout = setTimeout(function() {
req.close();
rep.close();
zapSocket.close();
throw new Error("Request timed out");
}, 1000);

rep.bind(port, function(){
req.mechanism.should.eql(0);
req.connect(port);
req.send('hello');
req.on('message', function(msg){
msg.should.be.an.instanceof(Buffer);
msg.toString().should.equal('world');
clearTimeout(timeout);
req.close();
rep.close();
zapSocket.close();
});
});
}
45 changes: 45 additions & 0 deletions test/test.socket.zap.plain.js
@@ -0,0 +1,45 @@
var should = require('should')
, semver = require('semver')
, zmq = require('../');

if (semver.gte(zmq.version, '4.0.0')) {
var zap = require('./zap')
, port = 'tcp://127.0.0.1:12346'
, zapSocket = zap.start()
, rep = zmq.socket('rep')
, req = zmq.socket('req');

rep.on('message', function(msg){
msg.should.be.an.instanceof(Buffer);
msg.toString().should.equal('hello');
rep.send('world');
});

rep.zap_domain = "test";
rep.plain_server = 1;
rep.mechanism.should.eql(1);

var timeout = setTimeout(function() {
req.close();
rep.close();
zapSocket.close();
throw new Error("Request timed out");
}, 1000);

rep.bind(port, function(){
req.plain_username = "user";
req.plain_password = "pass";
req.mechanism.should.eql(1);

req.connect(port);
req.send('hello');
req.on('message', function(msg){
msg.should.be.an.instanceof(Buffer);
msg.toString().should.equal('world');
clearTimeout(timeout);
req.close();
rep.close();
zapSocket.close();
});
});
}
46 changes: 46 additions & 0 deletions test/zap.js
@@ -0,0 +1,46 @@
// This is mainly for testing that the security mechanisms themselves are working
// not the ZAP protocol itself. As long as the request is valid, this will
// authenticate it.

var zmq = require('../');

module.exports.start = function() {
var zap = zmq.socket('router');
zap.on('message', function() {
var data = Array.prototype.slice.call(arguments);

if (!data || !data.length) throw new Error("Invalid ZAP request");

var returnPath = [],
frame = data.shift();
while (frame && (frame.length != 0)) {
returnPath.push(frame);
frame = data.shift();
}
returnPath.push(frame);

if (data.length < 6) throw new Error("Invalid ZAP request");

var zapReq = {
version: data.shift(),
requestId: data.shift(),
domain: new Buffer(data.shift()).toString('utf8'),
address: new Buffer(data.shift()).toString('utf8'),
identity: new Buffer(data.shift()).toString('utf8'),
mechanism: new Buffer(data.shift()).toString('utf8'),
credentials: data.slice(0)
};

zap.send(returnPath.concat([
zapReq.version,
zapReq.requestId,
new Buffer("200", "utf8"),
new Buffer("OK", "utf8"),
new Buffer(0),
new Buffer(0)
]));
});

zap.bindSync("inproc://zeromq.zap.01");
return zap;
}