From 58cf354711f9e7eb2ec2a54b5f623abb860dafcf Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Wed, 28 Jan 2009 15:55:41 -0800 Subject: [PATCH] Fixed the parsing of IPv6 literal addresses with a username prefixed. Fixes bug #6067. --- options.c | 119 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 52 deletions(-) diff --git a/options.c b/options.c index 8b67275c..0c64eaa1 100644 --- a/options.c +++ b/options.c @@ -2618,6 +2618,61 @@ void server_options(char **args, int *argc_p) out_of_memory("server_options"); } +/* If str points to a valid hostspec, return allocated memory containing the + * [USER@]HOST part of the string, and set the path_start_ptr to the part of + * the string after the host part. Otherwise, return NULL. If port_ptr is + * non-NULL, we must be parsing an rsync:// URL hostname, and we will set + * *port_ptr if a port number is found. Note that IPv6 IPs will have their + * (required for parsing) [ and ] chars elided from the returned string. */ +static char *parse_hostspec(char *str, char **path_start_ptr, int *port_ptr) +{ + char *s = str; + char *host_start = str; + int hostlen = 0, userlen = 0; + char *ret; + + for ( ; ; s++) { + if (!*s) { + /* It is only OK if we run out of string with rsync:// */ + if (port_ptr) + break; + return NULL; + } + if (*s == ':' || *s == '/') { + if (!hostlen) + hostlen = s - host_start; + if (*s++ == '/') { + if (!port_ptr) + return NULL; + } else if (port_ptr) { + *port_ptr = atoi(s); + while (isDigit(s)) s++; + if (*s++ != '/') + return NULL; + } + break; + } + if (*s == '@') { + userlen = s - str + 1; + host_start = s + 1; + } else if (*s == '[') { + if (s != host_start++) + return NULL; + while (*s && *s != ']' && *s != '/') s++; /*SHARED ITERATOR*/ + hostlen = s - host_start; + if (*s != ']' || (s[1] && s[1] != '/' && s[1] != ':') || !hostlen) + return NULL; + } + } + + *path_start_ptr = s; + ret = new_array(char, userlen + hostlen + 1); + if (userlen) + strlcpy(ret, str, userlen + 1); + strlcpy(ret + userlen, host_start, hostlen + 1); + return ret; +} + /* Look for a HOST specfication of the form "HOST:PATH", "HOST::PATH", or * "rsync://HOST:PORT/PATH". If found, *host_ptr will be set to some allocated * memory with the HOST. If a daemon-accessing spec was specified, the value @@ -2627,68 +2682,28 @@ void server_options(char **args, int *argc_p) * "[::ffff:127.0.0.1]") which is returned without the '[' and ']'. */ char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr) { - char *p; - int not_host; - int hostlen; + char *path; if (port_ptr && strncasecmp(URL_PREFIX, s, strlen(URL_PREFIX)) == 0) { - char *path; - s += strlen(URL_PREFIX); - if ((p = strchr(s, '/')) != NULL) { - hostlen = p - s; - path = p + 1; - } else { - hostlen = strlen(s); - path = ""; - } - if (*s == '[' && (p = strchr(s, ']')) != NULL) { - s++; - hostlen = p - s; - if (p[1] == ':') - *port_ptr = atoi(p+2); - } else { - if ((p = strchr(s, ':')) != NULL && p < s + hostlen) { - hostlen = p - s; - *port_ptr = atoi(p+1); - } + *host_ptr = parse_hostspec(s + strlen(URL_PREFIX), &path, port_ptr); + if (*host_ptr) { + if (!*port_ptr) + *port_ptr = RSYNC_PORT; + return path; } - if (!*port_ptr) - *port_ptr = RSYNC_PORT; - *host_ptr = new_array(char, hostlen + 1); - strlcpy(*host_ptr, s, hostlen + 1); - return path; - } - - if (*s == '[' && (p = strchr(s, ']')) != NULL && p[1] == ':') { - s++; - hostlen = p - s; - *p = '\0'; - not_host = strchr(s, '/') || !strchr(s, ':'); - *p = ']'; - if (not_host) - return NULL; - p++; - } else { - if (!(p = strchr(s, ':'))) - return NULL; - hostlen = p - s; - *p = '\0'; - not_host = strchr(s, '/') != NULL; - *p = ':'; - if (not_host) - return NULL; } - *host_ptr = new_array(char, hostlen + 1); - strlcpy(*host_ptr, s, hostlen + 1); + *host_ptr = parse_hostspec(s, &path, NULL); + if (!*host_ptr) + return NULL; - if (p[1] == ':') { + if (*path == ':') { if (port_ptr && !*port_ptr) *port_ptr = RSYNC_PORT; - return p + 2; + return path + 1; } if (port_ptr) *port_ptr = 0; - return p + 1; + return path; } -- 2.34.1