int uri_parse_authority(struct uri_parser *parser, struct uri_authority *auth) { const unsigned char *p; int ret; /* * authority = [ userinfo "@" ] host [ ":" port ] */ if (auth != NULL) memset(auth, 0, sizeof(*auth)); /* Scan ahead to check whether there is a [userinfo "@"] uri component */ for (p = parser->cur; p < parser->end; p++){ /* refuse 8bit characters */ if ((*p & 0x80) != 0) break; /* break at first delimiter */ if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0) break; } /* Extract userinfo */ if (p < parser->end && *p == '@') { if (auth != NULL) auth->enc_userinfo = t_strdup_until(parser->cur, p); parser->cur = p+1; } /* host */ if ((ret = uri_parse_host(parser, auth)) <= 0) { if (ret == 0) { if (p == parser->end || *p == ':' || *p == '/') parser->error = "Missing 'host' component"; else parser->error = "Invalid 'host' component"; return -1; } return ret; } /* [":" ... */ if (parser->cur >= parser->end || *parser->cur != ':') return 1; parser->cur++; /* ... port] */ if ((ret = uri_parse_port(parser, auth)) < 0) return ret; return 1; }
int proxy_uri_parse(pool *p, const char *uri, char **scheme, char **host, unsigned int *port, char **username, char **password) { char *ptr, *ptr2; size_t idx, len; if (uri == NULL || scheme == NULL || host == NULL || port == NULL) { errno = EINVAL; return -1; } /* First, look for a ':' */ ptr = strchr(uri, ':'); if (ptr == NULL) { pr_trace_msg(trace_channel, 4, "missing colon in URI '%.100s'", uri); errno = EINVAL; return -1; } len = (ptr - uri); *scheme = pstrndup(p, uri, len); idx = strspn(*scheme, "abcdefghijklmnopqrstuvwxyz+.-"); if (idx < len && (*scheme)[idx] != '\0') { /* Invalid character in the scheme string, according to RFC 1738 rules. */ pr_trace_msg(trace_channel, 4, "invalid character (%c) at index %lu in scheme '%.100s'", (*scheme)[idx], (unsigned long) idx, *scheme); errno = EINVAL; return -1; } /* The double-slashes must immediately follow the colon. */ if (*(ptr + 1) != '/' || *(ptr + 2) != '/') { pr_trace_msg(trace_channel, 4, "missing required '//' following colon in URI '%.100s'", uri); errno = EINVAL; return -1; } ptr += 3; if (*ptr == '\0') { /* The given URL looked like "scheme://". */ pr_trace_msg(trace_channel, 4, "missing required authority following '//' in URI '%.100s'", uri); errno = EINVAL; return -1; } /* Possible URIs at this point: * * scheme://host:port/path/... * scheme://host:port/ * scheme://host:port * scheme://host * scheme://username:password@host... * * And, in the case where 'host' is an IPv6 address: * * scheme://[host]:port/path/... * scheme://[host]:port/ * scheme://[host]:port * scheme://[host] * scheme://username:password@[host]... */ /* We explicitly do NOT support URL-encoded characters in the URIs we * will handle. */ ptr2 = strchr(ptr, '%'); if (ptr2 != NULL) { pr_trace_msg(trace_channel, 4, "invalid character (%%) at index %ld in scheme-specific info '%.100s'", (long) (ptr2 - ptr), ptr); errno = EINVAL; return -1; } ptr = uri_parse_userinfo(p, uri, ptr, username, password); ptr2 = strchr(ptr, ':'); if (ptr2 == NULL) { *host = uri_parse_host(p, uri, ptr, NULL); if (strncmp(*scheme, "ftp", 4) == 0 || strncmp(*scheme, "ftps", 5) == 0) { *port = 21; } else if (strncmp(*scheme, "sftp", 5) == 0) { *port = 22; } else { pr_trace_msg(trace_channel, 4, "unable to determine port for scheme '%.100s'", *scheme); errno = EINVAL; return -1; } } else { *host = uri_parse_host(p, uri, ptr, &ptr2); } /* Optional port field present? */ if (ptr2 != NULL) { ptr2 = strchr(ptr2, ':'); } if (ptr2 == NULL) { /* XXX How to configure "implicit" FTPS, if at all? */ if (strncmp(*scheme, "ftp", 4) == 0 || strncmp(*scheme, "ftps", 5) == 0) { *port = 21; } else if (strncmp(*scheme, "sftp", 5) == 0) { *port = 22; } else { pr_trace_msg(trace_channel, 4, "unable to determine port for scheme '%.100s'", *scheme); errno = EINVAL; return -1; } } else { register unsigned int i; char *ptr3, *portspec; size_t portspeclen; /* Look for any possible trailing '/'. */ ptr3 = strchr(ptr2, '/'); if (ptr3 == NULL) { portspec = ptr2 + 1; portspeclen = strlen(portspec); } else { portspeclen = ptr3 - (ptr2 + 1); portspec = pstrndup(p, ptr2 + 1, portspeclen); } /* Ensure that only numeric characters appear in the portspec. */ for (i = 0; i < portspeclen; i++) { if (isdigit((int) portspec[i]) == 0) { pr_trace_msg(trace_channel, 4, "invalid character (%c) at index %d in port specification '%.100s'", portspec[i], i, portspec); errno = EINVAL; return -1; } } /* The above check will rule out any negative numbers, since it will * reject the minus character. Thus we only need to check for a zero * port, or a number that's outside the 1-65535 range. */ *port = atoi(portspec); if (*port == 0 || *port >= 65536) { pr_trace_msg(trace_channel, 4, "port specification '%.100s' yields invalid port number %d", portspec, *port); errno = EINVAL; return -1; } } return 0; }