1
0
mirror of https://github.com/irssi/irssi.git synced 2024-12-04 14:46:39 -05:00

irssi proxy: allow listening on unix sockets

This commit is contained in:
Lukas Mai 2016-02-24 19:00:31 +01:00
parent 3fc7e4d4ea
commit 8c1da2890c
5 changed files with 148 additions and 56 deletions

View File

@ -6,7 +6,7 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */ #define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */ #define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */
#define IRSSI_ABI_VERSION 1 #define IRSSI_ABI_VERSION 2
#define DEFAULT_SERVER_ADD_PORT 6667 #define DEFAULT_SERVER_ADD_PORT 6667

View File

@ -31,6 +31,8 @@
#include "fe-common/core/printtext.h" /* FIXME: evil. need to do fe-proxy */ #include "fe-common/core/printtext.h" /* FIXME: evil. need to do fe-proxy */
#include <sys/un.h>
GSList *proxy_listens; GSList *proxy_listens;
GSList *proxy_clients; GSList *proxy_clients;
@ -39,6 +41,66 @@ static int ignore_next;
static int enabled = FALSE; static int enabled = FALSE;
static int is_all_digits(const char *s)
{
return strspn(s, "0123456789") == strlen(s);
}
static GIOChannel *net_listen_unix(const char *path)
{
struct sockaddr_un sa;
int saved_errno, handle;
g_return_val_if_fail(path != NULL, NULL);
handle = socket(AF_UNIX, SOCK_STREAM, 0);
if (handle == -1) {
return NULL;
}
fcntl(handle, F_SETFL, O_NONBLOCK);
memset(&sa, '\0', sizeof sa);
sa.sun_family = AF_UNIX;
strncpy(sa.sun_path, path, sizeof sa.sun_path - 1);
if (bind(handle, (struct sockaddr *)&sa, sizeof sa) == -1) {
saved_errno = errno;
goto error_close;
}
if (listen(handle, 1) == -1) {
saved_errno = errno;
goto error_unlink;
}
return g_io_channel_new(handle);
error_unlink:
unlink(sa.sun_path);
error_close:
close(handle);
errno = saved_errno;
return NULL;
}
static GIOChannel *net_accept_unix(GIOChannel *handle)
{
struct sockaddr_un sa;
int ret;
socklen_t addrlen;
g_return_val_if_fail(handle != NULL, NULL);
addrlen = sizeof sa;
ret = accept(g_io_channel_unix_get_fd(handle), (struct sockaddr *)&sa, &addrlen);
if (ret < 0)
return NULL;
fcntl(ret, F_SETFL, O_NONBLOCK);
return g_io_channel_new(ret);
}
static void remove_client(CLIENT_REC *rec) static void remove_client(CLIENT_REC *rec)
{ {
g_return_if_fail(rec != NULL); g_return_if_fail(rec != NULL);
@ -48,13 +110,13 @@ static void remove_client(CLIENT_REC *rec)
signal_emit("proxy client disconnected", 1, rec); signal_emit("proxy client disconnected", 1, rec);
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE, printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client %s:%d disconnected", rec->host, rec->port); "Proxy: Client %s disconnected", rec->addr);
g_free(rec->proxy_address); g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE); net_sendbuffer_destroy(rec->handle, TRUE);
g_source_remove(rec->recv_tag); g_source_remove(rec->recv_tag);
g_free_not_null(rec->nick); g_free_not_null(rec->nick);
g_free_not_null(rec->host); g_free_not_null(rec->addr);
g_free(rec); g_free(rec);
} }
@ -160,8 +222,8 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
} else { } else {
signal_emit("proxy client connected", 1, client); signal_emit("proxy client connected", 1, client);
printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE, printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client %s:%d connected", "Proxy: Client %s connected",
client->host, client->port); client->addr);
client->connected = TRUE; client->connected = TRUE;
proxy_dump_data(client); proxy_dump_data(client);
} }
@ -370,20 +432,30 @@ static void sig_listen(LISTEN_REC *listen)
GIOChannel *handle; GIOChannel *handle;
char host[MAX_IP_LEN]; char host[MAX_IP_LEN];
int port; int port;
char *addr;
g_return_if_fail(listen != NULL); g_return_if_fail(listen != NULL);
/* accept connection */ /* accept connection */
handle = net_accept(listen->handle, &ip, &port); if (listen->port) {
if (handle == NULL) handle = net_accept(listen->handle, &ip, &port);
return; if (handle == NULL)
net_ip2host(&ip, host); return;
net_ip2host(&ip, host);
addr = g_strdup_printf("%s:%d", host, port);
} else {
/* no port => this is a unix socket */
handle = net_accept_unix(listen->handle);
if (handle == NULL)
return;
addr = g_strdup("(local)");
}
sendbuf = net_sendbuffer_create(handle, 0); sendbuf = net_sendbuffer_create(handle, 0);
rec = g_new0(CLIENT_REC, 1); rec = g_new0(CLIENT_REC, 1);
rec->listen = listen; rec->listen = listen;
rec->handle = sendbuf; rec->handle = sendbuf;
rec->host = g_strdup(host); rec->addr = addr;
rec->port = port;
if (g_strcmp0(listen->ircnet, "?") == 0) { if (g_strcmp0(listen->ircnet, "?") == 0) {
rec->multiplex = TRUE; rec->multiplex = TRUE;
rec->proxy_address = g_strdup("multiplex.proxy"); rec->proxy_address = g_strdup("multiplex.proxy");
@ -404,8 +476,8 @@ static void sig_listen(LISTEN_REC *listen)
signal_emit("proxy client connecting", 1, rec); signal_emit("proxy client connecting", 1, rec);
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE, printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: New client %s:%d on port %d (%s)", "Proxy: New client %s on port %s (%s)",
rec->host, rec->port, listen->port, listen->ircnet); rec->addr, listen->port_or_path, listen->ircnet);
} }
static void sig_incoming(IRC_SERVER_REC *server, const char *line) static void sig_incoming(IRC_SERVER_REC *server, const char *line)
@ -601,14 +673,19 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
proxy_outserver_all(server, "PRIVMSG %s :\001ACTION %s\001", target, msg); proxy_outserver_all(server, "PRIVMSG %s :\001ACTION %s\001", target, msg);
} }
static LISTEN_REC *find_listen(const char *ircnet, int port) static LISTEN_REC *find_listen(const char *ircnet, int port, const char *port_or_path)
{ {
GSList *tmp; GSList *tmp;
for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) { for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) {
LISTEN_REC *rec = tmp->data; LISTEN_REC *rec = tmp->data;
if (rec->port == port && if ((port
? /* a tcp port */
rec->port == port
: /* a unix socket path */
g_strcmp0(rec->port_or_path, port_or_path) == 0
) &&
g_ascii_strcasecmp(rec->ircnet, ircnet) == 0) g_ascii_strcasecmp(rec->ircnet, ircnet) == 0)
return rec; return rec;
} }
@ -616,43 +693,48 @@ static LISTEN_REC *find_listen(const char *ircnet, int port)
return NULL; return NULL;
} }
static void add_listen(const char *ircnet, int port) static void add_listen(const char *ircnet, int port, const char *port_or_path)
{ {
LISTEN_REC *rec; LISTEN_REC *rec;
IPADDR ip4, ip6, *my_ip; IPADDR ip4, ip6, *my_ip;
GIOChannel *handle;
if (port <= 0 || *ircnet == '\0') if (*port_or_path == '\0' || port < 0 || *ircnet == '\0')
return; return;
/* bind to specific host/ip? */ if (port == 0) {
my_ip = NULL; /* listening on a unix socket */
if (*settings_get_str("irssiproxy_bind") != '\0') { handle = net_listen_unix(port_or_path);
if (net_gethostbyname(settings_get_str("irssiproxy_bind"), } else {
&ip4, &ip6) != 0) { /* bind to specific host/ip? */
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, my_ip = NULL;
"Proxy: can not resolve '%s' - aborting", if (*settings_get_str("irssiproxy_bind") != '\0') {
settings_get_str("irssiproxy_bind")); if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
return; &ip4, &ip6) != 0) {
} printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
"Proxy: can not resolve '%s' - aborting",
settings_get_str("irssiproxy_bind"));
return;
}
my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 || my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4; settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
}
handle = net_listen(my_ip, &port);
}
if (handle == NULL) {
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
"Proxy: Listen in port %s failed: %s",
port_or_path, g_strerror(errno));
return;
} }
rec = g_new0(LISTEN_REC, 1); rec = g_new0(LISTEN_REC, 1);
rec->handle = handle;
rec->ircnet = g_strdup(ircnet); rec->ircnet = g_strdup(ircnet);
rec->port = port; rec->port = port;
rec->port_or_path = g_strdup(port_or_path);
rec->handle = net_listen(my_ip, &rec->port);
if (rec->handle == NULL) {
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
"Proxy: Listen in port %d failed: %s",
rec->port, g_strerror(errno));
g_free(rec->ircnet);
g_free(rec);
return;
}
rec->tag = g_input_add(rec->handle, G_INPUT_READ, rec->tag = g_input_add(rec->handle, G_INPUT_READ,
(GInputFunction) sig_listen, rec); (GInputFunction) sig_listen, rec);
@ -667,8 +749,13 @@ static void remove_listen(LISTEN_REC *rec)
while (rec->clients != NULL) while (rec->clients != NULL)
remove_client(rec->clients->data); remove_client(rec->clients->data);
/* remove unix socket because bind wants to (re)create it */
if (rec->port == 0)
unlink(rec->port_or_path);
net_disconnect(rec->handle); net_disconnect(rec->handle);
g_source_remove(rec->tag); g_source_remove(rec->tag);
g_free(rec->port_or_path);
g_free(rec->ircnet); g_free(rec->ircnet);
g_free(rec); g_free(rec);
} }
@ -678,7 +765,7 @@ static void read_settings(void)
LISTEN_REC *rec; LISTEN_REC *rec;
GSList *remove_listens = NULL; GSList *remove_listens = NULL;
GSList *add_listens = NULL; GSList *add_listens = NULL;
char **ports, **tmp, *ircnet, *port; char **ports, **tmp, *ircnet, *port_or_path;
int portnum; int portnum;
remove_listens = g_slist_copy(proxy_listens); remove_listens = g_slist_copy(proxy_listens);
@ -686,20 +773,25 @@ static void read_settings(void)
ports = g_strsplit(settings_get_str("irssiproxy_ports"), " ", -1); ports = g_strsplit(settings_get_str("irssiproxy_ports"), " ", -1);
for (tmp = ports; *tmp != NULL; tmp++) { for (tmp = ports; *tmp != NULL; tmp++) {
ircnet = *tmp; ircnet = *tmp;
port = strchr(ircnet, '='); port_or_path = strchr(ircnet, '=');
if (port == NULL) if (port_or_path == NULL)
continue; continue;
*port++ = '\0'; *port_or_path++ = '\0';
portnum = atoi(port); if (is_all_digits(port_or_path)) {
if (portnum <= 0) portnum = atoi(port_or_path);
continue; if (portnum <= 0)
continue;
} else {
portnum = 0;
}
rec = find_listen(ircnet, portnum); rec = find_listen(ircnet, portnum, port_or_path);
if (rec == NULL) { if (rec == NULL) {
rec = g_new0(LISTEN_REC, 1); rec = g_new0(LISTEN_REC, 1);
rec->ircnet = ircnet; /* borrow */ rec->ircnet = ircnet; /* borrow */
rec->port = portnum; rec->port = portnum;
rec->port_or_path = port_or_path; /* borrow */
add_listens = g_slist_prepend(add_listens, rec); add_listens = g_slist_prepend(add_listens, rec);
} else { } else {
/* remove from the list of listens to remove == keep it */ /* remove from the list of listens to remove == keep it */
@ -714,7 +806,7 @@ static void read_settings(void)
while (add_listens != NULL) { while (add_listens != NULL) {
rec = add_listens->data; rec = add_listens->data;
add_listen(rec->ircnet, rec->port); add_listen(rec->ircnet, rec->port, rec->port_or_path);
add_listens = g_slist_remove(add_listens, rec); add_listens = g_slist_remove(add_listens, rec);
g_free(rec); g_free(rec);
} }

View File

@ -44,10 +44,10 @@ static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
CLIENT_REC *rec = tmp->data; CLIENT_REC *rec = tmp->data;
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE, printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
" %s:%d connect%s to %d (%s)", " %s connect%s to %s (%s)",
rec->host, rec->port, rec->addr,
rec->connected ? "ed" : "ing", rec->connected ? "ed" : "ing",
rec->listen->port, rec->listen->ircnet); rec->listen->port_or_path, rec->listen->ircnet);
} }
} }

