summary refs log blame commit diff stats
path: root/plugins/dns/dns.c
blob: 3610e9d05e81a5bbb3b61669ae6043ade123eaa2 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                           














                                                                       
                                                                            





















                                                                     
               





                       
                           








                                                                 
                          












                                                                  
                                                                         
                 
                                  

                                                       









































































































































                                                                                                          
                                                                                           
                            
                                                                                                                       


                                                    
                                                                                                      



                                                        

                                                                                                      













                                                                          
                           


                         
                                              


                            
                                            
         
                                                                             
                 
                                                                   




















                                                         
                                         
                                       







                                                    
                                                                                              


                                                         
                                                                               




                                                           
                                                                   
                                                                                                                      




                                                                                 
                               


   
                                                     




                                                                                
                                                     



                 

                                                                                   

                                                                                     
                                                                       





                                                     

                                                                              


                                                  
/* HexChat Win32 DNS Plugin
 * Copyright (C) 2003-2004 Peter Zelezny.
 * Copyright (C) 2012 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
 */
/*
 * Requires MS Visual Studio and IPV6 headers to compile (run nmake).
 * Compiling with gcc (mingw) will fail due to missing gai_strerror.
 */

#define DNS_VERSION "2.4"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#define USE_IPV6

#ifdef WIN32
#ifdef USE_IPV6
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <winsock2.h>
#endif
#include <io.h>
#else
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#endif

#include "hexchat-plugin.h"
#include "thread.h"

#define HELP "Usage: DNS <nickname|hostname|numerical address>\n"
#define HEAD "\0034[DNS]\017\t"

#define PIPE_READ 0
#define PIPE_WRITE 1
#define MAX_HOSTNAME 128

static hexchat_plugin *ph;
static thread *active_thread = NULL;


static int
waitline (void *source, char *buf, int bufsize)
{
	int i = 0;
	int len;

	while(1)
	{
		len = 1;
		/* we can't read() here, due to glib's giowin32 */
		if (ph->hexchat_read_fd (ph, source, buf + i, &len) != 0)
		{
			return -1;
		}
		if (buf[i] == '\n' || bufsize == i + 1)
		{
			buf[i] = 0;
			return i;
		}
		i++;
	}
}

static void *
thread_function (void *ud)
{
#ifdef USE_IPV6
	struct addrinfo *ent;
	struct addrinfo *cur;
	struct addrinfo hints;
#else
	struct hostent *ent;
#endif
	thread *th = ud;
	int fd = th->pipe_fd[PIPE_WRITE];
	int ret;
	char ipstring[MAX_HOSTNAME];
	char reverse[MAX_HOSTNAME];
//	int i;

	active_thread = th;

#ifdef USE_IPV6
	memset (&hints, 0, sizeof (hints));
	hints.ai_family = PF_UNSPEC; /* support ipv6 and ipv4 */
	hints.ai_flags = AI_CANONNAME;
//	hints.ai_socktype = SOCK_STREAM;

	ret = getaddrinfo (th->userdata, NULL, &hints, &ent);
	if (ret != 0)
	{
		sprintf (ipstring, "1%d\n", ret);	/* failed */
		write (fd, ipstring, strlen (ipstring));
//		Sleep (3000);
		active_thread = NULL;
		return 0;
	}

//	i = 0;
	cur = ent;
	while (cur)
	{
		/* find the numeric IP number */
		ipstring[0] = 0;
		getnameinfo (cur->ai_addr, cur->ai_addrlen,
						 ipstring, sizeof (ipstring), NULL, 0, NI_NUMERICHOST);

		if (cur->ai_canonname)
		{
			/* force reverse lookup if canonname & ipstring are the same */
			if (/*i == 0 &&*/ strcmp (cur->ai_canonname, ipstring) == 0)
				goto lamecode;
		}

		if (cur->ai_canonname)
		{
			write (fd, "0", 1);
			write (fd, ipstring, strlen (ipstring));
			write (fd, "\n", 1);
			write (fd, cur->ai_canonname, strlen (cur->ai_canonname));
		} else
		{
lamecode:
	//		ret = 1;
	//		if (i == 0)
			{
				/* reverse lookup */
				reverse[0] = 0;
				ret = getnameinfo (cur->ai_addr, cur->ai_addrlen,
							 reverse, sizeof (reverse), NULL, 0, NI_NAMEREQD);
			}

			write (fd, "0", 1);
			write (fd, ipstring, strlen (ipstring));

			write (fd, "\n", 1);
			if (ret == 0)
				write (fd, reverse, strlen (reverse));
		}
		write (fd, "\n", 1);

//		i++;
		cur = cur->ai_next;
	}

	/* tell the parent we're done */
	write (fd, "2\n", 2);
	freeaddrinfo (ent);

#else
	ent = gethostbyname (th->userdata);
	if (ent)
	{
		write (fd, "0", 1);
		write (fd, ent->h_name, strlen (ent->h_name));
		write (fd, "\n", 1);
		write (fd, ent->h_name, strlen (ent->h_name));
		write (fd, "\n", 1);
		write (fd, "2\n", 2);
	} else
	{
		write (fd, "10\n", 1);
	}
#endif

//	Sleep (3000);
	active_thread = NULL;	/* race condition, better than nothing */

	return 0;
}

