From b2e1afa2279a11b859dde76f430ff769f80f79d8 Mon Sep 17 00:00:00 2001 From: Cy Date: Sat, 16 Apr 2016 11:23:40 -0700 Subject: [PATCH 01/12] forgot an exception --- contrib/python/dynamicEndpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/python/dynamicEndpoints.py b/contrib/python/dynamicEndpoints.py index 46f8eebb7..5fb271221 100755 --- a/contrib/python/dynamicEndpoints.py +++ b/contrib/python/dynamicEndpoints.py @@ -346,7 +346,7 @@ def main(argv): # Announce we dropped privs logging.info("Dropped privileges: running as {}:{}".format( pwd.getpwuid(os.getuid())[0], grp.getgrgid(os.getgid())[0])) - except OSError: + except (OSError,KeyError): # Complain we couldn't drop privs right logging.warning("Could not drop privileges: running as {}:{}".format( pwd.getpwuid(os.getuid())[0], grp.getgrgid(os.getgid())[0])) From 4e90beb6ccfb702a191cc304ec0ac067d7faf3a9 Mon Sep 17 00:00:00 2001 From: Cy Date: Sat, 16 Apr 2016 20:25:40 -0700 Subject: [PATCH 02/12] Fixed broken bad cjdnsadmin.py What the heck were they even doing with oargList... --- contrib/python/cjdnsadmin/cjdnsadmin.py | 494 ++++++++++++------------ contrib/python/dynamicEndpoints.py | 18 +- 2 files changed, 260 insertions(+), 252 deletions(-) diff --git a/contrib/python/cjdnsadmin/cjdnsadmin.py b/contrib/python/cjdnsadmin/cjdnsadmin.py index baa758917..494bbb7c2 100644 --- a/contrib/python/cjdnsadmin/cjdnsadmin.py +++ b/contrib/python/cjdnsadmin/cjdnsadmin.py @@ -28,276 +28,284 @@ class Session(): - """Current cjdns admin session""" + """Current cjdns admin session""" - def __init__(self, socket): - self.socket = socket - self.queue = Queue.Queue() - self.messages = {} + def __init__(self, socket): + self.socket = socket + self.queue = Queue.Queue() + self.messages = {} - def disconnect(self): - self.socket.close() + def disconnect(self): + self.socket.close() - def getMessage(self, txid): - # print self, txid - return _getMessage(self, txid) + def getMessage(self, txid): + # print self, txid + return _getMessage(self, txid) - def functions(self): - print(self._functions) + def functions(self): + print(self._functions) def _randomString(): - """Random string for message signing""" + """Random string for message signing""" - return ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(10)) + return ''.join( + random.choice(string.ascii_uppercase + string.digits) + for x in range(10)) def _callFunc(session, funcName, password, args): - """Call custom cjdns admin function""" - - txid = _randomString() - sock = session.socket - sock.send('d1:q6:cookie4:txid10:' + txid + 'e') - msg = _getMessage(session, txid) - cookie = msg['cookie'] - txid = _randomString() - req = { - 'q': funcName, - 'hash': hashlib.sha256(password + cookie).hexdigest(), - 'cookie': cookie, - 'args': args, - 'txid': txid - } - - if password: - req['aq'] = req['q'] - req['q'] = 'auth' - reqBenc = bencode(req) - req['hash'] = hashlib.sha256(reqBenc).hexdigest() - - reqBenc = bencode(req) - sock.send(reqBenc) - return _getMessage(session, txid) + """Call custom cjdns admin function""" + + txid = _randomString() + sock = session.socket + sock.send('d1:q6:cookie4:txid10:' + txid + 'e') + msg = _getMessage(session, txid) + cookie = msg['cookie'] + txid = _randomString() + req = { + 'q': funcName, + 'hash': hashlib.sha256(password + cookie).hexdigest(), + 'cookie': cookie, + 'args': args, + 'txid': txid + } + + if password: + req['aq'] = req['q'] + req['q'] = 'auth' + reqBenc = bencode(req) + req['hash'] = hashlib.sha256(reqBenc).hexdigest() + + reqBenc = bencode(req) + sock.send(reqBenc) + return _getMessage(session, txid) def _receiverThread(session): - """Receiving messages from cjdns admin server""" - - timeOfLastSend = time.time() - timeOfLastRecv = time.time() - try: - while True: - if (timeOfLastSend + KEEPALIVE_INTERVAL_SECONDS < time.time()): - if (timeOfLastRecv + 10 < time.time()): - raise Exception("ping timeout") - session.socket.send( - 'd1:q18:Admin_asyncEnabled4:txid8:keepalive') - timeOfLastSend = time.time() - - # Did we get data from the socket? - got_data = False - - while True: - # This can be interrupted and we need to loop it. - - try: - data = session.socket.recv(BUFFER_SIZE) - except (socket.timeout): - # Stop retrying, but note we have no data - break - except socket.error as e: - if e.errno != errno.EINTR: - # Forward errors that aren't being interrupted - raise - # Otherwise it was interrupted so we try again. - else: - # Don't try again, we got data - got_data = True - break - - if not got_data: - # Try asking again. - continue - - - try: - benc = bdecode(data) - except (KeyError, ValueError): - print("error decoding [" + data + "]") - continue - - if benc['txid'] == 'keepaliv': - if benc['asyncEnabled'] == 0: - raise Exception("lost session") - timeOfLastRecv = time.time() - else: - # print "putting to queue " + str(benc) - session.queue.put(benc) - - except KeyboardInterrupt: - print("interrupted") - import thread - thread.interrupt_main() - except Exception as e: - # Forward along any errors, before killing the thread. - session.queue.put(e) + """Receiving messages from cjdns admin server""" + + timeOfLastSend = time.time() + timeOfLastRecv = time.time() + try: + while True: + if (timeOfLastSend + KEEPALIVE_INTERVAL_SECONDS < time.time()): + if (timeOfLastRecv + 10 < time.time()): + raise Exception("ping timeout") + session.socket.send( + 'd1:q18:Admin_asyncEnabled4:txid8:keepalive') + timeOfLastSend = time.time() + + # Did we get data from the socket? + got_data = False + + while True: + # This can be interrupted and we need to loop it. + + try: + data = session.socket.recv(BUFFER_SIZE) + except (socket.timeout): + # Stop retrying, but note we have no data + break + except socket.error as e: + if e.errno != errno.EINTR: + # Forward errors that aren't being interrupted + raise + # Otherwise it was interrupted so we try again. + else: + # Don't try again, we got data + got_data = True + break + + if not got_data: + # Try asking again. + continue + + + try: + benc = bdecode(data) + except (KeyError, ValueError): + print("error decoding [" + data + "]") + continue + + if benc['txid'] == 'keepaliv': + if benc['asyncEnabled'] == 0: + raise Exception("lost session") + timeOfLastRecv = time.time() + else: + # print "putting to queue " + str(benc) + session.queue.put(benc) + + except KeyboardInterrupt: + print("interrupted") + import thread + thread.interrupt_main() + except Exception as e: + # Forward along any errors, before killing the thread. + session.queue.put(e) def _getMessage(session, txid): - """Getting message associated with txid""" - - while True: - if txid in session.messages: - msg = session.messages[txid] - del session.messages[txid] - return msg - else: - # print "getting from queue" - try: - # apparently any timeout at all allows the thread to be - # stopped but none make it unstoppable with ctrl+c - next = session.queue.get(timeout=100) - except Queue.Empty: - continue - - if isinstance(next, Exception): - # If the receiveing thread had an error, throw one here too. - raise next - - if 'txid' in next: - session.messages[next['txid']] = next - # print "adding message [" + str(next) + "]" - else: - print "message with no txid: " + str(next) - - -def _functionFabric(func_name, argList, oargList, password): - """Function fabric for Session class""" - - def functionHandler(self, *args, **kwargs): - call_args = {} - - for (key, value) in oargList.items(): - call_args[key] = value - - for i, arg in enumerate(argList): - if (i < len(args)): - call_args[arg] = args[i] - - for (key, value) in kwargs.items(): - call_args[key] = value - - return _callFunc(self, func_name, password, call_args) - - functionHandler.__name__ = func_name - return functionHandler + """Getting message associated with txid""" + + while True: + if txid in session.messages: + msg = session.messages[txid] + del session.messages[txid] + return msg + else: + # print "getting from queue" + try: + # apparently any timeout at all allows the thread to be + # stopped but none make it unstoppable with ctrl+c + next = session.queue.get(timeout=100) + except Queue.Empty: + continue + + if isinstance(next, Exception): + # If the receiveing thread had an error, throw one here too. + raise next + + if 'txid' in next: + session.messages[next['txid']] = next + # print "adding message [" + str(next) + "]" + else: + print "message with no txid: " + str(next) + + +def _functionFabric(func_name, argList, oargs, password): + """Function fabric for Session class""" + + def functionHandler(self, *args, **kwargs): + call_args = {} + + pos = 0 + for value in args: + if (pos < len(argList)): + call_args[argList[pos]] = value + pos += 1 + + for (key, value) in kwargs.items(): + if not key in oargs: + # probably a positional argument, given a keyword name + # that happens in python + if pos < len(argList) and argList[pos] == key: + call_args[argList[pos]] = value + pos += 1 + continue + else: + print("warning: not an argument to this function",func_name,key) + print(oargs) + # TODO: check oargs[key] type matches value + # warn, if doesn't + call_args[key] = value + + return _callFunc(self, func_name, password, call_args) + + functionHandler.__name__ = func_name + return functionHandler def connect(ipAddr, port, password): - """Connect to cjdns admin with this attributes""" - - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.connect((ipAddr, port)) - sock.settimeout(2) - - # Make sure it pongs. - sock.send('d1:q4:pinge') - data = sock.recv(BUFFER_SIZE) - if (not data.endswith('1:q4:ponge')): - raise Exception( - "Looks like " + ipAddr + ":" + str(port) + - " is to a non-cjdns socket.") - - # Get the functions and make the object - page = 0 - availableFunctions = {} - while True: - sock.send( - 'd1:q24:Admin_availableFunctions4:argsd4:pagei' + - str(page) + 'eee') - data = sock.recv(BUFFER_SIZE) - benc = bdecode(data) - for func in benc['availableFunctions']: - availableFunctions[func] = benc['availableFunctions'][func] - if (not 'more' in benc): - break - page = page+1 - - funcArgs = {} - funcOargs = {} - - for (i, func) in availableFunctions.items(): - items = func.items() - - # grab all the required args first - # append all the optional args - rargList = [arg for arg,atts in items if atts['required']] - argList = rargList + [arg for arg,atts in items if not atts['required']] - - # for each optional arg setup a default value with - # a type which will be ignored by the core. - oargList = {} - for (arg,atts) in items: - if not atts['required']: - oargList[arg] = ( - "''" if (func[arg]['type'] == 'Int') - else "0") - - setattr(Session, i, _functionFabric( - i, argList, oargList, password)) - - funcArgs[i] = rargList - funcOargs[i] = oargList - - session = Session(sock) - - kat = threading.Thread(target=_receiverThread, args=[session]) - kat.setDaemon(True) - kat.start() - - # Check our password. - ret = _callFunc(session, "ping", password, {}) - if ('error' in ret): - raise Exception( - "Connect failed, incorrect admin password?\n" + str(ret)) - - session._functions = "" - - funcOargs_c = {} - for func in funcOargs: - funcOargs_c[func] = list( - [key + "=" + str(value) - for (key, value) in funcOargs[func].items()]) - - for func in availableFunctions: - session._functions += ( - func + "(" + ', '.join(funcArgs[func] + funcOargs_c[func]) + ")\n") - - # print session.functions - return session + """Connect to cjdns admin with this attributes""" + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.connect((ipAddr, port)) + sock.settimeout(2) + + # Make sure it pongs. + sock.send('d1:q4:pinge') + data = sock.recv(BUFFER_SIZE) + if (not data.endswith('1:q4:ponge')): + raise Exception( + "Looks like " + ipAddr + ":" + str(port) + + " is to a non-cjdns socket.") + + # Get the functions and make the object + page = 0 + availableFunctions = {} + while True: + sock.send( + 'd1:q24:Admin_availableFunctions4:argsd4:pagei' + + str(page) + 'eee') + data = sock.recv(BUFFER_SIZE) + benc = bdecode(data) + for func in benc['availableFunctions']: + availableFunctions[func] = benc['availableFunctions'][func] + if (not 'more' in benc): + break + page = page+1 + + funcArgs = {} + funcOargs = {} + + for (i, func) in availableFunctions.items(): + items = func.items() + + # required args + argList = [] + # optional args + oargs = {} + + for (arg,atts) in items: + if atts['required']: + argList.append(arg) + else: + oargs[arg] = atts['type'] + + setattr(Session, i, _functionFabric( + i, argList, oargs, password)) + + funcArgs[i] = argList + funcOargs[i] = oargs + + session = Session(sock) + + kat = threading.Thread(target=_receiverThread, args=[session]) + kat.setDaemon(True) + kat.start() + + # Check our password. + ret = _callFunc(session, "ping", password, {}) + if ('error' in ret): + raise Exception( + "Connect failed, incorrect admin password?\n" + str(ret)) + + session._functions = "" + + funcOargs_c = {} + for func in funcOargs: + funcOargs_c[func] = list( + [key + "=" + str(value) + for (key, value) in funcOargs[func].items()]) + + for func in availableFunctions: + session._functions += ( + func + "(" + ', '.join(funcArgs[func] + funcOargs_c[func]) + ")\n") + + # print session.functions + return session def connectWithAdminInfo(path = None): - """Connect to cjdns admin with data from user file""" - - if path is None: - path = os.path.expanduser('~/.cjdnsadmin') - try: - with open(path, 'r') as adminInfo: - data = json.load(adminInfo) - except IOError: - sys.stderr.write("""Please create a file named .cjdnsadmin in your + """Connect to cjdns admin with data from user file""" + + if path is None: + path = os.path.expanduser('~/.cjdnsadmin') + try: + with open(path, 'r') as adminInfo: + data = json.load(adminInfo) + except IOError: + sys.stderr.write("""Please create a file named .cjdnsadmin in your home directory with ip, port, and password of your cjdns engine in json. for example: { - "addr": "127.0.0.1", - "port": 11234, - "password": "You tell me! (Search in ~/cjdroute.conf)" + "addr": "127.0.0.1", + "port": 11234, + "password": "You tell me! (Search in ~/cjdroute.conf)" } """) - raise + raise - return connect(data['addr'], data['port'], data['password']) + return connect(data['addr'], data['port'], data['password']) diff --git a/contrib/python/dynamicEndpoints.py b/contrib/python/dynamicEndpoints.py index 5fb271221..a54faf2b6 100755 --- a/contrib/python/dynamicEndpoints.py +++ b/contrib/python/dynamicEndpoints.py @@ -114,11 +114,6 @@ def __init__(self, cjdns, configuration): # unresponsive. self.unresponsive = dict() - # Holds a cjdns log message subscription to messages about unresponsive - # nodes. - self.sub = self.cjdns.AdminLog_subscribe(MESSAGE_LINE, MESSAGE_FILE, - 'DEBUG') - # Add nodes from the given ConfigParser parser. for section in configuration.sections(): # Each section is named with a node key, and contains a @@ -130,16 +125,21 @@ def __init__(self, cjdns, configuration): # Add the node self.addNode(peerHostname, peerPort, peerPassword, section) + # Add all the nodes we're supposed to watch. + for node in self.nodes.values(): + self.lookup(node) + logging.info("{} peers added!".format(len(self.nodes))) + # Holds a cjdns log message subscription to messages about unresponsive + # nodes. + self.sub = self.cjdns.AdminLog_subscribe(MESSAGE_LINE, MESSAGE_FILE, + 'DEBUG') + if self.sub['error'] == 'none': # We successfully subscribed to messages. # When we die, try to unsubscribe atexit.register(self.stop) - # Add all the nodes we're supposed to watch. - for node in self.nodes.values(): - self.lookup(node) - logging.info("{} peers added!".format(len(self.nodes))) else: logging.error(self.sub) From 693745515e1db9d0291ac8a6f1c64ef56fc0a9f5 Mon Sep 17 00:00:00 2001 From: Cy Date: Sun, 17 Apr 2016 09:49:52 -0700 Subject: [PATCH 03/12] tools/* using script supporting dynamic peers --- contrib/nodejs/dynamic-peers.js | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 contrib/nodejs/dynamic-peers.js diff --git a/contrib/nodejs/dynamic-peers.js b/contrib/nodejs/dynamic-peers.js new file mode 100644 index 000000000..404e9ff3b --- /dev/null +++ b/contrib/nodejs/dynamic-peers.js @@ -0,0 +1,90 @@ +var path = require('path'); +var os = require('os'); +var fs = require('fs'); +var dns = require('dns'); + +var Cjdns = require( + path.join(os.homedir(),"packages","git","cjdns","tools", + "lib","cjdnsadmin","cjdnsadmin")); + +function ready(nodes) { + Cjdns.connectWithAdminInfo(function(cjdns) { + function link_up(publicKey,address,password) { + cjdns.UDPInterface_beginConnection(publicKey, + address, + password, + function() { + console.log("linked", + publicKey); + } + ); + } + function lookup(node) { + if(node.name) { + dns.resolve(node.name, + function(err, addresses) { + if(err) { throw err; } + link_up(node.publicKey, + addresses[0], + node.password); + // no need to wait for callback? + }); + } else { + link_up(node.publicKey, + node.address, + node.password); + } + } + + + function collectPeers(then) { + var peers = {}; + var count = 0; + function again(i) { + cjdns.InterfaceController_peerStats( + i,function (err, ret) { + if (err) { throw err; } + ret.peers.forEach(function (peer) { + if(peer.state != 'UNRESPONSIVE') { + if(!(peer.publicKey in peers)) + ++count; + peers[peer.publicKey] = peer; + } + }); + if (typeof(ret.more) !== 'undefined') { + again(i+1); + } else { + then(peers,count); + setTimeout(function() { + collectPeers(then); + },10000); + } + }); + } + again(0); + } + + for(var key in nodes) { + var node = nodes[key]; + node.publicKey = key; + } + collectPeers(function(peers,npeers) { + console.log("checking",npeers,"peers") + for(var key in nodes) { + if(key in peers) continue; + console.log("Peer not found",key,"poking."); + lookup(nodes[key]); + } + }); + }); +} + +var conf_name = process.env.conf; +if(!conf_name) conf_name = "cjdns_dynamic.conf"; + +fs.readFile( + path.join(os.homedir(),".config",conf_name), + function(err, data) { + if(err) { throw err; } + ready(JSON.parse(data)); + }); From 4ddf7ecb7090d93fb159e8adcfbea11c7cae5fcf Mon Sep 17 00:00:00 2001 From: user Date: Sun, 17 Apr 2016 17:17:41 +0000 Subject: [PATCH 04/12] make it more robust with promises --- contrib/nodejs/dynamic-peers.js | 90 ----------------------- contrib/nodejs/dynamic-peers/package.json | 7 ++ 2 files changed, 7 insertions(+), 90 deletions(-) delete mode 100644 contrib/nodejs/dynamic-peers.js create mode 100644 contrib/nodejs/dynamic-peers/package.json diff --git a/contrib/nodejs/dynamic-peers.js b/contrib/nodejs/dynamic-peers.js deleted file mode 100644 index 404e9ff3b..000000000 --- a/contrib/nodejs/dynamic-peers.js +++ /dev/null @@ -1,90 +0,0 @@ -var path = require('path'); -var os = require('os'); -var fs = require('fs'); -var dns = require('dns'); - -var Cjdns = require( - path.join(os.homedir(),"packages","git","cjdns","tools", - "lib","cjdnsadmin","cjdnsadmin")); - -function ready(nodes) { - Cjdns.connectWithAdminInfo(function(cjdns) { - function link_up(publicKey,address,password) { - cjdns.UDPInterface_beginConnection(publicKey, - address, - password, - function() { - console.log("linked", - publicKey); - } - ); - } - function lookup(node) { - if(node.name) { - dns.resolve(node.name, - function(err, addresses) { - if(err) { throw err; } - link_up(node.publicKey, - addresses[0], - node.password); - // no need to wait for callback? - }); - } else { - link_up(node.publicKey, - node.address, - node.password); - } - } - - - function collectPeers(then) { - var peers = {}; - var count = 0; - function again(i) { - cjdns.InterfaceController_peerStats( - i,function (err, ret) { - if (err) { throw err; } - ret.peers.forEach(function (peer) { - if(peer.state != 'UNRESPONSIVE') { - if(!(peer.publicKey in peers)) - ++count; - peers[peer.publicKey] = peer; - } - }); - if (typeof(ret.more) !== 'undefined') { - again(i+1); - } else { - then(peers,count); - setTimeout(function() { - collectPeers(then); - },10000); - } - }); - } - again(0); - } - - for(var key in nodes) { - var node = nodes[key]; - node.publicKey = key; - } - collectPeers(function(peers,npeers) { - console.log("checking",npeers,"peers") - for(var key in nodes) { - if(key in peers) continue; - console.log("Peer not found",key,"poking."); - lookup(nodes[key]); - } - }); - }); -} - -var conf_name = process.env.conf; -if(!conf_name) conf_name = "cjdns_dynamic.conf"; - -fs.readFile( - path.join(os.homedir(),".config",conf_name), - function(err, data) { - if(err) { throw err; } - ready(JSON.parse(data)); - }); diff --git a/contrib/nodejs/dynamic-peers/package.json b/contrib/nodejs/dynamic-peers/package.json new file mode 100644 index 000000000..13d065726 --- /dev/null +++ b/contrib/nodejs/dynamic-peers/package.json @@ -0,0 +1,7 @@ +{ + "name": "cjdns-dynamic-peers", + "version": "0.1.1", + "dependencies": { + "cjdns": "~0.17.2" + } +} From 53f518b7ab5bb5a1073228822cc752ff46ce4dcd Mon Sep 17 00:00:00 2001 From: user Date: Sun, 17 Apr 2016 19:22:00 +0000 Subject: [PATCH 05/12] nah, that promise library is too big --- contrib/nodejs/dynamic-peers/lib/promise.js | 103 ++++++++++++++++++++ contrib/nodejs/dynamic-peers/package.json | 2 +- 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 contrib/nodejs/dynamic-peers/lib/promise.js diff --git a/contrib/nodejs/dynamic-peers/lib/promise.js b/contrib/nodejs/dynamic-peers/lib/promise.js new file mode 100644 index 000000000..6a3d9f414 --- /dev/null +++ b/contrib/nodejs/dynamic-peers/lib/promise.js @@ -0,0 +1,103 @@ +// because depending on acorn is dumb + +var sentinel = "unset"; + +var Promise = function(callback) { + var result = sentinel; + var err = sentinel; + var stages = []; + function doit(res,mode) { + if(result !== sentinel) { + throw new Error("Don't call this twice"); + } + // don't you love tail recursion? + if(mode === ACCEPT) { + result = res; + } else { + err = res; + } + while(stages) { + var stage = stages[0]; + try { + if(mode === ACCEPT) { + res = stage.ok(res); + stages.pop(); + } else { + res = stage.err(err,res); + mode = ACCEPT; + stages.pop(); + } + } catch(e) { + mode = REJECT; + err = e; + stages.pop(); + } + } + } + function accept(res) { + return doit(res,ACCEPT); + } + function reject(e) { + return doit(res,REJECT); + } + return { + then: function(ok,notok) { + // if already set, don't bother with stages + if(err !== sentinel && notok) { + notok(err,result); + return this; + } + if(result !== sentinel) { + ok(result); + return this; + } + var stage = { + ok: ok, + err: notok + }; + stages.push(stage); + return this; + }, + catch: function(notok) { + return this.then(undefined,notok); + } + } +} + +module.exports = Promise; + +Promise.nodeify = function(fn) { + var that = this; + var args = Array.prototype.slice.call(arguments, 1); + return new Promise(function(accept,reject) { + args.push(function(err, res) { + if(err) reject(err); + else accept(res); + }); + try { fn.apply(that, args); } + catch(e) { + reject(e); + } + }); +} + +Promise.all = function(promises) { + return new Promise(function(accept,reject) { + var results = new Array(promises.length); + var i = 0; + var count = 0; + for(var promise of promises) { + ++count; + promise.then(function(res) { + results[i] = res; + if(count == 0) { + accept(results); + } else { + --count; + } + },function(err) { + reject(err); + }); + } + }); +} diff --git a/contrib/nodejs/dynamic-peers/package.json b/contrib/nodejs/dynamic-peers/package.json index 13d065726..86ab47795 100644 --- a/contrib/nodejs/dynamic-peers/package.json +++ b/contrib/nodejs/dynamic-peers/package.json @@ -2,6 +2,6 @@ "name": "cjdns-dynamic-peers", "version": "0.1.1", "dependencies": { - "cjdns": "~0.17.2" + "cjdns": "~0.17.2", } } From cb98f9e17ff2909224020872e0679d3c8016252b Mon Sep 17 00:00:00 2001 From: user Date: Sun, 17 Apr 2016 19:31:17 +0000 Subject: [PATCH 06/12] move script into place, test promises --- .../nodejs/dynamic-peers/lib/dynamic-peers.js | 87 +++++++++++++++++++ contrib/nodejs/dynamic-peers/lib/promise.js | 2 +- .../nodejs/dynamic-peers/lib/test-promise.js | 15 ++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 contrib/nodejs/dynamic-peers/lib/dynamic-peers.js create mode 100644 contrib/nodejs/dynamic-peers/lib/test-promise.js diff --git a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js new file mode 100644 index 000000000..34e529148 --- /dev/null +++ b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js @@ -0,0 +1,87 @@ +var path = require('path'); +var os = require('os'); +var fs = require('fs'); +var dns = require('dns'); +var Promise = require('./promise') + +var readFile = Promise.wrap(fs.readFile); +var resolve = Promise.wrap(dns.resolve); + +readFile +(path.join(os.homedir(),".config",conf_name)) +.then(JSON.parse) +.then(function (nodes) { + Cjdns.connectWithAdminInfo(function(cjdns) { + const peerStats = + Promise.denodify(cjdns.InterfaceController_peerStats); + + function link_up(node) { + cjdns.UDPInterface_beginConnection(node.publicKey, + node.address, + node.password, + function() { + console.log("linked", + publicKey); + } + ); + } + function lookup(node) { + if(node.name) { + resolve(node.name) + .then(function (addresses) { + node.address = addresses[0]; + link_up(node); + // no need to wait for callback? + },function(err) { + print(err); + link_up(node) + }); + } else { + link_up(node); + } + } + + function collectPeers(then) { + var peers = {}; + var count = 0; + function again(i) { + peerStats(i).then(function(ret) { + ret.peers.forEach(function (peer) { + if(peer.state != 'UNRESPONSIVE') { + if(!(peer.publicKey in peers)) + ++count; + peers[peer.publicKey] = peer; + } + }); + if (typeof(ret.more) !== 'undefined') { + again(i+1); + } else { + then(peers,count); + setTimeout(function() { + collectPeers(then); + },10000); + } + }); + } + again(0); + } + + for(var key in nodes) { + var node = nodes[key]; + node.publicKey = key; + } + collectPeers(function(peers,npeers) { + console.log("checking",npeers,"peers") + for(var key in nodes) { + if(key in peers) continue; + console.log("Peer not found",key,"poking."); + lookup(nodes[key]); + } + }); + }); +} + +var conf_name = process.env.conf; +if(!conf_name) conf_name = "cjdns_dynamic.conf"; + + diff --git a/contrib/nodejs/dynamic-peers/lib/promise.js b/contrib/nodejs/dynamic-peers/lib/promise.js index 6a3d9f414..d41e35e7f 100644 --- a/contrib/nodejs/dynamic-peers/lib/promise.js +++ b/contrib/nodejs/dynamic-peers/lib/promise.js @@ -66,7 +66,7 @@ var Promise = function(callback) { module.exports = Promise; -Promise.nodeify = function(fn) { +Promise.wrap = function(fn) { var that = this; var args = Array.prototype.slice.call(arguments, 1); return new Promise(function(accept,reject) { diff --git a/contrib/nodejs/dynamic-peers/lib/test-promise.js b/contrib/nodejs/dynamic-peers/lib/test-promise.js new file mode 100644 index 000000000..9a7d84ddf --- /dev/null +++ b/contrib/nodejs/dynamic-peers/lib/test-promise.js @@ -0,0 +1,15 @@ +var Promise = require('./promise'); +var fs = require('fs'); + +var p = new Promise(function(accept, reject) { + setTimeout(function() { + accept(0); + },1000); +}); + +p.then(function(i) { + return i + 19; +}).then(function(i) { + return i + 23; +}); + From 067a4167497d2fd216e3b07f4f3b9e6f14a5b542 Mon Sep 17 00:00:00 2001 From: user Date: Sun, 17 Apr 2016 19:44:40 +0000 Subject: [PATCH 07/12] various test-prompted fixes --- contrib/nodejs/dynamic-peers/lib/promise.js | 18 ++++++-- .../nodejs/dynamic-peers/lib/test-promise.js | 41 +++++++++++++++---- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/contrib/nodejs/dynamic-peers/lib/promise.js b/contrib/nodejs/dynamic-peers/lib/promise.js index d41e35e7f..cc820e164 100644 --- a/contrib/nodejs/dynamic-peers/lib/promise.js +++ b/contrib/nodejs/dynamic-peers/lib/promise.js @@ -2,6 +2,9 @@ var sentinel = "unset"; +const ACCEPT = true; +const REJECT = false; + var Promise = function(callback) { var result = sentinel; var err = sentinel; @@ -16,21 +19,23 @@ var Promise = function(callback) { } else { err = res; } - while(stages) { + while(stages.length > 0) { var stage = stages[0]; try { if(mode === ACCEPT) { res = stage.ok(res); - stages.pop(); + stages.shift(); } else { res = stage.err(err,res); mode = ACCEPT; - stages.pop(); + stages.shift(); } } catch(e) { mode = REJECT; err = e; - stages.pop(); + stages.shift(); + if(!stages.length) + throw e; } } } @@ -40,6 +45,7 @@ var Promise = function(callback) { function reject(e) { return doit(res,REJECT); } + callback(accept,reject); return { then: function(ok,notok) { // if already set, don't bother with stages @@ -101,3 +107,7 @@ Promise.all = function(promises) { } }); } + +Promise.success = new Promise(function(accept,reject) { + accept(true); +}); diff --git a/contrib/nodejs/dynamic-peers/lib/test-promise.js b/contrib/nodejs/dynamic-peers/lib/test-promise.js index 9a7d84ddf..e8e71e70d 100644 --- a/contrib/nodejs/dynamic-peers/lib/test-promise.js +++ b/contrib/nodejs/dynamic-peers/lib/test-promise.js @@ -1,15 +1,42 @@ var Promise = require('./promise'); var fs = require('fs'); -var p = new Promise(function(accept, reject) { - setTimeout(function() { - accept(0); - },1000); -}); +function dummyPromise() { + return new Promise(function(accept, reject) { + setTimeout(function() { + accept(0); + },1); + }); +} +var p = dummyPromise(); -p.then(function(i) { +p.then(function herp(i) { return i + 19; -}).then(function(i) { +}).then(function derp(i) { return i + 23; +}).then(function done(i) { + if(i != 42) { + throw new Error("Fail"); + } + dummyPromise() + .then(function(i) { + throw new Error("failure"); + },function(e) { + console.log("should never get here!"); + }) + .then(function(i) { + console.log("or here!"); + },function(e) { + console.log("got error",e); + throw new Error("something else went wrong"); + }) + .catch(function(e) { + console.log("we caught e",e); + return 42; + }) + .then(function(res) { + if(42 != res) throw new Error("fail..."); + }); }); + From b3fbe07845d64373a96a40fc908515cf6e74739b Mon Sep 17 00:00:00 2001 From: user Date: Sun, 17 Apr 2016 19:51:57 +0000 Subject: [PATCH 08/12] seems to be working --- .../nodejs/dynamic-peers/lib/dynamic-peers.js | 8 ++--- contrib/nodejs/dynamic-peers/lib/promise.js | 31 ++++++++++++------- .../nodejs/dynamic-peers/lib/test-promise.js | 10 +++--- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js index 34e529148..22afa1adb 100644 --- a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js +++ b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js @@ -7,6 +7,9 @@ var Promise = require('./promise') var readFile = Promise.wrap(fs.readFile); var resolve = Promise.wrap(dns.resolve); +var conf_name = process.env.conf; +if(!conf_name) conf_name = "cjdns_dynamic.conf"; + readFile (path.join(os.homedir(),".config",conf_name)) .then(JSON.parse) @@ -79,9 +82,6 @@ readFile } }); }); -} - -var conf_name = process.env.conf; -if(!conf_name) conf_name = "cjdns_dynamic.conf"; +}); diff --git a/contrib/nodejs/dynamic-peers/lib/promise.js b/contrib/nodejs/dynamic-peers/lib/promise.js index cc820e164..e0858719c 100644 --- a/contrib/nodejs/dynamic-peers/lib/promise.js +++ b/contrib/nodejs/dynamic-peers/lib/promise.js @@ -26,6 +26,11 @@ var Promise = function(callback) { res = stage.ok(res); stages.shift(); } else { + while(!stage.err) { + if(stages.length == 0) + throw err; + stage = stages.shift(); + } res = stage.err(err,res); mode = ACCEPT; stages.shift(); @@ -43,7 +48,7 @@ var Promise = function(callback) { return doit(res,ACCEPT); } function reject(e) { - return doit(res,REJECT); + return doit(e,REJECT); } callback(accept,reject); return { @@ -73,18 +78,20 @@ var Promise = function(callback) { module.exports = Promise; Promise.wrap = function(fn) { - var that = this; - var args = Array.prototype.slice.call(arguments, 1); - return new Promise(function(accept,reject) { - args.push(function(err, res) { - if(err) reject(err); - else accept(res); + return function() { + var that = this; + var args = Array.prototype.slice.call(arguments); + return new Promise(function(accept,reject) { + args.push(function(err, res) { + if(err) reject(err); + else accept(res); + }); + try { fn.apply(that, args); } + catch(e) { + reject(e); + } }); - try { fn.apply(that, args); } - catch(e) { - reject(e); - } - }); + } } Promise.all = function(promises) { diff --git a/contrib/nodejs/dynamic-peers/lib/test-promise.js b/contrib/nodejs/dynamic-peers/lib/test-promise.js index e8e71e70d..170bfe6f6 100644 --- a/contrib/nodejs/dynamic-peers/lib/test-promise.js +++ b/contrib/nodejs/dynamic-peers/lib/test-promise.js @@ -27,16 +27,16 @@ p.then(function herp(i) { .then(function(i) { console.log("or here!"); },function(e) { - console.log("got error",e); + //console.log("got error",e); throw new Error("something else went wrong"); }) .catch(function(e) { - console.log("we caught e",e); + //console.log("we caught e",e); return 42; }) - .then(function(res) { - if(42 != res) throw new Error("fail..."); - }); + .then(function(res) { + if(42 != res) throw new Error("fail..."); + }); }); From aad744c04a5712da42b75c83bf1df6427a2bd55b Mon Sep 17 00:00:00 2001 From: user Date: Sun, 17 Apr 2016 19:59:21 +0000 Subject: [PATCH 09/12] picky JSON --- contrib/nodejs/dynamic-peers/lib/dynamic-peers.js | 4 +++- contrib/nodejs/dynamic-peers/package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js index 22afa1adb..7f47a6409 100644 --- a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js +++ b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js @@ -2,7 +2,9 @@ var path = require('path'); var os = require('os'); var fs = require('fs'); var dns = require('dns'); -var Promise = require('./promise') +var Promise = require('./promise'); + +var Cjdns = require('cjdns'); var readFile = Promise.wrap(fs.readFile); var resolve = Promise.wrap(dns.resolve); diff --git a/contrib/nodejs/dynamic-peers/package.json b/contrib/nodejs/dynamic-peers/package.json index 86ab47795..13d065726 100644 --- a/contrib/nodejs/dynamic-peers/package.json +++ b/contrib/nodejs/dynamic-peers/package.json @@ -2,6 +2,6 @@ "name": "cjdns-dynamic-peers", "version": "0.1.1", "dependencies": { - "cjdns": "~0.17.2", + "cjdns": "~0.17.2" } } From 5e82b3618589f129f6011d20c803f9e0c285eedf Mon Sep 17 00:00:00 2001 From: user Date: Sun, 17 Apr 2016 20:06:13 +0000 Subject: [PATCH 10/12] Piccayunish nodejs module search path --- contrib/nodejs/dynamic-peers/lib/cjdns | 1 + contrib/nodejs/dynamic-peers/lib/dynamic-peers.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 120000 contrib/nodejs/dynamic-peers/lib/cjdns diff --git a/contrib/nodejs/dynamic-peers/lib/cjdns b/contrib/nodejs/dynamic-peers/lib/cjdns new file mode 120000 index 000000000..4e9c59a58 --- /dev/null +++ b/contrib/nodejs/dynamic-peers/lib/cjdns @@ -0,0 +1 @@ +../../../../tools/lib \ No newline at end of file diff --git a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js index 7f47a6409..9abcb9a3a 100644 --- a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js +++ b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js @@ -4,7 +4,7 @@ var fs = require('fs'); var dns = require('dns'); var Promise = require('./promise'); -var Cjdns = require('cjdns'); +var Cjdns = require('./cjdns/cjdnsadmin/cjdnsadmin'); var readFile = Promise.wrap(fs.readFile); var resolve = Promise.wrap(dns.resolve); From f0308fe5bc4d841e690261d29e85effa8271aef4 Mon Sep 17 00:00:00 2001 From: user Date: Mon, 18 Apr 2016 00:10:07 +0000 Subject: [PATCH 11/12] oh, no port specified, duh --- .../nodejs/dynamic-peers/lib/dynamic-peers.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js index 9abcb9a3a..a5ed65443 100644 --- a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js +++ b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js @@ -16,17 +16,26 @@ readFile (path.join(os.homedir(),".config",conf_name)) .then(JSON.parse) .then(function (nodes) { + console.log("got nodes"); Cjdns.connectWithAdminInfo(function(cjdns) { + console.log("connected"); const peerStats = - Promise.denodify(cjdns.InterfaceController_peerStats); + Promise.wrap(cjdns.InterfaceController_peerStats); function link_up(node) { + console.log("link up",node.publicKey, + node.address, node.port, + node.password); + // can't attach port until the DNS lookup + var address = node.address + ":" + node.port; cjdns.UDPInterface_beginConnection(node.publicKey, - node.address, + address, node.password, function() { - console.log("linked", - publicKey); + console.log( + "linked", + node.address, + node.publicKey); } ); } From b704922356fab25313355751c6b2cdbf81bc43af Mon Sep 17 00:00:00 2001 From: user Date: Mon, 18 Apr 2016 00:23:25 +0000 Subject: [PATCH 12/12] cleaning up debugging/logging --- contrib/nodejs/dynamic-peers/lib/dynamic-peers.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js index a5ed65443..ecf76f19f 100644 --- a/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js +++ b/contrib/nodejs/dynamic-peers/lib/dynamic-peers.js @@ -23,19 +23,13 @@ readFile Promise.wrap(cjdns.InterfaceController_peerStats); function link_up(node) { - console.log("link up",node.publicKey, - node.address, node.port, - node.password); // can't attach port until the DNS lookup var address = node.address + ":" + node.port; + console.log("link up", address, node.publicKey); cjdns.UDPInterface_beginConnection(node.publicKey, address, node.password, function() { - console.log( - "linked", - node.address, - node.publicKey); } ); }