Simplify configuration a bit.
authorJelmer Vernooij <jelmer@samba.org>
Sat, 1 Sep 2007 16:00:17 +0000 (18:00 +0200)
committerJelmer Vernooij <jelmer@samba.org>
Sat, 1 Sep 2007 16:00:17 +0000 (18:00 +0200)
Makefile.settings.in
NEWS
config.default
config.example
mods/socks.c [deleted file]
src/client.c
src/listener.c
src/listener.h
src/settings.c
src/settings.h
src/socks.h [new file with mode: 0644]

index 080a6e0f0225b6cecd0484f9e77152717316f207..80e264e2fe2c8f87a3aca98732699654739e7a4c 100644 (file)
@@ -20,7 +20,7 @@ EXEEXT = @EXEEXT@
 OBJEXT = @OBJEXT@
 CFLAGS = @CFLAGS@ @COMMON_CFLAGS@
 MODS_SHARED = log_irssi auto_away log_custom \
-       socks @MODS_SHARED@
+       @MODS_SHARED@
 LDFLAGS = @LDFLAGS@
 BINS = @BINS@
 scriptdir = $(cdatadir)/scripts
diff --git a/NEWS b/NEWS
index f5ee0604f7437a5cd037f4f0b92815899e7e3742..13ae6055dafe3b7390e34ee67a43632300d0b6c9 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,11 @@ Ctrlproxy 3.0.4 UNRELEASED
 
        * Integrate listeners into the core.
 
+       * Integrate socks support into listener code. Socks connections 
+         can now be accepted at the same port as other connections.
+
+       * Allow configuring a single listener in ~/.ctrlproxy/config.
+
 
 Ctrlproxy 3.0.3 2007-07-22
 
index 901b6f43ada5096a75c442db511a8e57d8859fb0..0147fce1b760e02e6a5f3dac6529e6aad78c3f9d 100644 (file)
@@ -3,7 +3,6 @@
 # Please adapt to your needs!
 
 [global]
-
 # Replication mechanism to use (some other IRC proxies call this backlog)
 # Possible values: none, simple, highlight, lastdisconnect
 # Meanings:
 #                         containing 'matches' (see below)
 replication = none
 
+# Prepend all lines in replication with the time a line was received when replicating
+report-time = false
+
+# What words to look for when remembering lines 
+# (in case "replication = highlight")
+# Seperate using semicolons
+# matches=ctrlproxy;foobar
+
+# Port at which CtrlProxy should listen for new connections
+# Connections can be plain IRC connections or using the SOCKS protocol.
+port=6680
+
+# Password for logging in to ctrlproxy
+password=
+
+# Set "bind" to make ctrlproxy only listen on a specific IP address:
+# Example: bind=192.168.4.3
+
 # Override motd-file location
 #motd-file = /tmp/my-motd
 
-# Prepend all lines with the time a line was received when replicating
-report-time = false
-
+# Save state to configuration file on exit
 autosave = true
 
 # Networks to connect to on startup. Seperate by semicolons
@@ -27,8 +42,11 @@ autoconnect = admin
 # autoconnect = admin;irc.oftc.net;irc.freenode.net;
 
 # Support for interfacing to ctrlproxy 
-# using /MSG ctrlproxy or /CTRLPROXY -->
-[admin]
+# using /MSG ctrlproxy 
+admin-user = ctrlproxy
+
+# Send ctrlproxy log messages to the admin channel
+admin-log = true
 
 # Irssi-style logging
 [log-irssi]
@@ -36,23 +54,9 @@ autoconnect = admin
 
 # Work as a socks proxy
 #[socks]
-#port = 8800
 #allow = jelmer:secret, foo:bar
 
-# Make sure messages are not sent too fast after each other 
-# (prevents being kicked by the server for 'Excess flooding'
-#[antiflood]
-
 # Automatically set AWAY after a certain period of time
 #[auto-away]
 #message = I'm currently away, sorry!
 #time = 300 # in seconds
-
-#[nickserv]
-# Learn new nickserv user/password combinations by interpreting traffic to server
-#learn = true
-
-#[listen]
-# Listen for network connections start port 6667
-# auto = true
-# autoport = 6667
index baac30961fb4f188eeea1d5d7e5a33a4e1c70c8f..3b7982fe1a54c50b84ae701acec85193102c5945 100644 (file)
@@ -3,27 +3,43 @@
 # Please adapt to your needs!
 
 [global]
-
-# Replication mechanism to use
+# Replication mechanism to use (some other IRC proxies call this backlog)
 # Possible values: none, simple, highlight, lastdisconnect
+# Meanings:
+#      none: No backlog
+#      simple: Send backlog since the user last said something
+#      lastdisconnect: Send backlog since the users' last disconnect
+#      highlight: Send backlog since last connect, but only lines 
+#                         containing 'matches' (see below)
 replication = none
 
-# Override motd-file location
-#motd-file = /tmp/my-motd
+# Prepend all lines in replication with the time a line was received when replicating
+report-time = false
 
-#Disable autosave on exit
-#autosave = False
-#
 # What words to look for when remembering lines 
 # (in case "replication = highlight")
 # Seperate using semicolons
-matches=ctrlproxy;foobar
+# matches=ctrlproxy;foobar
+
+# Port at which CtrlProxy should listen for new connections
+# Connections can be plain IRC connections or using the SOCKS protocol.
+port=6680
+
+# Password for logging in to ctrlproxy
+password=
+
+# Set "bind" to make ctrlproxy only listen on a specific IP address:
+# Example: bind=192.168.4.3
 
-# Prepend all lines with the time a line was received when replicating
-report-time=False
+# Override motd-file location
+#motd-file = /tmp/my-motd
+
+# Save state to configuration file on exit
+autosave = true
 
-# List of networks/servers to autoconnect to
-autoconnect=irc.oftc.net;irc.freenode.net;
+# Networks to connect to on startup. Seperate by semicolons
+autoconnect = admin
+# autoconnect = admin;irc.oftc.net;irc.freenode.net;
 
 # Support for interfacing to ctrlproxy 
 # using /MSG ctrlproxy or /CTRLPROXY -->
@@ -31,28 +47,13 @@ autoconnect=irc.oftc.net;irc.freenode.net;
 
 # Irssi-style logging
 [log-irssi]
-logfile = /home/jelmer/tmp/ctrlproxy
+logfile = /home/jelmer/tmp/ctrlproxy
 
 # Work as a socks proxy
 #[socks]
-#port = 8800
 #allow = jelmer:secret, foo:bar
 
-# Make sure messages are not sent too fast after each other 
-# (prevents being kicked by the server for 'Excess flooding'
-#[antiflood]
-
 # Automatically set AWAY after a certain period of time
 #[auto-away]
 #message = I'm currently away, sorry!
 #time = 300 # in seconds
