📄 cookies.c
字号:
/* According to netscape's specification, expiry time in the past means that discarding of a matching cookie is requested. */ if (cookie->expiry_time < cookies_now) cookie->discard_requested = 1; return 1; } else if (NAME_IS ("max-age")) { double maxage = -1; char *value_copy; if (!VALUE_NON_EMPTY) return 0; BOUNDED_TO_ALLOCA (value_b, value_e, value_copy); sscanf (value_copy, "%lf", &maxage); if (maxage == -1) /* something went wrong. */ return 0; cookie->permanent = 1; cookie->expiry_time = cookies_now + maxage; /* According to rfc2109, a cookie with max-age of 0 means that discarding of a matching cookie is requested. */ if (maxage == 0) cookie->discard_requested = 1; return 1; } else if (NAME_IS ("secure")) { /* ignore value completely */ cookie->secure = 1; return 1; } else /* Unrecognized attribute; ignore it. */ return 1;}#undef NAME_IS/* Returns non-zero for characters that are legal in the name of an attribute. This used to allow only alphanumerics, '-', and '_', but we need to be more lenient because a number of sites wants to use weirder attribute names. rfc2965 "informally specifies" attribute name (token) as "a sequence of non-special, non-white space characters". So we allow everything except the stuff we know could harm us. */#define ATTR_NAME_CHAR(c) ((c) > 32 && (c) < 127 \ && (c) != '"' && (c) != '=' \ && (c) != ';' && (c) != ',')/* Parse the contents of the `Set-Cookie' header. The header looks like this: name1=value1; name2=value2; ... Trailing semicolon is optional; spaces are allowed between all tokens. Additionally, values may be quoted. A new cookie is returned upon success, NULL otherwise. The specified CALLBACK function (normally `update_cookie_field' is used to update the fields of the newly created cookie structure. */static struct cookie *parse_set_cookies (const char *sc, int (*callback) (struct cookie *, const char *, const char *, const char *, const char *), int silent){ struct cookie *cookie = cookie_new (); /* #### Hand-written DFAs are no fun to debug. We'de be better off to rewrite this as an inline parser. */ enum { S_START, S_NAME, S_NAME_POST, S_VALUE_PRE, S_VALUE, S_QUOTED_VALUE, S_VALUE_TRAILSPACE, S_ATTR_ACTION, S_DONE, S_ERROR } state = S_START; const char *p = sc; char c; const char *name_b = NULL, *name_e = NULL; const char *value_b = NULL, *value_e = NULL; c = *p; while (state != S_DONE && state != S_ERROR) { switch (state) { case S_START: if (!c) state = S_DONE; else if (ISSPACE (c)) /* Strip all whitespace preceding the name. */ c = *++p; else if (ATTR_NAME_CHAR (c)) { name_b = p; state = S_NAME; } else /* empty attr name not allowed */ state = S_ERROR; break; case S_NAME: if (!c || c == ';' || c == '=' || ISSPACE (c)) { name_e = p; state = S_NAME_POST; } else if (ATTR_NAME_CHAR (c)) c = *++p; else state = S_ERROR; break; case S_NAME_POST: if (!c || c == ';') { value_b = value_e = NULL; if (c == ';') c = *++p; state = S_ATTR_ACTION; } else if (c == '=') { c = *++p; state = S_VALUE_PRE; } else if (ISSPACE (c)) /* Ignore space and keep the state. */ c = *++p; else state = S_ERROR; break; case S_VALUE_PRE: if (!c || c == ';') { value_b = value_e = p; if (c == ';') c = *++p; state = S_ATTR_ACTION; } else if (c == '"') { c = *++p; value_b = p; state = S_QUOTED_VALUE; } else if (ISSPACE (c)) c = *++p; else { value_b = p; value_e = NULL; state = S_VALUE; } break; case S_VALUE: if (!c || c == ';' || ISSPACE (c)) { value_e = p; state = S_VALUE_TRAILSPACE; } else { value_e = NULL; /* no trailing space */ c = *++p; } break; case S_QUOTED_VALUE: if (c == '"') { value_e = p; c = *++p; state = S_VALUE_TRAILSPACE; } else if (!c) state = S_ERROR; else c = *++p; break; case S_VALUE_TRAILSPACE: if (c == ';') { c = *++p; state = S_ATTR_ACTION; } else if (!c) state = S_ATTR_ACTION; else if (ISSPACE (c)) c = *++p; else state = S_VALUE; break; case S_ATTR_ACTION: { int legal = callback (cookie, name_b, name_e, value_b, value_e); if (!legal) { if (!silent) { char *name; BOUNDED_TO_ALLOCA (name_b, name_e, name); logprintf (LOG_NOTQUIET, _("Error in Set-Cookie, field `%s'"), escnonprint (name)); } state = S_ERROR; break; } state = S_START; } break; case S_DONE: case S_ERROR: /* handled by loop condition */ break; } } if (state == S_DONE) return cookie; delete_cookie (cookie); if (state != S_ERROR) abort (); if (!silent) logprintf (LOG_NOTQUIET, _("Syntax error in Set-Cookie: %s at position %d.\n"), escnonprint (sc), (int) (p - sc)); return NULL;}/* Sanity checks. These are important, otherwise it is possible for mailcious attackers to destroy important cookie information and/or violate your privacy. */#define REQUIRE_DIGITS(p) do { \ if (!ISDIGIT (*p)) \ return 0; \ for (++p; ISDIGIT (*p); p++) \ ; \} while (0)#define REQUIRE_DOT(p) do { \ if (*p++ != '.') \ return 0; \} while (0)/* Check whether ADDR matches <digits>.<digits>.<digits>.<digits>. We don't want to call network functions like inet_addr() because all we need is a check, preferrably one that is small, fast, and well-defined. */static intnumeric_address_p (const char *addr){ const char *p = addr; REQUIRE_DIGITS (p); /* A */ REQUIRE_DOT (p); /* . */ REQUIRE_DIGITS (p); /* B */ REQUIRE_DOT (p); /* . */ REQUIRE_DIGITS (p); /* C */ REQUIRE_DOT (p); /* . */ REQUIRE_DIGITS (p); /* D */ if (*p != '\0') return 0; return 1;}/* Check whether COOKIE_DOMAIN is an appropriate domain for HOST. Originally I tried to make the check compliant with rfc2109, but the sites deviated too often, so I had to fall back to "tail matching", as defined by the original Netscape's cookie spec. */static intcheck_domain_match (const char *cookie_domain, const char *host){ DEBUGP (("cdm: 1")); /* Numeric address requires exact match. It also requires HOST to be an IP address. */ if (numeric_address_p (cookie_domain)) return 0 == strcmp (cookie_domain, host); DEBUGP ((" 2")); /* For the sake of efficiency, check for exact match first. */ if (0 == strcasecmp (cookie_domain, host)) return 1; DEBUGP ((" 3")); /* HOST must match the tail of cookie_domain. */ if (!match_tail (host, cookie_domain, 1)) return 0; /* We know that COOKIE_DOMAIN is a subset of HOST; however, we must make sure that somebody is not trying to set the cookie for a subdomain shared by many entities. For example, "company.co.uk" must not be allowed to set a cookie for ".co.uk". On the other hand, "sso.redhat.de" should be able to set a cookie for ".redhat.de". The only marginally sane way to handle this I can think of is to reject on the basis of the length of the second-level domain name (but when the top-level domain is unknown), with the assumption that those of three or less characters could be reserved. For example: .co.org -> works because the TLD is known .co.uk -> doesn't work because "co" is only two chars long .com.au -> doesn't work because "com" is only 3 chars long .cnn.uk -> doesn't work because "cnn" is also only 3 chars long (ugh) .cnn.de -> doesn't work for the same reason (ugh!!) .abcd.de -> works because "abcd" is 4 chars long .img.cnn.de -> works because it's not trying to set the 2nd level domain .cnn.co.uk -> works for the same reason That should prevent misuse, while allowing reasonable usage. If someone knows of a better way to handle this, please let me know. */ { const char *p = cookie_domain; int dccount = 1; /* number of domain components */ int ldcl = 0; /* last domain component length */ int nldcl = 0; /* next to last domain component length */ int out; if (*p == '.') /* Ignore leading period in this calculation. */ ++p; DEBUGP ((" 4")); for (out = 0; !out; p++) switch (*p) { case '\0': out = 1; break; case '.': if (ldcl == 0) /* Empty domain component found -- the domain is invalid. */ return 0; if (*(p + 1) == '\0') { /* Tolerate trailing '.' by not treating the domain as one ending with an empty domain component. */ out = 1; break; } nldcl = ldcl; ldcl = 0; ++dccount; break; default: ++ldcl; } DEBUGP ((" 5")); if (dccount < 2) return 0; DEBUGP ((" 6")); if (dccount == 2) { int i; int known_toplevel = 0; static const char *known_toplevel_domains[] = { ".com", ".edu", ".net", ".org", ".gov", ".mil", ".int" }; for (i = 0; i < countof (known_toplevel_domains); i++) if (match_tail (cookie_domain, known_toplevel_domains[i], 1)) { known_toplevel = 1; break; } if (!known_toplevel && nldcl <= 3) return 0; } } DEBUGP ((" 7")); /* Don't allow the host "foobar.com" to set a cookie for domain "bar.com". */ if (*cookie_domain != '.') { int dlen = strlen (cookie_domain); int hlen = strlen (host); /* cookie host: hostname.foobar.com */ /* desired domain: bar.com */ /* '.' must be here in host-> ^ */ if (hlen > dlen && host[hlen - dlen - 1] != '.')
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -