Integrate listener into the core.
authorJelmer Vernooij <jelmer@samba.org>
Sat, 1 Sep 2007 14:38:45 +0000 (16:38 +0200)
committerJelmer Vernooij <jelmer@samba.org>
Sat, 1 Sep 2007 14:38:45 +0000 (16:38 +0200)
14 files changed:
Makefile
Makefile.settings.in
NEWS
mods/listener.c [deleted file]
src/admin.c
src/ctrlproxy.h
src/internals.h
src/listener.c [new file with mode: 0644]
src/listener.h [moved from mods/listener.h with 63% similarity]
src/main.c
src/settings.c
src/settings.h
src/ssl.h
src/tlscert.c

index e9a88ed86e3b735abe493181d2d1f2c42e8f842c..51a2bf365f07c05ae95e655c64711d21fb0c8267 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -48,6 +48,7 @@ objs = src/network.o \
           src/pipes.o \
           src/help.o \
           src/repl_backends.o \
+          src/listener.o \
           $(SSL_OBJS)
 
 headers = src/admin.h \
index c2a5a81bceadb2730fcec83f1813c325ecffb2a4..080a6e0f0225b6cecd0484f9e77152717316f207 100644 (file)
@@ -20,7 +20,7 @@ EXEEXT = @EXEEXT@
 OBJEXT = @OBJEXT@
 CFLAGS = @CFLAGS@ @COMMON_CFLAGS@
 MODS_SHARED = log_irssi auto_away log_custom \
-       socks listener @MODS_SHARED@
+       socks @MODS_SHARED@
 LDFLAGS = @LDFLAGS@
 BINS = @BINS@
 scriptdir = $(cdatadir)/scripts
diff --git a/NEWS b/NEWS
index 3124f6d3992076c3d920cfb547b6bd8624f57e3a..f5ee0604f7437a5cd037f4f0b92815899e7e3742 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,8 @@ Ctrlproxy 3.0.4 UNRELEASED
 
        * Allow specifying a network in the password, separated by a colon (:).
 
+       * Integrate listeners into the core.
+
 
 Ctrlproxy 3.0.3 2007-07-22
 
