diff options
author | Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com> | 2012-01-20 09:47:03 +0200 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2012-01-27 11:33:47 +0100 |
commit | bdea3cd2c1c13a65ac5b85b76d2e8aa759010a0d (patch) | |
tree | fd7b28012a10776daa774a6e7b25022c68ce893c | |
parent | e35ab6b77c07acb3f7cd9eece049156bea5bc9cd (diff) | |
download | pacrunner-bdea3cd2c1c13a65ac5b85b76d2e8aa759010a0d.tar.gz |
manual: Adding uri parsing capability
It's the most important function in this manual proxy feature. It takes servers
one by one, as well as excludes, and parses it. It's based on a simple duo of
separator and cursor, both are locations among the uri characters.
It follows this path:
- parsing the protocol, if present
- finding out the end of uri (removes the path, if present)
- skip authentication information, if present
- check if host is a plain ipv6
- parses the port, if present
- parses '*'/'.' (used for exclusion rule uri) and the host at the same time
-rw-r--r-- | src/manual.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/src/manual.c b/src/manual.c index 00bd6cb..fbf3aa8 100644 --- a/src/manual.c +++ b/src/manual.c @@ -23,10 +23,266 @@ #include <config.h> #endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> + #include "pacrunner.h" +enum pacrunner_manual_exclude_appliance { + PACRUNNER_MANUAL_EXCLUDE_POST = 0, + PACRUNNER_MANUAL_EXCLUDE_PRE = 1, + PACRUNNER_MANUAL_EXCLUDE_ANY = 2, +}; + +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; + long int port; + int length; + + 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; +} + GList **__pacrunner_manual_parse_servers(char **servers) { + char *host, *protocol; + char **uri; + int ret; + + if (servers == NULL) + return NULL; + + for (uri = (char **)servers; *uri != NULL; uri++) { + ret = parse_uri(*uri, &host, &protocol, TRUE, FALSE); + + if (ret < 0) + continue; + + g_free(host); + g_free(protocol); + } + return NULL; } |