url.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:11k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: url.c,v 1.113.2.1 1999/02/12 19:38:32 wessels Exp $
  3.  *
  4.  * DEBUG: section 23    URL Parsing
  5.  * AUTHOR: Duane Wessels
  6.  *
  7.  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
  8.  * ----------------------------------------------------------
  9.  *
  10.  *  Squid is the result of efforts by numerous individuals from the
  11.  *  Internet community.  Development is led by Duane Wessels of the
  12.  *  National Laboratory for Applied Network Research and funded by the
  13.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  14.  *  Duane Wessels and the University of California San Diego.  Please
  15.  *  see the COPYRIGHT file for full details.  Squid incorporates
  16.  *  software developed and/or copyrighted by other sources.  Please see
  17.  *  the CREDITS file for full details.
  18.  *
  19.  *  This program is free software; you can redistribute it and/or modify
  20.  *  it under the terms of the GNU General Public License as published by
  21.  *  the Free Software Foundation; either version 2 of the License, or
  22.  *  (at your option) any later version.
  23.  *  
  24.  *  This program is distributed in the hope that it will be useful,
  25.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *  GNU General Public License for more details.
  28.  *  
  29.  *  You should have received a copy of the GNU General Public License
  30.  *  along with this program; if not, write to the Free Software
  31.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  32.  *
  33.  */
  34. #include "squid.h"
  35. const char *RequestMethodStr[] =
  36. {
  37.     "NONE",
  38.     "GET",
  39.     "POST",
  40.     "PUT",
  41.     "HEAD",
  42.     "CONNECT",
  43.     "TRACE",
  44.     "PURGE"
  45. };
  46. const char *ProtocolStr[] =
  47. {
  48.     "NONE",
  49.     "http",
  50.     "ftp",
  51.     "gopher",
  52.     "wais",
  53.     "cache_object",
  54.     "icp",
  55. #if USE_HTCP
  56.     "htcp",
  57. #endif
  58.     "urn",
  59.     "whois",
  60.     "internal",
  61.     "https",
  62.     "TOTAL"
  63. };
  64. static request_t *urnParse(method_t method, char *urn);
  65. static const char *const valid_hostname_chars =
  66. #if ALLOW_HOSTNAME_UNDERSCORES
  67. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  68. "abcdefghijklmnopqrstuvwxyz"
  69. "0123456789-._";
  70. #else
  71. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  72. "abcdefghijklmnopqrstuvwxyz"
  73. "0123456789-.";
  74. #endif
  75. /* convert %xx in url string to a character 
  76.  * Allocate a new string and return a pointer to converted string */
  77. char *
  78. url_convert_hex(char *org_url, int allocate)
  79. {
  80.     static char code[] = "00";
  81.     char *url = NULL;
  82.     char *s = NULL;
  83.     char *t = NULL;
  84.     url = allocate ? (char *) xstrdup(org_url) : org_url;
  85.     if ((int) strlen(url) < 3 || !strchr(url, '%'))
  86. return url;
  87.     for (s = t = url; *(s + 2); s++) {
  88. if (*s == '%') {
  89.     code[0] = *(++s);
  90.     code[1] = *(++s);
  91.     *t++ = (char) strtol(code, NULL, 16);
  92. } else {
  93.     *t++ = *s;
  94. }
  95.     }
  96.     do {
  97. *t++ = *s;
  98.     } while (*s++);
  99.     return url;
  100. }
  101. void
  102. urlInitialize(void)
  103. {
  104.     debug(23, 5) ("urlInitialize: Initializing...n");
  105.     assert(sizeof(ProtocolStr) == (PROTO_MAX + 1) * sizeof(char *));
  106.     memset(&null_request_flags, '', sizeof(null_request_flags));
  107. }
  108. method_t
  109. urlParseMethod(const char *s)
  110. {
  111.     if (strcasecmp(s, "GET") == 0) {
  112. return METHOD_GET;
  113.     } else if (strcasecmp(s, "POST") == 0) {
  114. return METHOD_POST;
  115.     } else if (strcasecmp(s, "PUT") == 0) {
  116. return METHOD_PUT;
  117.     } else if (strcasecmp(s, "HEAD") == 0) {
  118. return METHOD_HEAD;
  119.     } else if (strcasecmp(s, "CONNECT") == 0) {
  120. return METHOD_CONNECT;
  121.     } else if (strcasecmp(s, "TRACE") == 0) {
  122. return METHOD_TRACE;
  123.     } else if (strcasecmp(s, "PURGE") == 0) {
  124. return METHOD_PURGE;
  125.     }
  126.     return METHOD_NONE;
  127. }
  128. protocol_t
  129. urlParseProtocol(const char *s)
  130. {
  131.     /* test common stuff first */
  132.     if (strcasecmp(s, "http") == 0)
  133. return PROTO_HTTP;
  134.     if (strcasecmp(s, "ftp") == 0)
  135. return PROTO_FTP;
  136.     if (strcasecmp(s, "https") == 0)
  137. return PROTO_HTTPS;
  138.     if (strcasecmp(s, "file") == 0)
  139. return PROTO_FTP;
  140.     if (strcasecmp(s, "gopher") == 0)
  141. return PROTO_GOPHER;
  142.     if (strcasecmp(s, "wais") == 0)
  143. return PROTO_WAIS;
  144.     if (strcasecmp(s, "cache_object") == 0)
  145. return PROTO_CACHEOBJ;
  146.     if (strcasecmp(s, "urn") == 0)
  147. return PROTO_URN;
  148.     if (strcasecmp(s, "whois") == 0)
  149. return PROTO_WHOIS;
  150.     if (strcasecmp(s, "internal") == 0)
  151. return PROTO_INTERNAL;
  152.     return PROTO_NONE;
  153. }
  154. int
  155. urlDefaultPort(protocol_t p)
  156. {
  157.     switch (p) {
  158.     case PROTO_HTTP:
  159. return 80;
  160.     case PROTO_HTTPS:
  161. return 443;
  162.     case PROTO_FTP:
  163. return 21;
  164.     case PROTO_GOPHER:
  165. return 70;
  166.     case PROTO_WAIS:
  167. return 210;
  168.     case PROTO_CACHEOBJ:
  169.     case PROTO_INTERNAL:
  170. return CACHE_HTTP_PORT;
  171.     case PROTO_WHOIS:
  172. return 43;
  173.     default:
  174. return 0;
  175.     }
  176. }
  177. request_t *
  178. urlParse(method_t method, char *url)
  179. {
  180.     LOCAL_ARRAY(char, proto, MAX_URL);
  181.     LOCAL_ARRAY(char, login, MAX_URL);
  182.     LOCAL_ARRAY(char, host, MAX_URL);
  183.     LOCAL_ARRAY(char, urlpath, MAX_URL);
  184.     request_t *request = NULL;
  185.     char *t = NULL;
  186.     int port;
  187.     protocol_t protocol = PROTO_NONE;
  188.     int l;
  189.     proto[0] = host[0] = urlpath[0] = login[0] = '';
  190.     if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) {
  191. /* terminate so it doesn't overflow other buffers */
  192. *(url + (MAX_URL >> 1)) = '';
  193. debug(23, 1) ("urlParse: URL too large (%d bytes)n", l);
  194. return NULL;
  195.     }
  196.     if (method == METHOD_CONNECT) {
  197. port = CONNECT_PORT;
  198. if (sscanf(url, "%[^:]:%d", host, &port) < 1)
  199.     return NULL;
  200.     } else if (!strncmp(url, "urn:", 4)) {
  201. return urnParse(method, url);
  202.     } else {
  203. if (sscanf(url, "%[^:]://%[^/]%[^rn]", proto, host, urlpath) < 2)
  204.     return NULL;
  205. protocol = urlParseProtocol(proto);
  206. port = urlDefaultPort(protocol);
  207. /* Is there any login informaiton? */
  208. if ((t = strrchr(host, '@'))) {
  209.     strcpy(login, host);
  210.     t = strrchr(login, '@');
  211.     *t = 0;
  212.     strcpy(host, t + 1);
  213. }
  214. if ((t = strrchr(host, ':'))) {
  215.     *t++ = '';
  216.     if (*t != '')
  217. port = atoi(t);
  218. }
  219.     }
  220.     for (t = host; *t; t++)
  221. *t = xtolower(*t);
  222.     if (strspn(host, valid_hostname_chars) != strlen(host)) {
  223. debug(23, 1) ("urlParse: Illegal character in hostname '%s'n", host);
  224. return NULL;
  225.     }
  226.     /* remove trailing dots from hostnames */
  227.     while ((l = strlen(host)) > 0 && host[--l] == '.')
  228. host[l] = '';
  229.     if (Config.appendDomain && !strchr(host, '.'))
  230. strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN);
  231.     if (port == 0) {
  232. debug(23, 3) ("urlParse: Invalid port == 0n");
  233. return NULL;
  234.     }
  235. #ifdef HARDCODE_DENY_PORTS
  236.     /* These ports are filtered in the default squid.conf, but
  237.      * maybe someone wants them hardcoded... */
  238.     if (port == 7 || port == 9 || port = 19) {
  239. debug(23, 0) ("urlParse: Deny access to port %dn", port);
  240. return NULL;
  241.     }
  242. #endif
  243.     if (stringHasWhitespace(urlpath)) {
  244. debug(23, 2) ("urlParse: URI has whitespace: {%s}n", url);
  245. switch (Config.uri_whitespace) {
  246. case URI_WHITESPACE_DENY:
  247.     return NULL;
  248. case URI_WHITESPACE_ALLOW:
  249.     break;
  250. case URI_WHITESPACE_ENCODE:
  251.     t = rfc1738_escape(urlpath);
  252.     xstrncpy(urlpath, t, MAX_URL);
  253.     break;
  254. case URI_WHITESPACE_CHOP:
  255.     *(urlpath + strcspn(urlpath, w_space)) = '';
  256.     break;
  257. }
  258.     }
  259.     request = requestCreate(method, protocol, urlpath);
  260.     xstrncpy(request->host, host, SQUIDHOSTNAMELEN);
  261.     xstrncpy(request->login, login, MAX_LOGIN_SZ);
  262.     request->port = (u_short) port;
  263.     return request;
  264. }
  265. static request_t *
  266. urnParse(method_t method, char *urn)
  267. {
  268.     debug(50, 5) ("urnParse: %sn", urn);
  269.     return requestCreate(method, PROTO_URN, urn + 4);
  270. }
  271. const char *
  272. urlCanonical(request_t * request)
  273. {
  274.     LOCAL_ARRAY(char, portbuf, 32);
  275.     LOCAL_ARRAY(char, urlbuf, MAX_URL);
  276.     if (request->canonical)
  277. return request->canonical;
  278.     if (request->protocol == PROTO_URN) {
  279. snprintf(urlbuf, MAX_URL, "urn:%s", strBuf(request->urlpath));
  280.     } else {
  281. switch (request->method) {
  282. case METHOD_CONNECT:
  283.     snprintf(urlbuf, MAX_URL, "%s:%d", request->host, request->port);
  284.     break;
  285. default:
  286.     portbuf[0] = '';
  287.     if (request->port != urlDefaultPort(request->protocol))
  288. snprintf(portbuf, 32, ":%d", request->port);
  289.     snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s%s",
  290. ProtocolStr[request->protocol],
  291. request->login,
  292. *request->login ? "@" : null_string,
  293. request->host,
  294. portbuf,
  295. strBuf(request->urlpath));
  296.     break;
  297. }
  298.     }
  299.     return (request->canonical = xstrdup(urlbuf));
  300. }
  301. char *
  302. urlCanonicalClean(const request_t * request)
  303. {
  304.     LOCAL_ARRAY(char, buf, MAX_URL);
  305.     LOCAL_ARRAY(char, portbuf, 32);
  306.     LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1);
  307.     char *t;
  308.     if (request->protocol == PROTO_URN) {
  309. snprintf(buf, MAX_URL, "urn:%s", strBuf(request->urlpath));
  310.     } else {
  311. switch (request->method) {
  312. case METHOD_CONNECT:
  313.     snprintf(buf, MAX_URL, "%s:%d", request->host, request->port);
  314.     break;
  315. default:
  316.     portbuf[0] = '';
  317.     if (request->port != urlDefaultPort(request->protocol))
  318. snprintf(portbuf, 32, ":%d", request->port);
  319.     loginbuf[0] = '';
  320.     if ((int) strlen(request->login) > 0) {
  321. strcpy(loginbuf, request->login);
  322. if ((t = strchr(loginbuf, ':')))
  323.     *t = '';
  324. strcat(loginbuf, "@");
  325.     }
  326.     snprintf(buf, MAX_URL, "%s://%s%s%s%s",
  327. ProtocolStr[request->protocol],
  328. loginbuf,
  329. request->host,
  330. portbuf,
  331. strBuf(request->urlpath));
  332.     /*
  333.      * strip arguments AFTER a question-mark
  334.      */
  335.     if (Config.onoff.strip_query_terms)
  336. if ((t = strchr(buf, '?')))
  337.     *(++t) = '';
  338.     break;
  339. }
  340.     }
  341.     if (stringHasWhitespace(buf))
  342. xstrncpy(buf, rfc1738_escape(buf), MAX_URL);
  343.     return buf;
  344. }
  345. int
  346. matchDomainName(const char *domain, const char *host)
  347. {
  348.     int offset;
  349.     if ((offset = strlen(host) - strlen(domain)) < 0)
  350. return 0; /* host too short */
  351.     if (strcasecmp(domain, host + offset) != 0)
  352. return 0; /* no match at all */
  353.     if (*domain == '.')
  354. return 1;
  355.     if (offset == 0)
  356. return 1;
  357.     if (*(host + offset - 1) == '.')
  358. return 1;
  359.     return 0;
  360. }
  361. int
  362. urlCheckRequest(const request_t * r)
  363. {
  364.     int rc = 0;
  365.     /* protocol "independent" methods */
  366.     if (r->method == METHOD_CONNECT)
  367. return 1;
  368.     if (r->method == METHOD_TRACE)
  369. return 1;
  370.     if (r->method == METHOD_PURGE)
  371. return 1;
  372.     /* does method match the protocol? */
  373.     switch (r->protocol) {
  374.     case PROTO_URN:
  375.     case PROTO_HTTP:
  376.     case PROTO_HTTPS:
  377.     case PROTO_CACHEOBJ:
  378. rc = 1;
  379. break;
  380.     case PROTO_FTP:
  381. if (r->method == METHOD_PUT)
  382.     rc = 1;
  383.     case PROTO_GOPHER:
  384.     case PROTO_WAIS:
  385.     case PROTO_WHOIS:
  386. if (r->method == METHOD_GET)
  387.     rc = 1;
  388. else if (r->method == METHOD_HEAD)
  389.     rc = 1;
  390. break;
  391.     default:
  392. break;
  393.     }
  394.     return rc;
  395. }
  396. /*
  397.  * Quick-n-dirty host extraction from a URL.  Steps:
  398.  *      Look for a colon
  399.  *      Skip any '/' after the colon
  400.  *      Copy the next SQUID_MAXHOSTNAMELEN bytes to host[]
  401.  *      Look for an ending '/' or ':' and terminate
  402.  *      Look for login info preceeded by '@'
  403.  */
  404. char *
  405. urlHostname(const char *url)
  406. {
  407.     LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN);
  408.     char *t;
  409.     host[0] = '';
  410.     if (NULL == (t = strchr(url, ':')))
  411. return NULL;
  412.     t++;
  413.     while (*t != '' && *t == '/')
  414. t++;
  415.     xstrncpy(host, t, SQUIDHOSTNAMELEN);
  416.     if ((t = strchr(host, '/')))
  417. *t = '';
  418.     if ((t = strchr(host, ':')))
  419. *t = '';
  420.     if ((t = strrchr(host, '@'))) {
  421. t++;
  422. xmemmove(host, t, strlen(t) + 1);
  423.     }
  424.     return host;
  425. }