aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorTomasz Bursztyka <tomasz.bursztyka@linux.intel.com>2012-01-20 09:47:15 +0200
committerSamuel Ortiz <sameo@linux.intel.com>2012-01-27 11:33:47 +0100
commit1b16a4dec2ba316da6e402b5ebe6beca3dc5969d (patch)
tree71febcfe7659f33f346611bbb0710c6238491a84 /tools
parentcd32c92c4a948086698e456a86eee565afb1ebb2 (diff)
downloadpacrunner-1b16a4dec2ba316da6e402b5ebe6beca3dc5969d.tar.gz
tools: Added a tool to test the manual proxy configuration logic
Diffstat (limited to 'tools')
-rw-r--r--tools/manual-proxy-test.c779
1 files changed, 779 insertions, 0 deletions
diff --git a/tools/manual-proxy-test.c b/tools/manual-proxy-test.c
new file mode 100644
index 0000000..67f87bb
--- /dev/null
+++ b/tools/manual-proxy-test.c
@@ -0,0 +1,779 @@
+/*
+ *
+ * PACrunner - Proxy configuration daemon
+ *
+ * Copyright (C) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#define DBG(fmt, arg...) do { \
+ printf("%s() " fmt "\n", __func__ , ## arg); \
+} while (0)
+
+enum pacrunner_manual_protocol {
+ PACRUNNER_PROTOCOL_ALL = 0,
+ PACRUNNER_PROTOCOL_HTTP = 1,
+ PACRUNNER_PROTOCOL_HTTPS = 2,
+ PACRUNNER_PROTOCOL_FTP = 3,
+ PACRUNNER_PROTOCOL_SOCKS4 = 4,
+ PACRUNNER_PROTOCOL_SOCKS5 = 5,
+ PACRUNNER_PROTOCOL_MAXIMUM_NUMBER = 6,
+ PACRUNNER_PROTOCOL_UNKNOWN = 7,
+};
+
+enum pacrunner_manual_exclude_appliance {
+ PACRUNNER_MANUAL_EXCLUDE_POST = 0,
+ PACRUNNER_MANUAL_EXCLUDE_PRE = 1,
+ PACRUNNER_MANUAL_EXCLUDE_ANY = 2,
+};
+
+struct pacrunner_manual_exclude {
+ enum pacrunner_manual_exclude_appliance appliance;
+ int host_length;
+ char *host;
+};
+
+static enum pacrunner_manual_protocol get_protocol_from_string(const char *protocol)
+{
+ if (protocol == NULL)
+ return PACRUNNER_PROTOCOL_ALL;
+
+ if (g_strcmp0(protocol, "http") == 0)
+ return PACRUNNER_PROTOCOL_HTTP;
+ if (g_strcmp0(protocol, "https") == 0)
+ return PACRUNNER_PROTOCOL_HTTPS;
+ if (g_strcmp0(protocol, "ftp") == 0)
+ return PACRUNNER_PROTOCOL_FTP;
+ if (g_strcmp0(protocol, "socks4") == 0)
+ return PACRUNNER_PROTOCOL_SOCKS4;
+ if (g_strcmp0(protocol, "socks5") == 0)
+ return PACRUNNER_PROTOCOL_SOCKS5;
+
+ return PACRUNNER_PROTOCOL_UNKNOWN;
+}
+
+static const char *get_protocol_to_string(enum pacrunner_manual_protocol protocol)
+{
+ switch (protocol) {
+ case PACRUNNER_PROTOCOL_ALL:
+ return "ALL";
+ case PACRUNNER_PROTOCOL_HTTP:
+ return "HTTP";
+ case PACRUNNER_PROTOCOL_HTTPS:
+ return "HTTPS";
+ case PACRUNNER_PROTOCOL_FTP:
+ return "FTP";
+ case PACRUNNER_PROTOCOL_SOCKS4:
+ return "SOCKS4";
+ case PACRUNNER_PROTOCOL_SOCKS5:
+ return "SOCKS5";
+ default:
+ break;
+ }
+
+ return "UNKNOWN";
+}
+
+static const char *get_appliance_to_string(enum pacrunner_manual_exclude_appliance appliance)
+{
+ switch (appliance) {
+ case PACRUNNER_MANUAL_EXCLUDE_POST:
+ return "POST";
+ case PACRUNNER_MANUAL_EXCLUDE_PRE:
+ return "PRE";
+ case PACRUNNER_MANUAL_EXCLUDE_ANY:
+ return "ANY";
+ default:
+ break;
+ }
+
+ return "UNKNOWN";
+}
+
+static int parse_uri(char *uri,
+ char **host,
+ char **protocol,
+ gboolean no_path,
+ gboolean exclusion)
+{
+ int ret = PACRUNNER_MANUAL_EXCLUDE_POST;
+ gboolean proto, post_confirmed, ipv6;
+ char *scheme, *sep, *cur;
+ int length;
+ long int port;
+
+ proto = post_confirmed = ipv6 = FALSE;
+ port = -1;
+
+ /**
+ * Make sure host and protocol, if given, are properly set.
+ */
+ if (host != NULL)
+ *host = NULL;
+
+ if (protocol != NULL)
+ *protocol = NULL;
+
+ /**
+ * The parsing will actually process on a copy of given uri
+ */
+ scheme = g_strdup(uri);
+ if (scheme == NULL)
+ goto error;
+
+ cur = scheme;
+
+ /**
+ * 1 - parsing protocol first
+ * Note: protocol scheme is here totally ignored
+ */
+ sep = strstr(cur, "://");
+ if (sep != NULL) {
+ if (sep == cur)
+ goto error;
+
+ if (protocol != NULL) {
+ *sep = '\0';
+
+ *protocol = g_strdup(cur);
+ if (*protocol == NULL)
+ goto error;
+ }
+
+ cur = sep + 3;
+ proto = TRUE;
+ }
+
+ /**
+ * 2 - detecting end of uri
+ * Note: in case of server/exclusion configuration,
+ * no path should be present
+ */
+ sep = strchr(cur, '/');
+ if (sep != NULL) {
+ if (exclusion == TRUE || (*(sep + 1) != '\0' &&
+ no_path == TRUE))
+ goto error;
+
+ *sep = '\0';
+ }
+
+ /**
+ * 3 - We skip <login:password> if present
+ * Note: exclusion rule cannot contain such authentication information
+ */
+ sep = strchr(cur, '@');
+ if (sep != NULL) {
+ if (exclusion == TRUE)
+ goto error;
+
+ *sep = '\0';
+ cur = sep + 1;
+ }
+
+ /**
+ * 4 - Are we in front of a possible IPv6 address?
+ * Note: ipv6 format is not checked!
+ */
+ sep = strchr(cur, '[');
+ if (sep != NULL) {
+ char *bracket;
+
+ bracket = strchr(cur, ']');
+ if (bracket == NULL)
+ goto error;
+
+ cur = sep;
+ sep = strchr(bracket, ':');
+
+ ipv6 = TRUE;
+ } else
+ sep = strchr(cur, ':');
+
+ /**
+ * 5 - Checking port validity if present
+ * Note: exclusion rule cannot embed port
+ */
+ if (sep != NULL) {
+ char *err = NULL;
+
+ if (exclusion == TRUE)
+ goto error;
+
+ errno = 0;
+ port = strtol(sep+1, &err, 10);
+ if (*err != '\0' || port <= 0 || port > USHRT_MAX ||
+ errno == ERANGE || errno == EINVAL)
+ goto error;
+
+ *sep = '\0';
+ }
+
+ /**
+ * 6 - We detect/trim '.'/'*' from start
+ * Note: This is valid only for exclusion URI since it defines
+ * its rule's appliance */
+ for (sep = cur; *sep != '\0' && (*sep == '*' || *sep == '.'); sep++)
+ *sep = '\0';
+
+ if (sep != cur) {
+ if (exclusion == FALSE)
+ goto error;
+
+ cur = sep;
+ post_confirmed = TRUE;
+ }
+
+ /**
+ * 7 - Parsing host if present
+ */
+ length = strlen(cur);
+ if (length > 0) {
+ const char *forbidden_chars;
+ char **forbidden = NULL;
+
+ /**
+ * We first detect/trim '.'/'*' from end
+ * Note: valid only for exclusion
+ */
+ for (sep = cur + length - 1;
+ *sep != '\0' && (*sep == '*' || *sep == '.'); sep--)
+ *sep = '\0';
+
+ if (sep - cur + 1 != length) {
+ if (exclusion == FALSE)
+ goto error;
+
+ length = sep - cur + 1;
+
+ ret = PACRUNNER_MANUAL_EXCLUDE_PRE;
+ if (post_confirmed == TRUE)
+ ret = PACRUNNER_MANUAL_EXCLUDE_ANY;
+ }
+
+ if ((length > 255) || (*cur == '-' || *sep == '-') ||
+ ((*cur == '\0') && (exclusion == FALSE ||
+ (exclusion == TRUE && proto == FALSE))))
+ goto error;
+
+ /**
+ * We do not allow some characters. However we do not run
+ * a strict check if it's an IP address which is given
+ */
+ if (ipv6 == TRUE)
+ forbidden_chars = "%?!,;@\\'*|<>{}()+=$&~# \"";
+ else
+ forbidden_chars = "%?!,;@\\'*|<>{}[]()+=$&~# \"";
+
+ forbidden = g_strsplit_set(cur, forbidden_chars, -1);
+ if (forbidden != NULL) {
+ length = g_strv_length(forbidden);
+ g_strfreev(forbidden);
+
+ if (length > 1)
+ goto error;
+ }
+
+ if (host != NULL && *cur != '\0') {
+ if (port > 0) {
+ /**
+ * Instead of transcoding the port back
+ * to string we just get the host:port line
+ * from the original uri.
+ * */
+ cur = uri + (cur - scheme);
+
+ sep = strchr(cur, '/');
+ if (sep != NULL)
+ length = sep - cur;
+ else
+ length = strlen(cur);
+
+ *host = g_strndup(cur, length);
+ } else
+ *host = g_strdup(cur);
+
+ if (*host == NULL)
+ goto error;
+ }
+ } else {
+ if (exclusion == FALSE ||
+ (exclusion == TRUE && proto == FALSE))
+ goto error;
+ else
+ ret = PACRUNNER_MANUAL_EXCLUDE_ANY;
+ }
+
+ g_free(scheme);
+
+ return ret;
+
+error:
+ if (protocol != NULL) {
+ g_free(*protocol);
+ *protocol = NULL;
+ }
+
+ g_free(scheme);
+
+ return -EINVAL;
+}
+
+static void free_exclude(gpointer data)
+{
+ struct pacrunner_manual_exclude *exclude;
+
+ exclude = (struct pacrunner_manual_exclude *) data;
+ if (exclude == NULL)
+ return;
+
+ g_free(exclude->host);
+ g_free(exclude);
+}
+
+static void __pacrunner_manual_destroy_excludes(GList **excludes)
+{
+ int i;
+
+ if (excludes == NULL)
+ return;
+
+ for (i = 0; i < PACRUNNER_PROTOCOL_MAXIMUM_NUMBER; i++)
+ g_list_free_full(excludes[i], free_exclude);
+
+ g_free(excludes);
+}
+
+static GList **__pacrunner_manual_create_excludes(const char **excludes)
+{
+ struct pacrunner_manual_exclude *exclude;
+ char *host, *protocol;
+ GList **result = NULL;
+ int ret, proto;
+ char **uri;
+
+ if (excludes == NULL)
+ return NULL;
+
+ result = g_try_malloc0(PACRUNNER_PROTOCOL_MAXIMUM_NUMBER *
+ sizeof(GList *));
+ if (result == NULL)
+ return NULL;
+
+ for (uri = (char **)excludes; *uri != NULL; uri++) {
+ ret = parse_uri(*uri, &host, &protocol, TRUE, TRUE);
+
+ if (ret < 0)
+ continue;
+
+ proto = get_protocol_from_string(protocol);
+ if (proto == PACRUNNER_PROTOCOL_UNKNOWN)
+ goto error;
+
+ exclude = g_try_malloc0(sizeof(
+ struct pacrunner_manual_exclude));
+ if (exclude == NULL)
+ goto error;
+
+ exclude->appliance = ret;
+ exclude->host = host;
+
+ if (host != NULL)
+ exclude->host_length = strlen(host);
+
+ result[proto] = g_list_append(result[proto], exclude);
+
+ g_free(protocol);
+ protocol = NULL;
+ host = NULL;
+ }
+
+ return result;
+
+error:
+ g_free(host);
+ g_free(protocol);
+
+ __pacrunner_manual_destroy_excludes(result);
+
+ return NULL;
+}
+
+static void __pacrunner_manual_destroy_servers(GList **servers)
+{
+ int i;
+
+ if (servers == NULL)
+ return;
+
+ for (i = 0; i < PACRUNNER_PROTOCOL_MAXIMUM_NUMBER; i++)
+ g_list_free_full(servers[i], g_free);
+
+ g_free(servers);
+}
+
+static GList **__pacrunner_manual_create_servers(const char **servers)
+{
+ char *host, *protocol;
+ GList **result;
+ char **uri;
+ int proto;
+ int ret;
+
+ if (servers == NULL)
+ return NULL;
+
+ result = g_try_malloc0(PACRUNNER_PROTOCOL_MAXIMUM_NUMBER *
+ sizeof(GList *));
+ if (result == NULL)
+ return NULL;
+
+ for (uri = (char **)servers; *uri != NULL; uri++) {
+ ret = parse_uri(*uri, &host, &protocol, TRUE, FALSE);
+
+ if (ret < 0)
+ continue;
+
+ proto = get_protocol_from_string(protocol);
+ if (proto == PACRUNNER_PROTOCOL_UNKNOWN)
+ goto error;
+
+ result[proto] = g_list_append(result[proto], host);
+
+ g_free(protocol);
+ }
+
+ return result;
+
+error:
+ g_free(host);
+ g_free(protocol);
+
+ __pacrunner_manual_destroy_servers(result);
+
+ return NULL;
+}
+
+static gboolean is_exclusion_matching(GList *excludes_list,
+ const char *host)
+{
+ struct pacrunner_manual_exclude *exclusion;
+ GList *excludes = NULL;
+ char *cursor;
+
+ for (excludes = excludes_list; excludes != NULL;
+ excludes = excludes->next) {
+ exclusion = (struct pacrunner_manual_exclude *) excludes->data;
+ if (exclusion == NULL)
+ continue;
+
+ cursor = NULL;
+
+ if (exclusion->host != NULL)
+ cursor = strstr(host, exclusion->host);
+
+ switch (exclusion->appliance) {
+ case PACRUNNER_MANUAL_EXCLUDE_POST:
+ if (cursor == NULL)
+ break;
+
+ if ((int)strlen(cursor) < exclusion->host_length)
+ break;
+
+ if (*(cursor + exclusion->host_length) == '\0')
+ return TRUE;
+
+ break;
+ case PACRUNNER_MANUAL_EXCLUDE_PRE:
+ if (cursor == host)
+ return TRUE;
+
+ break;
+ case PACRUNNER_MANUAL_EXCLUDE_ANY:
+ if (exclusion->host != NULL) {
+ if (cursor != NULL)
+ return TRUE;
+ else
+ break;
+ }
+
+ return TRUE;
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean is_url_excluded(GList **excludes,
+ const char *host,
+ enum pacrunner_manual_protocol proto)
+{
+ if (excludes == NULL)
+ return FALSE;
+
+ if (excludes[PACRUNNER_PROTOCOL_ALL] != NULL)
+ if (is_exclusion_matching(excludes[PACRUNNER_PROTOCOL_ALL],
+ host) == TRUE)
+ return TRUE;
+
+ if (proto == PACRUNNER_PROTOCOL_UNKNOWN)
+ return FALSE;
+
+ if (excludes[proto] != NULL)
+ if (is_exclusion_matching(excludes[proto], host) == TRUE)
+ return TRUE;
+
+ return FALSE;
+}
+
+static char *__pacrunner_manual_execute(const char *url,
+ GList **servers,
+ GList **excludes)
+{
+ char *protocol = NULL;
+ char *result = NULL;
+ char *host = NULL;
+ int proto;
+
+ if (servers == NULL)
+ return NULL;
+
+ if (parse_uri((char *)url, &host, &protocol, FALSE, FALSE) < 0)
+ goto direct;
+
+ proto = get_protocol_from_string(protocol);
+
+ if (is_url_excluded(excludes, host, proto) == TRUE)
+ goto direct;
+
+ if (servers[PACRUNNER_PROTOCOL_ALL] != NULL)
+ result = (char *)servers[PACRUNNER_PROTOCOL_ALL]->data;
+ else if (proto >= PACRUNNER_PROTOCOL_HTTP &&
+ proto < PACRUNNER_PROTOCOL_MAXIMUM_NUMBER)
+ if (servers[proto] != NULL)
+ result = (char *)servers[proto]->data;
+
+direct:
+ g_free(protocol);
+ g_free(host);
+
+ return result;
+}
+
+static void dump_proxy_exclusion(gpointer data, gpointer user_data)
+{
+ struct pacrunner_manual_exclude *exclusion;
+
+ exclusion = (struct pacrunner_manual_exclude *) data;
+ if (exclusion == NULL)
+ return;
+
+ printf("\tappliance: %s\n",
+ get_appliance_to_string(exclusion->appliance));
+ printf("\thost: %s (%d)\n", exclusion->host, exclusion->host_length);
+}
+
+static void dump_excludes(GList **excludes)
+{
+ int i;
+
+ printf("EXCLUDES:\n");
+
+ if (excludes == NULL) {
+ printf("\tNO EXCLUDES\n");
+ return;
+ }
+
+ for (i = 0; i < PACRUNNER_PROTOCOL_MAXIMUM_NUMBER; i++) {
+ printf("Protocol: %s\n", get_protocol_to_string(i));
+
+ if (excludes[i] == NULL) {
+ printf("\tNONE\n");
+ continue;
+ }
+
+ g_list_foreach(excludes[i], dump_proxy_exclusion, NULL);
+ printf("\n");
+ }
+}
+
+static void dump_proxy_server(gpointer data, gpointer user_data)
+{
+ printf("\t%s\n", (char *)data);
+}
+
+static void dump_servers(GList **servers)
+{
+ int i;
+
+ printf("SERVERS:\n");
+
+ if (servers == NULL) {
+ printf("\tNO SERVERS\n");
+ return;
+ }
+
+ for (i = 0; i < PACRUNNER_PROTOCOL_MAXIMUM_NUMBER; i++) {
+ printf("Protocol: %s\n", get_protocol_to_string(i));
+
+ if (servers[i] == NULL) {
+ printf("\tNONE\n");
+ continue;
+ }
+
+ g_list_foreach(servers[i], dump_proxy_server, NULL);
+ printf("\n");
+ }
+}
+
+static void print_help(void)
+{
+ printf("Manual Proxy configuration test tool\n"
+ "Usage:\n"
+ "-e,--excludes <list>: provides an exclusion list (optional)\n"
+ "-h,--help : prints this help message\n"
+ "-s,--servers <list>: provides the proxy server(s) (mandatory)\n"
+ "-t,--test <list>: provides the URLs to test (mandatory)\n"
+ "list should be made like \"elt1,elt2\" and so on\n"
+ "\n"
+ "Servers example:\n"
+ "http://stuff.com -> http proxy\n"
+ "stuff.com -> general proxy\n"
+ "ftp://foo.org:4578 -> ftp proxy, with specific port\n"
+ "\n"
+ "Exclusion example:\n"
+ "If a uri matches an exclusion rule, it will not be proxied\n"
+ "port number is not allowed in exclusion rules\n"
+ "'*' is not allowed for protocols\n"
+ "stuff*foo is not allowed\n"
+ "http:// -> all http\n"
+ "http://*foo* -> all http containing foo\n"
+ "*com -> all host ending with com\n"
+ "ftp://stuff* -> all ftp starting with stuff\n");
+}
+
+static struct option long_options[] = {
+ {"excludes", 1, 0, 'e'},
+ {"help", 0, 0, 'h'},
+ {"servers", 1, 0, 's'},
+ {"test", 1, 0, 't'},
+ {NULL, 0, 0, 0 }
+};
+
+static const char *short_options = "e:hs:t:";
+
+int main(int argc, char *argv[])
+{
+ char **excludes_list, **servers_list, **tests_list;
+ GList **excludes = NULL;
+ GList **servers = NULL;
+ int opt, option_index;
+ char **url;
+
+ excludes_list = NULL;
+ servers_list = NULL;
+ tests_list = NULL;
+ option_index = 0;
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ long_options, &option_index)) != -1) {
+ switch (opt) {
+ case 'e':
+ excludes_list = g_strsplit(optarg, ",", 0);
+ if (excludes_list == NULL)
+ exit(EXIT_FAILURE);
+
+ break;
+ case 'h':
+ print_help();
+ exit(EXIT_SUCCESS);
+
+ break;
+ case 's':
+ servers_list = g_strsplit(optarg, ",", 0);
+ if (servers_list == NULL)
+ exit(EXIT_FAILURE);
+
+ break;
+ case 't':
+ tests_list = g_strsplit(optarg, ",", 0);
+ if (tests_list == NULL)
+ exit(EXIT_FAILURE);
+
+ break;
+ case '?':
+ break;
+ default:
+ printf("Unknown option\n"
+ "please see --help for more informations\n");
+ exit(EXIT_FAILURE);
+
+ break;
+ }
+ }
+
+ if (servers_list == NULL || tests_list == NULL) {
+ printf("You must provide server(s) and test(s) options\n");
+ exit(EXIT_FAILURE);
+ }
+
+ servers = __pacrunner_manual_create_servers((const char **)
+ servers_list);
+ if (servers == NULL)
+ exit(EXIT_FAILURE);
+
+ excludes = __pacrunner_manual_create_excludes((const char **)
+ excludes_list);
+
+ dump_excludes(excludes);
+ dump_servers(servers);
+
+ g_strfreev(excludes_list);
+ g_strfreev(servers_list);
+
+ for (url = tests_list; *url != NULL; url++) {
+ char *proxy;
+
+ printf("Url: %s -> ", *url);
+
+ proxy = __pacrunner_manual_execute(*url, servers, excludes);
+ if (proxy == NULL)
+ printf("DIRECT\n");
+ else
+ printf("%s\n", proxy);
+ }
+
+ g_strfreev(tests_list);
+
+ __pacrunner_manual_destroy_excludes(excludes);
+ __pacrunner_manual_destroy_servers(servers);
+
+ return 0;
+}