diff --git a/mods/listener.c b/mods/listener.c
deleted file mode 100644 (file)
index cc2ce1e..0000000
+++ /dev/null
@@ -1,577 +0,0 @@
-/* 
-       ctrlproxy: A modular IRC proxy
-       (c) 2005-2006 Jelmer Vernooij <jelmer@nl.linux.org>
-       
-       Manual listen on ports
-
-       This program 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 2 of the License, or
-       (at your option) any later version.
-
-       This program 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 this program; if not, write to the Free Software
-       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "internals.h"
-#include "irc.h"
-#include "listener.h"
-#include "ssl.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <glib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
-#include <netdb.h>
-
-static GIConv iconv = (GIConv)-1;
-
-static gboolean handle_client_receive(GIOChannel *c, GIOCondition condition, gpointer data) 
-{
-       struct line *l;
-       struct listener *listener = data;
-       GError *error = NULL;
-       GIOStatus status;
-
-       g_assert(c != NULL);
-
-       while ((status = irc_recv_line(c, iconv, &error, &l)) == G_IO_STATUS_NORMAL) {
-               g_assert(l != NULL);
-
-               if (!l->args[0]){ 
-                       free_line(l);
-                       continue;
-               }
-
-               if (listener->password == NULL) {
-                       log_network(LOG_WARNING, listener->network, "No password set, allowing client _without_ authentication!");
-               }
-
-               if (!g_strcasecmp(l->args[0], "PASS")) {
-                       char *desc;
-                       struct network *n = listener->network;
-                       gboolean authenticated = FALSE;
-
-                       if (listener->password == NULL) {
-                               authenticated = TRUE;
-                       } else if (strcmp(l->args[1], listener->password) == 0) {
-                               authenticated = TRUE;
-                       } else if (strncmp(l->args[1], listener->password, strlen(listener->password)) == 0 &&
-                                          l->args[1][strlen(listener->password)+1] == ':') {
-                               authenticated = TRUE;
-                               n = find_network(listener->global, l->args[1]+strlen(listener->password)+1);
-                               if (n == NULL) {
-                                       log_network(LOG_WARNING, listener->network, "User tried to log in with incorrect password!");
-                                       irc_sendf(c, iconv, NULL, ":%s %d %s :Password mismatch", get_my_hostname(), ERR_PASSWDMISMATCH, "*");
-       
-                                       free_line(l);
-                                       return TRUE;
-                               }
-                       }
-
-                       if (authenticated) {
-                               log_network (LOG_INFO, n, "Client successfully authenticated");
-
-                               desc = g_io_channel_ip_get_description(c);
-                               client_init(n, c, desc);
-
-                               free_line(l); 
-                               return FALSE;
-                       } else {
-                               log_network(LOG_WARNING, listener->network, "User tried to log in with incorrect password!");
-                               irc_sendf(c, iconv, NULL, ":%s %d %s :Password mismatch", get_my_hostname(), ERR_PASSWDMISMATCH, "*");
-       
-                               free_line(l);
-                               return TRUE;
-                       }
-               } else {
-                       irc_sendf(c, iconv, NULL, ":%s %d %s :You are not registered", get_my_hostname(), ERR_NOTREGISTERED, "*");
-               }
-
-               free_line(l);
-       }
-
-       if (status != G_IO_STATUS_AGAIN)
-               return FALSE;
-       
-       return TRUE;
-}
-
-static gboolean handle_new_client(GIOChannel *c_server, GIOCondition condition, void *_listener)
-{
-       struct listener *listener = _listener;
-       GIOChannel *c;
-       int sock = accept(g_io_channel_unix_get_fd(c_server), NULL, 0);
-
-       if (sock < 0) {
-               log_global(LOG_WARNING, "Error accepting new connection: %s", strerror(errno));
-               return TRUE;
-       }
-
-       c = g_io_channel_unix_new(sock);
-
-       if (listener->ssl) {
-#ifdef HAVE_GNUTLS
-               c = ssl_wrap_iochannel(c, SSL_TYPE_SERVER, 
-                                                                                        NULL, listener->ssl_credentials);
-               g_assert(c != NULL);
-#else
-               log_global(LOG_WARNING, "SSL support not available, not listening for SSL connection");
-#endif
-       }
-
-       g_io_channel_set_close_on_unref(c, TRUE);
-       g_io_channel_set_encoding(c, NULL, NULL);
-       g_io_channel_set_flags(c, G_IO_FLAG_NONBLOCK, NULL);
-       g_io_add_watch(c, G_IO_IN, handle_client_receive, listener);
-
-       g_io_channel_unref(c);
-
-       listener->pending = g_list_append(listener->pending, c);
-
-       return TRUE;
-}
-
-/* FIXME: Store in global struct somehow */
-static GList *listeners = NULL;
-static int autoport = 6667;
-static gboolean auto_listener = FALSE;
-static GKeyFile *keyfile = NULL;
-
-static int next_autoport()
-{
-       return ++autoport;
-}
-
-/**
- * Start a listener.
- *
- * @param l Listener to start.
- */
-gboolean start_listener(struct listener *l)
-{
-       int sock = -1;
-       const int on = 1;
-       struct addrinfo *res, *all_res;
-       int error;
-       struct addrinfo hints;
-       struct listener_iochannel *lio;
-
-       memset(&hints, 0, sizeof(hints));
-       hints.ai_family = PF_UNSPEC;
-       hints.ai_socktype = SOCK_STREAM;
-       hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
-
-       g_assert(!l->active);
-
-       error = getaddrinfo(l->address, l->port, &hints, &all_res);
-       if (error) {
-               log_global(LOG_ERROR, "Can't get address for %s:%s", l->address?l->address:"", l->port);
-               return FALSE;
-       }
-
-       for (res = all_res; res; res = res->ai_next) {
-               GIOChannel *ioc;
-
-               lio = g_new0(struct listener_iochannel, 1);
-
-               if (getnameinfo(res->ai_addr, res->ai_addrlen, 
-                                               lio->address, NI_MAXHOST, lio->port, NI_MAXSERV, 
-                                               NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
-                       strcpy(lio->address, "");
-                       strcpy(lio->port, "");
-               }
-
-               sock = socket(PF_INET, SOCK_STREAM, 0);
-               if (sock < 0) {
-                       log_global(LOG_ERROR, "error creating socket: %s", strerror(errno));
-                       close(sock);
-                       g_free(lio);
-                       continue;
-               }
-
-               setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-       
-               if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
-                       /* Don't warn when binding to the same address using IPv4 
-                        * /and/ ipv6. */
-                       if (!l->active || errno != EADDRINUSE)  {
-                               log_global(LOG_ERROR, "bind to %s:%s failed: %s", l->address, 
-                                                  l->port, strerror(errno));
-                       }
-                       close(sock);
-                       g_free(lio);
-                       continue;
-               }
-
-               if (listen(sock, 5) < 0) {
-                       log_global(LOG_ERROR, "error listening on socket: %s", 
-                                               strerror(errno));
-                       close(sock);
-                       g_free(lio);
-                       continue;
-               }
-
-               ioc = g_io_channel_unix_new(sock);
-
-               if (ioc == NULL) {
-                       log_global(LOG_ERROR, 
-                                               "Unable to create GIOChannel for server socket");
-                       close(sock);
-                       g_free(lio);
-                       continue;
-               }
-
-               g_io_channel_set_close_on_unref(ioc, TRUE);
-
-               g_io_channel_set_encoding(ioc, NULL, NULL);
-               lio->watch_id = g_io_add_watch(ioc, G_IO_IN, handle_new_client, l);
-               g_io_channel_unref(ioc);
-               l->incoming = g_list_append(l->incoming, lio);
-
-               log_network( LOG_INFO, l->network, "Listening on %s:%s", 
-                                        lio->address, lio->port);
-               l->active = TRUE;
-       }
-
-       freeaddrinfo(all_res);
-       
-       return l->active;
-}
-
-gboolean stop_listener(struct listener *l)
-{
-       while (l->incoming != NULL) {
-               struct listener_iochannel *lio = l->incoming->data;
-
-               g_source_remove(lio->watch_id);
-               
-               log_global(LOG_INFO, "Stopped listening at %s:%s", lio->address, 
-                                        lio->port);
-               g_free(lio);
-
-               l->incoming = g_list_remove(l->incoming, lio);
-       }
-
-       return TRUE;
-}
-
-void free_listener(struct listener *l)
-{
-       g_free(l->password);
-       g_free(l->port);
-       g_free(l->address);
-       listeners = g_list_remove(listeners, l);
-       g_free(l);
-}
-
-struct listener *listener_init(const char *address, const char *port)
-{
-       struct listener *l = g_new0(struct listener, 1);
-
-       l->address = address?g_strdup(address):NULL;
-       l->port = g_strdup(port);
-
-       if (l->port == NULL) 
-               l->port = g_strdup("6667");
-
-       listeners = g_list_append(listeners, l);
-
-       return l;
-}
-
-static void update_config(struct global *global, const char *path)
-{
-       GList *gl;
-       char *filename;
-       GKeyFile *kf; 
-       GError *error = NULL;
-       char *default_password;
-
-       default_password = g_key_file_get_string(global->config->keyfile, "listener", "password", NULL);
-
-       g_key_file_set_boolean(global->config->keyfile, "listener", "auto", auto_listener);
-
-       g_key_file_set_integer(global->config->keyfile, "listener", "autoport", autoport);
-
-       filename = g_build_filename(path, "listener", NULL);
-
-       if (keyfile)
-               keyfile = g_key_file_new();
-
-       kf = keyfile;
-
-       for (gl = listeners; gl; gl = gl->next) {
-               struct listener *l = gl->data;
-               char *tmp;
-
-               if (!l->address) 
-                       tmp = g_strdup(l->port);
-               else
-                       tmp = g_strdup_printf("%s:%s", l->address, l->port);
-
-               if (l->password && !(default_password && strcmp(l->password, default_password) == 0)) 
-                       g_key_file_set_string(kf, tmp, "password", l->password);
-
-               if (l->network) {
-                       g_assert(l->network->info.name != NULL);
-                       g_key_file_set_string(kf, tmp, "network", l->network->info.name);
-               }
-
-               g_key_file_set_boolean(kf, tmp, "ssl", l->ssl);
-
-               g_free(tmp);
-       }
-
-       if (!g_key_file_save_to_file(kf, filename, &error)) {
-               log_global(LOG_WARNING, "Unable to save to \"%s\": %s", filename, error->message);
-       }
-       
-       g_free(filename);
-}
-
-static void auto_add_listener(struct network *n, void *private_data)
-{
-       GList *gl;
-       char *port;
-       struct listener *l;
-
-       /* See if there is already a listener for n */
-       for (gl = listeners; gl; gl = gl->next) {
-               l = gl->data;
-
-               if (l->network == n || l->network == NULL)
-                       return;
-       }
-
-       port = g_strdup_printf("%d", next_autoport());
-       l = listener_init(NULL, port);
-       l->global = n->global;
-       l->network = n;
-       start_listener(l);
-}
-
-static void load_config(struct global *global)
-{
-       char *filename = g_build_filename(global->config->config_dir, "listener", NULL);
-       int i;
-       char **groups;
-       gsize size;
-       GKeyFile *kf;
-       char *default_password;
-
-       default_password = g_key_file_get_string(global->config->keyfile, "listener", "password", NULL);
-       if (g_key_file_has_key(global->config->keyfile, "listener", "auto", NULL))
-               auto_listener = g_key_file_get_boolean(global->config->keyfile, "listener", "auto", NULL);
-
-       if (g_key_file_has_key(global->config->keyfile, "listener", "autoport", NULL))
-               autoport = g_key_file_get_integer(global->config->keyfile, "listener", "autoport", NULL);
-
-       if (auto_listener)
-               register_new_network_notify(global, auto_add_listener, NULL);
-
-       keyfile = kf = g_key_file_new();
-
-       if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
-               g_free(filename);
-               return;
-       }
-               
-       groups = g_key_file_get_groups(kf, &size);
-
-       for (i = 0; i < size; i++)
-       {
-               struct listener *l;
-               char *address, *port, *tmp;
-               
-               tmp = g_strdup(groups[i]);
-               port = strrchr(tmp, ':');
-               if (port) {
-                       address = tmp;
-                       *port = '\0';
-                       port++;
-               } else {
-                       port = tmp;
-                       address = NULL;
-               }
-                       
-               l = listener_init(address, port);
-               l->global = global;
-
-               g_free(tmp);
-
-               l->password = g_key_file_get_string(kf, groups[i], "password", NULL);
-               if (l->password == NULL)
-                       l->password = default_password;
-
-               if (g_key_file_has_key(kf, groups[i], "ssl", NULL))
-                       l->ssl = g_key_file_get_boolean(kf, groups[i], "ssl", NULL);
-
-#ifdef HAVE_GNUTLS
-               if (l->ssl)
-                       l->ssl_credentials = ssl_create_server_credentials(global, kf, groups[i]);
-#endif
-
-               if (g_key_file_has_key(kf, groups[i], "network", NULL)) {
-
-                       tmp = g_key_file_get_string(kf, groups[i], "network", NULL);
-                       l->network = find_network(global, tmp);
-                       if (!l->network) {
-                               log_global(LOG_ERROR, "Unable to find network named \"%s\"", tmp);
-                       }
-                       g_free(tmp);
-               }
-                       
-               start_listener(l);
-       }
-
-       g_strfreev(groups);
-       g_free(filename);
-}
-
-static void fini_plugin(void)
-{
-       GList *gl;
-       for(gl = listeners; gl; gl = gl->next) {
-               struct listener *l = gl->data;
-               if (l->active) 
-                       stop_listener(l);
-       }
-}
-
-
-void cmd_start_listener(admin_handle h, char **args, void *userdata)
-{
-       char *b, *p;
-       struct listener *l;
-
-       if (!args[1]) {
-               admin_out(h, "No port specified");
-               return;
-       }
-
-       if (!args[2]) {
-               admin_out(h, "No password specified");
-               return;
-       }
-
-       b = g_strdup(args[1]);
-       if ((p = strchr(b, ':'))) {
-               *p = '\0';
-               p++;
-       } else {
-               p = b;
-               b = NULL;
-       }
-
-       l = listener_init(b, p);
-       l->global = admin_get_global(h);
-
-       if (b) g_free(b); else g_free(p);
-
-       l->password = g_strdup(args[2]);
-
-       if (args[3]) {
-               l->network = find_network(admin_get_global(h), args[3]);
-               if (l->network == NULL) {
-                       admin_out(h, "No such network `%s'", args[3]);
-                       free_listener(l);
-                       return;
-               }
-       }
-
-       start_listener(l);
-}
-
-void cmd_stop_listener(admin_handle h, char **args, void *userdata)
-{
-       GList *gl;
-       char *b, *p;
-       int i = 0;
-
-       if (!args[0]) {
-               admin_out(h, "No port specified");
-               return;
-       }
-
-       b = g_strdup(args[1]);
-       if ((p = strchr(b, ':'))) {
-               *p = '\0';
-               p++;
-       } else {
-               p = b;
-               b = NULL;
-       }
-
-       for (gl = listeners; gl; gl = gl->next) {
-               struct listener *l = gl->data;
-
-               if (b && !l->address)
-                       continue;
-
-               if (b && l->address && strcmp(b, l->address) != 0)
-                       continue;
-
-               if (strcmp(p, l->port) != 0)
-                       continue;
-
-               stop_listener(l);
-               free_listener(l);
-               i++;
-       }
-
-       if (b) g_free(b); else g_free(p);
-
-       admin_out(h, "%d listeners stopped", i);
-}
-
-void cmd_list_listener(admin_handle h, char **args, void *userdata)
-{
-       GList *gl;
-
-       for (gl = listeners; gl; gl = gl->next) {
-               struct listener *l = gl->data;
-
-               admin_out(h, "%s:%s%s%s%s", l->address?l->address:"", l->port, l->network?" (":"", l->network?l->network->info.name:"", l->network?")":"");
-       }
-}
-
-const static struct admin_command listener_commands[] = {
-       { "STARTLISTENER", cmd_start_listener },
-       { "STOPLISTENER", cmd_stop_listener },
-       { "LISTLISTENER", cmd_list_listener },
-       { NULL }
-};
-
-static gboolean init_plugin(void)
-{
-       int i;
-       register_load_config_notify(load_config);
-       register_save_config_notify(update_config);
-
-       for (i = 0; listener_commands[i].name; i++)
-               register_admin_command(&listener_commands[i]);
-
-       atexit(fini_plugin);
-       return TRUE;
-}
-
-struct plugin_ops plugin = {
-       .name = "listener",
-       .version = 0,
-       .init = init_plugin,
-};
index c976d35228756b1edcda326ca88133cd3503d98c..0eeb53d64c80a8591a0663e105552be321e4b257 100644 (file)
@@ -723,6 +723,105 @@ void admin_log(enum log_level level, const struct network *n, const struct clien
        entered = FALSE;
 }
 
