Mercurial > hg > nginx
view src/core/ngx_inet.c @ 4192:61e4af19df9f
Autoindex: escape '?' in file names.
For files with '?' in their names autoindex generated links with '?' not
escaped. This resulted in effectively truncated links as '?' indicates
query string start.
This is an updated version of the patch originally posted at [1]. It
introduces generic NGX_ESCAPE_URI_COMPONENT which escapes everything but
unreserved characters as per RFC 3986. This approach also renders unneeded
special colon processing (as colon is percent-encoded now), it's dropped
accordingly.
[1] http://nginx.org/pipermail/nginx-devel/2010-February/000112.html
Reported by Konstantin Leonov.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Tue, 11 Oct 2011 17:56:51 +0000 |
parents | 4d4b5770616f |
children | 4af8ea9c3a86 |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev */ #include <ngx_config.h> #include <ngx_core.h> static ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u); static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u); static ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u); in_addr_t ngx_inet_addr(u_char *text, size_t len) { u_char *p, c; in_addr_t addr; ngx_uint_t octet, n; addr = 0; octet = 0; n = 0; for (p = text; p < text + len; p++) { c = *p; if (c >= '0' && c <= '9') { octet = octet * 10 + (c - '0'); continue; } if (c == '.' && octet < 256) { addr = (addr << 8) + octet; octet = 0; n++; continue; } return INADDR_NONE; } if (n != 3) { return INADDR_NONE; } if (octet < 256) { addr = (addr << 8) + octet; return htonl(addr); } return INADDR_NONE; } #if (NGX_HAVE_INET6) ngx_int_t ngx_inet6_addr(u_char *p, size_t len, u_char *addr) { u_char c, *zero, *digit, *s, *d; size_t len4; ngx_uint_t n, nibbles, word; if (len == 0) { return NGX_ERROR; } zero = NULL; digit = NULL; len4 = 0; nibbles = 0; word = 0; n = 8; if (p[0] == ':') { p++; len--; } for (/* void */; len; len--) { c = *p++; if (c == ':') { if (nibbles) { digit = p; len4 = len; *addr++ = (u_char) (word >> 8); *addr++ = (u_char) (word & 0xff); if (--n) { nibbles = 0; word = 0; continue; } } else { if (zero == NULL) { digit = p; len4 = len; zero = addr; continue; } } return NGX_ERROR; } if (c == '.' && nibbles) { if (n < 2 || digit == NULL) { return NGX_ERROR; } word = ngx_inet_addr(digit, len4 - 1); if (word == INADDR_NONE) { return NGX_ERROR; } word = ntohl(word); *addr++ = (u_char) ((word >> 24) & 0xff); *addr++ = (u_char) ((word >> 16) & 0xff); n--; break; } if (++nibbles > 4) { return NGX_ERROR; } if (c >= '0' && c <= '9') { word = word * 16 + (c - '0'); continue; } c |= 0x20; if (c >= 'a' && c <= 'f') { word = word * 16 + (c - 'a') + 10; continue; } return NGX_ERROR; } if (nibbles == 0 && zero == NULL) { return NGX_ERROR; } *addr++ = (u_char) (word >> 8); *addr++ = (u_char) (word & 0xff); if (--n) { if (zero) { n *= 2; s = addr - 1; d = s + n; while (s >= zero) { *d-- = *s--; } ngx_memzero(zero, n); return NGX_OK; } } else { if (zero == NULL) { return NGX_OK; } } return NGX_ERROR; } #endif size_t ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port) { u_char *p; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) size_t n; struct sockaddr_in6 *sin6; #endif #if (NGX_HAVE_UNIX_DOMAIN) struct sockaddr_un *saun; #endif switch (sa->sa_family) { case AF_INET: sin = (struct sockaddr_in *) sa; p = (u_char *) &sin->sin_addr; if (port) { p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d", p[0], p[1], p[2], p[3], ntohs(sin->sin_port)); } else { p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); } return (p - text); #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) sa; n = 0; if (port) { text[n++] = '['; } n = ngx_inet6_ntop(sin6->sin6_addr.s6_addr, &text[n], len); if (port) { n = ngx_sprintf(&text[1 + n], "]:%d", ntohs(sin6->sin6_port)) - text; } return n; #endif #if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: saun = (struct sockaddr_un *) sa; /* we do not include trailing zero in address length */ return ngx_snprintf(text, len, "unix:%s%Z", saun->sun_path) - text - 1; #endif default: return 0; } } size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len) { u_char *p; switch (family) { case AF_INET: p = addr; return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]) - text; #if (NGX_HAVE_INET6) case AF_INET6: return ngx_inet6_ntop(addr, text, len); #endif default: return 0; } } #if (NGX_HAVE_INET6) size_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len) { u_char *dst; size_t max, n; ngx_uint_t i, zero, last; if (len < NGX_INET6_ADDRSTRLEN) { return 0; } zero = (ngx_uint_t) -1; last = (ngx_uint_t) -1; max = 1; n = 0; for (i = 0; i < 16; i += 2) { if (p[i] || p[i + 1]) { if (max < n) { zero = last; max = n; } n = 0; continue; } if (n++ == 0) { last = i; } } if (max < n) { zero = last; max = n; } dst = text; n = 16; if (zero == 0) { if ((max == 5 && p[10] == 0xff && p[11] == 0xff) || (max == 6) || (max == 7 && p[14] != 0 && p[15] != 1)) { n = 12; } *dst++ = ':'; } for (i = 0; i < n; i += 2) { if (i == zero) { *dst++ = ':'; i += (max - 1) * 2; continue; } dst = ngx_sprintf(dst, "%uxi", p[i] * 256 + p[i + 1]); if (i < 14) { *dst++ = ':'; } } if (n == 12) { dst = ngx_sprintf(dst, "%ud.%ud.%ud.%ud", p[12], p[13], p[14], p[15]); } return dst - text; } #endif ngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr) { u_char *addr, *mask, *last; size_t len; ngx_int_t shift; #if (NGX_HAVE_INET6) ngx_int_t rc; ngx_uint_t s, i; #endif addr = text->data; last = addr + text->len; mask = ngx_strlchr(addr, last, '/'); len = (mask ? mask : last) - addr; cidr->u.in.addr = ngx_inet_addr(addr, len); if (cidr->u.in.addr != INADDR_NONE) { cidr->family = AF_INET; if (mask == NULL) { cidr->u.in.mask = 0xffffffff; return NGX_OK; } #if (NGX_HAVE_INET6) } else if (ngx_inet6_addr(addr, len, cidr->u.in6.addr.s6_addr) == NGX_OK) { cidr->family = AF_INET6; if (mask == NULL) { ngx_memset(cidr->u.in6.mask.s6_addr, 0xff, 16); return NGX_OK; } #endif } else { return NGX_ERROR; } mask++; shift = ngx_atoi(mask, last - mask); if (shift == NGX_ERROR) { return NGX_ERROR; } switch (cidr->family) { #if (NGX_HAVE_INET6) case AF_INET6: addr = cidr->u.in6.addr.s6_addr; mask = cidr->u.in6.mask.s6_addr; rc = NGX_OK; for (i = 0; i < 16; i++) { s = (shift > 8) ? 8 : shift; shift -= s; mask[i] = (u_char) (0 - (1 << (8 - s))); if (addr[i] != (addr[i] & mask[i])) { rc = NGX_DONE; addr[i] &= mask[i]; } } return rc; #endif default: /* AF_INET */ if (shift) { cidr->u.in.mask = htonl((ngx_uint_t) (0 - (1 << (32 - shift)))); } else { /* x86 compilers use a shl instruction that shifts by modulo 32 */ cidr->u.in.mask = 0; } if (cidr->u.in.addr == (cidr->u.in.addr & cidr->u.in.mask)) { return NGX_OK; } cidr->u.in.addr &= cidr->u.in.mask; return NGX_DONE; } } ngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len) { in_addr_t inaddr; ngx_uint_t family; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct in6_addr inaddr6; struct sockaddr_in6 *sin6; /* * prevent MSVC8 waring: * potentially uninitialized local variable 'inaddr6' used */ ngx_memzero(inaddr6.s6_addr, sizeof(struct in6_addr)); #endif inaddr = ngx_inet_addr(text, len); if (inaddr != INADDR_NONE) { family = AF_INET; len = sizeof(struct sockaddr_in); #if (NGX_HAVE_INET6) } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) { family = AF_INET6; len = sizeof(struct sockaddr_in6); #endif } else { return NGX_DECLINED; } addr->sockaddr = ngx_pcalloc(pool, len); if (addr->sockaddr == NULL) { return NGX_ERROR; } addr->sockaddr->sa_family = (u_char) family; addr->socklen = len; switch (family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) addr->sockaddr; ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16); break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) addr->sockaddr; sin->sin_addr.s_addr = inaddr; break; } return NGX_OK; } ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u) { u_char *p; p = u->url.data; if (ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) { return ngx_parse_unix_domain_url(pool, u); } if ((p[0] == ':' || p[0] == '/') && !u->listen) { u->err = "invalid host"; return NGX_ERROR; } if (p[0] == '[') { return ngx_parse_inet6_url(pool, u); } return ngx_parse_inet_url(pool, u); } static ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u) { #if (NGX_HAVE_UNIX_DOMAIN) u_char *path, *uri, *last; size_t len; struct sockaddr_un *saun; len = u->url.len; path = u->url.data; path += 5; len -= 5; if (u->uri_part) { last = path + len; uri = ngx_strlchr(path, last, ':'); if (uri) { len = uri - path; uri++; u->uri.len = last - uri; u->uri.data = uri; } } if (len == 0) { u->err = "no path in the unix domain socket"; return NGX_ERROR; } u->host.len = len++; u->host.data = path; if (len > sizeof(saun->sun_path)) { u->err = "too long path in the unix domain socket"; return NGX_ERROR; } u->socklen = sizeof(struct sockaddr_un); saun = (struct sockaddr_un *) &u->sockaddr; saun->sun_family = AF_UNIX; (void) ngx_cpystrn((u_char *) saun->sun_path, path, len); u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); if (u->addrs == NULL) { return NGX_ERROR; } saun = ngx_pcalloc(pool, sizeof(struct sockaddr_un)); if (saun == NULL) { return NGX_ERROR; } u->family = AF_UNIX; u->naddrs = 1; saun->sun_family = AF_UNIX; (void) ngx_cpystrn((u_char *) saun->sun_path, path, len); u->addrs[0].sockaddr = (struct sockaddr *) saun; u->addrs[0].socklen = sizeof(struct sockaddr_un); u->addrs[0].name.len = len + 4; u->addrs[0].name.data = u->url.data; return NGX_OK; #else u->err = "the unix domain sockets are not supported on this platform"; return NGX_ERROR; #endif } static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) { u_char *p, *host, *port, *last, *uri, *args; size_t len; ngx_int_t n; struct hostent *h; struct sockaddr_in *sin; u->socklen = sizeof(struct sockaddr_in); sin = (struct sockaddr_in *) &u->sockaddr; sin->sin_family = AF_INET; u->family = AF_INET; host = u->url.data; last = host + u->url.len; port = ngx_strlchr(host, last, ':'); uri = ngx_strlchr(host, last, '/'); args = ngx_strlchr(host, last, '?'); if (args) { if (uri == NULL) { uri = args; } else if (args < uri) { uri = args; } } if (uri) { if (u->listen || !u->uri_part) { u->err = "invalid host"; return NGX_ERROR; } u->uri.len = last - uri; u->uri.data = uri; last = uri; if (uri < port) { port = NULL; } } if (port) { port++; len = last - port; if (len == 0) { u->err = "invalid port"; return NGX_ERROR; } n = ngx_atoi(port, len); if (n < 1 || n > 65536) { u->err = "invalid port"; return NGX_ERROR; } u->port = (in_port_t) n; sin->sin_port = htons((in_port_t) n); u->port_text.len = len; u->port_text.data = port; last = port - 1; } else { if (uri == NULL) { if (u->listen) { /* test value as port only */ n = ngx_atoi(host, last - host); if (n != NGX_ERROR) { if (n < 1 || n > 65536) { u->err = "invalid port"; return NGX_ERROR; } u->port = (in_port_t) n; sin->sin_port = htons((in_port_t) n); u->port_text.len = last - host; u->port_text.data = host; u->wildcard = 1; return NGX_OK; } } } u->no_port = 1; } len = last - host; if (len == 0) { u->err = "no host"; return NGX_ERROR; } if (len == 1 && *host == '*') { len = 0; } u->host.len = len; u->host.data = host; if (u->no_resolve) { return NGX_OK; } if (len) { sin->sin_addr.s_addr = ngx_inet_addr(host, len); if (sin->sin_addr.s_addr == INADDR_NONE) { p = ngx_alloc(++len, pool->log); if (p == NULL) { return NGX_ERROR; } (void) ngx_cpystrn(p, host, len); h = gethostbyname((const char *) p); ngx_free(p); if (h == NULL || h->h_addr_list[0] == NULL) { u->err = "host not found"; return NGX_ERROR; } sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[0]); } if (sin->sin_addr.s_addr == INADDR_ANY) { u->wildcard = 1; } } else { sin->sin_addr.s_addr = INADDR_ANY; u->wildcard = 1; } if (u->no_port) { u->port = u->default_port; sin->sin_port = htons(u->default_port); } if (u->listen) { return NGX_OK; } if (ngx_inet_resolve_host(pool, u) != NGX_OK) { return NGX_ERROR; } return NGX_OK; } static ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u) { #if (NGX_HAVE_INET6) u_char *p, *host, *port, *last, *uri; size_t len; ngx_int_t n; struct sockaddr_in6 *sin6; u->socklen = sizeof(struct sockaddr_in6); sin6 = (struct sockaddr_in6 *) &u->sockaddr; sin6->sin6_family = AF_INET6; host = u->url.data + 1; last = u->url.data + u->url.len; p = ngx_strlchr(host, last, ']'); if (p == NULL) { u->err = "invalid host"; return NGX_ERROR; } if (last - p) { port = p + 1; uri = ngx_strlchr(port, last, '/'); if (uri) { if (u->listen || !u->uri_part) { u->err = "invalid host"; return NGX_ERROR; } u->uri.len = last - uri; u->uri.data = uri; } if (*port == ':') { port++; len = last - port; if (len == 0) { u->err = "invalid port"; return NGX_ERROR; } n = ngx_atoi(port, len); if (n < 1 || n > 65536) { u->err = "invalid port"; return NGX_ERROR; } u->port = (in_port_t) n; sin6->sin6_port = htons((in_port_t) n); u->port_text.len = len; u->port_text.data = port; } else { u->no_port = 1; } } len = p - host; if (len == 0) { u->err = "no host"; return NGX_ERROR; } u->host.len = len; u->host.data = host; if (ngx_inet6_addr(host, len, sin6->sin6_addr.s6_addr) != NGX_OK) { u->err = "invalid IPv6 address"; return NGX_ERROR; } if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { u->wildcard = 1; } u->family = AF_INET6; if (u->no_resolve) { return NGX_OK; } if (u->no_port) { u->port = u->default_port; sin6->sin6_port = htons(u->default_port); } return NGX_OK; #else u->err = "the INET6 sockets are not supported on this platform"; return NGX_ERROR; #endif } ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u) { u_char *p, *host; size_t len; in_port_t port; in_addr_t in_addr; ngx_uint_t i; struct hostent *h; struct sockaddr_in *sin; /* AF_INET only */ port = htons(u->port); in_addr = ngx_inet_addr(u->host.data, u->host.len); if (in_addr == INADDR_NONE) { host = ngx_alloc(u->host.len + 1, pool->log); if (host == NULL) { return NGX_ERROR; } (void) ngx_cpystrn(host, u->host.data, u->host.len + 1); h = gethostbyname((char *) host); ngx_free(host); if (h == NULL || h->h_addr_list[0] == NULL) { u->err = "host not found"; return NGX_ERROR; } if (u->one_addr == 0) { for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ } } else { i = 1; } /* MP: ngx_shared_palloc() */ u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t)); if (u->addrs == NULL) { return NGX_ERROR; } u->naddrs = i; for (i = 0; i < u->naddrs; i++) { sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in)); if (sin == NULL) { return NGX_ERROR; } sin->sin_family = AF_INET; sin->sin_port = port; sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]); u->addrs[i].sockaddr = (struct sockaddr *) sin; u->addrs[i].socklen = sizeof(struct sockaddr_in); len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; p = ngx_pnalloc(pool, len); if (p == NULL) { return NGX_ERROR; } len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1); u->addrs[i].name.len = len; u->addrs[i].name.data = p; } } else { /* MP: ngx_shared_palloc() */ u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); if (u->addrs == NULL) { return NGX_ERROR; } sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in)); if (sin == NULL) { return NGX_ERROR; } u->naddrs = 1; sin->sin_family = AF_INET; sin->sin_port = port; sin->sin_addr.s_addr = in_addr; u->addrs[0].sockaddr = (struct sockaddr *) sin; u->addrs[0].socklen = sizeof(struct sockaddr_in); p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1); if (p == NULL) { return NGX_ERROR; } u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", &u->host, ntohs(port)) - p; u->addrs[0].name.data = p; } return NGX_OK; }