diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/wee-command.c | 20 | ||||
-rw-r--r-- | src/core/wee-hook.c | 100 | ||||
-rw-r--r-- | src/core/wee-hook.h | 31 | ||||
-rw-r--r-- | src/core/wee-network.c | 443 | ||||
-rw-r--r-- | src/core/wee-network.h | 5 | ||||
-rw-r--r-- | src/core/weechat.c | 4 | ||||
-rw-r--r-- | src/gui/curses/Makefile.am | 3 | ||||
-rw-r--r-- | src/gui/gtk/Makefile.am | 3 | ||||
-rw-r--r-- | src/plugins/irc/irc-command.c | 16 | ||||
-rw-r--r-- | src/plugins/irc/irc-config.c | 10 | ||||
-rw-r--r-- | src/plugins/irc/irc-config.h | 2 | ||||
-rw-r--r-- | src/plugins/irc/irc-display.c | 6 | ||||
-rw-r--r-- | src/plugins/irc/irc-server.c | 485 | ||||
-rw-r--r-- | src/plugins/irc/irc-server.h | 10 | ||||
-rw-r--r-- | src/plugins/irc/irc.c | 17 | ||||
-rw-r--r-- | src/plugins/irc/irc.h | 4 | ||||
-rw-r--r-- | src/plugins/plugin.c | 1 | ||||
-rw-r--r-- | src/plugins/weechat-plugin.h | 23 |
18 files changed, 705 insertions, 478 deletions
diff --git a/src/core/wee-command.c b/src/core/wee-command.c index 296a7935d..94a7b05a9 100644 --- a/src/core/wee-command.c +++ b/src/core/wee-command.c @@ -1470,6 +1470,26 @@ command_plugin_list (char *name, int full) HOOK_FD(ptr_hook, flags)); } } + + /* connect hooked */ + hook_found = 0; + for (ptr_hook = weechat_hooks[HOOK_TYPE_CONNECT]; ptr_hook; + ptr_hook = ptr_hook->next_hook) + { + if (!ptr_hook->deleted && (ptr_hook->plugin == ptr_plugin)) + { + if (!hook_found) + gui_chat_printf (NULL, + _(" connect hooked:")); + hook_found = 1; + gui_chat_printf (NULL, + _(" socket: %d, address: %s, " + "port: %d"), + HOOK_CONNECT(ptr_hook, sock), + HOOK_CONNECT(ptr_hook, address), + HOOK_CONNECT(ptr_hook, port)); + } + } /* prints hooked */ hook_found = 0; diff --git a/src/core/wee-hook.c b/src/core/wee-hook.c index b904b3cdc..8de5b8eec 100644 --- a/src/core/wee-hook.c +++ b/src/core/wee-hook.c @@ -23,14 +23,19 @@ #include "config.h" #endif +#include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> #include <time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> #include "weechat.h" #include "wee-hook.h" #include "wee-log.h" +#include "wee-network.h" #include "wee-string.h" #include "wee-util.h" #include "../gui/gui-buffer.h" @@ -38,6 +43,9 @@ #include "../plugins/plugin.h" +char *hook_type_string[HOOK_NUM_TYPES] = +{ "command", "timer", "fd", "connect", "print", "signal", "config", + "completion", "modifier" }; struct t_hook *weechat_hooks[HOOK_NUM_TYPES]; struct t_hook *last_weechat_hook[HOOK_NUM_TYPES]; int hook_exec_recursion = 0; @@ -778,6 +786,54 @@ hook_fd_exec (fd_set *read_fds, fd_set *write_fds, fd_set *exception_fds) } /* + * hook_connect: hook a connection to peer (using fork) + */ + +struct t_hook * +hook_connect (struct t_weechat_plugin *plugin, char *address, int port, + int sock, int ipv6, void *gnutls_sess, char *local_hostname, + t_hook_callback_connect *callback, void *callback_data) +{ + struct t_hook *new_hook; + struct t_hook_connect *new_hook_connect; + + if ((sock < 0) || !address || (port <= 0)) + return NULL; + + new_hook = malloc (sizeof (*new_hook)); + if (!new_hook) + return NULL; + new_hook_connect = malloc (sizeof (*new_hook_connect)); + if (!new_hook_connect) + { + free (new_hook); + return NULL; + } + + hook_init_data (new_hook, plugin, HOOK_TYPE_CONNECT, callback_data); + + new_hook->hook_data = new_hook_connect; + new_hook_connect->callback = callback; + new_hook_connect->address = strdup (address); + new_hook_connect->port = port; + new_hook_connect->sock = sock; + new_hook_connect->ipv6 = ipv6; + new_hook_connect->gnutls_sess = gnutls_sess; + new_hook_connect->local_hostname = (local_hostname) ? + strdup (local_hostname) : NULL; + new_hook_connect->child_read = -1; + new_hook_connect->child_write = -1; + new_hook_connect->child_pid = 0; + new_hook_connect->hook_fd = NULL; + + hook_add_to_list (new_hook); + + network_connect_with_fork (new_hook); + + return new_hook; +} + +/* * hook_print: hook a message printed by WeeChat */ @@ -1273,6 +1329,22 @@ unhook (struct t_hook *hook) case HOOK_TYPE_FD: free ((struct t_hook_fd *)hook->hook_data); break; + case HOOK_TYPE_CONNECT: + if (HOOK_CONNECT(hook, address)) + free (HOOK_CONNECT(hook, address)); + if (HOOK_CONNECT(hook, hook_fd)) + unhook (HOOK_CONNECT(hook, hook_fd)); + if (HOOK_CONNECT(hook, child_pid) > 0) + { + kill (HOOK_CONNECT(hook, child_pid), SIGKILL); + waitpid (HOOK_CONNECT(hook, child_pid), NULL, 0); + } + if (HOOK_CONNECT(hook, child_read) != -1) + close (HOOK_CONNECT(hook, child_read)); + if (HOOK_CONNECT(hook, child_write) != -1) + close (HOOK_CONNECT(hook, child_write)); + free ((struct t_hook_connect *)hook->hook_data); + break; case HOOK_TYPE_PRINT: if (HOOK_PRINT(hook, message)) free (HOOK_PRINT(hook, message)); @@ -1388,11 +1460,12 @@ hook_print_log () (ptr_hook->plugin) ? ptr_hook->plugin->name : ""); log_printf (" deleted. . . . . . . . : %d", ptr_hook->deleted); log_printf (" running. . . . . . . . : %d", ptr_hook->running); + log_printf (" type . . . . . . . . . : %d (%s)", + ptr_hook->type, hook_type_string[ptr_hook->type]); + log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); switch (ptr_hook->type) { case HOOK_TYPE_COMMAND: - log_printf (" type . . . . . . . . . : %d (command)", ptr_hook->type); - log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); if (!ptr_hook->deleted) { log_printf (" command data:"); @@ -1406,8 +1479,6 @@ hook_print_log () } break; case HOOK_TYPE_TIMER: - log_printf (" type . . . . . . . . . : %d (timer)", ptr_hook->type); - log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); if (!ptr_hook->deleted) { log_printf (" timer data:"); @@ -1430,8 +1501,15 @@ hook_print_log () } break; case HOOK_TYPE_FD: - log_printf (" type . . . . . . . . . : %d (fd)", ptr_hook->type); - log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); + if (!ptr_hook->deleted) + { + log_printf (" fd data:"); + log_printf (" callback . . . . . . : 0x%x", HOOK_FD(ptr_hook, callback)); + log_printf (" fd . . . . . . . . . : %ld", HOOK_FD(ptr_hook, fd)); + log_printf (" flags. . . . . . . . : %ld", HOOK_FD(ptr_hook, flags)); + } + break; + case HOOK_TYPE_CONNECT: if (!ptr_hook->deleted) { log_printf (" fd data:"); @@ -1441,8 +1519,6 @@ hook_print_log () } break; case HOOK_TYPE_PRINT: - log_printf (" type . . . . . . . . . : %d (print)", ptr_hook->type); - log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); if (!ptr_hook->deleted) { log_printf (" print data:"); @@ -1452,8 +1528,6 @@ hook_print_log () } break; case HOOK_TYPE_SIGNAL: - log_printf (" type . . . . . . . . . : %d (signal)", ptr_hook->type); - log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); if (!ptr_hook->deleted) { log_printf (" signal data:"); @@ -1462,8 +1536,6 @@ hook_print_log () } break; case HOOK_TYPE_CONFIG: - log_printf (" type . . . . . . . . . : %d (config)", ptr_hook->type); - log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); if (!ptr_hook->deleted) { log_printf (" config data:"); @@ -1472,8 +1544,6 @@ hook_print_log () } break; case HOOK_TYPE_COMPLETION: - log_printf (" type . . . . . . . . . : %d (completion)", ptr_hook->type); - log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); if (!ptr_hook->deleted) { log_printf (" completion data:"); @@ -1482,8 +1552,6 @@ hook_print_log () } break; case HOOK_TYPE_MODIFIER: - log_printf (" type . . . . . . . . . : %d (modifier)", ptr_hook->type); - log_printf (" callback_data. . . . . : 0x%x", ptr_hook->callback_data); if (!ptr_hook->deleted) { log_printf (" modifier data:"); diff --git a/src/core/wee-hook.h b/src/core/wee-hook.h index 7c54e4eea..7959f0232 100644 --- a/src/core/wee-hook.h +++ b/src/core/wee-hook.h @@ -20,6 +20,10 @@ #ifndef __WEECHAT_HOOK_H #define __WEECHAT_HOOK_H 1 +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + struct t_gui_buffer; struct t_weelist; @@ -30,6 +34,7 @@ enum t_hook_type HOOK_TYPE_COMMAND = 0, /* new command */ HOOK_TYPE_TIMER, /* timer */ HOOK_TYPE_FD, /* socket of file descriptor */ + HOOK_TYPE_CONNECT, /* connect to peer with fork */ HOOK_TYPE_PRINT, /* printed message */ HOOK_TYPE_SIGNAL, /* signal */ HOOK_TYPE_CONFIG, /* config option */ @@ -46,6 +51,7 @@ enum t_hook_type #define HOOK_COMMAND(hook, var) (((struct t_hook_command *)hook->hook_data)->var) #define HOOK_TIMER(hook, var) (((struct t_hook_timer *)hook->hook_data)->var) #define HOOK_FD(hook, var) (((struct t_hook_fd *)hook->hook_data)->var) +#define HOOK_CONNECT(hook, var) (((struct t_hook_connect *)hook->hook_data)->var) #define HOOK_PRINT(hook, var) (((struct t_hook_print *)hook->hook_data)->var) #define HOOK_SIGNAL(hook, var) (((struct t_hook_signal *)hook->hook_data)->var) #define HOOK_CONFIG(hook, var) (((struct t_hook_config *)hook->hook_data)->var) @@ -103,6 +109,25 @@ struct t_hook_fd int flags; /* fd flags (read,write,..) */ }; +typedef int (t_hook_callback_connect)(void *data, int status); + +struct t_hook_connect +{ + t_hook_callback_connect *callback; /* connect callback */ + char *address; /* peer address */ + int port; /* peer port */ + int sock; /* socket (created by caller) */ + int ipv6; /* IPv6 connection ? */ +#ifdef HAVE_GNUTLS + gnutls_session_t *gnutls_sess; /* GnuTLS session (SSL connection) */ +#endif + char *local_hostname; /* force local hostname (optional) */ + int child_read; /* to read into child pipe */ + int child_write; /* to write into child pipe */ + pid_t child_pid; /* pid of child process (connecting) */ + struct t_hook *hook_fd; /* pointer to fd hook */ +}; + typedef int (t_hook_callback_print)(void *data, struct t_gui_buffer *buffer, time_t date, int tags_count, char **tags, char *prefix, @@ -191,6 +216,12 @@ extern int hook_fd_set (fd_set *read_fds, fd_set *write_fds, fd_set *exception_fds); extern void hook_fd_exec (fd_set *read_fds, fd_set *write_fds, fd_set *exception_fds); +extern struct t_hook *hook_connect (struct t_weechat_plugin *plugin, + char *address, int port, + int sock, int ipv6, void *gnutls_session, + char *local_hostname, + t_hook_callback_connect * callback, + void *callback_data); extern struct t_hook *hook_print (struct t_weechat_plugin *plugin, struct t_gui_buffer *buffer, char *tags, char *message, diff --git a/src/core/wee-network.c b/src/core/wee-network.c index 1c9e5dc63..3bfab7215 100644 --- a/src/core/wee-network.c +++ b/src/core/wee-network.c @@ -23,6 +23,7 @@ #include "config.h" #endif +#include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> @@ -32,11 +33,57 @@ #include <netdb.h> #include <errno.h> +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + #include "weechat.h" #include "wee-network.h" +#include "wee-hook.h" #include "wee-config.h" #include "wee-string.h" +#include "../plugins/plugin.h" + + +#ifdef HAVE_GNUTLS +gnutls_certificate_credentials gnutls_xcred; /* GnuTLS client credentials */ +const int gnutls_cert_type_prio[] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 }; +#if LIBGNUTLS_VERSION_NUMBER >= 0x010700 + const int gnutls_prot_prio[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, + GNUTLS_TLS1_0, GNUTLS_SSL3, 0 }; +#else + const int gnutls_prot_prio[] = { GNUTLS_TLS1_1, GNUTLS_TLS1_0, + GNUTLS_SSL3, 0 }; +#endif +#endif + + +/* + * network_init: init network + */ + +void +network_init () +{ +#ifdef HAVE_GNUTLS + gnutls_global_init (); + gnutls_certificate_allocate_credentials (&gnutls_xcred); + gnutls_certificate_set_x509_trust_file (gnutls_xcred, "ca.pem", GNUTLS_X509_FMT_PEM); +#endif +} + +/* + * network_end: end network + */ +void +network_end () +{ +#ifdef HAVE_GNUTLS + gnutls_certificate_free_credentials (gnutls_xcred); + gnutls_global_deinit(); +#endif +} /* * network_convbase64_8x3_to_6x4 : convert 3 bytes of 8 bits in 4 bytes of 6 bits @@ -101,9 +148,8 @@ network_base64encode (char *from, char *to) /* * network_pass_httpproxy: establish connection/authentification to an * http proxy - * return : - * - 0 if connexion throw proxy was successful - * - 1 if connexion fails + * return 1 if connection is ok + * 0 if error */ int @@ -135,26 +181,26 @@ network_pass_httpproxy (int sock, char *address, int port) m = send (sock, buffer, n, 0); if (n != m) - return 1; + return 0; n = recv (sock, buffer, sizeof (buffer), 0); /* success result must be like: "HTTP/1.0 200 OK" */ if (n < 12) - return 1; + return 0; if (memcmp (buffer, "HTTP/", 5) || memcmp (buffer + 9, "200", 3)) - return 1; + return 0; - return 0; + /* connection ok */ + return 1; } /* * network_resolve: resolve hostname on its IP address * (works with ipv4 and ipv6) - * return : - * - 0 if resolution was successful - * - 1 if resolution fails + * return 1 if resolution is ok + * 0 if error */ int @@ -169,16 +215,16 @@ network_resolve (char *hostname, char *ip, int *version) res = NULL; if (getaddrinfo (hostname, NULL, NULL, &res) != 0) - return 1; + return 0; if (!res) - return 1; + return 0; if (getnameinfo (res->ai_addr, res->ai_addrlen, ipbuffer, sizeof(ipbuffer), NULL, 0, NI_NUMERICHOST) != 0) { freeaddrinfo (res); - return 1; + return 0; } if ((res->ai_family == AF_INET) && (version != NULL)) @@ -190,15 +236,15 @@ network_resolve (char *hostname, char *ip, int *version) freeaddrinfo (res); - return 0; + /* resolution ok */ + return 1; } /* * network_pass_socks4proxy: establish connection/authentification thru a * socks4 proxy - * return : - * - 0 if connexion thru proxy was successful - * - 1 if connexion fails + * return 1 if connection is ok + * 0 if error */ int @@ -232,18 +278,19 @@ network_pass_socks4proxy (int sock, char *address, int port) send (sock, (char *) &socks4, 8 + strlen (socks4.user) + 1, 0); recv (sock, buffer, sizeof (buffer), 0); - if (buffer[0] == 0 && buffer[1] == 90) - return 0; + /* connection ok */ + if ((buffer[0] == 0) && (buffer[1] == 90)) + return 1; - return 1; + /* connection failed */ + return 0; } /* * network_pass_socks5proxy: establish connection/authentification thru a * socks5 proxy - * return : - * - 0 if connexion thru proxy was successful - * - 1 if connexion fails + * return 1 if connection is ok + * 0 if error */ int @@ -276,7 +323,7 @@ network_pass_socks5proxy (int sock, char *address, int port) send (sock, (char *) &socks5, sizeof(socks5), 0); /* server socks5 must respond with 2 bytes */ if (recv (sock, buffer, 2, 0) != 2) - return 1; + return 0; if (CONFIG_STRING(config_proxy_username) && CONFIG_STRING(config_proxy_username)[0]) @@ -288,7 +335,7 @@ network_pass_socks5proxy (int sock, char *address, int port) */ if (buffer[0] != 5 || buffer[1] != 2) - return 1; + return 0; /* authentication as in RFC 1929 */ username_len = strlen (CONFIG_STRING(config_proxy_username)); @@ -306,11 +353,11 @@ network_pass_socks5proxy (int sock, char *address, int port) /* server socks5 must respond with 2 bytes */ if (recv (sock, buffer, 2, 0) != 2) - return 1; + return 0; /* buffer[1] = auth state, must be 0 for success */ if (buffer[1] != 0) - return 1; + return 0; } else { @@ -319,8 +366,8 @@ network_pass_socks5proxy (int sock, char *address, int port) * - socks version (buffer[0]) = 5 => socks5 * - socks method (buffer[1]) = 0 => no authentication */ - if (!(buffer[0] == 5 && buffer[1] == 0)) - return 1; + if (!((buffer[0] == 5) && (buffer[1] == 0))) + return 0; } /* authentication successful then giving address/port to connect */ @@ -328,7 +375,7 @@ network_pass_socks5proxy (int sock, char *address, int port) addr_buffer_len = 4 + 1 + addr_len + 2; addr_buffer = malloc (addr_buffer_len * sizeof(*addr_buffer)); if (!addr_buffer) - return 1; + return 0; addr_buffer[0] = 5; /* version 5 */ addr_buffer[1] = 1; /* command: 1 for connect */ addr_buffer[2] = 0; /* reserved */ @@ -342,10 +389,10 @@ network_pass_socks5proxy (int sock, char *address, int port) /* dialog with proxy server */ if (recv (sock, buffer, 4, 0) != 4) - return 1; + return 0; - if (!(buffer[0] == 5 && buffer[1] == 0)) - return 1; + if (!((buffer[0] == 5) && (buffer[1] == 0))) + return 0; /* buffer[3] = address type */ switch (buffer[3]) @@ -356,7 +403,7 @@ network_pass_socks5proxy (int sock, char *address, int port) * address of 4 bytes and port of 2 bytes (= 6 bytes) */ if (recv (sock, buffer, 6, 0) != 6) - return 1; + return 0; break; case 3: /* domainname @@ -364,11 +411,11 @@ network_pass_socks5proxy (int sock, char *address, int port) */ /* reading address length */ if (recv (sock, buffer, 1, 0) != 1) - return 1; + return 0; addr_len = buffer[0]; /* reading address + port = addr_len + 2 */ if (recv (sock, buffer, addr_len + 2, 0) != (addr_len + 2)) - return 1; + return 0; break; case 4: /* ipv6 @@ -376,20 +423,20 @@ network_pass_socks5proxy (int sock, char *address, int port) * address of 16 bytes and port of 2 bytes (= 18 bytes) */ if (recv (sock, buffer, 18, 0) != 18) - return 1; + return 0; break; default: - return 1; + return 0; } - return 0; + /* connection ok */ + return 1; } /* * network_pass_proxy: establish connection/authentification to a proxy - * return : - * - 0 if connexion throw proxy was successful - * - 1 if connexion fails + * return 1 if connection is ok + * 0 if error */ int @@ -397,8 +444,8 @@ network_pass_proxy (int sock, char *address, int port) { int rc; - rc = 1; - if (CONFIG_BOOLEAN(config_proxy_type)) + rc = 0; + if (CONFIG_BOOLEAN(config_proxy_use)) { if (string_strcasecmp (CONFIG_STRING(config_proxy_type), "http") == 0) rc = network_pass_httpproxy (sock, address, port); @@ -412,7 +459,8 @@ network_pass_proxy (int sock, char *address, int port) /* * network_connect_to: connect to a remote host - * return 1 if ok, 0 if failed + * return 1 if connection is ok + * 0 if error */ int @@ -423,7 +471,7 @@ network_connect_to (int sock, unsigned long address, int port) char *ip4; int ret; - if (CONFIG_BOOLEAN(config_proxy_type)) + if (CONFIG_BOOLEAN(config_proxy_use)) { memset (&addr, 0, sizeof (addr)); addr.sin_addr.s_addr = htonl (address); @@ -435,11 +483,11 @@ network_connect_to (int sock, unsigned long address, int port) hostent = gethostbyname (CONFIG_STRING(config_proxy_address)); if (!hostent) return 0; - memcpy(&(addr.sin_addr),*(hostent->h_addr_list), sizeof(struct in_addr)); + memcpy(&(addr.sin_addr), *(hostent->h_addr_list), sizeof(struct in_addr)); ret = connect (sock, (struct sockaddr *) &addr, sizeof (addr)); if ((ret == -1) && (errno != EINPROGRESS)) return 0; - if (network_pass_proxy (sock, ip4, port) == -1) + if (!network_pass_proxy (sock, ip4, port)) return 0; } else @@ -454,3 +502,302 @@ network_connect_to (int sock, unsigned long address, int port) } return 1; } + +/* + * network_connect_child: child process trying to connect to peer + */ + +void +network_connect_child (struct t_hook *hook_connect) +{ + struct addrinfo hints, *res, *res_local; + char status_str[2]; + int rc; + + res = NULL; + res_local = NULL; + status_str[1] = '\0'; + + if (CONFIG_BOOLEAN(config_proxy_use)) + { + /* get info about peer */ + memset (&hints, 0, sizeof (hints)); + hints.ai_family = (CONFIG_BOOLEAN(config_proxy_ipv6)) ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo (CONFIG_STRING(config_proxy_address), NULL, &hints, &res) !=0) + { + /* address not found */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + return; + } + if (!res) + { + /* adddress not found */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + return; + } + if ((CONFIG_BOOLEAN(config_proxy_ipv6) && (res->ai_family != AF_INET6)) + || ((!CONFIG_BOOLEAN(config_proxy_ipv6) && (res->ai_family != AF_INET)))) + { + /* IP address not found */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_IP_ADDRESS_NOT_FOUND; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + freeaddrinfo (res); + return; + } + + if (CONFIG_BOOLEAN(config_proxy_ipv6)) + ((struct sockaddr_in6 *)(res->ai_addr))->sin6_port = htons (CONFIG_INTEGER(config_proxy_port)); + else + ((struct sockaddr_in *)(res->ai_addr))->sin_port = htons (CONFIG_INTEGER(config_proxy_port)); + + /* connect to peer */ + if (connect (HOOK_CONNECT(hook_connect, sock), + res->ai_addr, res->ai_addrlen) != 0) + { + /* connection refused */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_CONNECTION_REFUSED; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + freeaddrinfo (res); + return; + } + + if (!network_pass_proxy (HOOK_CONNECT(hook_connect, sock), + HOOK_CONNECT(hook_connect, address), + HOOK_CONNECT(hook_connect, port))) + { + /* proxy fails to connect to peer */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_PROXY_ERROR; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + freeaddrinfo (res); + return; + } + } + else + { + /* set local hostname/IP if asked by user */ + if (HOOK_CONNECT(hook_connect, local_hostname) + && HOOK_CONNECT(hook_connect, local_hostname[0])) + { + memset (&hints, 0, sizeof(hints)); + hints.ai_family = (HOOK_CONNECT(hook_connect, ipv6)) ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_STREAM; + rc = getaddrinfo (HOOK_CONNECT(hook_connect, local_hostname), + NULL, &hints, &res_local); + if ((rc != 0) || !res_local + || (HOOK_CONNECT(hook_connect, ipv6) + && (res_local->ai_family != AF_INET6)) + || ((!HOOK_CONNECT(hook_connect, ipv6) + && (res_local->ai_family != AF_INET)))) + { + /* fails to set local hostname/IP */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_LOCAL_HOSTNAME_ERROR; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + if (res_local) + freeaddrinfo (res_local); + return; + } + if (bind (HOOK_CONNECT(hook_connect, sock), + res_local->ai_addr, res_local->ai_addrlen) < 0) + { + /* fails to set local hostname/IP */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_LOCAL_HOSTNAME_ERROR; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + if (res_local) + freeaddrinfo (res_local); + return; + } + } + + /* get info about peer */ + memset (&hints, 0, sizeof(hints)); + hints.ai_family = (HOOK_CONNECT(hook_connect, ipv6)) ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_STREAM; + rc = getaddrinfo (HOOK_CONNECT(hook_connect, address), + NULL, &hints, &res); + if ((rc != 0) || !res) + { + /* address not found */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + if (res) + freeaddrinfo (res); + return; + } + if ((HOOK_CONNECT(hook_connect, ipv6) && (res->ai_family != AF_INET6)) + || ((!HOOK_CONNECT(hook_connect, ipv6) && (res->ai_family != AF_INET)))) + { + /* IP address not found */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_IP_ADDRESS_NOT_FOUND; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + if (res) + freeaddrinfo (res); + if (res_local) + freeaddrinfo (res_local); + return; + } + + /* connect to peer */ + if (HOOK_CONNECT(hook_connect, ipv6)) + ((struct sockaddr_in6 *)(res->ai_addr))->sin6_port = + htons (HOOK_CONNECT(hook_connect, port)); + else + ((struct sockaddr_in *)(res->ai_addr))->sin_port = + htons (HOOK_CONNECT(hook_connect, port)); + + if (connect (HOOK_CONNECT(hook_connect, sock), + res->ai_addr, res->ai_addrlen) != 0) + { + /* connection refused */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_CONNECTION_REFUSED; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + if (res) + freeaddrinfo (res); + if (res_local) + freeaddrinfo (res_local); + return; + } + } + + /* connection ok */ + status_str[0] = '0' + WEECHAT_HOOK_CONNECT_OK; + write (HOOK_CONNECT(hook_connect, child_write), status_str, 1); + if (res) + freeaddrinfo (res); + if (res_local) + freeaddrinfo (res_local); +} + +/* + * network_connect_child_read_cb: read connection progress from child process + */ + +int +network_connect_child_read_cb (void *arg_hook_connect) +{ + struct t_hook *hook_connect; + char buffer[1]; + int num_read, rc; + + hook_connect = (struct t_hook *)arg_hook_connect; + + num_read = read (HOOK_CONNECT(hook_connect, child_read), + buffer, sizeof (buffer)); + if (num_read > 0) + { + if (buffer[0] - '0' == WEECHAT_HOOK_CONNECT_OK) + { +#ifdef HAVE_GNUTLS + if (HOOK_CONNECT(hook_connect, gnutls_sess)) + { + gnutls_transport_set_ptr (*HOOK_CONNECT(hook_connect, gnutls_sess), + (gnutls_transport_ptr) ((unsigned long) HOOK_CONNECT(hook_connect, sock))); + while (1) + { + rc = gnutls_handshake (*HOOK_CONNECT(hook_connect, gnutls_sess)); + if ((rc == GNUTLS_E_SUCCESS) + || ((rc != GNUTLS_E_AGAIN) && (rc != GNUTLS_E_INTERRUPTED))) + break; + usleep (1000); + } + if (rc != GNUTLS_E_SUCCESS) + { + (void) (HOOK_CONNECT(hook_connect, callback)) + (hook_connect->callback_data, WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR); + unhook (hook_connect); + return WEECHAT_RC_OK; + } + } +#endif + } + (void) (HOOK_CONNECT(hook_connect, callback)) + (hook_connect->callback_data, buffer[0] - '0'); + unhook (hook_connect); + } + + return WEECHAT_RC_OK; +} + +/* + * network_connect_with_fork: connect with fork (called by hook_connect() only!) + */ + +void +network_connect_with_fork (struct t_hook *hook_connect) +{ + int child_pipe[2]; +#ifndef __CYGWIN__ + pid_t pid; +#endif + +#ifdef HAVE_GNUTLS + /* initialize GnuTLS if SSL asked */ + if (HOOK_CONNECT(hook_connect, gnutls_sess)) + { + if (gnutls_init (HOOK_CONNECT(hook_connect, gnutls_sess), GNUTLS_CLIENT) != 0) + { + (void) (HOOK_CONNECT(hook_connect, callback)) + (hook_connect->callback_data, + '0' + WEECHAT_HOOK_CONNECT_GNUTLS_INIT_ERROR); + unhook (hook_connect); + return; + } + gnutls_set_default_priority (*HOOK_CONNECT(hook_connect, gnutls_sess)); + gnutls_certificate_type_set_priority (*HOOK_CONNECT(hook_connect, gnutls_sess), + gnutls_cert_type_prio); + gnutls_protocol_set_priority (*HOOK_CONNECT(hook_connect, gnutls_sess), + gnutls_prot_prio); + gnutls_credentials_set (*HOOK_CONNECT(hook_connect, gnutls_sess), + GNUTLS_CRD_CERTIFICATE, + gnutls_xcred); + gnutls_transport_set_ptr (*HOOK_CONNECT(hook_connect, gnutls_sess), + (gnutls_transport_ptr) ((unsigned long) HOOK_CONNECT(hook_connect, sock))); + } +#endif + + /* create pipe for child process */ + if (pipe (child_pipe) < 0) + { + (void) (HOOK_CONNECT(hook_connect, callback)) + (hook_connect->callback_data, + '0' + WEECHAT_HOOK_CONNECT_MEMORY_ERROR); + unhook (hook_connect); + return; + } + HOOK_CONNECT(hook_connect, child_read) = child_pipe[0]; + HOOK_CONNECT(hook_connect, child_write) = child_pipe[1]; + +#ifdef __CYGWIN__ + /* connection may block under Cygwin, there's no other known way + to do better today, since connect() in child process seems not to work + any suggestion is welcome to improve that! + */ + network_connect_child (hook_connect); + network_connect_child_read_cb (hook_connect); +#else + switch (pid = fork ()) + { + /* fork failed */ + case -1: + (void) (HOOK_CONNECT(hook_connect, callback)) + (hook_connect->callback_data, + '0' + WEECHAT_HOOK_CONNECT_MEMORY_ERROR); + unhook (hook_connect); + return; + /* child process */ + case 0: + setuid (getuid ()); + network_connect_child (hook_connect); + _exit (EXIT_SUCCESS); + } + /* parent process */ + HOOK_CONNECT(hook_connect, child_pid) = pid; + HOOK_CONNECT(hook_connect, hook_fd) = hook_fd (NULL, + HOOK_CONNECT(hook_connect, child_read), + 1, 0, 0, + network_connect_child_read_cb, + hook_connect); +#endif +} diff --git a/src/core/wee-network.h b/src/core/wee-network.h index cf4c61f73..1b6e4cf5c 100644 --- a/src/core/wee-network.h +++ b/src/core/wee-network.h @@ -20,7 +20,12 @@ #ifndef __WEECHAT_NETWORK_H #define __WEECHAT_NETWORK_H 1 +struct t_hook; + +extern void network_init (); +extern void network_end (); extern int network_pass_proxy (int sock, char *address, int port); extern int network_connect_to (int sock, unsigned long address, int port); +extern void network_connect_with_fork (struct t_hook *); #endif /* wee-network.h */ diff --git a/src/core/weechat.c b/src/core/weechat.c index 44f622cad..8a4fa6388 100644 --- a/src/core/weechat.c +++ b/src/core/weechat.c @@ -56,6 +56,7 @@ #include "wee-debug.h" #include "wee-hook.h" #include "wee-log.h" +#include "wee-network.h" #include "wee-string.h" #include "wee-utf8.h" #include "wee-util.h" @@ -347,6 +348,8 @@ weechat_shutdown (int return_code, int crash) log_close (); if (weechat_local_charset) free (weechat_local_charset); + + network_end (); if (crash) abort(); @@ -374,6 +377,7 @@ main (int argc, char *argv[]) weechat_local_charset = strdup (""); #endif utf8_init (); + network_init (); util_catch_signal (SIGINT, SIG_IGN); /* ignore SIGINT signal */ util_catch_signal (SIGQUIT, SIG_IGN); /* ignore SIGQUIT signal */ diff --git a/src/gui/curses/Makefile.am b/src/gui/curses/Makefile.am index afaa1227c..7f2b59d5f 100644 --- a/src/gui/curses/Makefile.am +++ b/src/gui/curses/Makefile.am @@ -25,7 +25,8 @@ weechat_curses_LDADD = ./../../core/lib_weechat_core.a \ ../lib_weechat_gui_common.a \ ../../core/lib_weechat_core.a \ $(PLUGINS_LFLAGS) \ - $(NCURSES_LFLAGS) + $(NCURSES_LFLAGS) \ + $(GNUTLS_LFLAGS) weechat_curses_SOURCES = gui-curses-bar.c \ gui-curses-chat.c \ diff --git a/src/gui/gtk/Makefile.am b/src/gui/gtk/Makefile.am index 03192e8cc..a3b89bc17 100644 --- a/src/gui/gtk/Makefile.am +++ b/src/gui/gtk/Makefile.am @@ -25,7 +25,8 @@ weechat_gtk_LDADD = ./../../core/lib_weechat_core.a \ ../lib_weechat_gui_common.a \ ../../core/lib_weechat_core.a \ $(PLUGINS_LFLAGS) \ - $(GTK_LFLAGS) + $(GTK_LFLAGS) \ + $(GNUTLS_LFLAGS) weechat_gtk_SOURCES = gui-gtk-bar.c \ gui-gtk-chat.c \ diff --git a/src/plugins/irc/irc-command.c b/src/plugins/irc/irc-command.c index dbb6d1644..4498fceac 100644 --- a/src/plugins/irc/irc-command.c +++ b/src/plugins/irc/irc-command.c @@ -496,7 +496,7 @@ irc_command_connect_one_server (struct t_irc_server *server, int no_join) server->name); return 0; } - if (server->child_pid > 0) + if (server->hook_connect) { weechat_printf (NULL, _("%s%s: currently connecting to server " @@ -576,7 +576,7 @@ irc_command_connect (void *data, struct t_gui_buffer *buffer, int argc, ptr_server = ptr_server->next_server) { nb_connect++; - if (!ptr_server->is_connected && (ptr_server->child_pid == 0)) + if (!ptr_server->is_connected && (!ptr_server->hook_connect)) { if (!irc_command_connect_one_server (ptr_server, no_join)) connect_ok = 0; @@ -615,7 +615,7 @@ irc_command_connect (void *data, struct t_gui_buffer *buffer, int argc, server_tmp.nicks, server_tmp.username, server_tmp.realname, - server_tmp.hostname, + server_tmp.local_hostname, server_tmp.command, 1, /* command_delay */ server_tmp.autojoin, @@ -1138,7 +1138,7 @@ irc_command_disconnect_one_server (struct t_irc_server *server) if (!server) return 0; - if ((!server->is_connected) && (server->child_pid == 0) + if ((!server->is_connected) && (!server->hook_connect) && (server->reconnect_start == 0)) { weechat_printf (server->buffer, @@ -1186,7 +1186,7 @@ irc_command_disconnect (void *data, struct t_gui_buffer *buffer, int argc, for (ptr_server = irc_servers; ptr_server; ptr_server = ptr_server->next_server) { - if ((ptr_server->is_connected) || (ptr_server->child_pid != 0) + if ((ptr_server->is_connected) || (ptr_server->hook_connect) || (ptr_server->reconnect_start != 0)) { if (!irc_command_disconnect_one_server (ptr_server)) @@ -2373,7 +2373,7 @@ irc_command_reconnect_one_server (struct t_irc_server *server, int no_join) if (!server) return 0; - if ((!server->is_connected) && (server->child_pid == 0)) + if ((!server->is_connected) && (!server->hook_connect)) { weechat_printf (server->buffer, _("%s%s: not connected to server \"%s\"!"), @@ -2427,7 +2427,7 @@ irc_command_reconnect (void *data, struct t_gui_buffer *buffer, int argc, ptr_server = ptr_server->next_server) { nb_reconnect++; - if ((ptr_server->is_connected) || (ptr_server->child_pid != 0)) + if ((ptr_server->is_connected) || (ptr_server->hook_connect)) { if (!irc_command_reconnect_one_server (ptr_server, no_join)) reconnect_ok = 0; @@ -2771,7 +2771,7 @@ irc_command_server (void *data, struct t_gui_buffer *buffer, int argc, server_tmp.nicks, server_tmp.username, server_tmp.realname, - server_tmp.hostname, + server_tmp.local_hostname, server_tmp.command, 1, /* command_delay */ server_tmp.autojoin, diff --git a/src/plugins/irc/irc-config.c b/src/plugins/irc/irc-config.c index d55719c8e..603f0a0c9 100644 --- a/src/plugins/irc/irc-config.c +++ b/src/plugins/irc/irc-config.c @@ -35,8 +35,8 @@ char *irc_config_server_option_string[IRC_CONFIG_NUM_SERVER_OPTIONS] = { "autoconnect", "autoreconnect", "autoreconnect_delay", "addresses", "ipv6", - "ssl", "password", "nicks", "username", "realname", "hostname", "command", - "command_delay", "autojoin", "autorejoin", "notify_levels" + "ssl", "password", "nicks", "username", "realname", "local_hostname", + "command", "command_delay", "autojoin", "autorejoin", "notify_levels" }; char *irc_config_server_option_default[IRC_CONFIG_NUM_SERVER_OPTIONS] = { "off", "on", "30", "", "off", "off", "", "", "", "", "", "", "0", "", @@ -614,12 +614,12 @@ irc_config_server_new_option (struct t_config_file *config_file, callback_change, callback_change_data, callback_delete, callback_delete_data); break; - case IRC_CONFIG_SERVER_HOSTNAME: + case IRC_CONFIG_SERVER_LOCAL_HOSTNAME: new_option = weechat_config_new_option ( config_file, section, option_name, "string", - N_("custom hostname/IP for server (optional, if empty local hostname " - "is used)"), + N_("custom local hostname/IP for server (optional, if empty " + "local hostname is used)"), NULL, 0, 0, value, NULL, NULL, callback_change, callback_change_data, callback_delete, callback_delete_data); diff --git a/src/plugins/irc/irc-config.h b/src/plugins/irc/irc-config.h index dc161d94a..2610749af 100644 --- a/src/plugins/irc/irc-config.h +++ b/src/plugins/irc/irc-config.h @@ -38,7 +38,7 @@ enum t_irc_config_server_option IRC_CONFIG_SERVER_NICKS, IRC_CONFIG_SERVER_USERNAME, IRC_CONFIG_SERVER_REALNAME, - IRC_CONFIG_SERVER_HOSTNAME, + IRC_CONFIG_SERVER_LOCAL_HOSTNAME, IRC_CONFIG_SERVER_COMMAND, IRC_CONFIG_SERVER_COMMAND_DELAY, IRC_CONFIG_SERVER_AUTOJOIN, diff --git a/src/plugins/irc/irc-display.c b/src/plugins/irc/irc-display.c index 855652fd6..fa4034360 100644 --- a/src/plugins/irc/irc-display.c +++ b/src/plugins/irc/irc-display.c @@ -193,9 +193,9 @@ irc_display_server (struct t_irc_server *server, int with_detail) weechat_printf (NULL, " realname . . . . . : %s", (server->realname && server->realname[0]) ? server->realname : ""); - weechat_printf (NULL, " hostname . . . . . : %s", - (server->hostname && server->hostname[0]) ? - server->hostname : ""); + weechat_printf (NULL, " local_hostname . . : %s", + (server->local_hostname && server->local_hostname[0]) ? + server->local_hostname : ""); if (server->command && server->command[0]) string = strdup (server->command); else diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c index 63270e728..651b05fc8 100644 --- a/src/plugins/irc/irc-server.c +++ b/src/plugins/irc/irc-server.c @@ -22,18 +22,11 @@ #include <stdlib.h> #include <unistd.h> #include <errno.h> -#include <signal.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <sys/socket.h> #include <sys/time.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <time.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <netdb.h> #include "../weechat-plugin.h" #include "irc.h" @@ -53,17 +46,6 @@ struct t_irc_server *last_irc_server = NULL; struct t_irc_message *irc_recv_msgq = NULL; struct t_irc_message *irc_msgq_last_msg = NULL; -#ifdef HAVE_GNUTLS -const int gnutls_cert_type_prio[] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 }; -#if LIBGNUTLS_VERSION_NUMBER >= 0x010700 - const int gnutls_prot_prio[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, - GNUTLS_TLS1_0, GNUTLS_SSL3, 0 }; -#else - const int gnutls_prot_prio[] = { GNUTLS_TLS1_1, GNUTLS_TLS1_0, - GNUTLS_SSL3, 0 }; -#endif -#endif - /* * irc_server_set_addresses: set addresses for server @@ -205,10 +187,10 @@ irc_server_set_with_option (struct t_irc_server *server, free (server->realname); server->realname = strdup (weechat_config_string (option)); break; - case IRC_CONFIG_SERVER_HOSTNAME: - if (server->hostname) - free (server->hostname); - server->hostname = strdup (weechat_config_string (option)); + case IRC_CONFIG_SERVER_LOCAL_HOSTNAME: + if (server->local_hostname) + free (server->local_hostname); + server->local_hostname = strdup (weechat_config_string (option)); break; case IRC_CONFIG_SERVER_COMMAND: if (server->command) @@ -280,7 +262,7 @@ irc_server_init (struct t_irc_server *server) server->nicks = NULL; server->username = NULL; server->realname = NULL; - server->hostname = NULL; + server->local_hostname = NULL; server->command = NULL; server->command_delay = IRC_CONFIG_SERVER_DEFAULT_COMMAND_DELAY; server->autojoin = NULL; @@ -293,10 +275,8 @@ irc_server_init (struct t_irc_server *server) server->addresses_array = NULL; server->ports_array = NULL; server->current_address = 0; - server->child_pid = 0; - server->child_read = -1; - server->child_write = -1; server->sock = -1; + server->hook_connect = NULL; server->hook_fd = NULL; server->is_connected = 0; server->ssl_connected = 0; @@ -598,8 +578,8 @@ irc_server_free_data (struct t_irc_server *server) free (server->username); if (server->realname) free (server->realname); - if (server->hostname) - free (server->hostname); + if (server->local_hostname) + free (server->local_hostname); if (server->command) free (server->command); if (server->autojoin) @@ -677,7 +657,7 @@ struct t_irc_server * irc_server_new (char *name, int autoconnect, int autoreconnect, int autoreconnect_delay, int temp_server, char *addresses, int ipv6, int ssl, char *password, char *nicks, - char *username, char *realname, char *hostname, + char *username, char *realname, char *local_hostname, char *command, int command_delay, char *autojoin, int autorejoin, char *notify_levels) { @@ -690,12 +670,12 @@ irc_server_new (char *name, int autoconnect, int autoreconnect, { weechat_log_printf ("Creating new server (name:%s, addresses:%s, " "pwd:%s, nicks:%s, username:%s, realname:%s, " - "hostname: %s, command:%s, autojoin:%s, " + "local_hostname: %s, command:%s, autojoin:%s, " "autorejoin:%s, notify_levels:%s)", name, addresses, (password) ? password : "", (nicks) ? nicks : "", (username) ? username : "", (realname) ? realname : "", - (hostname) ? hostname : "", + (local_hostname) ? local_hostname : "", (command) ? command : "", (autojoin) ? autojoin : "", (autorejoin) ? "on" : "off", @@ -719,8 +699,8 @@ irc_server_new (char *name, int autoconnect, int autoreconnect, (username) ? strdup (username) : strdup ("weechat"); new_server->realname = (realname) ? strdup (realname) : strdup ("realname"); - new_server->hostname = - (hostname) ? strdup (hostname) : NULL; + new_server->local_hostname = + (local_hostname) ? strdup (local_hostname) : NULL; new_server->command = (command) ? strdup (command) : NULL; new_server->command_delay = command_delay; @@ -763,7 +743,7 @@ irc_server_duplicate (struct t_irc_server *server, char *new_name) server->nicks, server->username, server->realname, - server->hostname, + server->local_hostname, server->command, server->command_delay, server->autojoin, @@ -1599,47 +1579,22 @@ irc_server_timer_check_away (void *empty) } /* - * irc_server_child_kill: kill child process and close pipe - */ - -void -irc_server_child_kill (struct t_irc_server *server) -{ - /* kill process */ - if (server->child_pid > 0) - { - kill (server->child_pid, SIGKILL); - waitpid (server->child_pid, NULL, 0); - server->child_pid = 0; - } - - /* close pipe used with child */ - if (server->child_read != -1) - { - close (server->child_read); - server->child_read = -1; - } - if (server->child_write != -1) - { - close (server->child_write); - server->child_write = -1; - } -} - -/* * irc_server_close_connection: close server connection - * (kill child, close socket/pipes) */ void irc_server_close_connection (struct t_irc_server *server) { + if (server->hook_connect) + { + weechat_unhook (server->hook_connect); + server->hook_connect = NULL; + } if (server->hook_fd) { weechat_unhook (server->hook_fd); server->hook_fd = NULL; } - irc_server_child_kill (server); /* close network socket */ if (server->sock != -1) @@ -1743,257 +1698,102 @@ irc_server_switch_address (struct t_irc_server *server) } /* - * irc_server_child_read_cb: read connection progress from child process + * irc_server_connect_cb: read connection status */ int -irc_server_child_read_cb (void *arg_server) +irc_server_connect_cb (void *arg_server, int status) { struct t_irc_server *server; - char buffer[1]; - int num_read; int config_proxy_use; server = (struct t_irc_server *)arg_server; - num_read = read (server->child_read, buffer, sizeof (buffer)); - if (num_read > 0) - { - config_proxy_use = weechat_config_boolean ( - weechat_config_get ("weechat.proxy.use")); - switch (buffer[0]) - { - /* connection OK */ - case '0': - /* enable SSL if asked */ -#ifdef HAVE_GNUTLS - if (server->ssl_connected) - { - gnutls_transport_set_ptr (server->gnutls_sess, - (gnutls_transport_ptr) ((unsigned long) server->sock)); - if (gnutls_handshake (server->gnutls_sess) < 0) - { - weechat_printf (server->buffer, - _("%s%s: GnuTLS handshake failed"), - weechat_prefix ("error"), "irc"); - irc_server_close_connection (server); - irc_server_switch_address (server); - return WEECHAT_RC_OK; - } - } -#endif - /* kill child and login to server */ - weechat_unhook (server->hook_fd); - irc_server_child_kill (server); - irc_server_login (server); - server->hook_fd = weechat_hook_fd (server->sock, - 1, 0, 0, - irc_server_recv_cb, - server); - break; - /* adress not found */ - case '1': - weechat_printf (server->buffer, - (config_proxy_use) ? - _("%s%s: proxy address \"%s\" not found") : - _("%s%s: address \"%s\" not found"), - weechat_prefix ("error"), "irc", - server->addresses_array[server->current_address]); - irc_server_close_connection (server); - irc_server_switch_address (server); - break; - /* IP address not found */ - case '2': - weechat_printf (server->buffer, - (config_proxy_use) ? - _("%s%s: proxy IP address not found") : - _("%s%s: IP address not found"), - weechat_prefix ("error"), "irc"); - irc_server_close_connection (server); - irc_server_switch_address (server); - break; - /* connection refused */ - case '3': - weechat_printf (server->buffer, - (config_proxy_use) ? - _("%s%s: proxy connection refused") : - _("%s%s: connection refused"), - weechat_prefix ("error"), "irc"); - irc_server_close_connection (server); - irc_server_switch_address (server); - break; - /* proxy fails to connect to server */ - case '4': - weechat_printf (server->buffer, - _("%s%s: proxy fails to establish " - "connection to server " - "(check username/password if used " - "and if IRC server address/port is " - "allowed by proxy)"), - weechat_prefix ("error"), "irc"); - irc_server_close_connection (server); - irc_server_switch_address (server); - break; - /* fails to set local hostname/IP */ - case '5': - weechat_printf (server->buffer, - _("%s%s: unable to set local hostname/IP"), - weechat_prefix ("error"), "irc"); - irc_server_close_connection (server); - irc_server_reconnect_schedule (server); - break; - } - } - - return WEECHAT_RC_OK; -} - -/* - * irc_server_child: child process trying to connect to server - */ - -int -irc_server_child (struct t_irc_server *server) -{ - struct addrinfo hints, *res, *res_local; - int rc; - int config_proxy_use, config_proxy_ipv6, config_proxy_port; - char *config_proxy_address; + server->hook_connect = NULL; - res = NULL; - res_local = NULL; - config_proxy_use = weechat_config_boolean ( weechat_config_get ("weechat.proxy.use")); - config_proxy_ipv6 = weechat_config_integer ( - weechat_config_get ("weechat.proxy.ipv6")); - config_proxy_port = weechat_config_integer ( - weechat_config_get ("weechat.proxy.port")); - config_proxy_address = weechat_config_string ( - weechat_config_get ("weechat.proxy.address")); - if (config_proxy_use) - { - /* get info about server */ - memset (&hints, 0, sizeof (hints)); - hints.ai_family = (config_proxy_ipv6) ? AF_INET6 : AF_INET; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo (config_proxy_address, NULL, &hints, &res) !=0) - { - write (server->child_write, "1", 1); - return 0; - } - if (!res) - { - write (server->child_write, "1", 1); - return 0; - } - if ((config_proxy_ipv6 && (res->ai_family != AF_INET6)) - || ((!config_proxy_ipv6 && (res->ai_family != AF_INET)))) - { - write (server->child_write, "2", 1); - freeaddrinfo (res); - return 0; - } - - if (config_proxy_ipv6) - ((struct sockaddr_in6 *)(res->ai_addr))->sin6_port = htons (config_proxy_port); - else - ((struct sockaddr_in *)(res->ai_addr))->sin_port = htons (config_proxy_port); - - /* connect to server */ - if (connect (server->sock, res->ai_addr, res->ai_addrlen) != 0) - { - write (server->child_write, "3", 1); - freeaddrinfo (res); - return 0; - } - - if (weechat_network_pass_proxy (server->sock, - server->addresses_array[server->current_address], - server->ports_array[server->current_address])) - { - write (server->child_write, "4", 1); - freeaddrinfo (res); - return 0; - } - } - else + switch (status) { - /* set local hostname/IP if asked by user */ - if (server->hostname && server->hostname[0]) - { - memset (&hints, 0, sizeof(hints)); - hints.ai_family = (server->ipv6) ? AF_INET6 : AF_INET; - hints.ai_socktype = SOCK_STREAM; - rc = getaddrinfo (server->hostname, NULL, &hints, &res_local); - if ((rc != 0) || !res_local - || (server->ipv6 && (res_local->ai_family != AF_INET6)) - || ((!server->ipv6 && (res_local->ai_family != AF_INET)))) - { - write (server->child_write, "5", 1); - if (res_local) - freeaddrinfo (res_local); - return 0; - } - if (bind (server->sock, res_local->ai_addr, res_local->ai_addrlen) < 0) - { - write (server->child_write, "5", 1); - if (res_local) - freeaddrinfo (res_local); - return 0; - } - } - - /* get info about server */ - memset (&hints, 0, sizeof(hints)); - hints.ai_family = (server->ipv6) ? AF_INET6 : AF_INET; - hints.ai_socktype = SOCK_STREAM; - rc = getaddrinfo (server->addresses_array[server->current_address], - NULL, &hints, &res); - if ((rc != 0) || !res) - { - write (server->child_write, "1", 1); - if (res) - freeaddrinfo (res); - return 0; - } - if ((server->ipv6 && (res->ai_family != AF_INET6)) - || ((!server->ipv6 && (res->ai_family != AF_INET)))) - { - write (server->child_write, "2", 1); - if (res) - freeaddrinfo (res); - if (res_local) - freeaddrinfo (res_local); - return 0; - } - - /* connect to server */ - if (server->ipv6) - ((struct sockaddr_in6 *)(res->ai_addr))->sin6_port = - htons (server->ports_array[server->current_address]); - else - ((struct sockaddr_in *)(res->ai_addr))->sin_port = - htons (server->ports_array[server->current_address]); - - if (connect (server->sock, res->ai_addr, res->ai_addrlen) != 0) - { - write (server->child_write, "3", 1); - if (res) - freeaddrinfo (res); - if (res_local) - freeaddrinfo (res_local); - return 0; - } + case WEECHAT_HOOK_CONNECT_OK: + /* login to server */ + irc_server_login (server); + server->hook_fd = weechat_hook_fd (server->sock, + 1, 0, 0, + irc_server_recv_cb, + server); + break; + case WEECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND: + weechat_printf (server->buffer, + (config_proxy_use) ? + _("%s%s: proxy address \"%s\" not found") : + _("%s%s: address \"%s\" not found"), + weechat_prefix ("error"), "irc", + server->addresses_array[server->current_address]); + irc_server_close_connection (server); + irc_server_switch_address (server); + break; + case WEECHAT_HOOK_CONNECT_IP_ADDRESS_NOT_FOUND: + weechat_printf (server->buffer, + (config_proxy_use) ? + _("%s%s: proxy IP address not found") : + _("%s%s: IP address not found"), + weechat_prefix ("error"), "irc"); + irc_server_close_connection (server); + irc_server_switch_address (server); + break; + case WEECHAT_HOOK_CONNECT_CONNECTION_REFUSED: + weechat_printf (server->buffer, + (config_proxy_use) ? + _("%s%s: proxy connection refused") : + _("%s%s: connection refused"), + weechat_prefix ("error"), "irc"); + irc_server_close_connection (server); + irc_server_switch_address (server); + break; + case WEECHAT_HOOK_CONNECT_PROXY_ERROR: + weechat_printf (server->buffer, + _("%s%s: proxy fails to establish " + "connection to server " + "(check username/password if used " + "and if IRC server address/port is " + "allowed by proxy)"), + weechat_prefix ("error"), "irc"); + irc_server_close_connection (server); + irc_server_switch_address (server); + break; + case WEECHAT_HOOK_CONNECT_LOCAL_HOSTNAME_ERROR: + weechat_printf (server->buffer, + _("%s%s: unable to set local hostname/IP"), + weechat_prefix ("error"), "irc"); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + break; + case WEECHAT_HOOK_CONNECT_GNUTLS_INIT_ERROR: + weechat_printf (server->buffer, + _("%s%s: GnuTLS init error"), + weechat_prefix ("error"), "irc"); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + break; + case WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR: + weechat_printf (server->buffer, + _("%s%s: GnuTLS handshake failed"), + weechat_prefix ("error"), "irc"); + irc_server_close_connection (server); + irc_server_switch_address (server); + break; + case WEECHAT_HOOK_CONNECT_MEMORY_ERROR: + weechat_printf (server->buffer, + _("%s%s: not enough memory"), + weechat_prefix ("error"), "irc"); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + break; } - write (server->child_write, "0", 1); - if (res) - freeaddrinfo (res); - if (res_local) - freeaddrinfo (res_local); - return 0; + return WEECHAT_RC_OK; } /* @@ -2005,10 +1805,7 @@ irc_server_child (struct t_irc_server *server) int irc_server_connect (struct t_irc_server *server, int disable_autojoin) { - int child_pipe[2], set; -#ifndef __CYGWIN__ - pid_t pid; -#endif + int set; char *config_proxy_type, *config_proxy_address; int config_proxy_use, config_proxy_ipv6, config_proxy_port; @@ -2105,42 +1902,9 @@ irc_server_connect (struct t_irc_server *server, int disable_autojoin) (server->ssl) ? " (SSL)" : ""); } - /* close any opened connection and kill child process if running */ + /* close connection if open */ irc_server_close_connection (server); - /* init SSL if asked */ - server->ssl_connected = 0; -#ifdef HAVE_GNUTLS - if (server->ssl) - { - if (gnutls_init (&server->gnutls_sess, GNUTLS_CLIENT) != 0) - { - weechat_printf (server->buffer, - _("%s%s: GnuTLS init error"), - weechat_prefix ("error"), "irc"); - return 0; - } - gnutls_set_default_priority (server->gnutls_sess); - gnutls_certificate_type_set_priority (server->gnutls_sess, - gnutls_cert_type_prio); - gnutls_protocol_set_priority (server->gnutls_sess, gnutls_prot_prio); - gnutls_credentials_set (server->gnutls_sess, GNUTLS_CRD_CERTIFICATE, - gnutls_xcred); - server->ssl_connected = 1; - } -#endif - - /* create pipe for child process */ - if (pipe (child_pipe) < 0) - { - weechat_printf (server->buffer, - _("%s%s: cannot create pipe"), - weechat_prefix ("error"), "irc"); - return 0; - } - server->child_read = child_pipe[0]; - server->child_write = child_pipe[1]; - /* create socket and set options */ if (config_proxy_use) server->sock = socket ((config_proxy_ipv6) ? AF_INET6 : AF_INET, SOCK_STREAM, 0); @@ -2175,38 +1939,25 @@ irc_server_connect (struct t_irc_server *server, int disable_autojoin) "\"SO_KEEPALIVE\""), weechat_prefix ("error"), "irc"); } - -#ifdef __CYGWIN__ - /* connection may block under Cygwin, there's no other known way - to do better today, since connect() in child process seems not to work - any suggestion is welcome to improve that! - */ - irc_server_child (server); - server->child_pid = 0; - irc_server_child_read (server); -#else - switch (pid = fork ()) - { - /* fork failed */ - case -1: - irc_server_close_connection (server); - return 0; - /* child process */ - case 0: - setuid (getuid ()); - irc_server_child (server); - _exit (EXIT_SUCCESS); - } - /* parent process */ - server->child_pid = pid; - server->hook_fd = weechat_hook_fd (server->child_read, - 1, 0, 0, - irc_server_child_read_cb, - server); + + /* init SSL if asked */ + server->ssl_connected = 0; +#ifdef HAVE_GNUTLS + if (server->ssl) + server->ssl_connected = 1; #endif server->disable_autojoin = disable_autojoin; + server->hook_connect = weechat_hook_connect (server->addresses_array[server->current_address], + server->ports_array[server->current_address], + server->sock, + server->ipv6, + (server->ssl_connected) ? &server->gnutls_sess : NULL, + server->local_hostname, + irc_server_connect_cb, + server); + return 1; } @@ -2809,10 +2560,8 @@ irc_server_print_log () weechat_log_printf (" addresses_count . . : %d", ptr_server->addresses_count); weechat_log_printf (" addresses_array . . : 0x%x", ptr_server->addresses_array); weechat_log_printf (" ports_array . . . . : 0x%x", ptr_server->ports_array); - weechat_log_printf (" child_pid . . . . . : %d", ptr_server->child_pid); - weechat_log_printf (" child_read . . . . : %d", ptr_server->child_read); - weechat_log_printf (" child_write . . . . : %d", ptr_server->child_write); weechat_log_printf (" sock. . . . . . . . : %d", ptr_server->sock); + weechat_log_printf (" hook_connect. . . . : 0x%x", ptr_server->hook_connect); weechat_log_printf (" hook_fd . . . . . . : 0x%x", ptr_server->hook_fd); weechat_log_printf (" is_connected. . . . : %d", ptr_server->is_connected); weechat_log_printf (" ssl_connected . . . : %d", ptr_server->ssl_connected); diff --git a/src/plugins/irc/irc-server.h b/src/plugins/irc/irc-server.h index b63c18526..136ddf022 100644 --- a/src/plugins/irc/irc-server.h +++ b/src/plugins/irc/irc-server.h @@ -69,7 +69,7 @@ struct t_irc_server char *nicks; /* nicknames as one string */ char *username; /* user name */ char *realname; /* real name */ - char *hostname; /* custom hostname */ + char *local_hostname; /* custom local hostname */ char *command; /* command to run once connected */ int command_delay; /* delay after execution of command */ char *autojoin; /* channels to automatically join */ @@ -82,15 +82,13 @@ struct t_irc_server char **addresses_array; /* exploded addresses */ int *ports_array; /* ports for addresses */ int current_address; /* current address index in array */ - pid_t child_pid; /* pid of child process (connecting) */ - int child_read; /* to read into child pipe */ - int child_write; /* to write into child pipe */ int sock; /* socket for server (IPv4 or IPv6) */ - struct t_hook *hook_fd; /* hook for server socket or child pipe */ + struct t_hook *hook_connect; /* connection hook */ + struct t_hook *hook_fd; /* hook for server socket */ int is_connected; /* 1 if WeeChat is connected to server */ int ssl_connected; /* = 1 if connected with SSL */ #ifdef HAVE_GNUTLS - gnutls_session gnutls_sess; /* gnutls session (only if SSL is used) */ + gnutls_session_t gnutls_sess; /* gnutls session (only if SSL is used) */ #endif char *unterminated_message; /* beginning of a message in input buf */ int nicks_count; /* number of nicknames */ diff --git a/src/plugins/irc/irc.c b/src/plugins/irc/irc.c index c07e5ce1d..ee9f24481 100644 --- a/src/plugins/irc/irc.c +++ b/src/plugins/irc/irc.c @@ -46,10 +46,6 @@ struct t_weechat_plugin *weechat_irc_plugin = NULL; struct t_hook *irc_hook_timer = NULL; struct t_hook *irc_hook_timer_check_away = NULL; -#ifdef HAVE_GNUTLS -gnutls_certificate_credentials gnutls_xcred; /* gnutls client credentials */ -#endif - /* * irc_signal_quit_cb: callback for "quit" signal @@ -89,13 +85,6 @@ weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) weechat_plugin = plugin; -#ifdef HAVE_GNUTLS - /* init GnuTLS */ - gnutls_global_init (); - gnutls_certificate_allocate_credentials (&gnutls_xcred); - gnutls_certificate_set_x509_trust_file (gnutls_xcred, "ca.pem", GNUTLS_X509_FMT_PEM); -#endif - if (!irc_config_init ()) return WEECHAT_RC_ERROR; @@ -168,11 +157,5 @@ weechat_plugin_end (struct t_weechat_plugin *plugin) //irc_dcc_end (); irc_server_free_all (); -#ifdef HAVE_GNUTLS - /* GnuTLS end */ - gnutls_certificate_free_credentials (gnutls_xcred); - gnutls_global_deinit(); -#endif - return WEECHAT_RC_OK; } diff --git a/src/plugins/irc/irc.h b/src/plugins/irc/irc.h index f00c7922f..38b54d446 100644 --- a/src/plugins/irc/irc.h +++ b/src/plugins/irc/irc.h @@ -68,8 +68,4 @@ extern struct t_hook *irc_hook_timer_check_away; extern int irc_debug; -#ifdef HAVE_GNUTLS -extern gnutls_certificate_credentials gnutls_xcred; -#endif - #endif /* irc.h */ diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c index 250c0481f..734385c47 100644 --- a/src/plugins/plugin.c +++ b/src/plugins/plugin.c @@ -364,6 +364,7 @@ plugin_load (char *filename) new_plugin->hook_command = &hook_command; new_plugin->hook_timer = &hook_timer; new_plugin->hook_fd = &hook_fd; + new_plugin->hook_connect = &hook_connect; new_plugin->hook_print = &hook_print; new_plugin->hook_signal = &hook_signal; new_plugin->hook_signal_send = &hook_signal_send; diff --git a/src/plugins/weechat-plugin.h b/src/plugins/weechat-plugin.h index 8d49971a7..68e89b3ce 100644 --- a/src/plugins/weechat-plugin.h +++ b/src/plugins/weechat-plugin.h @@ -58,6 +58,17 @@ struct t_weelist; #define WEECHAT_HOTLIST_PRIVATE "2" #define WEECHAT_HOTLIST_HIGHLIGHT "3" +/* connect status for connection hooked */ +#define WEECHAT_HOOK_CONNECT_OK 0 +#define WEECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND 1 +#define WEECHAT_HOOK_CONNECT_IP_ADDRESS_NOT_FOUND 2 +#define WEECHAT_HOOK_CONNECT_CONNECTION_REFUSED 3 +#define WEECHAT_HOOK_CONNECT_PROXY_ERROR 4 +#define WEECHAT_HOOK_CONNECT_LOCAL_HOSTNAME_ERROR 5 +#define WEECHAT_HOOK_CONNECT_GNUTLS_INIT_ERROR 6 +#define WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR 7 +#define WEECHAT_HOOK_CONNECT_MEMORY_ERROR 8 + /* type of data for signal hooked */ #define WEECHAT_HOOK_SIGNAL_STRING "string" #define WEECHAT_HOOK_SIGNAL_INT "int" @@ -280,6 +291,12 @@ struct t_weechat_plugin int flag_exception, int (*callback)(void *data), void *callback_data); + struct t_hook *(*hook_connect) (struct t_weechat_plugin *plugin, + char *address, int port, + int sock, int ipv6, void *gnutls_sess, + char *local_hostname, + int (*callback)(void *data, int status), + void *callback_data); struct t_hook *(*hook_print) (struct t_weechat_plugin *plugin, struct t_gui_buffer *buffer, char *tags, char *message, @@ -711,6 +728,12 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin); weechat_plugin->hook_fd(weechat_plugin, __fd, __flag_read, \ __flag_write, __flag_exception, __callback, \ __data) +#define weechat_hook_connect(__address, __port, __sock, __ipv6, \ + __gnutls_sess, __local_hostname, \ + __callback, __data) \ + weechat_plugin->hook_connect(weechat_plugin, __address, __port, \ + __sock, __ipv6, __gnutls_sess, \ + __local_hostname, __callback, __data) #define weechat_hook_print(__buffer, __tags, __msg, __strip__colors, \ __callback, __data) \ weechat_plugin->hook_print(weechat_plugin, __buffer, __tags, \ |