aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2017-04-03 21:55:50 +0100
committerWilmer van der Gaast <github@wilmer.gaast.net>2017-04-06 21:25:08 +0100
commitd179fd900a89a41c8cf95a0f61caef4ec7c6c09e (patch)
tree0d0098865b464d2b4772c6ddd42e50dac1c189e3
parent60141cfa8c26971ec2258118a661609c7a15d261 (diff)
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.
-rw-r--r--irc.c74
-rw-r--r--irc.h3
-rw-r--r--irc_commands.c43
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 <sys/socket.h>
+
#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 },