View File

@ -9,17 +9,18 @@
typedef struct { typedef struct {
int port; int port;
char *port_or_path;
char *ircnet; char *ircnet;
int tag; int tag;
GIOChannel *handle; GIOChannel *handle;
GSList *clients; GSList *clients;
} LISTEN_REC; } LISTEN_REC;
typedef struct { typedef struct {
char *nick, *host; char *nick, *addr;
int port;
NET_SENDBUF_REC *handle; NET_SENDBUF_REC *handle;
int recv_tag; int recv_tag;
char *proxy_address; char *proxy_address;

View File

@ -149,8 +149,7 @@ static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
static void perl_client_fill_hash(HV *hv, CLIENT_REC *client) static void perl_client_fill_hash(HV *hv, CLIENT_REC *client)
{ {
(void) hv_store(hv, "nick", 4, new_pv(client->nick), 0); (void) hv_store(hv, "nick", 4, new_pv(client->nick), 0);
(void) hv_store(hv, "host", 4, new_pv(client->host), 0); (void) hv_store(hv, "addr", 4, new_pv(client->addr), 0);
(void) hv_store(hv, "port", 4, newSViv(client->port), 0);
(void) hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0); (void) hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0);
(void) hv_store(hv, "server", 6, iobject_bless(client->server), 0); (void) hv_store(hv, "server", 6, iobject_bless(client->server), 0);
(void) hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0); (void) hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0);