UNPKG

toxcore

Version:
365 lines (326 loc) 10.5 kB
/* * This file is part of node-toxcore. * * node-toxcore is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * node-toxcore is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with node-toxcore. If not, see <http://www.gnu.org/licenses/>. * */ var buffertools = require('buffertools'); var dns = require('dns'); var ref = require('ref'); var ffi = require('ffi'); var path = require('path'); var util = require('util'); var _ = require('underscore'); var consts = require(path.join(__dirname, 'consts')); var errors = require(path.join(__dirname, 'errors')); var _util = require(path.join(__dirname, 'util')); buffertools.extend(); // No idea if there's actually a max size, this should be good enough var TOXDNS3_RECORD_SIZE_MAX = 256; // Public keys var TOXDNS_PUBKEY_TOXME_IO = '1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25'; // Common types var UInt8Ptr = ref.refType('uint8'); var UInt32Ptr = ref.refType('uint32'); var VoidPtr = ref.refType('void'); /** * Creates a ToxDns instance. * @class * @param {Object} [opts] Options * @param {String} [opts.path] Path to libtoxdns.so * @param {(Buffer|String)} [opts.key] Public key of ToxDns3 service */ var ToxDns = function(opts) { if(!opts) opts = {}; var libpath = opts['path']; var key = opts['key']; this._library = this._createLibrary(libpath); this._initKey(key); this._initHandle(this._key); }; /** * Get the internal Library instance. * @return {ffi.Library} */ ToxDns.prototype.getLibrary = function() { return this._library; } /** * Create the libtoxdns Library instance (libtoxdns). * @private * @param {String} [libpath='libtoxdns'] - Path to libtoxdns * @return {ffi.Library} */ ToxDns.prototype._createLibrary = function(libpath) { libpath = libpath || 'libtoxdns'; return ffi.Library(libpath, { 'tox_dns3_new': [ VoidPtr, [ UInt8Ptr ] ], 'tox_dns3_kill': [ 'void', [ VoidPtr ] ], 'tox_generate_dns3_string': [ 'int', [ VoidPtr, UInt8Ptr, 'uint16', UInt32Ptr, UInt8Ptr, 'uint8' ] ], 'tox_decrypt_dns3_TXT': [ 'int', [ VoidPtr, UInt8Ptr, UInt8Ptr, 'uint32', 'uint32' ] ] }); }; /** * Format the string which is sent in the TXT request. * @private * @param {String} generated - Generated output * @param {String} domain - Domain from the ToxDns address * @return {String} Formatted string to send */ ToxDns.prototype._format = function(generated, domain) { return util.format('_%s._tox.%s', generated, domain); }; /** * Parse a ToxDns address, returning an object with two properties: 'name' * (before @) and 'domain' (after @). If not recognized as an address, * undefined is returned. * @private * @param {String} address - Address string to parse * @return {Object} Object with a 'name' and a 'domain', or undefined if not * the given address could not be parsed */ ToxDns.prototype._parse = function(address) { if(address.indexOf('@') !== -1) { var split = address.split('@'); return { name: split[0], domain: split[1] }; } //} else if(this.hasDomain()) { // return { name: address, domain: this.getDomain() }; //} }; /** * Resolves a toxdns address using toxdns v3. Assumes the domain name in the * given address uses the public key set in the ToxDns constructor. * @param {String} address - ToxDns address * @param {ToxDns~dataCallback} [callback] */ ToxDns.prototype.resolve = function(address, callback) { var toxdns = this, format = ToxDns.prototype._format.bind(this), addr = this._parse(address); if(!addr) { if(callback) { callback(new Error('Not a toxdns address')); } return; } this.generate(addr.name, function(err, info) { if(!err) { var full = format(info.record, addr.domain); dns.resolveTxt(full, function(err, txts) { if(!err) { var result = txts[0][0], id = result.match(/(^|;)id=([a-zA-Z0-9]+)/)[2]; toxdns.decrypt(id, info.id, function(err, addr) { if(!err) { if(callback) { callback(undefined, addr); } } else if(callback) { callback(err); } }); } else if(callback) { callback(err); } }); } else if(callback) { callback(err); } }); }; /** * Same as ToxDns#resolve(), but returns the Tox address * as a hex string via the callback. * @param {String} address - ToxDns address * @param {ToxDns~stringCallback} [callback] */ ToxDns.prototype.resolveHex = function(address, callback) { _util.hexify(this.resolve.bind(this, address), callback); }; /** * Get the public key. * @return {Buffer} Public key */ ToxDns.prototype.getKey = function() { return this._key; }; /** * Get the public key as a hex string. * @return {String} Public key as a hex string */ ToxDns.prototype.getKeyHex = function() { return this._key.toHex().toString().toUpperCase(); }; /** * Initialize the public key. * @private * @param {(Buffer|String)} key - Public key */ ToxDns.prototype._initKey = function(key) { if(!key) key = TOXDNS_PUBKEY_TOXME_IO; // Use toxme.io public key by default // If key is a String, assume a hex String if(_.isString(key)) key = new Buffer(key).fromHex(); this._key = key; }; /** * Get the handle object. * @return {Object} */ ToxDns.prototype.getHandle = function() { return this._handle; } /** * Whether or not this ToxDns instance has a handle. * @return {Boolean} true if handle, false if not */ ToxDns.prototype.hasHandle = function() { return !!this.getHandle(); }; /** * Synchronous tox_dns3_new(3). * Initializes the handle for this ToxDns instance. * @private * @param {Buffer} buffer - Server's public key */ ToxDns.prototype._initHandle = function(buffer) { if(buffer) { this._handle = this.getLibrary().tox_dns3_new(buffer); } }; /** * Asynchronous tox_dns3_kill(3). * @param {ToxDns~errorCallback} [callback] */ ToxDns.prototype.kill = function(callback) { var toxdns = this; this.getLibrary().tox_dns3_kill.async(this.getHandle(), function(err) { if(!err) { toxdns._handle = undefined; } if(callback) { callback(err); } }); }; /** * Synchronous tox_dns3_kill(3). */ ToxDns.prototype.killSync = function() { this.getLibrary().tox_dns3_kill(this.getHandle()); this._handle = undefined; }; /** * Asynchronous tox_generate_dns3_string(3). * @param {String} name - Name to generate a request string for * @param {ToxDns~generateCallback} [callback] */ ToxDns.prototype.generate = function(name, callback) { var namebuf = new Buffer(name), outbuf = new Buffer(TOXDNS3_RECORD_SIZE_MAX), requestId = ref.alloc('uint32'); this.getLibrary().tox_generate_dns3_string.async( this.getHandle(), outbuf, outbuf.length, requestId, namebuf, namebuf.length, function(err, res) { if(!err && res < 0) { //err = createNegativeReturnError('tox_generate_dns3_string', res); // TODO: Better errors err = new Error('tox_generate_dns3_string returned ' + res); } var str, id; if(!err) { str = outbuf.slice(0, res).toString(); id = Number(requestId.deref()); } if(callback) { callback(err, { record: str, id: id }); } }); }; /** * Synchronous tox_generate_dns3_string(3). * @param {String} name - Name to generate a request string for * @return {Object} Object containing an 'id' (Number) and a generated * 'record' (String) */ ToxDns.prototype.generateSync = function(name) { var namebuf = new Buffer(name), outbuf = new Buffer(TOXDNS3_RECORD_SIZE_MAX), requestId = ref.alloc('uint32'), length = this.getLibrary().tox_generate_dns3_string( this.getHandle(), outbuf, outbuf.length, requestId, namebuf, namebuf.length); if(length < 0) throw new Error('tox_generate_dns3_string returned ' + length); var str = outbuf.slice(0, length).toString(), id = Number(requestId.deref()); return { record: str, id: id }; }; /** * Asynchronous tox_decrypt_dns3_TXT(3). * @param {String} record * @param {Number} requestId * @param {ToxDns~dataCallback} [callback] */ ToxDns.prototype.decrypt = function(record, requestId, callback) { var toxId = new Buffer(consts.TOX_FRIEND_ADDRESS_SIZE), recordBuffer = new Buffer(record); this.getLibrary().tox_decrypt_dns3_TXT.async( this.getHandle(), toxId, recordBuffer, recordBuffer.length, requestId, function(err, res) { if(!err && res !== 0) { //err = createNonZeroReturnError('tox_decrypt_dns3_TXT', res); err = new Error('tox_decrypt_dns3_TXT returned ' + res); } if(callback) { callback(err, toxId); } }); }; /** * Asynchronous tox_decrypt_dns3_TXT(3). * @param {String} record * @param {Number} requestId * @return {Buffer} Tox address */ ToxDns.prototype.decryptSync = function(record, requestId) { var toxId = new Buffer(consts.TOX_FRIEND_ADDRESS_SIZE), recordBuffer = new Buffer(record), success = this.getLibrary().tox_decrypt_dns3_TXT( this.getHandle(), toxId, recordBuffer, recordBuffer.length, requestId); if(success !== 0) throw new Error('tox_decrypt_dns3_TXT returned ' + success); return toxId; }; /** * Callback that returns some error, if any. * @callback ToxDns~errorCallback * @param {Error} error - error, if any */ /** * Callback that returns some data in a Buffer. * @callback ToxDns~dataCallback * @param {Error} error - error, if any * @param {Buffer} data */ /** * Callback that returns an Object with a record and id. * @callback ToxDns~generateCallback * @param {Error} error - error, if any * @param {Object} object - Generate object * @param {String} object.record - String sent in the DNS TXT request * @param {Number} object.id - Id required when decrypting results */ /** * Callback that returns some string. * @callback ToxDns~stringCallback * @param {Error} error - error, if any * @param {String} value */ module.exports = ToxDns;