diff --git a/po/POTFILES.in b/po/POTFILES.in index fa918608..c2a5ede1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -10,6 +10,7 @@ src/common/inbound.c src/common/notify.c src/common/outbound.c src/common/plugin.c +src/common/plugin-identd.c src/common/plugin-timer.c src/common/server.c src/common/servlist.c diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 0cbf650b..ea941671 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -26,6 +26,7 @@ EXTRA_DIST = \ notify.h \ outbound.h \ plugin.h \ + plugin-identd.h \ plugin-timer.h \ proto-irc.h \ server.h \ @@ -60,7 +61,7 @@ noinst_PROGRAMS = make-te libhexchatcommon_a_SOURCES = cfgfiles.c chanopt.c ctcp.c dcc.c hexchat.c \ history.c ignore.c inbound.c marshal.c modes.c network.c notify.c \ - outbound.c plugin.c plugin-timer.c proto-irc.c server.c servlist.c \ + outbound.c plugin.c plugin-identd.c plugin-timer.c proto-irc.c server.c servlist.c \ $(ssl_c) text.c tree.c url.c userlist.c util.c libhexchatcommon_a_CFLAGS = $(LIBPROXY_CFLAGS) diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c index 54694452..8ed31c4f 100644 --- a/src/common/cfgfiles.c +++ b/src/common/cfgfiles.c @@ -477,6 +477,7 @@ const struct prefs vars[] = {"gui_win_width", P_OFFINT (hex_gui_win_width), TYPE_INT}, {"identd", P_OFFINT (hex_identd), TYPE_BOOL}, + {"identd_port", P_OFFINT (hex_identd_port), TYPE_INT}, {"input_balloon_chans", P_OFFINT (hex_input_balloon_chans), TYPE_BOOL}, {"input_balloon_hilight", P_OFFINT (hex_input_balloon_hilight), TYPE_BOOL}, @@ -772,7 +773,6 @@ load_default_config(void) prefs.hex_gui_ulist_resizable = 1; prefs.hex_gui_ulist_style = 1; prefs.hex_gui_win_save = 1; - prefs.hex_identd = 1; prefs.hex_input_flash_hilight = 1; prefs.hex_input_flash_priv = 1; prefs.hex_input_tray_hilight = 1; diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 7746a866..b52b06c7 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -20,7 +20,6 @@ - @@ -29,6 +28,7 @@ + @@ -53,7 +53,7 @@ - + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 5039f3d5..e80d9a63 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -29,9 +29,6 @@ Header Files - - Header Files - Header Files @@ -110,6 +107,9 @@ Header Files + + Header Files + @@ -127,9 +127,6 @@ Source Files - - Source Files - Source Files @@ -187,6 +184,9 @@ Source Files + + Source Files + diff --git a/src/common/hexchat.c b/src/common/hexchat.c index 021c5838..71ea9791 100644 --- a/src/common/hexchat.c +++ b/src/common/hexchat.c @@ -42,6 +42,7 @@ #include "ignore.h" #include "hexchat-plugin.h" #include "plugin.h" +#include "plugin-identd.h" #include "plugin-timer.h" #include "notify.h" #include "server.h" @@ -388,6 +389,7 @@ irc_init (session *sess) done_init = TRUE; plugin_add (sess, NULL, NULL, timer_plugin_init, NULL, NULL, FALSE); + plugin_add (sess, NULL, NULL, identd_plugin_init, identd_plugin_deinit, NULL, FALSE); #ifdef USE_PLUGIN if (!arg_skip_plugins) diff --git a/src/common/hexchat.h b/src/common/hexchat.h index c1acfe45..dfb1c52f 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -263,6 +263,7 @@ struct hexchatprefs int hex_gui_win_state; int hex_gui_win_top; int hex_gui_win_width; + int hex_identd_port; int hex_input_balloon_time; int hex_irc_ban_type; int hex_irc_join_delay; diff --git a/src/common/identd.c b/src/common/identd.c deleted file mode 100644 index 183d4a0e..00000000 --- a/src/common/identd.c +++ /dev/null @@ -1,195 +0,0 @@ -/* HexChat - * Copyright (C) 1998-2010 Peter Zelezny. - * Copyright (C) 2009-2013 Berke Viktor. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/* simple identd server for HexChat under Win32 */ - -#include "inet.h" -#include "hexchat.h" -#include "hexchatc.h" -#include "text.h" - -static int identd_is_running = FALSE; -static int identd_ipv6_is_running = FALSE; - -static int -identd (char *username) -{ - int sok, read_sok, len; - char *p; - char buf[256]; - char outbuf[256]; - char ipbuf[INET_ADDRSTRLEN]; - struct sockaddr_in addr; - - sok = socket (AF_INET, SOCK_STREAM, 0); - if (sok == INVALID_SOCKET) - { - g_free (username); - return 0; - } - - len = 1; - setsockopt (sok, SOL_SOCKET, SO_REUSEADDR, (char *) &len, sizeof (len)); - - memset (&addr, 0, sizeof (addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons (113); - - if (bind (sok, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR) - { - closesocket (sok); - g_free (username); - return 0; - } - - if (listen (sok, 1) == SOCKET_ERROR) - { - closesocket (sok); - g_free (username); - return 0; - } - - len = sizeof (addr); - read_sok = accept (sok, (struct sockaddr *) &addr, &len); - closesocket (sok); - if (read_sok == INVALID_SOCKET) - { - g_free (username); - return 0; - } - - identd_is_running = FALSE; - -#if 0 /* causes random crashes, probably due to CreateThread */ - EMIT_SIGNAL (XP_TE_IDENTD, current_sess, inet_ntoa (addr.sin_addr), username, NULL, NULL, 0); -#endif - inet_ntop (AF_INET, &addr.sin_addr, ipbuf, sizeof (ipbuf)); - g_snprintf (outbuf, sizeof (outbuf), "*\tServicing ident request from %s as %s\n", ipbuf, username); - PrintText (current_sess, outbuf); - - recv (read_sok, buf, sizeof (buf) - 1, 0); - buf[sizeof (buf) - 1] = 0; /* ensure null termination */ - - p = strchr (buf, ','); - if (p) - { - g_snprintf (outbuf, sizeof (outbuf) - 1, "%d, %d : USERID : UNIX : %s\r\n", - atoi (buf), atoi (p + 1), username); - outbuf[sizeof (outbuf) - 1] = 0; /* ensure null termination */ - send (read_sok, outbuf, strlen (outbuf), 0); - } - - sleep (1); - closesocket (read_sok); - g_free (username); - - return 0; -} - -static int -identd_ipv6 (char *username) -{ - int sok, read_sok, len; - char *p; - char buf[256]; - char outbuf[256]; - char ipbuf[INET6_ADDRSTRLEN]; - struct sockaddr_in6 addr; - - sok = socket (AF_INET6, SOCK_STREAM, 0); - if (sok == INVALID_SOCKET) - { - g_free (username); - return 0; - } - - len = 1; - setsockopt (sok, SOL_SOCKET, SO_REUSEADDR, (char *) &len, sizeof (len)); - - memset (&addr, 0, sizeof (addr)); - addr.sin6_family = AF_INET6; - addr.sin6_port = htons (113); - - if (bind (sok, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR) - { - closesocket (sok); - g_free (username); - return 0; - } - - if (listen (sok, 1) == SOCKET_ERROR) - { - closesocket (sok); - g_free (username); - return 0; - } - - len = sizeof (addr); - read_sok = accept (sok, (struct sockaddr *) &addr, &len); - closesocket (sok); - if (read_sok == INVALID_SOCKET) - { - g_free (username); - return 0; - } - - identd_ipv6_is_running = FALSE; - - inet_ntop (AF_INET6, &addr.sin6_addr, ipbuf, sizeof (ipbuf)); - g_snprintf (outbuf, sizeof (outbuf), "*\tServicing ident request from %s as %s\n", ipbuf, username); - PrintText (current_sess, outbuf); - - recv (read_sok, buf, sizeof (buf) - 1, 0); - buf[sizeof (buf) - 1] = 0; /* ensure null termination */ - - p = strchr (buf, ','); - if (p) - { - g_snprintf (outbuf, sizeof (outbuf) - 1, "%d, %d : USERID : UNIX : %s\r\n", atoi (buf), atoi (p + 1), username); - outbuf[sizeof (outbuf) - 1] = 0; /* ensure null termination */ - send (read_sok, outbuf, strlen (outbuf), 0); - } - - sleep (1); - closesocket (read_sok); - g_free (username); - - return 0; -} - -void -identd_start (char *username) -{ - DWORD tid; - - DWORD tidv6; - if (identd_ipv6_is_running == FALSE) - { - identd_ipv6_is_running = TRUE; - CloseHandle (CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) identd_ipv6, - g_strdup (username), 0, &tidv6)); - } - - if (identd_is_running == FALSE) - { - identd_is_running = TRUE; - CloseHandle (CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) identd, - g_strdup (username), 0, &tid)); - } -} diff --git a/src/common/plugin-identd.c b/src/common/plugin-identd.c new file mode 100644 index 00000000..753fde05 --- /dev/null +++ b/src/common/plugin-identd.c @@ -0,0 +1,216 @@ +/* HexChat +* +* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include +#include +#include "hexchat-plugin.h" + +#define _(x) hexchat_gettext(ph,x) + +static hexchat_plugin *ph; +static GSocketService *service; +static GHashTable *responses; + +struct ident_info +{ + GSocketConnection *conn; + gchar *username; + gchar read_buf[16]; +} typedef ident_info; + +static int +identd_cleanup_response_cb (gpointer userdata) +{ + g_return_val_if_fail (responses != NULL, 0); + + g_hash_table_remove (responses, userdata); + + return 0; +} + +static int +identd_command_cb (char *word[], char *word_eol[], void *userdata) +{ + g_return_val_if_fail (responses != NULL, HEXCHAT_EAT_ALL); + + if (word[2] && *word[2] && word[3] && *word[3]) + { + guint64 port = g_ascii_strtoull (word[2], NULL, 0); + + if (port && port <= G_MAXUINT16) + { + g_hash_table_insert (responses, GINT_TO_POINTER (port), g_strdup (word[3])); + /* Automatically remove entry after 30 seconds */ + hexchat_hook_timer (ph, 30000, identd_cleanup_response_cb, GINT_TO_POINTER (port)); + } + } + else + { + hexchat_command (ph, "HELP IDENTD"); + } + + return HEXCHAT_EAT_HEXCHAT; +} + +static void +identd_write_ready (GOutputStream *stream, GAsyncResult *res, ident_info *info) +{ + g_output_stream_write_finish (stream, res, NULL); + + g_free (info->username); + g_object_unref (info->conn); + g_free (info); +} + +static void +identd_read_ready (GInputStream *in_stream, GAsyncResult *res, ident_info *info) +{ + GSocketAddress *sok_addr; + GOutputStream *out_stream; + guint64 local, remote; + gchar buf[512], *p; + + if (g_input_stream_read_finish (in_stream, res, NULL)) + { + local = g_ascii_strtoull (info->read_buf, NULL, 0); + p = strchr (info->read_buf, ','); + if (!p) + goto cleanup; + + remote = g_ascii_strtoull (p + 1, NULL, 0); + + if (!local || !remote || local > G_MAXUINT16 || remote > G_MAXUINT16) + goto cleanup; + + info->username = g_strdup (g_hash_table_lookup (responses, GINT_TO_POINTER (local))); + if (!info->username) + goto cleanup; + g_hash_table_remove (responses, GINT_TO_POINTER (local)); + + if ((sok_addr = g_socket_connection_get_remote_address (info->conn, NULL))) + { + GInetAddress *inet_addr; + gchar *addr; + + inet_addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sok_addr)); + addr = g_inet_address_to_string (inet_addr); + + hexchat_printf (ph, _("*\tServicing ident request from %s as %s"), addr, info->username); + + g_object_unref (sok_addr); + g_object_unref (inet_addr); + g_free (addr); + } + + g_snprintf (buf, sizeof (buf), "%"G_GUINT16_FORMAT", %"G_GUINT16_FORMAT" : USERID : UNIX : %s\r\n", (guint16)local, (guint16)remote, info->username); + out_stream = g_io_stream_get_output_stream (G_IO_STREAM (info->conn)); + g_output_stream_write_async (out_stream, buf, strlen (buf), G_PRIORITY_DEFAULT, + NULL, (GAsyncReadyCallback)identd_write_ready, info); + } + + return; + +cleanup: + g_object_unref (info->conn); + g_free (info); +} + +static gboolean +identd_incoming_cb (GSocketService *service, GSocketConnection *conn, + GObject *source, gpointer userdata) +{ + GInputStream *stream; + ident_info *info; + + info = g_new0 (ident_info, 1); + + info->conn = conn; + g_object_ref (conn); + + stream = g_io_stream_get_input_stream (G_IO_STREAM (conn)); + g_input_stream_read_async (stream, info->read_buf, sizeof (info->read_buf), G_PRIORITY_DEFAULT, + NULL, (GAsyncReadyCallback)identd_read_ready, info); + + return TRUE; +} + +static gboolean +identd_start_server (void) +{ + GError *error = NULL; + int enabled, port = 113; + + if (hexchat_get_prefs (ph, "identd", NULL, &enabled) == 3 && enabled) + { + if (!enabled) + return TRUE; /*...*/ + } + if (hexchat_get_prefs (ph, "identd_port", NULL, &port) == 2 && (port <= 0 || port > G_MAXUINT16)) + { + port = 113; + } + + service = g_socket_service_new (); + + g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), port, NULL, &error); + if (error) + { + hexchat_printf (ph, _("*\tError starting identd server: %s"), error->message); + + g_object_unref (service); + return FALSE; + } + /*hexchat_printf (ph, "*\tIdentd listening on port: %d", port); */ + + g_signal_connect (G_OBJECT (service), "incoming", G_CALLBACK(identd_incoming_cb), NULL); + g_socket_service_start (service); + + return TRUE; +} + +int +identd_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name, + char **plugin_desc, char **plugin_version, char *arg) +{ + ph = plugin_handle; + *plugin_name = ""; + *plugin_desc = ""; + *plugin_version = ""; + + + responses = g_hash_table_new_full (NULL, NULL, NULL, g_free); + hexchat_hook_command (ph, "IDENTD", HEXCHAT_PRI_NORM, identd_command_cb, + _("IDENTD "), NULL); + + return identd_start_server (); +} + +int +identd_plugin_deinit (void) +{ + if (service) + { + g_socket_service_stop (service); + g_object_unref (service); + } + + g_hash_table_destroy (responses); + + return 1; +} diff --git a/src/common/identd.h b/src/common/plugin-identd.h similarity index 78% rename from src/common/identd.h rename to src/common/plugin-identd.h index 3b29135f..5efc2600 100644 --- a/src/common/identd.h +++ b/src/common/plugin-identd.h @@ -17,9 +17,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef HEXCHAT_IDENTD_H -#define HEXCHAT_IDENTD_H +#ifndef HEXCHAT_PLUGIN_IDENTD_H +#define HEXCHAT_PLUGIN_IDENTD_H -void identd_start (char *username); +int identd_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name, + char **plugin_desc, char **plugin_version, char *arg); + +int identd_plugin_deinit (); #endif diff --git a/src/common/server.c b/src/common/server.c index f3633ce5..2b34dbde 100644 --- a/src/common/server.c +++ b/src/common/server.c @@ -61,10 +61,6 @@ #include "ssl.h" #endif -#ifdef WIN32 -#include "identd.h" -#endif - #ifdef USE_LIBPROXY #include #endif @@ -944,28 +940,6 @@ server_read_child (GIOChannel *source, GIOCondition condition, server *serv) waitline2 (source, ip, sizeof ip); waitline2 (source, outbuf, sizeof outbuf); EMIT_SIGNAL (XP_TE_CONNECT, sess, host, ip, outbuf, NULL, 0); -#ifdef WIN32 - if (prefs.hex_identd) - { - if (serv->network && ((ircnet *)serv->network)->user) - { - identd_start (((ircnet *)serv->network)->user); - } - else - { - identd_start (prefs.hex_irc_user_name); - } - } -#else - g_snprintf (outbuf, sizeof (outbuf), "%s/auth/xchat_auth", - g_get_home_dir ()); - if (access (outbuf, X_OK) == 0) - { - g_snprintf (outbuf, sizeof (outbuf), "exec -d %s/auth/xchat_auth %s", - g_get_home_dir (), prefs.hex_irc_user_name); - handle_command (serv->server_session, outbuf, FALSE); - } -#endif break; case '4': /* success */ waitline2 (source, tbuf, sizeof (tbuf)); @@ -982,6 +956,29 @@ server_read_child (GIOChannel *source, GIOCondition condition, server *serv) else closesocket (serv->proxy_sok4); } + + { + struct sockaddr addr; + int addr_len = sizeof (addr); + guint16 port; + + if (!getsockname (serv->sok, &addr, &addr_len)) + { + if (addr.sa_family == AF_INET) + port = ntohs(((struct sockaddr_in *)&addr)->sin_port); + else + port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); + + g_snprintf (outbuf, sizeof (outbuf), "IDENTD %"G_GUINT16_FORMAT" ", port); + if (serv->network && ((ircnet *)serv->network)->user) + g_strlcat (outbuf, ((ircnet *)serv->network)->user, sizeof (outbuf)); + else + g_strlcat (outbuf, prefs.hex_irc_user_name, sizeof (outbuf)); + + handle_command (serv->server_session, outbuf, FALSE); + } + } + server_connect_success (serv); break; case '5': /* prefs ip discovered */