+static void cmd_start_listener(admin_handle h, char **args, void *userdata)
+{
+       char *b, *p;
+       struct listener_config *cfg;
+       struct listener *l;
+
+       if (!args[1]) {
+               admin_out(h, "No port specified");
+               return;
+       }
+
+       if (!args[2]) {
+               admin_out(h, "No password specified");
+               return;
+       }
+
+       cfg = g_new0(struct listener_config, 1);
+
+       b = g_strdup(args[1]);
+       if ((p = strchr(b, ':'))) {
+               *p = '\0';
+               p++;
+               cfg->address = b;
+               cfg->port = g_strdup(p);
+       } else {
+               cfg->port = g_strdup(b);
+               cfg->address = NULL;
+       }
+
+       if (args[3]) {
+               cfg->network = g_strdup(args[3]);
+               if (find_network(admin_get_global(h), args[3]) == NULL) {
+                       admin_out(h, "No such network `%s`", args[3]);
+                       return;
+               }
+       }
+
+       l = listener_init(admin_get_global(h), cfg);
+       l->config->password = g_strdup(args[2]);
+       l->global = admin_get_global(h);
+
+       start_listener(l);
+}
+
+static void cmd_stop_listener(admin_handle h, char **args, void *userdata)
+{
+       GList *gl;
+       char *b, *p;
+       int i = 0;
+
+       if (!args[0]) {
+               admin_out(h, "No port specified");
+               return;
+       }
+
+       b = g_strdup(args[1]);
+       if ((p = strchr(b, ':'))) {
+               *p = '\0';
+               p++;
+       } else {
+               p = b;
+               b = NULL;
+       }
+
+       for (gl = admin_get_global(h)->listeners; gl; gl = gl->next) {
+               struct listener *l = gl->data;
+
+               if (b && l->config->address == NULL)
+                       continue;
+
+               if (b && l->config->address != NULL && strcmp(b, l->config->address) != 0)
+                       continue;
+
+               if (strcmp(p, l->config->port) != 0)
+                       continue;
+
+               stop_listener(l);
+               free_listener(l);
+               i++;
+       }
+
+       if (b) g_free(b); else g_free(p);
+
+       admin_out(h, "%d listeners stopped", i);
+}
+
+static void cmd_list_listener(admin_handle h, char **args, void *userdata)
+{
+       GList *gl;
+
+       for (gl = admin_get_global(h)->listeners; gl; gl = gl->next) {
+               struct listener *l = gl->data;
+
+               admin_out(h, "%s:%s%s%s%s", l->config->address?l->config->address:"", l->config->port, 
+                                 l->network?" (":"", l->network?l->network->info.name:"", 
+                                 l->network?")":"");
+       }
+}
+
 const static struct admin_command builtin_commands[] = {
        { "ADDNETWORK", add_network },
        { "ADDSERVER", add_server },
@@ -740,6 +839,9 @@ const static struct admin_command builtin_commands[] = {
        { "DETACH", detach_client },
        { "HELP", cmd_help },
        { "DUMPJOINEDCHANNELS", dump_joined_channels },
+       { "STARTLISTENER", cmd_start_listener },
+       { "STOPLISTENER", cmd_stop_listener },
+       { "LISTLISTENER", cmd_list_listener },
 #ifdef DEBUG
        { "ABORT", do_abort },
 #endif
index 9862399ec8db7c9591c0ab3792b944ec3f103b0a..60e7dee90f7cafb3133382818d4e6d7e789b1483 100644 (file)
@@ -67,6 +67,7 @@ struct global {
        GList *new_network_notifiers;
        GList *networks;
        GList *nickserv_nicks;
+       GList *listeners;
 
        GIOChannel *unix_incoming;
        gint unix_incoming_id;
index d05bca14c12443574c61355f89c258b0ebb373ba..fdc189be5a08318b95f1dddfd5670c70f999d95b 100644 (file)
@@ -48,6 +48,7 @@
 #endif /* HAVE_NETINET_IN6_H */
 #include "ctrlproxy.h"
 #include "plugins.h"
+#include "listener.h"
 
 #define DEFAULT_RECONNECT_INTERVAL     60
 #define MIN_SILENT_TIME                                60
diff --git a/src/listener.c b/src/listener.c
new file mode 100644 (file)
index 0000000..43aa3c6
--- /dev/null
@@ -0,0 +1,344 @@
+/* 
+       ctrlproxy: A modular IRC proxy
+       (c) 2005-2006 Jelmer Vernooij <jelmer@nl.linux.org>
+       
+       Manual listen on ports
+
+       This program 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 2 of the License, or
+       (at your option) any later version.
+
+       This program 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 this program; if not, write to the Free Software
+       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "internals.h"
+#include "irc.h"
+#include "listener.h"
+#include "ssl.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <glib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include <netdb.h>
+
+#define DEFAULT_PORT "6667"
+
+static GIConv iconv = (GIConv)-1;
+
+static gboolean handle_client_receive(GIOChannel *c, GIOCondition condition, gpointer data) 
+{
+       struct line *l;
+       struct listener *listener = data;
+       GError *error = NULL;
+       GIOStatus status;
+
+       g_assert(c != NULL);
+
+       while ((status = irc_recv_line(c, iconv, &error, &l)) == G_IO_STATUS_NORMAL) {
+               g_assert(l != NULL);
+
+               if (!l->args[0]){ 
+                       free_line(l);
+                       continue;
+               }
+
+               if (listener->config->password == NULL) {
+                       log_network(LOG_WARNING, listener->network, "No password set, allowing client _without_ authentication!");
+               }
+
+               if (!g_strcasecmp(l->args[0], "PASS")) {
+                       char *desc;
+                       struct network *n = listener->network;
+                       gboolean authenticated = FALSE;
+
+                       if (listener->config->password == NULL) {
+                               authenticated = TRUE;
+                       } else if (strcmp(l->args[1], listener->config->password) == 0) {
+                               authenticated = TRUE;
+                       } else if (strncmp(l->args[1], listener->config->password, strlen(listener->config->password)) == 0 &&
+                                          l->args[1][strlen(listener->config->password)+1] == ':') {
+                               authenticated = TRUE;
+                               n = find_network(listener->global, l->args[1]+strlen(listener->config->password)+1);
+                               if (n == NULL) {
+                                       log_network(LOG_WARNING, listener->network, "User tried to log in with incorrect password!");
+                                       irc_sendf(c, iconv, NULL, ":%s %d %s :Password mismatch", get_my_hostname(), ERR_PASSWDMISMATCH, "*");
+       
+                                       free_line(l);
+                                       return TRUE;
+                               }
+                       }
+
+                       if (authenticated) {
+                               log_network (LOG_INFO, n, "Client successfully authenticated");
+
+                               desc = g_io_channel_ip_get_description(c);
+                               client_init(n, c, desc);
+
+                               free_line(l); 
+                               return FALSE;
+                       } else {
+                               log_network(LOG_WARNING, listener->network, "User tried to log in with incorrect password!");
+                               irc_sendf(c, iconv, NULL, ":%s %d %s :Password mismatch", get_my_hostname(), ERR_PASSWDMISMATCH, "*");
+       
+                               free_line(l);
+                               return TRUE;
+                       }
+               } else {
+                       irc_sendf(c, iconv, NULL, ":%s %d %s :You are not registered", get_my_hostname(), ERR_NOTREGISTERED, "*");
+               }
+
+               free_line(l);
+       }
+
+       if (status != G_IO_STATUS_AGAIN)
+               return FALSE;
+       
+       return TRUE;
+}
+
+static gboolean handle_new_client(GIOChannel *c_server, GIOCondition condition, void *_listener)
+{
+       struct listener *listener = _listener;
+       GIOChannel *c;
+       int sock = accept(g_io_channel_unix_get_fd(c_server), NULL, 0);
+
+       if (sock < 0) {
+               log_global(LOG_WARNING, "Error accepting new connection: %s", strerror(errno));
+               return TRUE;
+       }
+
+       c = g_io_channel_unix_new(sock);
+
+       if (listener->config->ssl) {
+#ifdef HAVE_GNUTLS
+               c = ssl_wrap_iochannel(c, SSL_TYPE_SERVER, 
+                                                        NULL, listener->config->ssl_credentials);
+               g_assert(c != NULL);
+#else
+               log_global(LOG_WARNING, "SSL support not available, not listening for SSL connection");
+#endif
+       }
+
+       g_io_channel_set_close_on_unref(c, TRUE);
+       g_io_channel_set_encoding(c, NULL, NULL);
+       g_io_channel_set_flags(c, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_add_watch(c, G_IO_IN, handle_client_receive, listener);
+
+       g_io_channel_unref(c);
+
+       listener->pending = g_list_append(listener->pending, c);
+
+       return TRUE;
+}
+
+static int next_autoport(struct global *global)
+{
+       return ++global->config->listener_autoport;
+}
+
+/**
+ * Start a listener.
+ *
+ * @param l Listener to start.
+ */
+gboolean start_listener(struct listener *l)
+{
+       int sock = -1;
+       const int on = 1;
+       struct addrinfo *res, *all_res;
+       int error;
+       struct addrinfo hints;
+       struct listener_iochannel *lio;
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
+
+       g_assert(!l->active);
+
+       error = getaddrinfo(l->config->address, l->config->port != NULL?l->config->port:DEFAULT_PORT, &hints, &all_res);
+       if (error) {
+               log_global(LOG_ERROR, "Can't get address for %s:%s", l->config->address?l->config->address:"", l->config->port);
+               return FALSE;
+       }
+
+       for (res = all_res; res; res = res->ai_next) {
+               GIOChannel *ioc;
+
+               lio = g_new0(struct listener_iochannel, 1);
+
+               if (getnameinfo(res->ai_addr, res->ai_addrlen, 
+                                               lio->address, NI_MAXHOST, lio->port, NI_MAXSERV, 
+                                               NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+                       strcpy(lio->address, "");
+                       strcpy(lio->port, "");
+               }
+
+               sock = socket(PF_INET, SOCK_STREAM, 0);
+               if (sock < 0) {
+                       log_global(LOG_ERROR, "error creating socket: %s", strerror(errno));
+                       close(sock);
+                       g_free(lio);
+                       continue;
+               }
+
+               setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+       
+               if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+                       /* Don't warn when binding to the same address using IPv4 
+                        * /and/ ipv6. */
+                       if (!l->active || errno != EADDRINUSE)  {
+                               log_global(LOG_ERROR, "bind to %s:%s failed: %s", 
+                                                  l->config->address, l->config->port, 
+                                                  strerror(errno));
+                       }
+                       close(sock);
+                       g_free(lio);
+                       continue;
+               }
+
+               if (listen(sock, 5) < 0) {
+                       log_global(LOG_ERROR, "error listening on socket: %s", 
+                                               strerror(errno));
+                       close(sock);
+                       g_free(lio);
+                       continue;
+               }
+
+               ioc = g_io_channel_unix_new(sock);
+
+               if (ioc == NULL) {
+                       log_global(LOG_ERROR, 
+                                               "Unable to create GIOChannel for server socket");
+                       close(sock);
+                       g_free(lio);
+                       continue;
+               }
+
+               g_io_channel_set_close_on_unref(ioc, TRUE);
+
+               g_io_channel_set_encoding(ioc, NULL, NULL);
+               lio->watch_id = g_io_add_watch(ioc, G_IO_IN, handle_new_client, l);
+               g_io_channel_unref(ioc);
+               l->incoming = g_list_append(l->incoming, lio);
+
+               log_network( LOG_INFO, l->network, "Listening on %s:%s", 
+                                        lio->address, lio->port);
+               l->active = TRUE;
+       }
+
+       freeaddrinfo(all_res);
+       
+       return l->active;
+}
+
+gboolean stop_listener(struct listener *l)
+{
+       while (l->incoming != NULL) {
+               struct listener_iochannel *lio = l->incoming->data;
+
+               g_source_remove(lio->watch_id);
+               
+               log_global(LOG_INFO, "Stopped listening at %s:%s", lio->address, 
+                                        lio->port);
+               g_free(lio);
+
+               l->incoming = g_list_remove(l->incoming, lio);
+       }
+
+       return TRUE;
+}
+
+void free_listener(struct listener *l)
+{
+       l->global->listeners = g_list_remove(l->global->listeners, l);
+       g_free(l);
+}
+
+struct listener *listener_init(struct global *global, struct listener_config *cfg)
+{
+       struct listener *l = g_new0(struct listener, 1);
+
+       l->config = cfg;
+       l->global = global;
+
+       if (l->config->network != NULL) {
+               l->network = find_network(global, l->config->network);
+               if (l->network == NULL) {
+                       free_listener(l);
+                       return NULL;
+               }
+       }
+
+       l->global->listeners = g_list_append(l->global->listeners, l);
+
+       return l;
+}
+
+static void auto_add_listener(struct network *n, void *private_data)
+{
+       GList *gl;
+       struct listener *l;
+       struct listener_config *cfg;
+       
+
+       /* See if there is already a listener for n */
+       for (gl = n->global->listeners; gl; gl = gl->next) {
+               l = gl->data;
+
+               if (l->network == n || l->network == NULL)
+                       return;
+       }
+
+       cfg = g_new0(struct listener_config, 1);
+       cfg->network = g_strdup(n->config->name);
+       cfg->port = g_strdup_printf("%d", next_autoport(n->global));
+       l = listener_init(n->global, cfg);
+       start_listener(l);
+}
+
+gboolean init_listeners(struct global *global)
+{
+       GList *gl;
+       gboolean ret = TRUE;
+
+       if (global->config->auto_listener)
+               register_new_network_notify(global, auto_add_listener, NULL);
+
+       for (gl = global->config->listeners; gl; gl = gl->next) {
+               struct listener_config *cfg = gl->data;
+               struct listener *l = listener_init(global, cfg);
+
+               ret &= start_listener(l);
+       }
+       return ret;
+}
+
+void fini_listeners(struct global *global)
+{
+       GList *gl;
+       for(gl = global->listeners; gl; gl = gl->next) {
+               struct listener *l = gl->data;
+
+               if (l->active) 
+                       stop_listener(l);
+       }
+}
similarity index 63%
rename from mods/listener.h
rename to src/listener.h
index af0638e0f5fa42f2d989a35dc546621c88ff7f74..53c5bfabd9bdf620d15db7da59f519d338b3d918 100644 (file)
  */
 struct listener {
        int active:1;
-       int ssl:1;
        GList *incoming;
        GList *pending;
-       char *password;
-       char *address;
-       char *port;
+       struct listener_config *config;
        struct network *network;
-       gpointer ssl_credentials;
        struct global *global;
 };
 
@@ -30,12 +26,11 @@ struct listener_iochannel {
        gint watch_id;
 };
 
-G_MODULE_EXPORT struct listener *listener_init(const char *addr, const char *port);
+G_MODULE_EXPORT struct listener *listener_init(struct global *global, struct listener_config *);
 G_MODULE_EXPORT gboolean start_listener(struct listener *);
 G_MODULE_EXPORT gboolean stop_listener(struct listener *);
-
-#if defined(_WIN32) && !defined(LISTENER_CORE_BUILD)
-#pragma comment(lib,"liblistener.lib")
-#endif
+G_MODULE_EXPORT void fini_listeners(struct global *);
+G_MODULE_EXPORT void free_listener(struct listener *l);
+G_MODULE_EXPORT gboolean init_listeners(struct global *global);
 
 #endif
index 81cbe750f0cdef4ac162e99d135340cf04319946..7f2f34d77dd49a1c8dae2034242b2f7848f612ae 100644 (file)
@@ -95,6 +95,7 @@ static void clean_exit()
                save_configuration(my_global->config, path);
        nickserv_save(my_global, path);
        stop_unix_socket(my_global);
+       fini_listeners(my_global);
 
        free_global(my_global);
 
@@ -300,6 +301,8 @@ int main(int argc, char **argv)
 
        autoconnect_networks(my_global);
 
+       init_listeners(my_global);
+
        g_option_context_free(pc);
 
        atexit(clean_exit);
index 08ba845f9a74fb928145f4be0ce88c77f7c89d48..0930c8293ebd969f4d13bcc561ef705cf71952af 100644 (file)
@@ -19,6 +19,7 @@
 
 
 #include "internals.h"
+#include "ssl.h"
 
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
@@ -143,6 +144,53 @@ static void config_save_network(const char *dir, struct network_config *n)
        g_free(fn);
 }
 
+static void config_save_listeners(struct ctrlproxy_config *cfg, const char *path)
+{
+       GList *gl;
+       char *filename;
+       GKeyFile *kf; 
+       GError *error = NULL;
+       char *default_password;
+
+       default_password = g_key_file_get_string(cfg->keyfile, "listener", "password", NULL);
+
+       g_key_file_set_boolean(cfg->keyfile, "listener", "auto", cfg->auto_listener);
+
+       g_key_file_set_integer(cfg->keyfile, "listener", "autoport", cfg->listener_autoport);
+
+       filename = g_build_filename(path, "listener", NULL);
+
+       kf = g_key_file_new();
+
+       for (gl = cfg->listeners; gl; gl = gl->next) {
+               struct listener_config *l = gl->data;
+               char *tmp;
+
+               if (!l->address) 
+                       tmp = g_strdup(l->port);
+               else
+                       tmp = g_strdup_printf("%s:%s", l->address, l->port);
+
+               if (l->password != NULL && 
+                       !(default_password != NULL && strcmp(l->password, default_password) == 0)) 
+                       g_key_file_set_string(kf, tmp, "password", l->password);
+
+               if (l->network != NULL) {
+                       g_key_file_set_string(kf, tmp, "network", l->network);
+               }
+
+               g_key_file_set_boolean(kf, tmp, "ssl", l->ssl);
+
+               g_free(tmp);
+       }
+
+       if (!g_key_file_save_to_file(kf, filename, &error)) {
+               log_global(LOG_WARNING, "Unable to save to \"%s\": %s", filename, error->message);
+       }
+       
+       g_free(filename);
+}
+
 static void config_save_networks(const char *config_dir, GList *networks)
 {
        char *networksdir = g_build_filename(config_dir, "networks", NULL);
@@ -197,6 +245,8 @@ void save_configuration(struct ctrlproxy_config *cfg, const char *configuration_
 
        config_save_networks(configuration_dir, cfg->networks);
 
+       config_save_listeners(cfg, configuration_dir);
+
        i = 0;
        list = g_new0(char *, g_list_length(cfg->networks)+1);
        for (gl = cfg->networks; gl; gl = gl->next) {
@@ -426,6 +476,75 @@ static struct network_config *find_create_network_config(struct ctrlproxy_config
        return nc;
 }
 
+static void config_load_listeners(struct ctrlproxy_config *cfg)
+{
+       char *filename = g_build_filename(cfg->config_dir, "listener", NULL);
+       int i;
+       char **groups;
+       gsize size;
+       GKeyFile *kf;
+       char *default_password;
+       GError *error = NULL;
+
+       default_password = g_key_file_get_string(cfg->keyfile, "listener", "password", NULL);
+       if (g_key_file_has_key(cfg->keyfile, "listener", "auto", NULL))
+               cfg->auto_listener = g_key_file_get_boolean(cfg->keyfile, "listener", "auto", NULL);
+
+       if (g_key_file_has_key(cfg->keyfile, "listener", "autoport", NULL))
+               cfg->listener_autoport = g_key_file_get_integer(cfg->keyfile, "listener", "autoport", NULL);
+
+       kf = g_key_file_new();
+
+       if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) {
+               log_global(LOG_ERROR, "Can't parse configuration file '%s': %s", filename, error->message);
+               g_free(filename);
+               return;
+       }
+               
+       groups = g_key_file_get_groups(kf, &size);
+
+       for (i = 0; i < size; i++)
+       {
+               struct listener_config *l;
+               char *tmp;
+               
+               l = g_new0(struct listener_config, 1);
+
+               tmp = g_strdup(groups[i]);
+               l->port = strrchr(tmp, ':');
+               if (l->port != NULL) {
+                       l->address = tmp;
+                       *l->port = '\0';
+                       l->port++;
+               } else {
+                       l->port = tmp;
+                       l->address = NULL;
+               }
+
+               l->password = g_key_file_get_string(kf, groups[i], "password", NULL);
+               if (l->password == NULL)
+                       l->password = default_password;
+
+               if (g_key_file_has_key(kf, groups[i], "ssl", NULL))
+                       l->ssl = g_key_file_get_boolean(kf, groups[i], "ssl", NULL);
+
+#ifdef HAVE_GNUTLS
+               if (l->ssl)
+                       l->ssl_credentials = ssl_create_server_credentials(cfg, kf, groups[i]);
+#endif
+
+               if (g_key_file_has_key(kf, groups[i], "network", NULL))
+                       l->network = g_key_file_get_string(kf, groups[i], "network", NULL);
+
+               cfg->listeners = g_list_append(cfg->listeners, l);
+       }
+
+       g_strfreev(groups);
+       g_free(filename);
+}
+
+
+
 static void config_load_networks(struct ctrlproxy_config *cfg)
 {
        char *networksdir = g_build_filename(cfg->config_dir, "networks", NULL);
@@ -526,6 +645,8 @@ struct ctrlproxy_config *load_configuration(const char *dir)
 
        config_load_networks(cfg);
 
+       config_load_listeners(cfg);
+
        size = 0;
        autoconnect_list = g_key_file_get_string_list(kf, "global", "autoconnect", &size, NULL);
                
index 6d81e633e3c1886c38c6fed8e633637c1d6afc1f..d565efae3145851ec3f263630de8c5becc43073b 100644 (file)
@@ -81,6 +81,15 @@ struct network_config
        } type_settings; 
 };
 
+struct listener_config {
+       gboolean ssl;
+       gpointer ssl_credentials;
+       char *password;
+       char *address;
+       char *port;
+       char *network;
+};
+
 /**
  * Configuration
  */
@@ -99,6 +108,9 @@ struct ctrlproxy_config {
        gboolean report_time;
        int max_who_age;
        GKeyFile *keyfile;
+       GList *listeners;
+       gboolean auto_listener;
+       int listener_autoport;
 };
 
 /* config.c */
index 8f22a42169b9c6bb6097c286d7a887bbacf04289..544c7ee04bd4587f1ce4f80774a2dd4377e6d152 100644 (file)
--- a/src/ssl.h
+++ b/src/ssl.h
@@ -49,7 +49,7 @@ typedef enum {
 void ssl_cert_generate(const char *keyfile, const char *certfile,
                       const char *cafile);
 
-gpointer ssl_create_server_credentials(struct global *global
+gpointer ssl_create_server_credentials(struct ctrlproxy_config *cfg
                                                                           GKeyFile *kf, const char *group);
 
 #endif /* SSL_H */
index 09abc9afbe249f54459c0825e4cb76dacfa814ca..d65f4d0866cf464c47d8bbf350d6c1efe4acc892 100644 (file)
@@ -146,18 +146,18 @@ failed:
        log_global(LOG_WARNING, "TLS certificate generation failed");
 }
 
-gpointer ssl_create_server_credentials(struct global *global, 
+gpointer ssl_create_server_credentials(struct ctrlproxy_config *config,
                                                                           GKeyFile *kf, const char *group)
 {
        if (!g_key_file_has_key(kf, group, "keyfile", NULL) &&
                !g_key_file_has_key(kf, group, "certfile", NULL)) {
-               char *keyfile = g_build_filename(global->config->config_dir, "key.pem", NULL);
-               char *certfile = g_build_filename(global->config->config_dir, "cert.pem", NULL);
+               char *keyfile = g_build_filename(config->config_dir, "key.pem", NULL);
+               char *certfile = g_build_filename(config->config_dir, "cert.pem", NULL);
                g_key_file_set_string(kf, group, "keyfile", keyfile);
                g_key_file_set_string(kf, group, "certfile", certfile);
                if (!g_file_test(keyfile, G_FILE_TEST_EXISTS) && 
                        !g_file_test(certfile, G_FILE_TEST_EXISTS)) {
-                       char *cafile = g_build_filename(global->config->config_dir, "ca.pem", NULL);
+                       char *cafile = g_build_filename(config->config_dir, "ca.pem", NULL);
                        ssl_cert_generate(keyfile, certfile, cafile);
                        g_free(cafile);
                }