mirror of
https://github.com/irssi/irssi.git
synced 2024-09-08 04:26:01 -04:00
Merge pull request #427 from mauke/irssiproxy-unix-sockets
irssi proxy: allow listening on unix sockets
This commit is contained in:
commit
64bac950fc
@ -6,7 +6,7 @@
|
||||
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
|
||||
#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
|
||||
|
||||
|
@ -31,6 +31,8 @@
|
||||
|
||||
#include "fe-common/core/printtext.h" /* FIXME: evil. need to do fe-proxy */
|
||||
|
||||
#include <sys/un.h>
|
||||
|
||||
GSList *proxy_listens;
|
||||
GSList *proxy_clients;
|
||||
|
||||
@ -39,6 +41,66 @@ static int ignore_next;
|
||||
|
||||
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)
|
||||
{
|
||||
g_return_if_fail(rec != NULL);
|
||||
@ -48,13 +110,13 @@ static void remove_client(CLIENT_REC *rec)
|
||||
|
||||
signal_emit("proxy client disconnected", 1, rec);
|
||||
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);
|
||||
net_sendbuffer_destroy(rec->handle, TRUE);
|
||||
g_source_remove(rec->recv_tag);
|
||||
g_free_not_null(rec->nick);
|
||||
g_free_not_null(rec->host);
|
||||
g_free_not_null(rec->addr);
|
||||
g_free(rec);
|
||||
}
|
||||
|
||||
@ -160,8 +222,8 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
|
||||
} else {
|
||||
signal_emit("proxy client connected", 1, client);
|
||||
printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE,
|
||||
"Proxy: Client %s:%d connected",
|
||||
client->host, client->port);
|
||||
"Proxy: Client %s connected",
|
||||
client->addr);
|
||||
client->connected = TRUE;
|
||||
proxy_dump_data(client);
|
||||
}
|
||||
@ -202,7 +264,7 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
|
||||
|
||||
if (g_strcmp0(cmd, "PROXY") == 0) {
|
||||
if (g_ascii_strcasecmp(args, "CTCP ON") == 0) {
|
||||
/* client wants all ctcps */
|
||||
/* client wants all ctcps */
|
||||
client->want_ctcp = 1;
|
||||
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
|
||||
CLIENT_REC *rec = tmp->data;
|
||||
@ -370,20 +432,30 @@ static void sig_listen(LISTEN_REC *listen)
|
||||
GIOChannel *handle;
|
||||
char host[MAX_IP_LEN];
|
||||
int port;
|
||||
char *addr;
|
||||
|
||||
g_return_if_fail(listen != NULL);
|
||||
|
||||
/* accept connection */
|
||||
handle = net_accept(listen->handle, &ip, &port);
|
||||
if (handle == NULL)
|
||||
return;
|
||||
net_ip2host(&ip, host);
|
||||
if (listen->port) {
|
||||
handle = net_accept(listen->handle, &ip, &port);
|
||||
if (handle == NULL)
|
||||
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);
|
||||
rec = g_new0(CLIENT_REC, 1);
|
||||
rec->listen = listen;
|
||||
rec->handle = sendbuf;
|
||||
rec->host = g_strdup(host);
|
||||
rec->port = port;
|
||||
rec->addr = addr;
|
||||
if (g_strcmp0(listen->ircnet, "?") == 0) {
|
||||
rec->multiplex = TRUE;
|
||||
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);
|
||||
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
|
||||
"Proxy: New client %s:%d on port %d (%s)",
|
||||
rec->host, rec->port, listen->port, listen->ircnet);
|
||||
"Proxy: New client %s on port %s (%s)",
|
||||
rec->addr, listen->port_or_path, listen->ircnet);
|
||||
}
|
||||
|
||||
static void sig_incoming(IRC_SERVER_REC *server, const char *line)
|
||||
@ -494,12 +566,12 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
|
||||
static void event_connected(IRC_SERVER_REC *server)
|
||||
{
|
||||
GSList *tmp;
|
||||
const char *chatnet;
|
||||
const char *chatnet;
|
||||
|
||||
if (!IS_IRC_SERVER(server))
|
||||
return;
|
||||
|
||||
chatnet = server->connrec->chatnet;
|
||||
chatnet = server->connrec->chatnet;
|
||||
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
|
||||
CLIENT_REC *rec = tmp->data;
|
||||
|
||||
@ -508,7 +580,7 @@ static void event_connected(IRC_SERVER_REC *server)
|
||||
(chatnet != NULL &&
|
||||
g_ascii_strcasecmp(chatnet, rec->listen->ircnet) == 0))) {
|
||||
proxy_outdata(rec, ":%s NOTICE %s :Connected to server\r\n",
|
||||
rec->proxy_address, rec->nick);
|
||||
rec->proxy_address, rec->nick);
|
||||
rec->server = server;
|
||||
proxy_client_reset_nick(rec);
|
||||
}
|
||||
@ -516,7 +588,7 @@ static void event_connected(IRC_SERVER_REC *server)
|
||||
}
|
||||
|
||||
static void proxy_server_disconnected(CLIENT_REC *client,
|
||||
IRC_SERVER_REC *server)
|
||||
IRC_SERVER_REC *server)
|
||||
{
|
||||
GSList *tmp;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) {
|
||||
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)
|
||||
return rec;
|
||||
}
|
||||
@ -616,48 +693,53 @@ static LISTEN_REC *find_listen(const char *ircnet, int port)
|
||||
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;
|
||||
IPADDR ip4, ip6, *my_ip;
|
||||
GIOChannel *handle;
|
||||
|
||||
if (port <= 0 || *ircnet == '\0')
|
||||
if (*port_or_path == '\0' || port < 0 || *ircnet == '\0')
|
||||
return;
|
||||
|
||||
/* bind to specific host/ip? */
|
||||
my_ip = NULL;
|
||||
if (*settings_get_str("irssiproxy_bind") != '\0') {
|
||||
if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
|
||||
&ip4, &ip6) != 0) {
|
||||
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
|
||||
"Proxy: can not resolve '%s' - aborting",
|
||||
settings_get_str("irssiproxy_bind"));
|
||||
return;
|
||||
}
|
||||
if (port == 0) {
|
||||
/* listening on a unix socket */
|
||||
handle = net_listen_unix(port_or_path);
|
||||
} else {
|
||||
/* bind to specific host/ip? */
|
||||
my_ip = NULL;
|
||||
if (*settings_get_str("irssiproxy_bind") != '\0') {
|
||||
if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
|
||||
&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 ||
|
||||
settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
|
||||
my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
|
||||
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->handle = handle;
|
||||
rec->ircnet = g_strdup(ircnet);
|
||||
rec->port = port;
|
||||
|
||||
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->port_or_path = g_strdup(port_or_path);
|
||||
|
||||
rec->tag = g_input_add(rec->handle, G_INPUT_READ,
|
||||
(GInputFunction) sig_listen, rec);
|
||||
(GInputFunction) sig_listen, rec);
|
||||
|
||||
proxy_listens = g_slist_append(proxy_listens, rec);
|
||||
proxy_listens = g_slist_append(proxy_listens, rec);
|
||||
}
|
||||
|
||||
static void remove_listen(LISTEN_REC *rec)
|
||||
@ -667,8 +749,13 @@ static void remove_listen(LISTEN_REC *rec)
|
||||
while (rec->clients != NULL)
|
||||
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);
|
||||
g_source_remove(rec->tag);
|
||||
g_free(rec->port_or_path);
|
||||
g_free(rec->ircnet);
|
||||
g_free(rec);
|
||||
}
|
||||
@ -678,7 +765,7 @@ static void read_settings(void)
|
||||
LISTEN_REC *rec;
|
||||
GSList *remove_listens = NULL;
|
||||
GSList *add_listens = NULL;
|
||||
char **ports, **tmp, *ircnet, *port;
|
||||
char **ports, **tmp, *ircnet, *port_or_path;
|
||||
int portnum;
|
||||
|
||||
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);
|
||||
for (tmp = ports; *tmp != NULL; tmp++) {
|
||||
ircnet = *tmp;
|
||||
port = strchr(ircnet, '=');
|
||||
if (port == NULL)
|
||||
port_or_path = strchr(ircnet, '=');
|
||||
if (port_or_path == NULL)
|
||||
continue;
|
||||
|
||||
*port++ = '\0';
|
||||
portnum = atoi(port);
|
||||
if (portnum <= 0)
|
||||
continue;
|
||||
*port_or_path++ = '\0';
|
||||
if (is_all_digits(port_or_path)) {
|
||||
portnum = atoi(port_or_path);
|
||||
if (portnum <= 0)
|
||||
continue;
|
||||
} else {
|
||||
portnum = 0;
|
||||
}
|
||||
|
||||
rec = find_listen(ircnet, portnum);
|
||||
rec = find_listen(ircnet, portnum, port_or_path);
|
||||
if (rec == NULL) {
|
||||
rec = g_new0(LISTEN_REC, 1);
|
||||
rec->ircnet = ircnet; /* borrow */
|
||||
rec->port = portnum;
|
||||
rec->port_or_path = port_or_path; /* borrow */
|
||||
add_listens = g_slist_prepend(add_listens, rec);
|
||||
} else {
|
||||
/* remove from the list of listens to remove == keep it */
|
||||
@ -714,7 +806,7 @@ static void read_settings(void)
|
||||
|
||||
while (add_listens != NULL) {
|
||||
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);
|
||||
g_free(rec);
|
||||
}
|
||||
|
@ -44,10 +44,10 @@ static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
|
||||
CLIENT_REC *rec = tmp->data;
|
||||
|
||||
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
|
||||
" %s:%d connect%s to %d (%s)",
|
||||
rec->host, rec->port,
|
||||
" %s connect%s to %s (%s)",
|
||||
rec->addr,
|
||||
rec->connected ? "ed" : "ing",
|
||||
rec->listen->port, rec->listen->ircnet);
|
||||
rec->listen->port_or_path, rec->listen->ircnet);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,17 +9,18 @@
|
||||
|
||||
typedef struct {
|
||||
int port;
|
||||
char *port_or_path;
|
||||
char *ircnet;
|
||||
|
||||
int tag;
|
||||
GIOChannel *handle;
|
||||
|
||||
GSList *clients;
|
||||
|
||||
} LISTEN_REC;
|
||||
|
||||
typedef struct {
|
||||
char *nick, *host;
|
||||
int port;
|
||||
char *nick, *addr;
|
||||
NET_SENDBUF_REC *handle;
|
||||
int recv_tag;
|
||||
char *proxy_address;
|
||||
|
@ -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)
|
||||
{
|
||||
(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, "port", 4, newSViv(client->port), 0);
|
||||
(void) hv_store(hv, "addr", 4, new_pv(client->addr), 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, "pass_sent", 9, newSViv(client->pass_sent), 0);
|
||||
|
Loading…
Reference in New Issue
Block a user