static int
dns_close_pipe (int fd)
{
	close (fd);
	return 0;
}

/* read messages comming from the child (through the pipe) */

static int
dns_read_cb (int fd, int flags, thread *th, void *source)
{
	char buf[512];
	char buf2[512];

	while (waitline (source, buf, sizeof (buf)))
	{
		switch (buf[0])
		{
		case '0':		/* got data to show */
			waitline (source, buf2, sizeof (buf2));
			if (buf2[0] == 0)
				hexchat_printf(ph, HEAD"\002Numerical\002: %s\n", buf + 1);
			else
				hexchat_printf(ph, HEAD"\002Canonical\002: %s \002Numerical\002: %s\n", buf2, buf + 1);
			return 1;

		case '1':		/* failed */
			hexchat_printf(ph, HEAD"Lookup failed. %s\n", gai_strerrorA (atoi (buf + 1)));

		case '2':		/* done */
		//	close (th->pipe_fd[PIPE_WRITE]);
		//	close (th->pipe_fd[PIPE_READ]);
			hexchat_hook_timer(ph, 3000, dns_close_pipe, (void *)th->pipe_fd[PIPE_WRITE]);
			hexchat_hook_timer(ph, 4000, dns_close_pipe, (void *)th->pipe_fd[PIPE_READ]);
			free (th->userdata); 	/* hostname strdup'ed */
			free (th);
			return 0;
		}
	}

	return 1;
}

/* find hostname from nickname (search the userlist, current chan only) */

static char *
find_nick_host (char *nick)
{
	hexchat_list *list;
	char *at;
	const char *host;

	list = hexchat_list_get (ph, "users");
	if (!list)
		return NULL;

	while (hexchat_list_next (ph, list))
	{
		if (stricmp (nick, hexchat_list_str (ph, list, "nick")) == 0)
		{
			host = hexchat_list_str (ph, list, "host");
			if (host)
			{
				at = strrchr (host, '@');
				if (at)
					return at + 1;
			}
			break;
		}
	}

	return NULL;
}

static int
dns_cmd_cb (char *word[], char *word_eol[], void *ud)
{
	thread *th;
	char *nickhost;

	if (!word[2][0])
	{
		hexchat_print (ph, HELP);
		return HEXCHAT_EAT_ALL;
	}

	th = thread_new ();
	if (th)
	{
		nickhost = find_nick_host (word[2]);
		if (nickhost)
		{
			hexchat_printf (ph, HEAD"Looking up %s (%s)...\n", nickhost, word[2]);
			th->userdata = strdup (nickhost);
		} else
		{
			hexchat_printf (ph, HEAD"Looking up %s...\n", word[2]);
			th->userdata = strdup (word[2]);
		}

		if (thread_start (th, thread_function, th))
		{
			hexchat_hook_fd(ph, th->pipe_fd[PIPE_READ],
							HEXCHAT_FD_READ | HEXCHAT_FD_EXCEPTION | HEXCHAT_FD_NOTSOCKET,
							(void *)dns_read_cb, th);

		}
	}

	return HEXCHAT_EAT_ALL;
}

int
hexchat_plugin_deinit (hexchat_plugin *plugin_handle)
{
	while (active_thread)	/* children will set this var to NULL soon... */
	{
		Sleep (1000);
	}
	hexchat_printf (ph, "DNS plugin unloaded\n");
	return 1;
}

