From d179fd900a89a41c8cf95a0f61caef4ec7c6c09e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 3 Apr 2017 21:55:50 +0100 Subject: Add PROXY command. Not actually an IRC protocol command, it's a HAProxy trick supported by stunnel to indicate where the connection originally came from. Looks a little better on public servers. --- irc.c | 74 +++++++++++++++++++++++++++++++++++++--------------------- irc.h | 3 +++ irc_commands.c | 43 ++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 26 deletions(-) diff --git a/irc.c b/irc.c index 7f3f66a7..655aa967 100644 --- a/irc.c +++ b/irc.c @@ -40,9 +40,6 @@ static char *set_eval_utf8_nicks(set_t *set, char *value); irc_t *irc_new(int fd) { irc_t *irc; - struct sockaddr_storage sock; - socklen_t socklen = sizeof(sock); - char *host = NULL, *myhost = NULL; irc_user_t *iu; GSList *l; set_t *s; @@ -64,23 +61,6 @@ irc_t *irc_new(int fd) irc->iconv = (GIConv) - 1; irc->oconv = (GIConv) - 1; - if (global.conf->hostname) { - myhost = g_strdup(global.conf->hostname); - } else if (getsockname(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) { - myhost = reverse_lookup((struct sockaddr*) &sock, socklen); - } - - if (getpeername(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) { - host = reverse_lookup((struct sockaddr*) &sock, socklen); - } - - if (host == NULL) { - host = g_strdup("localhost.localdomain"); - } - if (myhost == NULL) { - myhost = g_strdup("localhost.localdomain"); - } - if (global.conf->ping_interval > 0 && global.conf->ping_timeout > 0) { irc->ping_source_id = b_timeout_add(global.conf->ping_interval * 1000, irc_userping, irc); } @@ -127,17 +107,16 @@ irc_t *irc_new(int fd) s = set_add(&b->set, "utf8_nicks", "false", set_eval_utf8_nicks, irc); irc->root = iu = irc_user_new(irc, ROOT_NICK); - iu->host = g_strdup(myhost); iu->fullname = g_strdup(ROOT_FN); iu->f = &irc_user_root_funcs; iu = irc_user_new(irc, NS_NICK); - iu->host = g_strdup(myhost); iu->fullname = g_strdup(ROOT_FN); iu->f = &irc_user_root_funcs; irc->user = g_new0(irc_user_t, 1); - irc->user->host = g_strdup(host); + + irc_set_hosts(irc, NULL, 0); conf_loaddefaults(irc); @@ -153,9 +132,6 @@ irc_t *irc_new(int fd) "See doc/README for more information."); } - g_free(myhost); - g_free(host); - /* libpurple doesn't like fork()s after initializing itself, so this is the right moment to initialize it. */ #ifdef WITH_PURPLE @@ -177,6 +153,52 @@ irc_t *irc_new(int fd) return irc; } +void irc_set_hosts(irc_t *irc, const struct sockaddr *remote_addr, const socklen_t remote_addrlen) +{ + struct sockaddr_storage sock; + socklen_t socklen = sizeof(sock); + char *host = NULL, *myhost = NULL; + struct irc_user *iu; + + if (global.conf->hostname) { + myhost = g_strdup(global.conf->hostname); + } else if (getsockname(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) { + myhost = reverse_lookup((struct sockaddr*) &sock, socklen); + } + + if (remote_addrlen > 0) { + host = reverse_lookup(remote_addr, remote_addrlen); + } else if (getpeername(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) { + host = reverse_lookup((struct sockaddr*) &sock, socklen); + } + + if (myhost == NULL) { + myhost = g_strdup("localhost.localdomain"); + } + if (host == NULL) { + host = g_strdup("localhost.localdomain"); + } + + if (irc->root->host != irc->root->nick) { + g_free(irc->root->host); + } + irc->root->host = g_strdup(myhost); + if ((iu = irc_user_by_name(irc, NS_NICK))) { + if (iu->host != iu->nick) { + g_free(iu->host); + } + iu->host = g_strdup(myhost); + } + + if (irc->user->host != irc->user->nick) { + g_free(irc->user->host); + } + irc->user->host = g_strdup(host); + + g_free(myhost); + g_free(host); +} + /* immed=1 makes this function pretty much equal to irc_free(), except that this one will "log". In case the connection is already broken and we shouldn't try to write to it. */ diff --git a/irc.h b/irc.h index c56f847f..8a37b0ca 100644 --- a/irc.h +++ b/irc.h @@ -26,6 +26,8 @@ #ifndef _IRC_H #define _IRC_H +#include + #define IRC_MAX_LINE 512 #define IRC_MAX_ARGS 16 #define IRC_WORD_WRAP 425 @@ -270,6 +272,7 @@ extern GSList *irc_plugins; /* struct irc_plugin */ extern GSList *irc_connection_list; irc_t *irc_new(int fd); +void irc_set_hosts(irc_t *irc, const struct sockaddr *remote_addr, const socklen_t remote_addrlen); void irc_abort(irc_t *irc, int immed, char *format, ...) G_GNUC_PRINTF(3, 4); void irc_free(irc_t *irc); void irc_setpass(irc_t *irc, const char *pass); diff --git a/irc_commands.c b/irc_commands.c index dd5bdf38..4a8bae4d 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -58,6 +58,48 @@ static void irc_cmd_pass(irc_t *irc, char **cmd) } } +/* http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt + + This isn't actually IRC, it's used by for example stunnel4 to indicate + the origin of the secured counterpart of the connection. It'll go wrong + with arguments starting with : like for example "::1" but I guess I'm + okay with that. */ +static void irc_cmd_proxy(irc_t *irc, char **cmd) +{ + struct addrinfo hints, *ai; + struct sockaddr_storage sock; + socklen_t socklen = sizeof(sock); + + if (getpeername(irc->fd, (struct sockaddr*) &sock, &socklen) != 0) { + return; + } + + ipv64_normalise_mapped(&sock, &socklen); + + /* Only accept PROXY "command" on localhost sockets. */ + if (!((sock.ss_family == AF_INET && + ntohl(((struct sockaddr_in*)&sock)->sin_addr.s_addr) == INADDR_LOOPBACK) || + (sock.ss_family == AF_INET6 && + IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6*)&sock)->sin6_addr)))) { + return; + } + + /* And only once. Do this with a pretty dumb regex-match for + now, maybe better to use some sort of flag.. */ + if (!g_regex_match_simple("^(ip6-)?localhost(.(localdomain.?)?)?$", irc->user->host, 0, 0)) { + return; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(cmd[2], NULL, &hints, &ai) != 0) { + return; + } + + irc_set_hosts(irc, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); +} + static gboolean irc_sasl_plain_parse(char *input, char **user, char **pass) { int i, part, len; @@ -807,6 +849,7 @@ static void irc_cmd_rehash(irc_t *irc, char **cmd) static const command_t irc_commands[] = { { "cap", 1, irc_cmd_cap, 0 }, { "pass", 1, irc_cmd_pass, 0 }, + { "proxy", 5, irc_cmd_proxy, IRC_CMD_PRE_LOGIN }, { "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN }, { "nick", 1, irc_cmd_nick, 0 }, { "quit", 0, irc_cmd_quit, 0 }, -- cgit v1.2.3