#kiwi.theme_relaxed .server_select .basic tr.have_key { font-size:0.8em; }
#kiwi.theme_relaxed .server_select .basic tr.channel td { padding-top:1em; }
#kiwi.theme_relaxed .server_select .basic { border-bottom: 1px dashed gray; margin-bottom:1em; }
-#kiwi.theme_relaxed .server_select .basic .show_more { display: block; width:110px; margin:10px 0 0 0; font-size:0.8em; background: url(../img/more.png) no-repeat right 7px; }
+#kiwi.theme_relaxed .server_select .basic .show_more { display: block; width:110px; margin:10px 0 0 0; font-size:0.8em; }
#kiwi.theme_relaxed .server_select.single_server .basic { border:none; }
#kiwi.theme_relaxed .server_select .status { text-align: center; font-weight: bold; padding:1em; }
#kiwi.theme_relaxed .server_select .status.ok { }
#kiwi.theme_cli .server_select .basic tr.have_pass { font-size:0.8em; }
#kiwi.theme_cli .server_select .basic tr.channel td { padding-top:1em; }
#kiwi.theme_cli .server_select .basic { border-bottom: 1px dashed gray; margin-bottom:1em; }
-#kiwi.theme_cli .server_select .basic .show_more { display: block; width:116px; margin:10px 0 0 0; font-size:0.8em; background: url(../img/more.png) no-repeat right 7px; }
+#kiwi.theme_cli .server_select .basic .show_more { display: block; width:116px; margin:10px 0 0 0; font-size:0.8em; }
#kiwi.theme_cli .server_select.single_server .basic { border:none; }
#kiwi.theme_cli .server_select .status { text-align: center; font-weight: bold; padding:1em; }
#kiwi.theme_cli .server_select .status.ok { }
#kiwi.theme_basic .server_select .basic tr.have_pass { font-size:0.8em; }
#kiwi.theme_basic .server_select .basic tr.channel td { padding-top:1em; }
#kiwi.theme_basic .server_select .basic { border-bottom: 1px dashed gray; margin-bottom:1em; }
-#kiwi.theme_basic .server_select .basic .show_more { display: block; width:110px; margin:10px 0 0 0; font-size:0.8em; background: url(../img/more.png) no-repeat right 7px; }
+#kiwi.theme_basic .server_select .basic .show_more { display: block; width:110px; margin:10px 0 0 0; font-size:0.8em; }
#kiwi.theme_basic .server_select.single_server .basic { border:none; }
#kiwi.theme_basic .server_select .status { text-align: center; font-weight: bold; padding:1em; }
#kiwi.theme_basic .server_select .status.ok { }
</tr>\r
</table>\r
\r
- <a href="" onclick="return false;" class="show_more"><%= server_network %></a>\r
+ <a href="" onclick="return false;" class="show_more"><%= server_network %> <i class="icon-caret-down"></i></a>\r
</div>\r
\r
\r
\r
// Takes instances of model_network\r
this.connections = new _kiwi.model.NetworkPanelList();\r
+\r
+ // Set any default settings before anything else is applied\r
+ if (this.server_settings && this.server_settings.client && this.server_settings.client.settings) {\r
+ this.applyDefaultClientSettings(this.server_settings.client.settings);\r
+ }\r
};\r
\r
\r
\r
this.initializeClient();\r
this.initializeGlobals();\r
- this.applyDefaultClientSettings(this.server_settings.client.settings);\r
\r
this.view.barsHide(true);\r
\r
\r
if (this.server_settings.client.channel)\r
defaults.channel = this.server_settings.client.channel;\r
+\r
+ if (this.server_settings.client.channel_key)\r
+ defaults.channel_key = this.server_settings.client.channel_key;\r
}\r
\r
\r
defaults.channel = this.server_settings.connection.channel;\r
}\r
\r
+ if (this.server_settings.connection.channel_key) {\r
+ defaults.channel_key = this.server_settings.connection.channel_key;\r
+ }\r
+\r
if (this.server_settings.connection.nick) {\r
defaults.nick = this.server_settings.connection.nick;\r
}\r
}
}
+ this.more_shown = false;
+
_kiwi.gateway.bind('onconnect', this.networkConnected, this);
_kiwi.gateway.bind('connecting', this.networkConnecting, this);
_kiwi.gateway.bind('onirc_error', this.onIrcError, this);
},
showMore: function (event) {
- $('.more', this.$el).slideDown('fast');
- $('input.server', this.$el).select();
+ if (!this.more_shown) {
+ $('.more', this.$el).slideDown('fast');
+ $('.show_more', this.$el)
+ .children('.icon-caret-down')
+ .removeClass('icon-caret-down')
+ .addClass('icon-caret-up');
+ $('input.server', this.$el).select();
+ this.more_shown = true;
+ } else {
+ $('.more', this.$el).slideUp('fast');
+ $('.show_more', this.$el)
+ .children('.icon-caret-up')
+ .removeClass('icon-caret-up')
+ .addClass('icon-caret-down');
+ $('input.nick', this.$el).select();
+ this.more_shown = false;
+ }
},
populateFields: function (defaults) {
// ssl_cert: "cert.pem"
//});
+// Network interface for outgoing connections
+conf.outgoing_address = {
+ IPv4: '0.0.0.0'
+ //IPv6: '::'
+};
// Do we want to enable the built in Identd server?
port: 6697,
ssl: true,
channel: '#kiwiirc',
+ channel_key: '',
nick: 'kiwi_?',
settings: {
theme: 'relaxed',
//conf.restrict_server_port = 6667;
//conf.restrict_server_ssl = false;
//conf.restrict_server_channel = "#kiwiirc";
+//conf.restrict_server_channel_key = "";
//conf.restrict_server_password = "";
//conf.restrict_server_nick = "kiwi_";
"daemonize2": "0.4.0-rc.5",\r
"eventemitter2": "0.4.11",\r
"ipaddr.js": "0.1.1",\r
- "socksjs": "0.3.3",\r
+ "socksjs": "0.4.1",\r
"iconv-lite" : "0.2.10",\r
+ "spdy": "1.9.1",\r
"po2json": "0.0.6"\r
}\r
}\r
var net = require('net');
-function IdentdServer(opts) {
+var IdentdServer = module.exports = function(opts) {
+
+ var that = this;
+
+ var default_user_id = 'kiwi',
+ default_system_id = 'UNIX-KiwiIRC';
// Option defaults
opts = opts || {};
opts.bind_addr = opts.bind_addr || '0.0.0.0';
opts.bind_port = opts.bind_port || 113;
- opts.system_id = opts.system_id || 'UNIX-KiwiIRC';
- opts.user_id = opts.user_id || 'kiwi';
+ opts.system_id = opts.system_id || default_system_id;
+ opts.user_id = opts.user_id || default_user_id;
var server = net.createServer(function(socket) {
- var user, system;
+ var buffer = '';
- if (typeof opts.user_id === 'function') {
- user = opts.user_id(socket).toString();
- } else {
- user = opts.user_id.toString();
- }
+ socket.on('data', function(data){
+ var data_line, response;
- if (typeof opts.system_id === 'function') {
- system = opts.system_id(socket).toString();
- } else {
- system = opts.system_id.toString();
- }
+ buffer += data.toString();
+
+ // If we exceeed 512 bytes, presume a flood and disconnect
+ if (buffer.length < 512) {
+
+ // Wait until we have a full line of data before processing it
+ if (buffer.indexOf('\n') === -1)
+ return;
+
+ // Get the first line of data and process it for a rsponse
+ data_line = buffer.split('\n')[0];
+ response = that.processLine(data_line);
+
+ }
+
+ // Close down the socket while sending the response
+ socket.removeAllListeners();
+ socket.end(response);
+ });
- socket.end('25,25 : USERID : ' + system + ' : ' + user);
});
server.on('listening', function() {
this.stop = function(callback) {
server.close(callback);
};
-}
-module.exports = IdentdServer;
\ No newline at end of file
+ /**
+ * Process a line of data for an Identd response
+ *
+ * @param {String} The line of data to process
+ * @return {String} Data to send back to the Identd client
+ */
+ this.processLine = function(line) {
+ var ports = line.split(','),
+ port_here = 0,
+ port_there = 0;
+
+ // We need 2 port number to make this work
+ if (ports.length < 2)
+ return;
+
+ port_here = parseInt(ports[0], 10);
+ port_there = parseInt(ports[1], 10);
+
+ // Make sure we have both ports to work with
+ if (!port_here || !port_there)
+ return;
+
+ if (typeof opts.user_id === 'function') {
+ user = (opts.user_id(port_here, port_there) || '').toString() || default_user_id;
+ } else {
+ user = opts.user_id.toString();
+ }
+
+ if (typeof opts.system_id === 'function') {
+ system = (opts.system_id(port_here, port_there) || '').toString() || default_system_id;
+ } else {
+ system = opts.system_id.toString();
+ }
+
+ return port_here.toString() + ' , ' + port_there.toString() + ' : USERID : ' + system + ' : ' + user;
+ };
+};
var members = command.trailing.split(' ');
var member_list = [];
var that = this;
- var i = 0;
_.each(members, function (member) {
- var j, k, modes = [];
+ var i = 0,
+ j = 0,
+ modes = [];
// Make sure we have some prefixes already
if (that.irc_connection.options.PREFIX) {
- for (j = 0; j < member.length; j++) {
- for (k = 0; k < that.irc_connection.options.PREFIX.length; k++) {
- if (member.charAt(j) === that.irc_connection.options.PREFIX[k].symbol) {
- modes.push(that.irc_connection.options.PREFIX[k].mode);
- i++;
- }
+ for (j = 0; j < that.irc_connection.options.PREFIX.length; j++) {
+ if (member.charAt(i) === that.irc_connection.options.PREFIX[j].symbol) {
+ modes.push(that.irc_connection.options.PREFIX[j].mode);
+ i++;
}
}
}
var net = require('net'),
tls = require('tls'),
util = require('util'),
+ dns = require('dns'),
_ = require('lodash'),
EventBinder = require('./eventbinder.js'),
IrcServer = require('./server.js'),
this.held_data = '';
this.applyIrcEvents();
-
- // Call any modules before making the connection
- global.modules.emit('irc connecting', {connection: this})
- .done(function () {
- that.connect();
- });
};
util.inherits(IrcConnection, EE);
*/
IrcConnection.prototype.connect = function () {
var that = this;
- var socks;
// The socket connect event to listener for
var socket_connect_event_name = 'connect';
+ // The destination address
+ var dest_addr = this.socks ?
+ this.socks.host :
+ this.irc_host.hostname;
// Make sure we don't already have an open connection
this.disposeSocket();
- // Are we connecting through a SOCKS proxy?
- if (this.socks) {
- this.socket = Socks.connect({
- host: this.irc_host.hostname,
- port: this.irc_host.port,
- ssl: this.ssl,
- rejectUnauthorized: global.config.reject_unauthorised_certificates
- }, {host: this.socks.host,
- port: this.socks.port,
- user: this.socks.user,
- pass: this.socks.pass
- });
+ // Get the IP family for the dest_addr (either socks or IRCd destination)
+ getConnectionFamily(dest_addr, function (err, family, host) {
+ var outgoing;
+
+ // Decide which net. interface to make the connection through
+ if (global.config.outgoing_address) {
+ if ((family === 'IPv6') && (global.config.outgoing_address.IPv6)) {
+ outgoing = global.config.outgoing_address.IPv6;
+ } else {
+ outgoing = global.config.outgoing_address.IPv4 || '0.0.0.0';
+
+ // We don't have an IPv6 interface but dest_addr may still resolve to
+ // an IPv4 address. Reset `host` and try connecting anyway, letting it
+ // fail if an IPv4 resolved address is not found
+ host = dest_addr;
+ }
- } else if (this.ssl) {
- this.socket = tls.connect({
- host: this.irc_host.hostname,
- port: this.irc_host.port,
- rejectUnauthorized: global.config.reject_unauthorised_certificates
- });
+ } else {
+ // No config was found so use the default
+ outgoing = '0.0.0.0';
+ }
- socket_connect_event_name = 'secureConnect';
+ // Are we connecting through a SOCKS proxy?
+ if (this.socks) {
+ that.socket = Socks.connect({
+ host: host,
+ port: that.irc_host.port,
+ ssl: that.ssl,
+ rejectUnauthorized: global.config.reject_unauthorised_certificates
+ }, {host: that.socks.host,
+ port: that.socks.port,
+ user: that.socks.user,
+ pass: that.socks.pass,
+ localAddress: outgoing
+ });
- } else {
- this.socket = net.connect({
- host: this.irc_host.hostname,
- port: this.irc_host.port
+ } else {
+ // No socks connection, connect directly to the IRCd
+
+ if (that.ssl) {
+ that.socket = tls.connect({
+ host: host,
+ port: that.irc_host.port,
+ rejectUnauthorized: global.config.reject_unauthorised_certificates,
+ localAddress: outgoing
+ });
+
+ socket_connect_event_name = 'secureConnect';
+
+ } else {
+ that.socket = net.connect({
+ host: host,
+ port: that.irc_host.port,
+ localAddress: outgoing
+ });
+ }
+ }
+
+ // Apply the socket listeners
+ that.socket.on(socket_connect_event_name, function () {
+ that.connected = true;
+
+ // Make note of the port numbers for any identd lookups
+ // Nodejs < 0.9.6 has no socket.localPort so check this first
+ if (this.localPort) {
+ global.clients.port_pairs[this.localPort.toString() + '_' + this.remotePort.toString()] = that;
+ }
+
+ socketConnectHandler.call(that);
});
- }
- this.socket.on(socket_connect_event_name, function () {
- that.connected = true;
- socketConnectHandler.call(that);
- });
+ that.socket.on('error', function (event) {
+ that.emit('error', event);
+ });
- this.socket.on('error', function (event) {
- that.emit('error', event);
- });
+ that.socket.on('data', function () {
+ parse.apply(that, arguments);
+ });
- this.socket.on('data', function () {
- parse.apply(that, arguments);
- });
+ that.socket.on('close', function (had_error) {
+ that.connected = false;
- this.socket.on('close', function (had_error) {
- that.connected = false;
- that.emit('close');
+ // Remove this socket form the identd lookup
+ // Nodejs < 0.9.6 has no socket.localPort so check this first
+ if (this.localPort) {
+ delete global.clients.port_pairs[this.localPort.toString() + '_' + this.remotePort.toString()];
+ }
+
+ that.emit('close');
- // Close the whole socket down
- that.disposeSocket();
+ // Close the whole socket down
+ that.disposeSocket();
+ });
});
};
}
};
+function getConnectionFamily(host, callback) {
+ if (net.isIP(host)) {
+ if (net.isIPv4(host)) {
+ setImmediate(callback, null, 'IPv4', host);
+ } else {
+ setImmediate(callback, null, 'IPv6', host);
+ }
+ } else {
+ dns.resolve6(host, function (err, addresses) {
+ if (!err) {
+ callback(null, 'IPv6', addresses[0]);
+ } else {
+ dns.resolve4(host, function (err, addresses) {
+ if (!err) {
+ callback(null, 'IPv4',addresses[0]);
+ } else {
+ callback(err);
+ }
+ });
+ }
+ });
+ }
+}
+
function onChannelJoin(event) {
var chan;
that.irc_connections[con_num] = null;
global.servers.removeConnection(this);
});
+
+ // Call any modules before making the connection
+ global.modules.emit('irc connecting', {connection: con})
+ .done(function () {
+ con.connect();
+ });
};
State.prototype.sendIrcCommand = function () {
// Load any modules in the config
if (global.config.module_dir) {
(global.config.modules || []).forEach(function (module_name) {
- if (modules.load(global.config.module_dir + module_name + '.js')) {
+ if (modules.load(module_name)) {
console.log('Module ' + module_name + ' loaded successfuly');
} else {
console.log('Module ' + module_name + ' failed to load');
clients: Object.create(null),
addresses: Object.create(null),
+ // Local and foriegn port pairs for identd lookups
+ // {'65483_6667': client_obj, '54356_6697': client_obj}
+ port_pairs: {},
+
add: function (client) {
this.clients[client.hash] = client;
if (typeof this.addresses[client.real_address] === 'undefined') {
* Identd server
*/
if (global.config.identd && global.config.identd.enabled) {
- new Identd({
+ var identd_resolve_user = function(port_here, port_there) {
+ var key = port_here.toString() + '_' + port_there.toString();
+
+ if (typeof global.clients.port_pairs[key] == 'undefined') {
+ return;
+ }
+
+ return global.clients.port_pairs[key].username;
+ };
+
+ var identd_server = new Identd({
bind_addr: global.config.identd.address,
- bind_port: global.config.identd.port
- }).start();
+ bind_port: global.config.identd.port,
+ user_id: identd_resolve_user
+ });
+
+ identd_server.start();
}
});
+process.on('SIGUSR2', function() {
+ console.log('Connected clients: ' + _.size(global.clients.clients).toString());
+ console.log('Num. remote hosts: ' + _.size(global.clients.addresses).toString());
+});
/*
*/
// Hold the loaded modules
-var registered_modules = {};
+var registered_modules = [];
function loadModule (module_file) {
- var module;
+ var module,
+ full_module_filename = global.config.module_dir + module_file;
// Get an instance of the module and remove it from the cache
try {
- module = require(module_file);
- delete require.cache[require.resolve(module_file)];
+ module = require(full_module_filename);
+ delete require.cache[require.resolve(full_module_filename)];
} catch (err) {
// Module was not found
- console.log(err);
return false;
}
// Find a registered collection, .dispose() of it and remove it
function unloadModule (module) {
+ var found_module = false;
+
registered_modules = _.reject(registered_modules, function (registered_module) {
- if (module === registered_module) {
- module.dispose();
+ if (module.toLowerCase() === registered_module.module_name.toLowerCase()) {
+ found_module = true;
+
+ registered_module.dispose();
return true;
}
});
+
+ return found_module;
}
* To be created by modules to bind to server events
*/
function Module (module_name) {
- registered_modules[module_name] = this;
-}
+ registered_modules.push(this);
+ this.module_name = module_name;
+ // Holder for all the bound events by this module
+ this._events = {};
+}
-// Holder for all the bound events by this module
-Module.prototype._events = {};
// Keep track of this modules events and bind
}
}
- active_publisher.removeListener(event_name, fn);
+ active_publisher.off(event_name, fn);
};
function EmitCall (event_name, event_data) {
var that = this,
completed = false,
- completed_fn = [];
+ completed_fn = [],
+
+ // Has event.preventDefault() been called
+ prevented = false,
+ prevented_fn = [];
// Emit this event to an array of listeners
// If wait is true, this callback must be called to continue running listeners
callback: function () {
// Invalidate this callback incase a listener decides to call it again
- callback = undefined;
+ event_obj.callback = undefined;
nextListener.apply(that);
+ },
+
+ // Prevents the default 'done' functions from executing
+ preventDefault: function () {
+ prevented = true;
}
};
function emitComplete() {
completed = true;
- // Call the completed functions
- (completed_fn || []).forEach(function (fn) {
+ var funcs = prevented ? prevented_fn : completed_fn;
+
+ // Call the completed/prevented functions
+ (funcs || []).forEach(function (fn) {
if (typeof fn === 'function') fn();
});
}
- function done(fn) {
+ function addCompletedFunc(fn) {
// Only accept functions
if (typeof fn !== 'function') return false;
completed_fn.push(fn);
// If we have already completed the emits, call this now
- if (completed) fn();
+ if (completed && !prevented) fn();
+
+ return this;
+ }
+
+
+
+ function addPreventedFunc(fn) {
+ // Only accept functions
+ if (typeof fn !== 'function') return false;
+
+ prevented_fn.push(fn);
+
+ // If we have already completed the emits, call this now
+ if (completed && prevented) fn();
+
+ return this;
}
return {
callListeners: callListeners,
- done: done
+ done: addCompletedFunc,
+ prevented: addPreventedFunc
};
}
daemon.sendSignal("SIGUSR1");\r
break;\r
\r
+ case 'stats':\r
+ console.log('Writing stats to log file..');\r
+ daemon.sendSignal("SIGUSR2");\r
+ break;\r
+\r
case 'build':\r
require('../client/assets/src/build.js');\r
break;\r
dns = require('dns'),
url = require('url'),
_ = require('lodash'),
+ spdy = require('spdy'),
Client = require('./client.js').Client,
HttpHandler = require('./httphandler.js').HttpHandler,
rehash = require('./rehash.js'),
events.EventEmitter.call(this);
-
+
http_handler = new HttpHandler(web_config);
-
+
// Standard options for the socket.io connections
ws_opts = {
'log level': 0,
}
}
- hs = https.createServer(opts, handleHttpRequest);
-
+ hs = spdy.createServer(opts, handleHttpRequest);
+
// Start socket.io listening on this weblistener
this.ws = ws.listen(hs, _.extend({ssl: true}, ws_opts));
hs.listen(web_config.port, web_config.address, function () {
hs.on('error', function (err) {
that.emit('error', err);
- })
-
+ });
+
this.ws.enable('browser client minification');
this.ws.enable('browser client etag');
this.ws.set('transports', transports);
}
handshakeData.real_address = address;
-
+
// If enabled, don't go over the connection limit
if (global.config.max_client_conns && global.config.max_client_conns > 0) {
if (global.clients.numOnAddress(address) + 1 > global.config.max_client_conns) {
} else {
handshakeData.revdns = _.first(domains) || address;
}
-
+
// All is well, authorise the connection
callback(null, true);
});
config = require('../server/configuration.js'),\r
_ = require('lodash');\r
\r
-var module = new kiwiModules.Module('Control');\r
+var control_module = new kiwiModules.Module('Control');\r
\r
\r
+/**\r
+ * The socket client\r
+ */\r
function SocketClient (socket) {\r
this.socket = socket;\r
+ this.socket_closing = false;\r
\r
this.remoteAddress = this.socket.remoteAddress;\r
console.log('Control connection from ' + this.socket.remoteAddress + ' opened');\r
\r
SocketClient.prototype.write = function(data, append) {\r
if (typeof append === 'undefined') append = '\n';\r
- this.socket.write(data + append);\r
+ if (this.socket && !this.socket_closing)\r
+ this.socket.write(data + append);\r
};\r
SocketClient.prototype.displayPrompt = function(prompt) {\r
prompt = prompt || 'Kiwi > ';\r
SocketClient.prototype.onData = function(data) {\r
data = data.toString().trim();\r
\r
- try {\r
- switch (data) {\r
- case 'modules':\r
- this.write('Loaded modules: ' + Object.keys(kiwiModules.getRegisteredModules()).join(', '));\r
- break;\r
-\r
- case 'stats':\r
- this.write('Connected clients: ' + _.size(global.clients.clients).toString());\r
- this.write('Num. remote hosts: ' + _.size(global.clients.addresses).toString());\r
- break;\r
-\r
- case 'rehash':\r
- rehash.rehashAll();\r
- this.write('Rehashed');\r
- break;\r
\r
- case 'reconfig':\r
- if (config.loadConfig()) {\r
- this.write('New config file loaded');\r
- } else {\r
- this.write("No new config file was loaded");\r
- }\r
- break;\r
\r
- case 'exit':\r
- case 'quit':\r
- this.socket.destroy();\r
- break;\r
+ try {\r
+ var data_split = data.split(' ');\r
\r
- default:\r
- this.write('Unrecognised command: ' + data);\r
+ if (typeof socket_commands[data_split[0]] === 'function') {\r
+ socket_commands[data_split[0]].call(this, data_split.slice(1));\r
+ } else {\r
+ this.write('Unrecognised command: ' + data);\r
}\r
+\r
} catch (err) {\r
console.log('[Control error] ' + err);\r
this.write('An error occured. Check the Kiwi server log for more details');\r
\r
SocketClient.prototype.onClose = function() {\r
this.unbindEvents();\r
+ this.socket = null;\r
console.log('Control connection from ' + this.remoteAddress + ' closed');\r
};\r
\r
\r
\r
+/**\r
+ * Available commands\r
+ * Each function is run in context of the SocketClient\r
+ */\r
+var socket_commands = {\r
+ module: function(data) {\r
+ switch(data[0]) {\r
+ case 'reload':\r
+ if (!data[1]) {\r
+ this.write('A module name must be specified');\r
+ return;\r
+ }\r
+\r
+ if (!kiwiModules.unload(data[1])) {\r
+ this.write('Module ' + (data[1] || '') + ' is not loaded');\r
+ return;\r
+ }\r
+\r
+ if (!kiwiModules.load(data[1])) {\r
+ this.write('Error loading module ' + (data[1] || ''));\r
+ }\r
+ this.write('Module ' + data[1] + ' reloaded');\r
+\r
+ break;\r
+\r
+ case 'list':\r
+ case 'ls':\r
+ default:\r
+ var module_names = [];\r
+ kiwiModules.getRegisteredModules().forEach(function(module) {\r
+ module_names.push(module.module_name);\r
+ });\r
+ this.write('Loaded modules: ' + module_names.join(', '));\r
+ }\r
+\r
+ },\r
+\r
+ stats: function(data) {\r
+ this.write('Connected clients: ' + _.size(global.clients.clients).toString());\r
+ this.write('Num. remote hosts: ' + _.size(global.clients.addresses).toString());\r
+ },\r
+\r
+ rehash: function(data) {\r
+ rehash.rehashAll();\r
+ this.write('Rehashed');\r
+ },\r
\r
+ reconfig: function(data) {\r
+ if (config.loadConfig()) {\r
+ this.write('New config file loaded');\r
+ } else {\r
+ this.write("No new config file was loaded");\r
+ }\r
+ },\r
+\r
+ quit: function(data) {\r
+ this.socket.destroy();\r
+ this.socket_closing = true;\r
+ },\r
+ exit: function(data) {\r
+ this.socket.destroy();\r
+ this.socket_closing = true;\r
+ }\r
+};\r
+\r
+\r
+/**\r
+ * Start the control socket server to serve connections\r
+ */\r
var server = net.createServer(function (socket) {\r
new SocketClient(socket);\r
});\r
-server.listen(8888);
\ No newline at end of file
+server.listen(8888);\r
+\r
+control_module.on('dispose', function() {\r
+ server.close();\r
+});\r
--- /dev/null
+/**
+ * DNS Blacklist support
+ *
+ * Check the client against a blacklist before connection to an IRC server
+ */
+
+var dns = require('dns'),
+ kiwiModules = require('../server/modules');
+
+
+// The available DNS zones to check against
+var bl_zones = {
+ dronebl: '.dnsbl.dronebl.org'
+};
+
+// The DNS zone we should use
+var current_bl = 'dronebl';
+
+
+var module = new kiwiModules.Module('DNSBL');
+
+module.on('irc connecting', function (event, event_data) {
+ event.wait = true;
+
+ var client_addr = event_data.connection.state.client.websocket.handshake.real_address;
+
+ isBlacklisted(client_addr, function(is_blocked) {
+ if (is_blocked) {
+ var err = new Error('DNSBL blocked');
+ err.code = 'Blacklisted';
+
+ event_data.connection.emit('error', err);
+ event.preventDefault();
+ event.callback();
+
+ } else {
+ event.callback();
+ }
+ });
+});
+
+
+
+// The actual checking against the DNS blacklist
+function isBlacklisted(ip, callback) {
+ var host_lookup = reverseIp(ip) + bl_zones[current_bl];
+
+ dns.resolve4(host_lookup, function(err, domain) {
+ if (err) {
+ // Not blacklisted
+ callback(false);
+ } else {
+ // It is blacklisted
+ callback(true);
+ }
+ });
+}
+
+
+function reverseIp(ip) {
+ return ip.split('.').reverse().join('.');
+}
\ No newline at end of file