int
hexchat_plugin_init
				(hexchat_plugin *plugin_handle, char **plugin_name,
				char **plugin_desc, char **plugin_version, char *arg)
{
	/* we need to save this for use with any hexchat_* functions */
	ph = plugin_handle;

	*plugin_name = "DNS";
	*plugin_desc = "Threaded IPv4/6 DNS Command";
	*plugin_version = DNS_VERSION;

	hexchat_hook_command(ph, "DNS", HEXCHAT_PRI_LOW, dns_cmd_cb, HELP, 0);
	hexchat_printf (ph, "DNS plugin loaded\n");

	return 1;       /* return 1 for success */
}
tab = strchr (text, '\t'); if (tab && tab < (text + len)) { leftlen = tab - text; gtk_xtext_append_indent (xtbuf, text, leftlen, tab + 1, len - (leftlen + 1), timet); } else gtk_xtext_append_indent (xtbuf, 0, 0, text, len, timet); } void PrintTextRaw (void *xtbuf, unsigned char *text, int indent, time_t stamp) { char *last_text = text; int len = 0; int beep_done = FALSE; /* split the text into separate lines */ while (1) { switch (*text) { case 0: PrintTextLine (xtbuf, last_text, len, indent, stamp); return; case '\n': PrintTextLine (xtbuf, last_text, len, indent, stamp); text++; if (*text == 0) return; last_text = text; len = 0; break; case ATTR_BEEP: *text = ' '; if (!beep_done) /* beeps may be slow, so only do 1 per line */ { beep_done = TRUE; if (!prefs.hex_input_filter_beep) gdk_beep (); } default: text++; len++; } } } static void pevent_dialog_close (GtkWidget *wid, gpointer arg) { pevent_dialog = NULL; pevent_save (NULL); } static void pevent_edited (GtkCellRendererText *render, gchar *pathstr, gchar *new_text, gpointer data) { GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (pevent_dialog_list)); GtkTreePath *path; GtkTreeIter iter; GtkXText *xtext = GTK_XTEXT (pevent_dialog_twid); int len, m; const char *text; char *out; int sig; if (!gtkutil_treeview_get_selected (GTK_TREE_VIEW (pevent_dialog_list), &iter, ROW_COLUMN, &sig, -1)) return; text = new_text; len = strlen (new_text); if (pevt_build_string (text, &out, &m) != 0) { fe_message (_("There was an error parsing the string"), FE_MSG_ERROR); return; } if (m > (te[sig].num_args & 0x7f)) { g_free (out); out = g_strdup_printf ( _("This signal is only passed %d args, $%d is invalid"), te[sig].num_args & 0x7f, m); fe_message (out, FE_MSG_WARN); g_free (out); return; } path = gtk_tree_path_new_from_string (pathstr); gtk_tree_model_get_iter (model, &iter, path); gtk_list_store_set (GTK_LIST_STORE (model), &iter, TEXT_COLUMN, new_text, -1); gtk_tree_path_free (path); g_free (pntevts_text[sig]); g_free (pntevts[sig]); pntevts_text[sig] = g_strdup (text); pntevts[sig] = out; out = g_malloc (len + 2); memcpy (out, text, len + 1); out[len] = '\n'; out[len + 1] = 0; check_special_chars (out, TRUE); PrintTextRaw (xtext->buffer, out, 0, 0); g_free (out); /* Scroll to bottom */ gtk_adjustment_set_value (xtext->adj, gtk_adjustment_get_upper (xtext->adj)); /* save this when we exit */ prefs.save_pevents = 1; } static void pevent_dialog_hfill (GtkWidget *list, int e) { int i = 0; char *text; GtkTreeIter iter; GtkListStore *store; store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list))); gtk_list_store_clear (store); while (i < (te[e].num_args & 0x7f)) { text = _(te[e].help[i]); i++; if (text[0] == '\001') text++; gtk_list_store_insert_with_values (store, &iter, -1, 0, i, 1, text, -1); } } static void pevent_selection_changed (GtkTreeSelection *sel, gpointer userdata) { GtkTreeIter iter; int sig; if (!gtkutil_treeview_get_selected (GTK_TREE_VIEW (pevent_dialog_list), &iter, ROW_COLUMN, &sig, -1)) { gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pevent_dialog_hlist)))); return; } pevent_dialog_hfill (pevent_dialog_hlist, sig); } static void pevent_dialog_fill (GtkWidget *list) { int i; GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list))); gtk_list_store_clear (store); i = NUM_XP; do { i--; gtk_list_store_insert_with_values (store, &iter, 0, EVENT_COLUMN, te[i].name, TEXT_COLUMN, pntevts_text[i], ROW_COLUMN, i, -1); } while (i != 0); } static void pevent_save_req_cb (void *arg1, char *file) { if (file) pevent_save (file); } static void pevent_save_cb (GtkWidget * wid, void *data) { if (data) { gtkutil_file_req (_("Print Texts File"), pevent_save_req_cb, NULL, NULL, NULL, FRF_WRITE); return; } pevent_save (NULL); } static void pevent_load_req_cb (void *arg1, char *file) { if (file) { pevent_load (file); pevent_make_pntevts (); pevent_dialog_fill (pevent_dialog_list); prefs.save_pevents = 1; } } static void pevent_load_cb (GtkWidget * wid, void *data) { gtkutil_file_req (_("Print Texts File"), pevent_load_req_cb, NULL, NULL, NULL, 0); } static void pevent_ok_cb (GtkWidget * wid, void *data) { gtk_widget_destroy (pevent_dialog); } static void pevent_test_cb (GtkWidget * wid, GtkWidget * twid) { int len, n; char *out, *text; for (n = 0; n < NUM_XP; n++) { text = _(pntevts_text[n]); len = strlen (text); out = g_malloc (len + 2); memcpy (out, text, len + 1); out[len] = '\n'; out[len + 1] = 0; check_special_chars (out, TRUE); PrintTextRaw (GTK_XTEXT (twid)->buffer, out, 0, 0); g_free (out); } } static GtkWidget * pevent_treeview_new (GtkWidget *box) { GtkWidget *scroll; GtkListStore *store; GtkTreeViewColumn *col; GtkTreeSelection *sel; GtkWidget *view; GtkCellRenderer *render; scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); gtk_widget_set_size_request (GTK_WIDGET (scroll), -1, 250); store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); g_return_val_if_fail (store != NULL, NULL); view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE); gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), TRUE); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE); sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (pevent_selection_changed), NULL); render = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes ( GTK_TREE_VIEW (view), EVENT_COLUMN, _("Event"), render, "text", EVENT_COLUMN, NULL); render = gtk_cell_renderer_text_new (); g_object_set (render, "editable", TRUE, NULL); g_signal_connect (G_OBJECT (render), "edited", G_CALLBACK (pevent_edited), NULL); gtk_tree_view_insert_column_with_attributes ( GTK_TREE_VIEW (view), TEXT_COLUMN, _("Text"), render, "text", TEXT_COLUMN, NULL); col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), EVENT_COLUMN); gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_column_set_resizable (col, TRUE); gtk_tree_view_column_set_min_width (col, 100); gtk_container_add (GTK_CONTAINER (scroll), view); gtk_container_add (GTK_CONTAINER (box), scroll); return view; } static GtkWidget * pevent_hlist_treeview_new (GtkWidget *box) { GtkWidget *scroll; GtkListStore *store; GtkTreeViewColumn *col; GtkWidget *view; GtkCellRenderer *render; scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING); g_return_val_if_fail (store != NULL, NULL); view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE); gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE); gtk_widget_set_can_focus (view, FALSE); render = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes ( GTK_TREE_VIEW (view), 0, _("$ Number"), render, "text", 0, NULL); render = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes ( GTK_TREE_VIEW (view), 1, _("Description"), render, "text", 1, NULL); col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0); gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_container_add (GTK_CONTAINER (scroll), view); gtk_container_add (GTK_CONTAINER (box), scroll); return view; } void pevent_dialog_show () { GtkWidget *vbox, *hbox, *wid, *pane; if (pevent_dialog) { mg_bring_tofront (pevent_dialog); return; } pevent_dialog = mg_create_generic_tab ("edit events", _("Edit Events"), TRUE, FALSE, pevent_dialog_close, NULL, 600, 455, &vbox, 0); pane = gtk_vpaned_new (); gtk_box_pack_start (GTK_BOX (vbox), pane, TRUE, TRUE, 0); pevent_dialog_list = pevent_treeview_new (pane); pevent_dialog_fill (pevent_dialog_list); pevent_dialog_hlist = pevent_hlist_treeview_new (pane); wid = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (wid), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_box_pack_start (GTK_BOX (vbox), wid, FALSE, TRUE, 0); pevent_dialog_twid = gtk_xtext_new (colors, 0); gtk_widget_set_sensitive (pevent_dialog_twid, FALSE); gtk_widget_set_size_request (pevent_dialog_twid, -1, 75); gtk_container_add (GTK_CONTAINER (wid), pevent_dialog_twid); gtk_xtext_set_font (GTK_XTEXT (pevent_dialog_twid), prefs.hex_text_font); hbox = gtk_hbutton_box_new (); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 4); gtkutil_button (hbox, GTK_STOCK_SAVE_AS, NULL, pevent_save_cb, (void *) 1, _("Save As...")); gtkutil_button (hbox, GTK_STOCK_OPEN, NULL, pevent_load_cb, NULL, _("Load From...")); gtkutil_button (hbox, NULL, NULL, pevent_test_cb, pevent_dialog_twid, _("Test All")); gtkutil_button (hbox, GTK_STOCK_OK, NULL, pevent_ok_cb, NULL, _("OK")); gtk_widget_show_all (pevent_dialog); }