Make Service communicate via empty-line-delimited Dictionary objects instead of the old size prefix way.

This commit is contained in:
Adam Ierymenko 2014-05-08 21:27:59 +00:00
parent 98f0418fb9
commit 99c5fae9da
4 changed files with 64 additions and 79 deletions

View File

@ -45,22 +45,21 @@ var ZEROTIER_IDTOOL = '/usr/local/bin/zerotier-idtool';
// From Constants.hpp in node/ // From Constants.hpp in node/
var ZT_NETWORK_AUTOCONF_DELAY = 60000; var ZT_NETWORK_AUTOCONF_DELAY = 60000;
var ZT_NETWORK_CERTIFICATE_TTL_WINDOW = (ZT_NETWORK_AUTOCONF_DELAY * 4);
// Connect to redis, assuming database 0 and no auth (for now) // Connect to redis, assuming database 0 and no auth (for now)
var redis = require('redis'); var redis = require('redis');
var DB = redis.createClient(); var DB = redis.createClient();
DB.on("error",function(err) { DB.on("error",function(err) { console.error('redis query error: '+err); });
console.error('redis query error: '+err);
});
// Global variables -- these are initialized on startup or netconf-init message // Global variables -- these are initialized on startup or netconf-init message
var netconfSigningIdentity = null; // identity of netconf master, with private key portion var netconfSigningIdentity = null; // identity of netconf master, with private key portion
// spawn() function to launch sub-processes
var spawn = require('child_process').spawn; var spawn = require('child_process').spawn;
// Returns true for fields that are "true" according to ZT redis schema
function ztDbTrue(v) { return ((v === '1')||(v === 'true')||(v > 0)); } function ztDbTrue(v) { return ((v === '1')||(v === 'true')||(v > 0)); }
function csvToArray(csv) { return (((typeof csv === 'string')&&(csv.length > 0)) ? csv.split(',') : []); }
function arrayToCsv(a) { return ((Array.isArray(a)) ? ((a.length > 0) ? a.join(',') : '') : (((a !== null)&&(typeof a !== 'undefined')) ? a.toString() : '')); }
// //
// ZeroTier One Dictionary -- encoding-compatible with Dictionary in C++ code base // ZeroTier One Dictionary -- encoding-compatible with Dictionary in C++ code base
@ -214,11 +213,16 @@ function Identity(idstr)
function generateCertificateOfMembership(nwid,peerAddress,callback) function generateCertificateOfMembership(nwid,peerAddress,callback)
{ {
var comTimestamp = '0,' + Date.now().toString(16) + ',' + (ZT_NETWORK_AUTOCONF_DELAY * 4).toString(16); // The first fields of these COM tuples come from
// CertificateOfMembership.hpp's enum of required
// certificate default fields.
var comTimestamp = '0,' + Date.now().toString(16) + ',' + ZT_NETWORK_CERTIFICATE_TTL_WINDOW.toString(16);
var comNwid = '1,' + nwid + ',0'; var comNwid = '1,' + nwid + ',0';
var comIssuedTo = '2,' + peerAddress + ',ffffffffffffffff'; var comIssuedTo = '2,' + peerAddress + ',ffffffffffffffff';
var cert = ''; var cert = '';
var certErr = ''; var certErr = '';
var idtool = spawn(ZEROTIER_IDTOOL,[ 'mkcom',netconfSigningIdentity,comTimestamp,comNwid,comIssuedTo ]); var idtool = spawn(ZEROTIER_IDTOOL,[ 'mkcom',netconfSigningIdentity,comTimestamp,comNwid,comIssuedTo ]);
idtool.stdout.on('data',function(data) { idtool.stdout.on('data',function(data) {
cert += data; cert += data;
@ -332,15 +336,19 @@ function doNetconfRequest(message)
v4NeedAssign = (network['v4AssignMode'] === 'zt'); v4NeedAssign = (network['v4AssignMode'] === 'zt');
v6NeedAssign = (network['v6AssignMode'] === 'zt'); v6NeedAssign = (network['v6AssignMode'] === 'zt');
var ipa = csvToArray(member['ipAssignments']); var ipacsv = member['ipAssignments'];
if (ipacsv) {
var ipa = ipacsv.split(',');
for(var i=0;i<ipa.length;++i) { for(var i=0;i<ipa.length;++i) {
if (ipa[i]) if (ipa[i]) {
ipAssignments.push(ipa[i]); ipAssignments.push(ipa[i]);
if ((ipa[i].indexOf('.') > 0)&&(v4NeedAssign)) if ((ipa[i].indexOf('.') > 0)&&(v4NeedAssign))
v4Assignments.push(ipa[i]); v4Assignments.push(ipa[i]);
else if ((ipa[i].indexOf(':') > 0)&&(v6NeedAssign)) else if ((ipa[i].indexOf(':') > 0)&&(v6NeedAssign))
v6Assignments.push(ipa[i]); v6Assignments.push(ipa[i]);
} }
}
}
return next(null); return next(null);

View File

@ -366,6 +366,14 @@ error_no_byte_order_defined;
*/ */
#define ZT_ANTIRECURSION_HISTORY_SIZE 16 #define ZT_ANTIRECURSION_HISTORY_SIZE 16
/**
* TTL for certificates of membership on private networks
*
* This is the max delta for the timestamp field of a COM, so it's a window
* plus or minus the certificate's timestamp. In milliseconds.
*/
#define ZT_NETWORK_CERTIFICATE_TTL_WINDOW (ZT_NETWORK_AUTOCONF_DELAY * 4)
/** /**
* How often to broadcast beacons over physical local LANs * How often to broadcast beacons over physical local LANs
*/ */

View File

@ -43,6 +43,7 @@
#include <sys/select.h> #include <sys/select.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "Constants.hpp"
#include "Service.hpp" #include "Service.hpp"
#include "RuntimeEnvironment.hpp" #include "RuntimeEnvironment.hpp"
#include "Utils.hpp" #include "Utils.hpp"
@ -91,32 +92,19 @@ bool Service::send(const Dictionary &msg)
{ {
if (_childStdin <= 0) if (_childStdin <= 0)
return false; return false;
std::string mser(msg.toString());
std::string mser = msg.toString(); mser.append(ZT_EOL_S);
if (mser.length() > ZT_SERVICE_MAX_MESSAGE_SIZE) return ((long)::write(_childStdin,mser.data(),mser.length()) == (long)mser.length());
return false;
// This can technically block. We'll fix this if it ends up being a
// problem.
uint32_t len = Utils::hton((uint32_t)mser.length());
if (write(_childStdin,&len,4) != 4)
return false;
if ((int)write(_childStdin,mser.data(),mser.length()) != (int)mser.length())
return false;
return true;
} }
void Service::threadMain() void Service::threadMain()
throw() throw()
{ {
char buf[131072]; char buf[16384];
fd_set readfds,writefds,exceptfds; fd_set readfds,writefds,exceptfds;
struct timeval tv; struct timeval tv;
int eolsInARow = 0;
std::string stderrBuf; std::string stderrBuf,stdoutBuf;
std::string stdoutBuf;
unsigned int stdoutExpecting = 0;
while (_run) { while (_run) {
if (_pid <= 0) { if (_pid <= 0) {
@ -184,18 +172,18 @@ void Service::threadMain()
tv.tv_sec = 1; tv.tv_sec = 1;
tv.tv_usec = 0; tv.tv_usec = 0;
select(std::max(_childStdout,_childStderr)+1,&readfds,&writefds,&exceptfds,&tv); ::select(std::max(_childStdout,_childStderr)+1,&readfds,&writefds,&exceptfds,&tv);
if (!_run) { if (!_run) {
if (_childStdin > 0) close(_childStdin); if (_childStdin > 0) ::close(_childStdin);
_childStdin = 0; _childStdin = 0;
if (_childStdout > 0) close(_childStdout); if (_childStdout > 0) ::close(_childStdout);
if (_childStderr > 0) close(_childStderr); if (_childStderr > 0) ::close(_childStderr);
return; return;
} }
if ((_childStderr > 0)&&(FD_ISSET(_childStderr,&readfds))) { if ((_childStderr > 0)&&(FD_ISSET(_childStderr,&readfds))) {
int n = (int)read(_childStderr,buf,sizeof(buf)); int n = (int)::read(_childStderr,buf,sizeof(buf));
for(int i=0;i<n;++i) { for(int i=0;i<n;++i) {
if ((buf[i] == '\r')||(buf[i] == '\n')) { if ((buf[i] == '\r')||(buf[i] == '\n')) {
stderrBuf = Utils::trim(stderrBuf); stderrBuf = Utils::trim(stderrBuf);
@ -207,29 +195,20 @@ void Service::threadMain()
} }
if ((_childStdout > 0)&&(FD_ISSET(_childStdout,&readfds))) { if ((_childStdout > 0)&&(FD_ISSET(_childStdout,&readfds))) {
int n = (int)read(_childStdout,buf,sizeof(buf)); int n = (int)::read(_childStdout,buf,sizeof(buf));
for(int i=0;i<n;++i) { for(int i=0;i<n;++i) {
stdoutBuf.push_back(buf[i]); if ((buf[i] == '\n')||(buf[i] == '\r')) {
if (stdoutExpecting) { if (buf[i] == '\n')
if (stdoutBuf.length() == stdoutExpecting) { ++eolsInARow;
} else eolsInARow = 0;
if (eolsInARow >= 2) {
// Two CRs in a row ends a message
try { try {
_handler(_arg,*this,Dictionary(stdoutBuf)); _handler(_arg,*this,Dictionary(stdoutBuf));
} catch ( ... ) {
LOG("unexpected exception handling message from service %s",_name.c_str());
}
stdoutBuf = ""; stdoutBuf = "";
stdoutExpecting = 0; } catch ( ... ) {} // handlers should not throw
} } else stdoutBuf.push_back(buf[i]);
} else if (stdoutBuf.length() == 4) {
stdoutExpecting = Utils::ntoh(*((const uint32_t *)stdoutBuf.data()));
stdoutBuf = "";
if (stdoutExpecting > ZT_SERVICE_MAX_MESSAGE_SIZE) {
LOG("message size overrun from service %s: %u bytes -- restarting service",_name.c_str(),stdoutExpecting);
stdoutExpecting = 0;
kill(_pid,SIGKILL);
break;
}
}
} }
} }
} }

View File

@ -34,12 +34,6 @@
#include "Constants.hpp" #include "Constants.hpp"
#include "Dictionary.hpp" #include "Dictionary.hpp"
#include "Thread.hpp" #include "Thread.hpp"
#include "Mutex.hpp"
/**
* Maximum size of a service message in bytes (sanity limit)
*/
#define ZT_SERVICE_MAX_MESSAGE_SIZE 131072
namespace ZeroTier { namespace ZeroTier {
@ -91,20 +85,12 @@ public:
/** /**
* @return Name of service * @return Name of service
*/ */
inline const char *name() const inline const char *name() const throw() { return _name.c_str(); }
throw()
{
return _name.c_str();
}
/** /**
* @return True if subprocess is running * @return True if subprocess is running
*/ */
inline bool running() const inline bool running() const throw() { return (_pid > 0); }
throw()
{
return (_pid > 0);
}
/** /**
* Thread main method; do not call elsewhere * Thread main method; do not call elsewhere
@ -114,15 +100,19 @@ public:
private: private:
const RuntimeEnvironment *_r; const RuntimeEnvironment *_r;
Thread _thread; Thread _thread;
std::string _path; std::string _path;
std::string _name; std::string _name;
void *_arg; void *_arg;
void (*_handler)(void *,Service &,const Dictionary &); void (*_handler)(void *,Service &,const Dictionary &);
long _pid; volatile long _pid;
int _childStdin;
int _childStdout; volatile int _childStdin;
int _childStderr; volatile int _childStdout;
volatile int _childStderr;
volatile bool _run; volatile bool _run;
}; };
#endif // __WINDOWS__ #endif // __WINDOWS__