summaryrefslogtreecommitdiff
path: root/src/core/wee-network.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/wee-network.c')
-rw-r--r--src/core/wee-network.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/src/core/wee-network.c b/src/core/wee-network.c
new file mode 100644
index 000000000..1c9e5dc63
--- /dev/null
+++ b/src/core/wee-network.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org>
+ * See README for License detail, AUTHORS for developers list.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* wee-network.c: network functions for WeeChat */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include "weechat.h"
+#include "wee-network.h"
+#include "wee-config.h"
+#include "wee-string.h"
+
+
+/*
+ * network_convbase64_8x3_to_6x4 : convert 3 bytes of 8 bits in 4 bytes of 6 bits
+ */
+
+void
+network_convbase64_8x3_to_6x4 (char *from, char *to)
+{
+ unsigned char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ to[0] = base64_table [ (from[0] & 0xfc) >> 2 ];
+ to[1] = base64_table [ ((from[0] & 0x03) << 4) + ((from[1] & 0xf0) >> 4) ];
+ to[2] = base64_table [ ((from[1] & 0x0f) << 2) + ((from[2] & 0xc0) >> 6) ];
+ to[3] = base64_table [ from[2] & 0x3f ];
+}
+
+/*
+ * network_base64encode: encode a string in base64
+ */
+
+void
+network_base64encode (char *from, char *to)
+{
+ char *f, *t;
+ int from_len;
+
+ from_len = strlen (from);
+
+ f = from;
+ t = to;
+
+ while (from_len >= 3)
+ {
+ network_convbase64_8x3_to_6x4 (f, t);
+ f += 3 * sizeof (*f);
+ t += 4 * sizeof (*t);
+ from_len -= 3;
+ }
+
+ if (from_len > 0)
+ {
+ char rest[3] = { 0, 0, 0 };
+ switch (from_len)
+ {
+ case 1 :
+ rest[0] = f[0];
+ network_convbase64_8x3_to_6x4 (rest, t);
+ t[2] = t[3] = '=';
+ break;
+ case 2 :
+ rest[0] = f[0];
+ rest[1] = f[1];
+ network_convbase64_8x3_to_6x4 (rest, t);
+ t[3] = '=';
+ break;
+ }
+ t[4] = 0;
+ }
+}
+
+/*
+ * network_pass_httpproxy: establish connection/authentification to an
+ * http proxy
+ * return :
+ * - 0 if connexion throw proxy was successful
+ * - 1 if connexion fails
+ */
+
+int
+network_pass_httpproxy (int sock, char *address, int port)
+{
+
+ char buffer[256], authbuf[128], authbuf_base64[196];
+ int n, m;
+
+ if (CONFIG_STRING(config_proxy_username)
+ && CONFIG_STRING(config_proxy_username)[0])
+ {
+ /* authentification */
+ snprintf (authbuf, sizeof (authbuf), "%s:%s",
+ CONFIG_STRING(config_proxy_username),
+ (CONFIG_STRING(config_proxy_password)) ?
+ CONFIG_STRING(config_proxy_password) : "");
+ network_base64encode (authbuf, authbuf_base64);
+ n = snprintf (buffer, sizeof (buffer),
+ "CONNECT %s:%d HTTP/1.0\r\nProxy-Authorization: Basic %s\r\n\r\n",
+ address, port, authbuf_base64);
+ }
+ else
+ {
+ /* no authentification */
+ n = snprintf (buffer, sizeof (buffer),
+ "CONNECT %s:%d HTTP/1.0\r\n\r\n", address, port);
+ }
+
+ m = send (sock, buffer, n, 0);
+ if (n != m)
+ return 1;
+
+ n = recv (sock, buffer, sizeof (buffer), 0);
+
+ /* success result must be like: "HTTP/1.0 200 OK" */
+ if (n < 12)
+ return 1;
+
+ if (memcmp (buffer, "HTTP/", 5) || memcmp (buffer + 9, "200", 3))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * network_resolve: resolve hostname on its IP address
+ * (works with ipv4 and ipv6)
+ * return :
+ * - 0 if resolution was successful
+ * - 1 if resolution fails
+ */
+
+int
+network_resolve (char *hostname, char *ip, int *version)
+{
+ char ipbuffer[NI_MAXHOST];
+ struct addrinfo *res;
+
+ if (version != NULL)
+ *version = 0;
+
+ res = NULL;
+
+ if (getaddrinfo (hostname, NULL, NULL, &res) != 0)
+ return 1;
+
+ if (!res)
+ return 1;
+
+ if (getnameinfo (res->ai_addr, res->ai_addrlen, ipbuffer, sizeof(ipbuffer),
+ NULL, 0, NI_NUMERICHOST) != 0)
+ {
+ freeaddrinfo (res);
+ return 1;
+ }
+
+ if ((res->ai_family == AF_INET) && (version != NULL))
+ *version = 4;
+ if ((res->ai_family == AF_INET6) && (version != NULL))
+ *version = 6;
+
+ strcpy (ip, ipbuffer);
+
+ freeaddrinfo (res);
+
+ return 0;
+}
+
+/*
+ * network_pass_socks4proxy: establish connection/authentification thru a
+ * socks4 proxy
+ * return :
+ * - 0 if connexion thru proxy was successful
+ * - 1 if connexion fails
+ */
+
+int
+network_pass_socks4proxy (int sock, char *address, int port)
+{
+ /*
+ * socks4 protocol is explained here:
+ * http://archive.socks.permeo.com/protocol/socks4.protocol
+ *
+ */
+
+ struct s_socks4
+ {
+ char version; /* 1 byte */ /* socks version : 4 or 5 */
+ char method; /* 1 byte */ /* socks method : connect (1) or bind (2) */
+ unsigned short port; /* 2 bytes */ /* destination port */
+ unsigned long address; /* 4 bytes */ /* destination address */
+ char user[64]; /* username (64 characters seems to be enought) */
+ } socks4;
+ unsigned char buffer[24];
+ char ip_addr[NI_MAXHOST];
+
+ socks4.version = 4;
+ socks4.method = 1;
+ socks4.port = htons (port);
+ network_resolve (address, ip_addr, NULL);
+ socks4.address = inet_addr (ip_addr);
+ strncpy (socks4.user, CONFIG_STRING(config_proxy_username),
+ sizeof (socks4.user) - 1);
+
+ 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;
+
+ return 1;
+}
+
+/*
+ * network_pass_socks5proxy: establish connection/authentification thru a
+ * socks5 proxy
+ * return :
+ * - 0 if connexion thru proxy was successful
+ * - 1 if connexion fails
+ */
+
+int
+network_pass_socks5proxy (int sock, char *address, int port)
+{
+ /*
+ * socks5 protocol is explained in RFC 1928
+ * socks5 authentication with username/pass is explained in RFC 1929
+ */
+
+ struct s_sock5
+ {
+ char version; /* 1 byte */ /* socks version : 4 or 5 */
+ char nmethods; /* 1 byte */ /* size in byte(s) of field 'method', here 1 byte */
+ char method; /* 1-255 bytes */ /* socks method : noauth (0), auth(user/pass) (2), ... */
+ } socks5;
+ unsigned char buffer[288];
+ int username_len, password_len, addr_len, addr_buffer_len;
+ unsigned char *addr_buffer;
+
+ socks5.version = 5;
+ socks5.nmethods = 1;
+
+ if (CONFIG_STRING(config_proxy_username)
+ && CONFIG_STRING(config_proxy_username)[0])
+ socks5.method = 2; /* with authentication */
+ else
+ socks5.method = 0; /* without authentication */
+
+ send (sock, (char *) &socks5, sizeof(socks5), 0);
+ /* server socks5 must respond with 2 bytes */
+ if (recv (sock, buffer, 2, 0) != 2)
+ return 1;
+
+ if (CONFIG_STRING(config_proxy_username)
+ && CONFIG_STRING(config_proxy_username)[0])
+ {
+ /* with authentication */
+ /* -> socks server must respond with :
+ * - socks version (buffer[0]) = 5 => socks5
+ * - socks method (buffer[1]) = 2 => authentication
+ */
+
+ if (buffer[0] != 5 || buffer[1] != 2)
+ return 1;
+
+ /* authentication as in RFC 1929 */
+ username_len = strlen (CONFIG_STRING(config_proxy_username));
+ password_len = strlen (CONFIG_STRING(config_proxy_password));
+
+ /* make username/password buffer */
+ buffer[0] = 1;
+ buffer[1] = (unsigned char) username_len;
+ memcpy(buffer + 2, CONFIG_STRING(config_proxy_username), username_len);
+ buffer[2 + username_len] = (unsigned char) password_len;
+ memcpy (buffer + 3 + username_len,
+ CONFIG_STRING(config_proxy_password), password_len);
+
+ send (sock, buffer, 3 + username_len + password_len, 0);
+
+ /* server socks5 must respond with 2 bytes */
+ if (recv (sock, buffer, 2, 0) != 2)
+ return 1;
+
+ /* buffer[1] = auth state, must be 0 for success */
+ if (buffer[1] != 0)
+ return 1;
+ }
+ else
+ {
+ /* without authentication */
+ /* -> socks server must respond with :
+ * - socks version (buffer[0]) = 5 => socks5
+ * - socks method (buffer[1]) = 0 => no authentication
+ */
+ if (!(buffer[0] == 5 && buffer[1] == 0))
+ return 1;
+ }
+
+ /* authentication successful then giving address/port to connect */
+ addr_len = strlen(address);
+ addr_buffer_len = 4 + 1 + addr_len + 2;
+ addr_buffer = malloc (addr_buffer_len * sizeof(*addr_buffer));
+ if (!addr_buffer)
+ return 1;
+ addr_buffer[0] = 5; /* version 5 */
+ addr_buffer[1] = 1; /* command: 1 for connect */
+ addr_buffer[2] = 0; /* reserved */
+ addr_buffer[3] = 3; /* address type : ipv4 (1), domainname (3), ipv6 (4) */
+ addr_buffer[4] = (unsigned char) addr_len;
+ memcpy (addr_buffer + 5, address, addr_len); /* server address */
+ *((unsigned short *) (addr_buffer + 5 + addr_len)) = htons (port); /* server port */
+
+ send (sock, addr_buffer, addr_buffer_len, 0);
+ free (addr_buffer);
+
+ /* dialog with proxy server */
+ if (recv (sock, buffer, 4, 0) != 4)
+ return 1;
+
+ if (!(buffer[0] == 5 && buffer[1] == 0))
+ return 1;
+
+ /* buffer[3] = address type */
+ switch (buffer[3])
+ {
+ case 1:
+ /* ipv4
+ * server socks return server bound address and port
+ * address of 4 bytes and port of 2 bytes (= 6 bytes)
+ */
+ if (recv (sock, buffer, 6, 0) != 6)
+ return 1;
+ break;
+ case 3:
+ /* domainname
+ * server socks return server bound address and port
+ */
+ /* reading address length */
+ if (recv (sock, buffer, 1, 0) != 1)
+ return 1;
+ addr_len = buffer[0];
+ /* reading address + port = addr_len + 2 */
+ if (recv (sock, buffer, addr_len + 2, 0) != (addr_len + 2))
+ return 1;
+ break;
+ case 4:
+ /* ipv6
+ * server socks return server bound address and port
+ * address of 16 bytes and port of 2 bytes (= 18 bytes)
+ */
+ if (recv (sock, buffer, 18, 0) != 18)
+ return 1;
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * network_pass_proxy: establish connection/authentification to a proxy
+ * return :
+ * - 0 if connexion throw proxy was successful
+ * - 1 if connexion fails
+ */
+
+int
+network_pass_proxy (int sock, char *address, int port)
+{
+ int rc;
+
+ rc = 1;
+ if (CONFIG_BOOLEAN(config_proxy_type))
+ {
+ if (string_strcasecmp (CONFIG_STRING(config_proxy_type), "http") == 0)
+ rc = network_pass_httpproxy (sock, address, port);
+ if (string_strcasecmp (CONFIG_STRING(config_proxy_type), "socks4") == 0)
+ rc = network_pass_socks4proxy (sock, address, port);
+ if (string_strcasecmp (CONFIG_STRING(config_proxy_type), "socks5") == 0)
+ rc = network_pass_socks5proxy (sock, address, port);
+ }
+ return rc;
+}
+
+/*
+ * network_connect_to: connect to a remote host
+ * return 1 if ok, 0 if failed
+ */
+
+int
+network_connect_to (int sock, unsigned long address, int port)
+{
+ struct sockaddr_in addr;
+ struct hostent *hostent;
+ char *ip4;
+ int ret;
+
+ if (CONFIG_BOOLEAN(config_proxy_type))
+ {
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_addr.s_addr = htonl (address);
+ ip4 = inet_ntoa(addr.sin_addr);
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_port = htons (CONFIG_INTEGER(config_proxy_port));
+ addr.sin_family = AF_INET;
+ hostent = gethostbyname (CONFIG_STRING(config_proxy_address));
+ if (!hostent)
+ return 0;
+ 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)
+ return 0;
+ }
+ else
+ {
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_port = htons (port);
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl (address);
+ ret = connect (sock, (struct sockaddr *) &addr, sizeof (addr));
+ if ((ret == -1) && (errno != EINPROGRESS))
+ return 0;
+ }
+ return 1;
+}