-
-#[nickserv]
-# Learn new nickserv user/password combinations by interpreting traffic to server
-#learn = true
-
-[listener]
-# Listen for network connections start port 6667
-# auto = true
-# autoport = 6667
-password = secret
diff --git a/mods/socks.c b/mods/socks.c
deleted file mode 100644 (file)
index 6296c43..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
-/* 
-       ctrlproxy: A modular IRC proxy
-       (c) 2005 Jelmer Vernooij <jelmer@nl.linux.org>
-       SOCKS server (see RFC 1928, 1929 and 1961)
-
-       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.
-*/
-
-/* TODO:
- *  - ipv6 support (listen)
- *  - support for ipv4 and ipv6 atyp's
- *  - support for gssapi method
- *  - support for ident method
- */
-
-#include "ctrlproxy.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>
-#include <netdb.h>
-
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "socks"
-
-#define SOCKS_VERSION 0x05
-#define DEFAULT_SOCKS_PORT 1080
-
-#define SOCKS_METHOD_NOAUTH                    0x00
-#define SOCKS_METHOD_GSSAPI            0x01
-#define SOCKS_METHOD_USERNAME_PW       0x02
-#define SOCKS_METHOD_NOACCEPTABLE      0xFF
-
-#define ATYP_IPV4                                      0x01
-#define ATYP_FQDN                                      0x03
-#define ATYP_IPV6                                      0x04
-
-#define CMD_CONNECT                                    0x01
-#define CMD_BIND                                       0x02
-#define CMD_UDP_ASSOCIATE                      0x03
-
-#define REP_OK                                         0x00
-#define REP_GENERAL_FAILURE                    0x01
-#define REP_NOT_ALLOWED                                0x02
-#define REP_NET_UNREACHABLE                    0x03
-#define REP_HOST_UNREACHABLE           0x04
-#define REP_CONN_REFUSED                       0x05
-#define REP_TTL_EXPIRED                                0x06
-#define REP_CMD_NOT_SUPPORTED          0x07
-#define REP_ATYP_NOT_SUPPORTED         0x08
-
-static GIOChannel *server_channel = NULL;
-static int server_channel_in = -1;
-enum socks_state { STATE_NEW = 0, STATE_AUTH, STATE_NORMAL };
-
-struct allow_rule {
-       const char *username;
-       const char *password;
-};
-
-static GList *allow_rules = NULL;
-static GList *pending_clients = NULL;
-
-struct socks_method;
-struct socks_client
-{
-       GIOChannel *connection;
-       const char *user;
-       const char *password;
-       gint watch_id;
-       struct socks_method *method;
-       enum socks_state state;
-       void *method_data;
-       struct sockaddr *clientname;
-       socklen_t clientname_len;
-       struct global *global;
-};
-
-static gboolean socks_reply(GIOChannel *ioc, guint8 err, guint8 atyp, guint8 data_len, gchar *data, guint16 port)
-{
-       gchar *header = g_new0(gchar, 7 + data_len);
-       GIOStatus status;
-       gsize read;
-
-       header[0] = SOCKS_VERSION;
-       header[1] = err;
-       header[2] = 0x0; /* Reserved */
-       header[3] = atyp;
-       memcpy(header+4, data, data_len);
-       *((guint16 *)(header+4+data_len)) = htons(port);
-
-       status = g_io_channel_write_chars(ioc, header, 6 + data_len, &read, NULL);
-
-       g_free(header);
-
-       g_io_channel_flush(ioc, NULL);
-
-       return (err == REP_OK);
-}
-
-static gboolean socks_error(GIOChannel *ioc, guint8 err)
-{
-       guint8 data = 0x0;
-       return socks_reply(ioc, err, ATYP_FQDN, 1, (gchar *)&data, 0);
-}
-
-static gboolean anon_acceptable(struct socks_client *cl)
-{
-       return FALSE; /* Don't allow anonymous connects */
-}
-
-static gboolean pass_acceptable(struct socks_client *cl)
-{
-       return TRUE; /* FIXME: Check whether there is a password specified */
-}
-
-static gboolean pass_handle_data(struct socks_client *cl)
-{
-       GList *gl;
-       gchar header[2];
-       gsize read;
-       GIOStatus status;
-       gchar uname[0x100], pass[0x100];
-
-       status = g_io_channel_read_chars(cl->connection, header, 2, &read, NULL);
-       if (status != G_IO_STATUS_NORMAL) {
-               return FALSE;
-       }
-
-       if (header[0] != SOCKS_VERSION && header[0] != 0x1) {
-               log_global(LOG_WARNING, "Client suddenly changed socks uname/pwd version to %x", header[0]);
-               return socks_error(cl->connection, REP_GENERAL_FAILURE);
-       }
-
-       status = g_io_channel_read_chars(cl->connection, uname, header[1], &read, NULL);
-       if (status != G_IO_STATUS_NORMAL) {
-               return FALSE;
-       }
-
-       uname[(guint8)header[1]] = '\0';
-
-       status = g_io_channel_read_chars(cl->connection, header, 1, &read, NULL);
-       if (status != G_IO_STATUS_NORMAL) {
-               return FALSE;
-       }
-
-       status = g_io_channel_read_chars(cl->connection, pass, header[0], &read, NULL);
-       if (status != G_IO_STATUS_NORMAL) {
-               return FALSE;
-       }
-
-       pass[(guint8)header[0]] = '\0';
-
-       header[0] = 0x1;
-       header[1] = 0x0; /* set to non-zero if invalid */
-
-       for (gl = allow_rules; gl; gl = gl->next)
-       {
-               struct allow_rule *r = gl->data;
-
-               if (r->password == NULL || r->username == NULL) 
-                       continue;
-
-               if (strcmp(r->username, uname)) 
-                       continue;
-
-               if (strcmp(r->password, pass))
-                       continue;
-
-               break;
-       }
-
-       header[1] = (gl == NULL);
-
-       status = g_io_channel_write_chars(cl->connection, header, 2, &read, NULL);
-       if (status != G_IO_STATUS_NORMAL) {
-               return FALSE;
-       } 
-
-       g_io_channel_flush(cl->connection, NULL);
-
-       if (header[1] == 0x0) {
-               cl->state = STATE_NORMAL;               
-               return TRUE;
-       } else {
-               log_global(LOG_WARNING, "Password mismatch for user %s", uname);
-               return FALSE;
-       }
-}
-
-static struct network *socks_map_network_fqdn(struct global *global, const char *hostname, guint16 port)
-{
-       return find_network_by_hostname(global, hostname, port, TRUE);
-}
-
-struct socks_method {
-       gint id;
-       const char *name;
-       gboolean (*acceptable) (struct socks_client *cl);
-       gboolean (*handle_data) (struct socks_client *cl);
-} socks_methods[] = {
-       { SOCKS_METHOD_NOAUTH, "none", anon_acceptable, NULL },
-       { SOCKS_METHOD_GSSAPI, "gssapi", NULL, NULL },
-       { SOCKS_METHOD_USERNAME_PW, "username/password", pass_acceptable, pass_handle_data },
-       { -1, NULL, NULL }
-};
-
-static gboolean handle_client_data (GIOChannel *ioc, GIOCondition o, gpointer data)
-{
-       struct socks_client *cl = data;
-       GIOStatus status;
-       int i;
-
-       if (o == G_IO_HUP) {
-               pending_clients = g_list_remove(pending_clients, cl);
-               g_free(cl->clientname);
-               g_free(cl);
-               return FALSE;
-       }
-
-       if (cl->state == STATE_NEW) {
-               gchar header[2];
-               gchar methods[0x100];
-               gsize read;
-               status = g_io_channel_read_chars(ioc, header, 2, &read, NULL);
-               if (status != G_IO_STATUS_NORMAL) {
-                       return FALSE;
-               }
-
-               if (header[0] != SOCKS_VERSION) 
-               {
-                       log_global(LOG_WARNING, "Ignoring client with socks version %d", header[0]);
-                       return FALSE;
-               }
-
-               /* None by default */
-               cl->method = NULL;
-
-               status = g_io_channel_read_chars(ioc, methods, header[1], &read, NULL);
-               if (status != G_IO_STATUS_NORMAL) {
-                       return FALSE;
-               }
-               for (i = 0; i < header[1]; i++) {
-                       int j;
-                       for (j = 0; socks_methods[j].id != -1; j++)
-                       {
-                               if (socks_methods[j].id == methods[i] && 
-                                       socks_methods[j].acceptable &&
-                                       socks_methods[j].acceptable(cl)) {
-                                       cl->method = &socks_methods[j];
-                                       break;
-                               }
-                       }
-               }
-
-               header[0] = SOCKS_VERSION;
-               header[1] = cl->method?cl->method->id:SOCKS_METHOD_NOACCEPTABLE;
-
-               status = g_io_channel_write_chars(ioc, header, 2, &read, NULL);
-               if (status != G_IO_STATUS_NORMAL) {
-                       return FALSE;
-               } 
-
-               g_io_channel_flush(ioc, NULL);
-
-               if (!cl->method) {
-                       log_global(LOG_WARNING, "Refused client because no valid method was available");
-                       return FALSE;
-               }
-
-               log_global(LOG_INFO, "Accepted socks client authenticating using %s", cl->method->name);
-
-               if (!cl->method->handle_data) {
-                       cl->state = STATE_NORMAL;
-               } else {
-                       cl->state = STATE_AUTH;
-               }
-       } else if (cl->state == STATE_AUTH) {
-               return cl->method->handle_data(cl);
-       } else if (cl->state == STATE_NORMAL) {
-               gchar header[4];
-               gsize read;
-
-               status = g_io_channel_read_chars(ioc, header, 4, &read, NULL);
-               if (status != G_IO_STATUS_NORMAL) {
-                       return FALSE;
-               }
-
-               if (header[0] != SOCKS_VERSION) {
-                       log_global(LOG_WARNING, "Client suddenly changed socks version to %x", header[0]);
-                       return socks_error(ioc, REP_GENERAL_FAILURE);
-               }
-
-               if (header[1] != CMD_CONNECT) {
-                       log_global(LOG_WARNING, "Client used unknown command %x", header[1]);
-                       return socks_error(ioc, REP_CMD_NOT_SUPPORTED);
-               }
-
-               /* header[2] is reserved */
-       
-               switch (header[3]) {
-                       case ATYP_IPV4: 
-                               return socks_error(ioc, REP_ATYP_NOT_SUPPORTED);
-
-                       case ATYP_IPV6:
-                               return socks_error(ioc, REP_ATYP_NOT_SUPPORTED);
-
-                       case ATYP_FQDN:
-                               {
-                                       char hostname[0x100];
-                                       guint16 port;
-                                       char *desc;
-                                       struct network *result;
-                                       
-                                       status = g_io_channel_read_chars(ioc, header, 1, &read, NULL);
-                                       status = g_io_channel_read_chars(ioc, hostname, header[0], &read, NULL);
-                                       hostname[(guint8)header[0]] = '\0';
-
-                                       status = g_io_channel_read_chars(ioc, header, 2, &read, NULL);
-                                       port = ntohs(*(guint16 *)header);
-
-                                       log_global(LOG_INFO, "Request to connect to %s:%d", hostname, port);
-
-                                       result = socks_map_network_fqdn(cl->global, hostname, port);
-
-                                       if (!result) {
-                                               log_global(LOG_WARNING, "Unable to return network matching %s:%d", hostname, port);
-                                               return socks_error(ioc, REP_NET_UNREACHABLE);
-                                       } 
-
-                                       if (result->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED && 
-                                               !connect_network(result)) {
-                                               log_network(LOG_ERROR, result, "Unable to connect");
-                                               return socks_error(ioc, REP_NET_UNREACHABLE);
-                                       }
-
-                                       if (result->config->type == NETWORK_TCP) {
-#ifdef HAVE_IPV6
-                                               struct sockaddr_in6 *name6; 
-#endif
-                                               struct sockaddr_in *name4; 
-                                               int atyp, len, port;
-                                               gchar *data;
-
-#ifdef HAVE_IPV6
-                                               name6 = (struct sockaddr_in6 *)result->connection.data.tcp.local_name;
-#endif
-                                               name4 = (struct sockaddr_in *)result->connection.data.tcp.local_name;
-
-                                               if (name4->sin_family == AF_INET) {
-                                                       atyp = ATYP_IPV4;
-                                                       data = (gchar *)&name4->sin_addr;
-                                                       len = 4;
-                                                       port = name4->sin_port;
-#ifdef HAVE_IPV6
-                                               } else if (name6->sin6_family == AF_INET6) {
-                                                       atyp = ATYP_IPV6;
-                                                       data = (gchar *)&name6->sin6_addr;
-                                                       len = 16;
-                                                       port = name6->sin6_port;
-#endif
-                                               } else {
-                                                       log_network(LOG_ERROR, result, "Unable to obtain local address for connection to server");
-                                                       return socks_error(ioc, REP_NET_UNREACHABLE);
-                                               }
-                                                       
-                                               socks_reply(ioc, REP_OK, atyp, len, data, port); 
-                                               
-                                       } else {
-                                               gchar *data = g_strdup("xlocalhost");
-                                               data[0] = strlen(data+1);
-                                               
-                                               socks_reply(ioc, REP_OK, ATYP_FQDN, data[0]+1, data, 1025);
-                                       }
-
-                                       desc = g_io_channel_ip_get_description(ioc);
-                                       client_init(result, ioc, desc);
-                                       g_free(desc);
-
-                                       pending_clients = g_list_remove(pending_clients, cl);
-
-                                       g_free(cl->clientname);
-                                       g_free(cl);
-
-                                       return FALSE;
-                               }
-                       default:
-                               return socks_error(ioc, REP_ATYP_NOT_SUPPORTED);
-
-               }
-       }
-       
-       return TRUE;
-}
-
-static gboolean handle_new_client (GIOChannel *ioc, GIOCondition o, gpointer data)
-{
-       /* Spawn off new client */
-       struct socks_client *cl;
-       int ns;
-       
-       cl = g_new0(struct socks_client, 1);
-       cl->global = data;
-#ifdef HAVE_IPV6
-       cl->clientname_len = sizeof(struct sockaddr_in6);
-#else
-       cl->clientname_len = sizeof(struct sockaddr_in);
-#endif
-       cl->clientname = g_malloc(cl->clientname_len);
-       
-       ns = accept(g_io_channel_unix_get_fd(ioc), cl->clientname, &cl->clientname_len);
-       if (!ns) {
-               g_free(cl->clientname);
-               g_free(cl);
-               log_global(LOG_ERROR, "Unable to accept connection");
-               return TRUE;
-       }
-       
-       cl->connection = g_io_channel_unix_new(ns);
-       g_io_channel_set_close_on_unref(cl->connection, TRUE);
-       cl->state = STATE_NEW;
-       g_io_channel_set_encoding(cl->connection, NULL, NULL);
-       g_io_channel_set_flags(cl->connection, G_IO_FLAG_NONBLOCK, NULL);
-       cl->watch_id = g_io_add_watch(cl->connection, G_IO_IN | G_IO_HUP, handle_client_data, cl);
-       g_io_channel_unref(cl->connection);
-
-       pending_clients = g_list_append(pending_clients, cl);
-       
-       return TRUE;
-}
-
-void kill_pending_client(struct socks_client *sc)
-{
-       g_source_remove(sc->watch_id);
-
-       g_free(sc->clientname);
-       g_free(sc);
-
-       pending_clients = g_list_remove(pending_clients, sc);
-}
-
-static void fini_plugin(void)
-{
-       while (pending_clients) {
-               struct socks_client *sc = pending_clients->data;
-               kill_pending_client(sc);
-       }
-
-       /* Close port */
-       if (server_channel_in != -1)
-               g_source_remove(server_channel_in);
-}
-
-static void load_config(struct global *global)
-{
-       int sock;
-       const int on = 1;
-       struct sockaddr_in addr;
-       guint16 port;
-       GKeyFile *kf = global->config->keyfile;
-       gsize size, i;
-       char **allows;
-
-       allows = g_key_file_get_string_list(kf, "socks", "allow", &size, NULL);
-
-       if (allows == NULL)
-               return;
-
-       for (i = 0; i < size; i++) {
-               struct allow_rule *r = g_new0(struct allow_rule, 1);
-               char **parts = g_strsplit(allows[i], ":", 2);
-                                       
-               r->username = parts[0];
-               r->password = parts[1];
-
-               g_free(parts);
-               allow_rules = g_list_append(allow_rules, r);
-       }
-
-       g_strfreev(allows);
-
-       if (g_key_file_has_key(kf, "socks", "port", NULL)) 
-               port = g_key_file_get_integer(kf, "socks", "port", NULL);
-       else 
-               port = DEFAULT_SOCKS_PORT;
-
-       sock = socket(PF_INET, SOCK_STREAM, 0);
-       if (sock < 0) {
-               log_global(LOG_ERROR, "error creating socket: %s", strerror(errno));
-               return;
-       }
-
-    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
-       addr.sin_family = AF_INET;
-       addr.sin_port = htons(port);
-       addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
-       if (bind (sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-               log_global(LOG_ERROR, "Unable to bind to port %d: %s", port, strerror(errno));
-               return;
-       }
-
-       if (listen(sock, 5) < 0) {
-               log_global(LOG_ERROR, "error listening on socket: %s", strerror(errno));
-               return;
-       }
-
-       server_channel = g_io_channel_unix_new(sock);
-       g_io_channel_set_close_on_unref(server_channel, TRUE);
-
-       if (server_channel == NULL) {
-               log_global(LOG_ERROR, "Unable to create GIOChannel for server socket");
-               return;
-       }
-
-       server_channel_in = g_io_add_watch(server_channel, G_IO_IN, handle_new_client, global);
-       g_io_channel_unref(server_channel);
-
-       log_global(LOG_INFO, "Listening for SOCKS connections on port %d", port);
-}
-
-static gboolean init_plugin(void)
-{
-       register_load_config_notify(load_config);
-       atexit(fini_plugin);
-       return TRUE;
-}
-
-struct plugin_ops plugin = {
-       .name = "socks",
-       .version = 0,
-       .init = init_plugin,
-};
index cf2a315f671c84641b1f22548d1b69fb52a414ce..462f8b093c954d65592654895fc82e307700bcce 100644 (file)
@@ -114,9 +114,9 @@ static gboolean process_from_client(struct client *c, struct line *l)
                                                 "Please register only once per session", NULL);
        } else if (!g_strcasecmp(l->args[0], "CTRLPROXY")) {
                admin_process_command(c, l, 1);
-       } else if (!c->network->global->config->admin_noprivmsg && 
+       } else if (c->network->global->config->admin_user != NULL && 
                           !g_strcasecmp(l->args[0], "PRIVMSG") && 
-                          !g_strcasecmp(l->args[1], "CTRLPROXY")) {
+                          !g_strcasecmp(l->args[1], c->network->global->config->admin_user)) {
                admin_process_command(c, l, 2);
        } else if (!g_strcasecmp(l->args[0], "PRIVMSG") && l->argc > 2 && 
                        l->args[2][0] == '\001' && 
index 43aa3c68ae441670f341faef26516803f5afc5fc..6e4c138450d60545b68ee26b0cfe04948148817c 100644 (file)
@@ -23,6 +23,7 @@
 #include "irc.h"
 #include "listener.h"
 #include "ssl.h"
+#include "socks.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 
 static GIConv iconv = (GIConv)-1;
 
+static gboolean kill_pending_client(struct pending_client *pc)
+{
+       pc->listener->pending = g_list_remove(pc->listener->pending, pc);
+
+       g_source_remove(pc->watch_id);
+
+       g_free(pc->clientname);
+
+       g_free(pc);
+
+       return TRUE;
+}
+
 static gboolean handle_client_receive(GIOChannel *c, GIOCondition condition, gpointer data) 
 {
        struct line *l;
-       struct listener *listener = data;
+       struct pending_client *pc = data;
        GError *error = NULL;
        GIOStatus status;
 
+       if (condition == G_IO_HUP) {
+               kill_pending_client(pc);
+               return FALSE;   
+       }
+
        g_assert(c != NULL);
 
        while ((status = irc_recv_line(c, iconv, &error, &l)) == G_IO_STATUS_NORMAL) {
@@ -59,25 +78,25 @@ static gboolean handle_client_receive(GIOChannel *c, GIOCondition condition, gpo
                        continue;
                }
 
-               if (listener->config->password == NULL) {
-                       log_network(LOG_WARNING, listener->network, "No password set, allowing client _without_ authentication!");
+               if (pc->listener->config->password == NULL) {
+                       log_network(LOG_WARNING, pc->listener->network, "No password set, allowing client _without_ authentication!");
                }
 
                if (!g_strcasecmp(l->args[0], "PASS")) {
                        char *desc;
-                       struct network *n = listener->network;
+                       struct network *n = pc->listener->network;
                        gboolean authenticated = FALSE;
 
-                       if (listener->config->password == NULL) {
+                       if (pc->listener->config->password == NULL) {
                                authenticated = TRUE;
-                       } else if (strcmp(l->args[1], listener->config->password) == 0) {
+                       } else if (strcmp(l->args[1], pc->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] == ':') {
+                       } else if (strncmp(l->args[1], pc->listener->config->password, strlen(pc->listener->config->password)) == 0 &&
+                                          l->args[1][strlen(pc->listener->config->password)+1] == ':') {
                                authenticated = TRUE;
-                               n = find_network(listener->global, l->args[1]+strlen(listener->config->password)+1);
+                               n = find_network(pc->listener->global, l->args[1]+strlen(pc->listener->config->password)+1);
                                if (n == NULL) {
-                                       log_network(LOG_WARNING, listener->network, "User tried to log in with incorrect password!");
+                                       log_network(LOG_WARNING, pc->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);
@@ -91,10 +110,12 @@ static gboolean handle_client_receive(GIOChannel *c, GIOCondition condition, gpo
                                desc = g_io_channel_ip_get_description(c);
                                client_init(n, c, desc);
 
+                               kill_pending_client(pc);
+
                                free_line(l); 
                                return FALSE;
                        } else {
-                               log_network(LOG_WARNING, listener->network, "User tried to log in with incorrect password!");
+                               log_network(LOG_WARNING, pc->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);
@@ -116,6 +137,7 @@ static gboolean handle_client_receive(GIOChannel *c, GIOCondition condition, gpo
 static gboolean handle_new_client(GIOChannel *c_server, GIOCondition condition, void *_listener)
 {
        struct listener *listener = _listener;
+       struct pending_client *pc;
        GIOChannel *c;
        int sock = accept(g_io_channel_unix_get_fd(c_server), NULL, 0);
 
@@ -139,11 +161,14 @@ static gboolean handle_new_client(GIOChannel *c_server, GIOCondition condition,
        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);
+       pc = g_new0(struct pending_client, 1);
+       pc->connection = c;
+       pc->watch_id = g_io_add_watch(c, G_IO_IN | G_IO_HUP, handle_client_receive, pc);
+       pc->listener = listener;
 
        g_io_channel_unref(c);
 
-       listener->pending = g_list_append(listener->pending, c);
+       listener->pending = g_list_append(listener->pending, pc);
 
        return TRUE;
 }
@@ -342,3 +367,330 @@ void fini_listeners(struct global *global)
                        stop_listener(l);
        }
 }
+
+
+/* TODO:
+ *  - support for ipv4 and ipv6 atyp's
+ *  - support for gssapi method
+ *  - support for ident method
+ */
+
+static gboolean socks_reply(GIOChannel *ioc, guint8 err, guint8 atyp, guint8 data_len, gchar *data, guint16 port)
+{
+       gchar *header = g_new0(gchar, 7 + data_len);
+       GIOStatus status;
+       gsize read;
+
+       header[0] = SOCKS_VERSION;
+       header[1] = err;
+       header[2] = 0x0; /* Reserved */
+       header[3] = atyp;
+       memcpy(header+4, data, data_len);
+       *((guint16 *)(header+4+data_len)) = htons(port);
+
+       status = g_io_channel_write_chars(ioc, header, 6 + data_len, &read, NULL);
+
+       g_free(header);
+
+       g_io_channel_flush(ioc, NULL);
+
+       return (err == REP_OK);
+}
+
+static gboolean socks_error(GIOChannel *ioc, guint8 err)
+{
+       guint8 data = 0x0;
+       return socks_reply(ioc, err, ATYP_FQDN, 1, (gchar *)&data, 0);
+}
+
+static gboolean anon_acceptable(struct pending_client *cl)
+{
+       return FALSE; /* Don't allow anonymous connects */
+}
+
+static gboolean pass_acceptable(struct pending_client *cl)
+{
+       return TRUE; /* FIXME: Check whether there is a password specified */
+}
+
+static gboolean pass_handle_data(struct pending_client *cl)
+{
+       GList *gl;
+       gchar header[2];
+       gsize read;
+       GIOStatus status;
+       gchar uname[0x100], pass[0x100];
+
+       status = g_io_channel_read_chars(cl->connection, header, 2, &read, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               return FALSE;
+       }
+
+       if (header[0] != SOCKS_VERSION && header[0] != 0x1) {
+               log_global(LOG_WARNING, "Client suddenly changed socks uname/pwd version to %x", header[0]);
+               return socks_error(cl->connection, REP_GENERAL_FAILURE);
+       }
+
+       status = g_io_channel_read_chars(cl->connection, uname, header[1], &read, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               return FALSE;
+       }
+
+       uname[(guint8)header[1]] = '\0';
+
+       status = g_io_channel_read_chars(cl->connection, header, 1, &read, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               return FALSE;
+       }
+
+       status = g_io_channel_read_chars(cl->connection, pass, header[0], &read, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               return FALSE;
+       }
+
+       pass[(guint8)header[0]] = '\0';
+
+       header[0] = 0x1;
+       header[1] = 0x0; /* set to non-zero if invalid */
+
+       for (gl = cl->listener->config->allow_rules; gl; gl = gl->next)
+       {
+               struct allow_rule *r = gl->data;
+
+               if (r->password == NULL || r->username == NULL) 
+                       continue;
+
+               if (strcmp(r->username, uname)) 
+                       continue;
+
+               if (strcmp(r->password, pass))
+                       continue;
+
+               break;
+       }
+
+       header[1] = (gl == NULL);
+
+       status = g_io_channel_write_chars(cl->connection, header, 2, &read, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               return FALSE;
+       } 
+
+       g_io_channel_flush(cl->connection, NULL);
+
+       if (header[1] == 0x0) {
+               cl->socks.state = SOCKS_STATE_NORMAL;           
+               return TRUE;
+       } else {
+               log_global(LOG_WARNING, "Password mismatch for user %s", uname);
+               return FALSE;
+       }
+}
+
+static struct socks_method {
+       gint id;
+       const char *name;
+       gboolean (*acceptable) (struct pending_client *cl);
+       gboolean (*handle_data) (struct pending_client *cl);
+} socks_methods[] = {
+       { SOCKS_METHOD_NOAUTH, "none", anon_acceptable, NULL },
+       { SOCKS_METHOD_GSSAPI, "gssapi", NULL, NULL },
+       { SOCKS_METHOD_USERNAME_PW, "username/password", pass_acceptable, pass_handle_data },
+       { -1, NULL, NULL }
+};
+
+static gboolean handle_client_data (GIOChannel *ioc, GIOCondition o, gpointer data)
+{
+       struct pending_client *cl = data;
+       GIOStatus status;
+       int i;
+
+       if (cl->socks.state == SOCKS_STATE_NEW) {
+               gchar header[2];
+               gchar methods[0x100];
+               gsize read;
+               status = g_io_channel_read_chars(ioc, header, 2, &read, NULL);
+               if (status != G_IO_STATUS_NORMAL) {
+                       kill_pending_client(cl);
+                       return FALSE;
+               }
+
+               if (header[0] != SOCKS_VERSION) 
+               {
+                       log_global(LOG_WARNING, "Ignoring client with socks version %d", header[0]);
+                       kill_pending_client(cl);
+                       return FALSE;
+               }
+
+               /* None by default */
+               cl->socks.method = NULL;
+
+               status = g_io_channel_read_chars(ioc, methods, header[1], &read, NULL);
+               if (status != G_IO_STATUS_NORMAL) {
+                       kill_pending_client(cl);
+                       return FALSE;
+               }
+               for (i = 0; i < header[1]; i++) {
+                       int j;
+                       for (j = 0; socks_methods[j].id != -1; j++)
+                       {
+                               if (socks_methods[j].id == methods[i] && 
+                                       socks_methods[j].acceptable &&
+                                       socks_methods[j].acceptable(cl)) {
+                                       cl->socks.method = &socks_methods[j];
+                                       break;
+                               }
+                       }
+               }
+
+               header[0] = SOCKS_VERSION;
+               header[1] = cl->socks.method?cl->socks.method->id:SOCKS_METHOD_NOACCEPTABLE;
+
+               status = g_io_channel_write_chars(ioc, header, 2, &read, NULL);
+               if (status != G_IO_STATUS_NORMAL) {
+                       kill_pending_client(cl);
+                       return FALSE;
+               } 
+
+               g_io_channel_flush(ioc, NULL);
+
+               if (!cl->socks.method) {
+                       log_global(LOG_WARNING, "Refused client because no valid method was available");
+                       kill_pending_client(cl);
+                       return FALSE;
+               }
+
+               log_global(LOG_INFO, "Accepted socks client authenticating using %s", cl->socks.method->name);
+
+               if (!cl->socks.method->handle_data) {
+                       cl->socks.state = SOCKS_STATE_NORMAL;
+               } else {
+                       cl->socks.state = SOCKS_STATE_AUTH;
+               }
+       } else if (cl->socks.state == SOCKS_STATE_AUTH) {
+               gboolean ret;
+               ret = cl->socks.method->handle_data(cl);
+               if (!ret) 
+                       kill_pending_client(cl);
+               return ret;
+       } else if (cl->socks.state == SOCKS_STATE_NORMAL) {
+               gchar header[4];
+               gsize read;
+
+               status = g_io_channel_read_chars(ioc, header, 4, &read, NULL);
+               if (status != G_IO_STATUS_NORMAL) {
+                       kill_pending_client(cl);
+                       return FALSE;
+               }
+
+               if (header[0] != SOCKS_VERSION) {
+                       log_global(LOG_WARNING, "Client suddenly changed socks version to %x", header[0]);
+                       kill_pending_client(cl);
+                       return socks_error(ioc, REP_GENERAL_FAILURE);
+               }
+
+               if (header[1] != CMD_CONNECT) {
+                       log_global(LOG_WARNING, "Client used unknown command %x", header[1]);
+                       kill_pending_client(cl);
+                       return socks_error(ioc, REP_CMD_NOT_SUPPORTED);
+               }
+
+               /* header[2] is reserved */
+       
+               switch (header[3]) {
+                       case ATYP_IPV4: 
+                               kill_pending_client(cl);
+                               return socks_error(ioc, REP_ATYP_NOT_SUPPORTED);
+
+                       case ATYP_IPV6:
+                               kill_pending_client(cl);
+                               return socks_error(ioc, REP_ATYP_NOT_SUPPORTED);
+
+                       case ATYP_FQDN:
+                               {
+                                       char hostname[0x100];
+                                       guint16 port;
+                                       char *desc;
+                                       struct network *result;
+                                       
+                                       status = g_io_channel_read_chars(ioc, header, 1, &read, NULL);
+                                       status = g_io_channel_read_chars(ioc, hostname, header[0], &read, NULL);
+                                       hostname[(guint8)header[0]] = '\0';
+
+                                       status = g_io_channel_read_chars(ioc, header, 2, &read, NULL);
+                                       port = ntohs(*(guint16 *)header);
+
+                                       log_global(LOG_INFO, "Request to connect to %s:%d", hostname, port);
+
+                                       result = find_network_by_hostname(cl->listener->global, hostname, port, TRUE);
+
+                                       if (!result) {
+                                               log_global(LOG_WARNING, "Unable to return network matching %s:%d", hostname, port);
+                                               kill_pending_client(cl);
+                                               return socks_error(ioc, REP_NET_UNREACHABLE);
+                                       } 
+
+                                       if (result->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED && 
+                                               !connect_network(result)) {
+                                               log_network(LOG_ERROR, result, "Unable to connect");
+                                               kill_pending_client(cl);
+                                               return socks_error(ioc, REP_NET_UNREACHABLE);
+                                       }
+
+                                       if (result->config->type == NETWORK_TCP) {
+#ifdef HAVE_IPV6
+                                               struct sockaddr_in6 *name6; 
+#endif
+                                               struct sockaddr_in *name4; 
+                                               int atyp, len, port;
+                                               gchar *data;
+
+#ifdef HAVE_IPV6
+                                               name6 = (struct sockaddr_in6 *)result->connection.data.tcp.local_name;
+#endif
+                                               name4 = (struct sockaddr_in *)result->connection.data.tcp.local_name;
+
+                                               if (name4->sin_family == AF_INET) {
+                                                       atyp = ATYP_IPV4;
+                                                       data = (gchar *)&name4->sin_addr;
+                                                       len = 4;
+                                                       port = name4->sin_port;
+#ifdef HAVE_IPV6
+                                               } else if (name6->sin6_family == AF_INET6) {
+                                                       atyp = ATYP_IPV6;
+                                                       data = (gchar *)&name6->sin6_addr;
+                                                       len = 16;
+                                                       port = name6->sin6_port;
+#endif
+                                               } else {
+                                                       log_network(LOG_ERROR, result, "Unable to obtain local address for connection to server");
+                                                       kill_pending_client(cl);
+                                                       return socks_error(ioc, REP_NET_UNREACHABLE);
+                                               }
+                                                       
+                                               socks_reply(ioc, REP_OK, atyp, len, data, port); 
+                                               
+                                       } else {
+                                               gchar *data = g_strdup("xlocalhost");
+                                               data[0] = strlen(data+1);
+                                               
+                                               socks_reply(ioc, REP_OK, ATYP_FQDN, data[0]+1, data, 1025);
+                                       }
+
+                                       desc = g_io_channel_ip_get_description(ioc);
+                                       client_init(result, ioc, desc);
+                                       g_free(desc);
+
+                                       kill_pending_client(cl);
+
+                                       return FALSE;
+                               }
+                       default:
+                               kill_pending_client(cl);
+                               return socks_error(ioc, REP_ATYP_NOT_SUPPORTED);
+
+               }
+       }
+       
+       return TRUE;
+}
index 53c5bfabd9bdf620d15db7da59f519d338b3d918..cd8956ddab66774d8fb32ac44f1d264b9f8c516c 100644 (file)
@@ -8,6 +8,7 @@
 #define G_MODULE_EXPORT
 #endif
 
+
 /**
  * A listener.
  */
@@ -26,6 +27,28 @@ struct listener_iochannel {
        gint watch_id;
 };
 
+struct socks_method;
+
+struct pending_client {
+       GIOChannel *connection;
+       const char *user;
+       const char *password;
+       gint watch_id;
+       struct sockaddr *clientname;
+       socklen_t clientname_len;
+       struct listener *listener;
+       struct {
+               struct socks_method *method;
+               enum state { 
+                       SOCKS_UNUSED = 0,
+                       SOCKS_STATE_NEW = -1, 
+                       SOCKS_STATE_AUTH, 
+                       SOCKS_STATE_NORMAL 
+               } state;
+               void *method_data;
+       } socks;
+};
+
 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 *);
index 0930c8293ebd969f4d13bcc561ef705cf71952af..e2ab46e6442ed4caaf92d8aa983f7af3f18ea075 100644 (file)
@@ -30,6 +30,7 @@
 #include <glib/gstdio.h>
 
 #define DEFAULT_ADMIN_PORT 6680
+#define DEFAULT_SOCKS_PORT 1080
 
 gboolean g_key_file_save_to_file(GKeyFile *kf, const gchar *file, GError **error)
 {
@@ -154,9 +155,10 @@ static void config_save_listeners(struct ctrlproxy_config *cfg, const char *path
 
        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);
+       if (cfg->auto_listener) {
+               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);
 
@@ -164,24 +166,34 @@ static void config_save_listeners(struct ctrlproxy_config *cfg, const char *path
 
        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->is_default) {
+                       g_key_file_set_string(cfg->keyfile, "global", "port", l->port);
+                       if (l->address != NULL)
+                               g_key_file_set_string(cfg->keyfile, "global", "bind", l->address);
+                       if (l->password != NULL)
+                               g_key_file_set_string(cfg->keyfile, "global", "password", l->password);
 
-               if (l->password != NULL && 
-                       !(default_password != NULL && strcmp(l->password, default_password) == 0)) 
-                       g_key_file_set_string(kf, tmp, "password", l->password);
+                       g_key_file_set_boolean(cfg->keyfile, "global", "ssl", l->ssl);
+               } else {
+                       char *tmp;
+                       if (!l->address) 
+                               tmp = g_strdup(l->port);
+                       else
+                               tmp = g_strdup_printf("%s:%s", l->address, l->port);
 
-               if (l->network != NULL) {
-                       g_key_file_set_string(kf, tmp, "network", l->network);
-               }
+                       if (l->password != NULL && 
+                               !(default_password != NULL && strcmp(l->password, default_password) == 0)) 
+                               g_key_file_set_string(kf, tmp, "password", l->password);
 
-               g_key_file_set_boolean(kf, tmp, "ssl", l->ssl);
+                       if (l->network != NULL) {
+                               g_key_file_set_string(kf, tmp, "network", l->network);
+                       }
 
-               g_free(tmp);
+                       g_key_file_set_boolean(kf, tmp, "ssl", l->ssl);
+               
+                       g_free(tmp);
+               }
        }
 
        if (!g_key_file_save_to_file(kf, filename, &error)) {
@@ -228,8 +240,9 @@ void save_configuration(struct ctrlproxy_config *cfg, const char *configuration_
                cfg->keyfile = g_key_file_new();
 
        g_key_file_set_boolean(cfg->keyfile, "global", "autosave", cfg->autosave);
-       g_key_file_set_boolean(cfg->keyfile, "admin", "without_privmsg", cfg->admin_noprivmsg);
-       g_key_file_set_boolean(cfg->keyfile, "admin", "log", cfg->admin_log);
+       if (cfg->admin_user != NULL)
+               g_key_file_set_string(cfg->keyfile, "global", "admin-user", cfg->admin_user);
+       g_key_file_set_boolean(cfg->keyfile, "global", "admin-log", cfg->admin_log);
        g_key_file_set_integer(cfg->keyfile, "global", "max_who_age", cfg->max_who_age);
 
        if (cfg->client_charset != NULL)
@@ -476,6 +489,49 @@ static struct network_config *find_create_network_config(struct ctrlproxy_config
        return nc;
 }
 
+static void config_load_listeners_socks(struct ctrlproxy_config *cfg)
+{
+       char **allows;
+       gsize size, i;
+       GKeyFile *kf = cfg->keyfile;
+       struct listener_config *l;
+
+       allows = g_key_file_get_string_list(kf, "socks", "allow", &size, NULL);
+
+       if (allows == NULL)
+               return;
+
+       l = g_new0(struct listener_config, 1);
+
+       if (g_key_file_has_key(kf, "socks", "port", NULL)) 
+               l->port = g_key_file_get_string(kf, "socks", "port", NULL);
+       else 
+               l->port = g_strdup_printf("%d", DEFAULT_SOCKS_PORT);
+
+       /* We can use the socks listener as default listener, if there was 
+        * no default listener specified */
+       if (cfg->listeners == NULL ||
+               !((struct listener_config *)cfg->listeners->data)->is_default)
+               l->is_default = TRUE;
+
+       g_key_file_remove_key(kf, "socks", "port", NULL);
+
+       for (i = 0; i < size; i++) {
+               struct allow_rule *r = g_new0(struct allow_rule, 1);
+               char **parts = g_strsplit(allows[i], ":", 2);
+                                       
+               r->username = parts[0];
+               r->password = parts[1];
+
+               g_free(parts);
+               l->allow_rules = g_list_append(l->allow_rules, r);
+       }
+
+       g_strfreev(allows);
+
+       cfg->listeners = g_list_append(cfg->listeners, l);
+}
+
 static void config_load_listeners(struct ctrlproxy_config *cfg)
 {
        char *filename = g_build_filename(cfg->config_dir, "listener", NULL);
@@ -493,6 +549,18 @@ static void config_load_listeners(struct ctrlproxy_config *cfg)
        if (g_key_file_has_key(cfg->keyfile, "listener", "autoport", NULL))
                cfg->listener_autoport = g_key_file_get_integer(cfg->keyfile, "listener", "autoport", NULL);
 
+       if (g_key_file_has_key(cfg->keyfile, "global", "port", NULL)) {
+               struct listener_config *l = g_new0(struct listener_config, 1);
+               l->port = g_key_file_get_string(cfg->keyfile, "global", "port", NULL);
+               l->password = g_key_file_get_string(cfg->keyfile, "global", "password", NULL);
+               l->address = g_key_file_get_string(cfg->keyfile, "global", "bind", NULL);
+               l->ssl = g_key_file_has_key(cfg->keyfile, "global", "ssl", NULL) &&
+                                g_key_file_get_boolean(cfg->keyfile, "global", "ssl", NULL);
+               l->is_default = TRUE;
+
+               cfg->listeners = g_list_append(cfg->listeners, l);
+       }
+
        kf = g_key_file_new();
 
        if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) {
@@ -543,8 +611,6 @@ static void config_load_listeners(struct ctrlproxy_config *cfg)
        g_free(filename);
 }
 
-
-
 static void config_load_networks(struct ctrlproxy_config *cfg)
 {
        char *networksdir = g_build_filename(cfg->config_dir, "networks", NULL);
@@ -629,13 +695,25 @@ struct ctrlproxy_config *load_configuration(const char *dir)
        if (!g_file_test(cfg->motd_file, G_FILE_TEST_EXISTS))
                log_global(LOG_ERROR, "Can't open MOTD file '%s' for reading", cfg->motd_file);
 
-    if (g_key_file_has_key(kf, "admin", "without_privmsg", NULL))
-        cfg->admin_noprivmsg = g_key_file_get_boolean(kf, "admin", "without_privmsg", NULL);
+    if (g_key_file_has_key(kf, "admin", "without_privmsg", NULL)) {
+               if (g_key_file_get_boolean(kf, "admin", "without_privmsg", NULL)) {
+                       cfg->admin_user = NULL;
+               } else {
+                       cfg->admin_user = g_strdup("ctrlproxy");
+               }
+               g_key_file_remove_key(kf, "admin", "without_privmsg", NULL);
+       }
+
+       if (g_key_file_has_key(kf, "global", "admin-user", NULL)) {
+               cfg->admin_user = g_key_file_get_string(kf, "global", "admin-user", NULL);
+       }
 
        cfg->admin_log = TRUE;
     if (g_key_file_has_key(kf, "admin", "log", NULL) && !g_key_file_get_boolean(kf, "admin", "log", NULL))
         cfg->admin_log = FALSE;
-
+       g_key_file_remove_key(kf, "admin", "log", NULL);
+    if (g_key_file_has_key(kf, "global", "admin-log", NULL) && !g_key_file_get_boolean(kf, "global", "admin-log", NULL))
+        cfg->admin_log = FALSE;
 
        for (gl = cfg->networks; gl; gl = gl->next) {
                struct network_config *nc = gl->data;
@@ -646,6 +724,7 @@ struct ctrlproxy_config *load_configuration(const char *dir)
        config_load_networks(cfg);
 
        config_load_listeners(cfg);
+       config_load_listeners_socks(cfg);
 
        size = 0;
        autoconnect_list = g_key_file_get_string_list(kf, "global", "autoconnect", &size, NULL);
@@ -738,11 +817,10 @@ void free_config(struct ctrlproxy_config *cfg)
 
 gboolean create_configuration(const char *config_dir)
 {
-       GKeyFile *kf;
        struct global *global;
        char port[250];
-       char *pass, *listenerfile;
-       GError *error = NULL;
+       struct listener_config *l;
+       char *pass;
 
        if (g_file_test(config_dir, G_FILE_TEST_IS_DIR)) {
                fprintf(stderr, "%s already exists\n", config_dir);
@@ -759,8 +837,6 @@ gboolean create_configuration(const char *config_dir)
        global->config->config_dir = g_strdup(config_dir);
        save_configuration(global->config, config_dir);
 
-       kf = g_key_file_new();
-
        snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
        printf("Please specify port the administration interface should listen on.\n"
                   "Prepend with a colon to listen on a specific address.\n"
@@ -774,20 +850,16 @@ gboolean create_configuration(const char *config_dir)
        if (strlen(port) == 0) 
                snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
 
+       l = g_new0(struct listener_config, 1);
        pass = getpass("Please specify a password for the administration interface: "); 
-       g_key_file_set_string(kf, port, "network", "admin");
+       l->port = port;
        if (!strcmp(pass, "")) {
                fprintf(stderr, "Warning: no password specified. Authentication disabled!\n");
        } else {
-               g_key_file_set_string(kf, port, "password", pass);
+               l->password = pass;
        }
 
-       listenerfile = g_build_filename(config_dir, "listener", NULL);
-
-       if (!g_key_file_save_to_file(kf, listenerfile, &error)) {
-               fprintf(stderr, "Error saving %s: %s\n", listenerfile, error->message);
-               return FALSE;
-       }
+       global->config->listeners = g_list_append(global->config->listeners, l);
 
        return TRUE;
 }
index d565efae3145851ec3f263630de8c5becc43073b..2afe9f707cbf65860a0fbd9c79a7d389fb19892c 100644 (file)
@@ -81,6 +81,11 @@ struct network_config
        } type_settings; 
 };
 
+struct allow_rule {
+       const char *username;
+       const char *password;
+};
+
 struct listener_config {
        gboolean ssl;
        gpointer ssl_credentials;
@@ -88,6 +93,8 @@ struct listener_config {
        char *address;
        char *port;
        char *network;
+       GList *allow_rules;
+       gboolean is_default; /* Whether this is the "default" listener, stored in ~/.ctrlproxy/config */
 };
 
 /**
@@ -104,7 +111,7 @@ struct ctrlproxy_config {
        char *linestack_backend;
        char *client_charset;
        gboolean admin_log;
-       gboolean admin_noprivmsg;
+       char *admin_user;
        gboolean report_time;
        int max_who_age;
        GKeyFile *keyfile;
diff --git a/src/socks.h b/src/socks.h
new file mode 100644 (file)
index 0000000..9c83ffb
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+       ctrlproxy: A modular IRC proxy
+       (c) 2005-2007 Jelmer Vernooij <jelmer@nl.linux.org>
+
+       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.
+*/
+
+#ifndef __SOCKS_H__
+#define __SOCKS_H__
+
+#define SOCKS_VERSION 0x05
+
+#define SOCKS_METHOD_NOAUTH                    0x00
+#define SOCKS_METHOD_GSSAPI            0x01
+#define SOCKS_METHOD_USERNAME_PW       0x02
+#define SOCKS_METHOD_NOACCEPTABLE      0xFF
+
+#define ATYP_IPV4                                      0x01
+#define ATYP_FQDN                                      0x03
+#define ATYP_IPV6                                      0x04
+
+#define CMD_CONNECT                                    0x01
+#define CMD_BIND                                       0x02
+#define CMD_UDP_ASSOCIATE                      0x03
+
+#define REP_OK                                         0x00
+#define REP_GENERAL_FAILURE                    0x01
+#define REP_NOT_ALLOWED                                0x02
+#define REP_NET_UNREACHABLE                    0x03
+#define REP_HOST_UNREACHABLE           0x04
+#define REP_CONN_REFUSED                       0x05
+#define REP_TTL_EXPIRED                                0x06
+#define REP_CMD_NOT_SUPPORTED          0x07
+#define REP_ATYP_NOT_SUPPORTED         0x08
+
+#endif /* __SOCKS_H__ */