1*4ee8f2c5SMilton Miller#!/usr/bin/awk -f 2*4ee8f2c5SMilton Miller# 3*4ee8f2c5SMilton Miller# A Minimal HTTP/1.1 server to redirect http URIs to https 4*4ee8f2c5SMilton Miller 5*4ee8f2c5SMilton MillerBEGIN { 6*4ee8f2c5SMilton Miller CRLF = "\r\n" 7*4ee8f2c5SMilton Miller dquote = "\"" 8*4ee8f2c5SMilton Miller 9*4ee8f2c5SMilton Miller methods["GET"] = 1 10*4ee8f2c5SMilton Miller methods["HEAD"] = 1 11*4ee8f2c5SMilton Miller 12*4ee8f2c5SMilton Miller errors[400] = "400 Bad Request" 13*4ee8f2c5SMilton Miller errors[404] = "404 Not Found" 14*4ee8f2c5SMilton Miller errors[500] = "500 Internal Server Error" 15*4ee8f2c5SMilton Miller errors[501] = "501 Not Implemented" 16*4ee8f2c5SMilton Miller errors[505] = "505 HTTP Version Not Supported" 17*4ee8f2c5SMilton Miller msgtxt[505] = "HTTP/1.1 only" 18*4ee8f2c5SMilton Miller 19*4ee8f2c5SMilton Miller # Only forward these resources to the designated paths over https 20*4ee8f2c5SMilton Miller https_resources["/"] = "/" 21*4ee8f2c5SMilton Miller} 22*4ee8f2c5SMilton Miller 23*4ee8f2c5SMilton Miller# Strip trailing CR(\r) before LF(\n) RFC2616 19.3 24*4ee8f2c5SMilton Miller/\r$/ { sub(/\r$/, "") } 25*4ee8f2c5SMilton Miller 26*4ee8f2c5SMilton Miller# The first line is the HTTP request. 27*4ee8f2c5SMilton Millermethod == "" { 28*4ee8f2c5SMilton Miller if ($0 == "") 29*4ee8f2c5SMilton Miller next 30*4ee8f2c5SMilton Miller 31*4ee8f2c5SMilton Miller method = $1 32*4ee8f2c5SMilton Miller request_uri = $2 33*4ee8f2c5SMilton Miller version = $3 34*4ee8f2c5SMilton Miller 35*4ee8f2c5SMilton Miller validate_request() 36*4ee8f2c5SMilton Miller 37*4ee8f2c5SMilton Miller # headers start on the next line 38*4ee8f2c5SMilton Miller next 39*4ee8f2c5SMilton Miller} 40*4ee8f2c5SMilton Miller 41*4ee8f2c5SMilton Miller# a header continuation line RFC2616 4.2 42*4ee8f2c5SMilton Miller/^[ \t]+/ { 43*4ee8f2c5SMilton Miller # Replace leading, trailing whitespace with space below 44*4ee8f2c5SMilton Miller sub(/[ \t]*$/, "") 45*4ee8f2c5SMilton Miller sub(/^[ \t]*/, "") 46*4ee8f2c5SMilton Miller trace("extend header >"header"< with content >"$0"<") 47*4ee8f2c5SMilton Miller 48*4ee8f2c5SMilton Miller headers[header] = headers[header] " " $0 49*4ee8f2c5SMilton Miller next 50*4ee8f2c5SMilton Miller} 51*4ee8f2c5SMilton Miller 52*4ee8f2c5SMilton Miller# Header lines start with a token and have a : seperator. Implied LWS is 53*4ee8f2c5SMilton Miller# allowed around the : seperator. LWS at the beginning and end can be removed. 54*4ee8f2c5SMilton Millermatch($0, /[ \t]*:[ \t]*/) { 55*4ee8f2c5SMilton Miller header = substr($0, 1, RSTART - 1) 56*4ee8f2c5SMilton Miller content = substr($0, RSTART + RLENGTH) 57*4ee8f2c5SMilton Miller sub(/[ \t]*$/, "", content) 58*4ee8f2c5SMilton Miller 59*4ee8f2c5SMilton Miller # Field names are a single token. LWS is impled allowed at the 60*4ee8f2c5SMilton Miller # : seperator. Any beginning or trailing LWS is not significant. 61*4ee8f2c5SMilton Miller if (!is_token(header)) 62*4ee8f2c5SMilton Miller respond_error(400) 63*4ee8f2c5SMilton Miller 64*4ee8f2c5SMilton Miller # Headers are case insensitive, so normalize token to upper case. 65*4ee8f2c5SMilton Miller header = toupper(header) 66*4ee8f2c5SMilton Miller 67*4ee8f2c5SMilton Miller # RFC2616 4.2 multiple instances of a headers is only valid for for 68*4ee8f2c5SMilton Miller # comma separated lists. Remove any trailing LWS, add ", " seperator. 69*4ee8f2c5SMilton Miller prior = "" 70*4ee8f2c5SMilton Miller if (header in headers) 71*4ee8f2c5SMilton Miller prior = headers[header] ", " 72*4ee8f2c5SMilton Miller headers[header] = prior content 73*4ee8f2c5SMilton Miller 74*4ee8f2c5SMilton Miller trace("found header >"header"< with content >"headers[header]"<") 75*4ee8f2c5SMilton Miller 76*4ee8f2c5SMilton Miller next 77*4ee8f2c5SMilton Miller} 78*4ee8f2c5SMilton Miller 79*4ee8f2c5SMilton Miller# A blank line marks the end of the headers. 80*4ee8f2c5SMilton Miller/^$/ { 81*4ee8f2c5SMilton Miller # Could read request body here but we don't care. 82*4ee8f2c5SMilton Miller trace("end of request headers") 83*4ee8f2c5SMilton Miller validate_request() 84*4ee8f2c5SMilton Miller 85*4ee8f2c5SMilton Miller validate_uri(request_uri, split_uri) 86*4ee8f2c5SMilton Miller host = find_host() 87*4ee8f2c5SMilton Miller path = split_uri["path"] 88*4ee8f2c5SMilton Miller validate_path_and_respond(host, path) 89*4ee8f2c5SMilton Miller 90*4ee8f2c5SMilton Miller next 91*4ee8f2c5SMilton Miller} 92*4ee8f2c5SMilton Miller 93*4ee8f2c5SMilton Miller# Should never get here: in headers a line without an indent nor a : is invalid. 94*4ee8f2c5SMilton Miller{ 95*4ee8f2c5SMilton Miller trace("Unparsed header line : >" $0 "<") 96*4ee8f2c5SMilton Miller 97*4ee8f2c5SMilton Miller header = $0 98*4ee8f2c5SMilton Miller headers[header] = "" 99*4ee8f2c5SMilton Miller 100*4ee8f2c5SMilton Miller # check HTTP version before bad request error response 101*4ee8f2c5SMilton Miller validate_request() 102*4ee8f2c5SMilton Miller respond_error(400) 103*4ee8f2c5SMilton Miller next 104*4ee8f2c5SMilton Miller} 105*4ee8f2c5SMilton Miller 106*4ee8f2c5SMilton Miller############################################################ 107*4ee8f2c5SMilton Miller 108*4ee8f2c5SMilton Millerfunction validate_request() 109*4ee8f2c5SMilton Miller{ 110*4ee8f2c5SMilton Miller trace("version >"version"<") 111*4ee8f2c5SMilton Miller trace("uri >"request_uri"<") 112*4ee8f2c5SMilton Miller trace("method >"method"<") 113*4ee8f2c5SMilton Miller if (version !~ /HTTP\/0*1[.][0-9]+$/) # Support leading 0s, two halves 114*4ee8f2c5SMilton Miller respond_error(505) # Version Not Supported 115*4ee8f2c5SMilton Miller if (bad_uric(request_uri)) 116*4ee8f2c5SMilton Miller respond_error(400) # Bad Request (bogus encoding) 117*4ee8f2c5SMilton Miller if (!method in methods) 118*4ee8f2c5SMilton Miller respond_error(501) # Not Implemented 119*4ee8f2c5SMilton Miller} 120*4ee8f2c5SMilton Miller 121*4ee8f2c5SMilton Millerfunction validate_uri(request_uri, split_uri) 122*4ee8f2c5SMilton Miller{ 123*4ee8f2c5SMilton Miller split_url_components(request_uri, split_uri) 124*4ee8f2c5SMilton Miller trace(dump_split_url(split_uri)) 125*4ee8f2c5SMilton Miller 126*4ee8f2c5SMilton Miller if (!is_http_request_uri(split_uri)) 127*4ee8f2c5SMilton Miller respond_error(400) # Bad Request (didn't parse) 128*4ee8f2c5SMilton Miller} 129*4ee8f2c5SMilton Miller 130*4ee8f2c5SMilton Millerfunction find_host() 131*4ee8f2c5SMilton Miller{ 132*4ee8f2c5SMilton Miller # RFC2616 5.2 133*4ee8f2c5SMilton Miller if (!("HOST" in headers)) 134*4ee8f2c5SMilton Miller respond_error(400) 135*4ee8f2c5SMilton Miller 136*4ee8f2c5SMilton Miller host = headers["HOST"] 137*4ee8f2c5SMilton Miller trace("initial host is >" host "<") 138*4ee8f2c5SMilton Miller if ("host" in split_uri) 139*4ee8f2c5SMilton Miller host = split_uri["host"] 140*4ee8f2c5SMilton Miller else if (match(host, /:[0-9]*$/)) 141*4ee8f2c5SMilton Miller # RFC 2616 14.23 Host header is host:port of URI 142*4ee8f2c5SMilton Miller # RFC 2616 3.2.2 port may be not given or empty 143*4ee8f2c5SMilton Miller host = substr(host, 1, RSTART - 1) 144*4ee8f2c5SMilton Miller trace("prioritized host is >" host "<") 145*4ee8f2c5SMilton Miller 146*4ee8f2c5SMilton Miller # A very relaxed check for domainlabel or IPv4. 147*4ee8f2c5SMilton Miller if (host !~ /^[0-9a-zA-Z.-]+$/) 148*4ee8f2c5SMilton Miller respond_error(400) 149*4ee8f2c5SMilton Miller trace("host passed regex") 150*4ee8f2c5SMilton Miller 151*4ee8f2c5SMilton Miller return host 152*4ee8f2c5SMilton Miller} 153*4ee8f2c5SMilton Miller 154*4ee8f2c5SMilton Millerfunction validate_path_and_respond(host, path) 155*4ee8f2c5SMilton Miller{ 156*4ee8f2c5SMilton Miller lookup = unescape(path) 157*4ee8f2c5SMilton Miller 158*4ee8f2c5SMilton Miller # URIs must be unescaped before compare, but forwarded unmodified 159*4ee8f2c5SMilton Miller trace("lookup path is >" lookup "<") 160*4ee8f2c5SMilton Miller 161*4ee8f2c5SMilton Miller # Translate our whitelisted URI 162*4ee8f2c5SMilton Miller if (lookup in https_resources) { 163*4ee8f2c5SMilton Miller newpath = "https://" host https_resources[lookup] 164*4ee8f2c5SMilton Miller trace("Redirecting to >" newpath "<\n") 165*4ee8f2c5SMilton Miller response = "308 Permanent Redirect" 166*4ee8f2c5SMilton Miller reason = "Access with a https:// URL" 167*4ee8f2c5SMilton Miller content = response CRLF newpath CRLF CRLF reason CRLF 168*4ee8f2c5SMilton Miller respond_and_exit(response, content, newpath) 169*4ee8f2c5SMilton Miller } 170*4ee8f2c5SMilton Miller 171*4ee8f2c5SMilton Miller # Rather than be an open redirector, return Not Found 172*4ee8f2c5SMilton Miller respond_error(404) # Not Found 173*4ee8f2c5SMilton Miller 174*4ee8f2c5SMilton Miller # get noisy response if we didn't exit above 175*4ee8f2c5SMilton Miller trace("Failed to exit after response!") 176*4ee8f2c5SMilton Miller exit 3 177*4ee8f2c5SMilton Miller} 178*4ee8f2c5SMilton Miller 179*4ee8f2c5SMilton Millerfunction is_token(token) 180*4ee8f2c5SMilton Miller{ 181*4ee8f2c5SMilton Miller # US ASCII (0-127) excluding CTL (000-037, 177, SP (040), seperators 182*4ee8f2c5SMilton Miller if (match(token, /[^\041-\176]/) || 183*4ee8f2c5SMilton Miller match(token, /[()<>@,;:\/[]?=\{\}" \t/)) 184*4ee8f2c5SMilton Miller return 0 185*4ee8f2c5SMilton Miller 186*4ee8f2c5SMilton Miller return 1 187*4ee8f2c5SMilton Miller} 188*4ee8f2c5SMilton Miller 189*4ee8f2c5SMilton Miller# unreserved, reserved, or encoded. 190*4ee8f2c5SMilton Millerfunction bad_uric(URI) 191*4ee8f2c5SMilton Miller{ 192*4ee8f2c5SMilton Miller # hide encoded 193*4ee8f2c5SMilton Miller gsub(/%[0-9a-fA-F][0-9a-fA-F]/, "", URI) 194*4ee8f2c5SMilton Miller 195*4ee8f2c5SMilton Miller # fail if remaining characters are not in (mark alpha numeric reserved) 196*4ee8f2c5SMilton Miller if (URI ~ /[^-_.!~*'()a-zA-Z0-9";\/?:@&=+$,]/) 197*4ee8f2c5SMilton Miller return 1 198*4ee8f2c5SMilton Miller return 0 199*4ee8f2c5SMilton Miller} 200*4ee8f2c5SMilton Miller 201*4ee8f2c5SMilton Miller# We only expect a few chars so call index vs building table hex2int[chr] 202*4ee8f2c5SMilton Millerfunction hex2dec(chr) 203*4ee8f2c5SMilton Miller{ 204*4ee8f2c5SMilton Miller v = index("0123456789abcdef", tolower(chr)) 205*4ee8f2c5SMilton Miller if (v) 206*4ee8f2c5SMilton Miller return v - 1 207*4ee8f2c5SMilton Miller 208*4ee8f2c5SMilton Miller trace("bad hex2dec character >" chr "<") 209*4ee8f2c5SMilton Miller # bad_uric should have caught input 210*4ee8f2c5SMilton Miller respond_error(500) # Internal Server Error 211*4ee8f2c5SMilton Miller} 212*4ee8f2c5SMilton Miller 213*4ee8f2c5SMilton Miller# Do % hex hex -> code replacement 214*4ee8f2c5SMilton Millerfunction unescape(input, out) 215*4ee8f2c5SMilton Miller{ 216*4ee8f2c5SMilton Miller i = index(input, "%") 217*4ee8f2c5SMilton Miller 218*4ee8f2c5SMilton Miller if (i == 0) 219*4ee8f2c5SMilton Miller return input 220*4ee8f2c5SMilton Miller 221*4ee8f2c5SMilton Miller out = "" 222*4ee8f2c5SMilton Miller while (i) { 223*4ee8f2c5SMilton Miller code = (hex2dec(substr(input, i + 1, 1)) * 16 + \ 224*4ee8f2c5SMilton Miller hex2dec(substr(input, i + 2, 1))) 225*4ee8f2c5SMilton Miller out = out substr(input, 1, i - 1) sprintf("%c", code) 226*4ee8f2c5SMilton Miller input = substr(input, i + 3) 227*4ee8f2c5SMilton Miller i = index(input, "%") 228*4ee8f2c5SMilton Miller } 229*4ee8f2c5SMilton Miller return out input 230*4ee8f2c5SMilton Miller} 231*4ee8f2c5SMilton Miller 232*4ee8f2c5SMilton Miller# With cues from RFC2396 appendix B etal 233*4ee8f2c5SMilton Millerfunction split_url_components(url, components) 234*4ee8f2c5SMilton Miller{ 235*4ee8f2c5SMilton Miller if (match(url, /#/)) { 236*4ee8f2c5SMilton Miller components["frag"] = substr(url, RSTART + 1) 237*4ee8f2c5SMilton Miller url = substr(url, 1, RSTART - 1) 238*4ee8f2c5SMilton Miller } 239*4ee8f2c5SMilton Miller 240*4ee8f2c5SMilton Miller if (match(url, /\?/)) { 241*4ee8f2c5SMilton Miller components["query"] = substr(url, RSTART + 1) 242*4ee8f2c5SMilton Miller url = substr(url, 1, RSTART - 1) 243*4ee8f2c5SMilton Miller } 244*4ee8f2c5SMilton Miller 245*4ee8f2c5SMilton Miller if (match(url, /^[^:\/?#]+:/)) { 246*4ee8f2c5SMilton Miller components["scheme"] = substr(url, 1, RLENGTH - 1) ; 247*4ee8f2c5SMilton Miller url = substr(url, RLENGTH + 1) 248*4ee8f2c5SMilton Miller } 249*4ee8f2c5SMilton Miller 250*4ee8f2c5SMilton Miller # Maybe return early: Separate the path from the authority. 251*4ee8f2c5SMilton Miller if (substr(url, 1, 2) != "//") { 252*4ee8f2c5SMilton Miller components["path"] = url; 253*4ee8f2c5SMilton Miller return 254*4ee8f2c5SMilton Miller } else if (match(substr(url, 3), "/")) { 255*4ee8f2c5SMilton Miller components["path"] = substr(url, 3 + RSTART - 1) # include the / 256*4ee8f2c5SMilton Miller url = substr(url, 3, RSTART - 1) 257*4ee8f2c5SMilton Miller } else { 258*4ee8f2c5SMilton Miller url = substr(url, 3) 259*4ee8f2c5SMilton Miller } 260*4ee8f2c5SMilton Miller 261*4ee8f2c5SMilton Miller # Parse userinfo@host:port 262*4ee8f2c5SMilton Miller if (match(url, /@/)) { 263*4ee8f2c5SMilton Miller userinfo = substr(url, 1, RSTART - 1) 264*4ee8f2c5SMilton Miller url = substr(url, RSTART + 1) 265*4ee8f2c5SMilton Miller 266*4ee8f2c5SMilton Miller components["userinfo"] = userinfo 267*4ee8f2c5SMilton Miller if (match(userinfo, ":")) { 268*4ee8f2c5SMilton Miller # NOT RECOMMENDED 269*4ee8f2c5SMilton Miller components["password"] = substr(userinfo, RSTART + 1) 270*4ee8f2c5SMilton Miller userinfo = substr(userinfo, RSTART - 1) 271*4ee8f2c5SMilton Miller } 272*4ee8f2c5SMilton Miller components["user"] = userinfo; 273*4ee8f2c5SMilton Miller } 274*4ee8f2c5SMilton Miller if (match(url, ":")) { 275*4ee8f2c5SMilton Miller # port is numeric or empty 276*4ee8f2c5SMilton Miller components["port"] = substr(url, RSTART + 1) 277*4ee8f2c5SMilton Miller url = substr(url, 1, RSTART - 1) 278*4ee8f2c5SMilton Miller } 279*4ee8f2c5SMilton Miller if (url) 280*4ee8f2c5SMilton Miller components["host"] = url 281*4ee8f2c5SMilton Miller} 282*4ee8f2c5SMilton Miller 283*4ee8f2c5SMilton Millerfunction dump_field_if_present(key, array) 284*4ee8f2c5SMilton Miller{ 285*4ee8f2c5SMilton Miller r="" 286*4ee8f2c5SMilton Miller if (key in array) 287*4ee8f2c5SMilton Miller r=sprintf(dquote key dquote": "dquote"%s"dquote"\n", array[key]) 288*4ee8f2c5SMilton Miller return r 289*4ee8f2c5SMilton Miller} 290*4ee8f2c5SMilton Miller 291*4ee8f2c5SMilton Millerfunction dump_split_url(components) 292*4ee8f2c5SMilton Miller{ 293*4ee8f2c5SMilton Miller r= "split_url = {\n" 294*4ee8f2c5SMilton Miller r=r dump_field_if_present("scheme", components) 295*4ee8f2c5SMilton Miller r=r dump_field_if_present("userinfo", components) 296*4ee8f2c5SMilton Miller r=r dump_field_if_present("host", components) 297*4ee8f2c5SMilton Miller r=r dump_field_if_present("port", components) 298*4ee8f2c5SMilton Miller r=r dump_field_if_present("path", components) 299*4ee8f2c5SMilton Miller r=r dump_field_if_present("query", components) 300*4ee8f2c5SMilton Miller r=r dump_field_if_present("frag", components) 301*4ee8f2c5SMilton Miller r=r "}\n" 302*4ee8f2c5SMilton Miller 303*4ee8f2c5SMilton Miller return r 304*4ee8f2c5SMilton Miller} 305*4ee8f2c5SMilton Miller 306*4ee8f2c5SMilton Miller# RFC2616 3.2.2 307*4ee8f2c5SMilton Millerfunction is_http_request_uri(split_url) 308*4ee8f2c5SMilton Miller{ 309*4ee8f2c5SMilton Miller # Fragments are handled by the client, user info is not on the wire. 310*4ee8f2c5SMilton Miller if (("frag" in split_url) || ("userinfo" in split_url)) 311*4ee8f2c5SMilton Miller return 0 312*4ee8f2c5SMilton Miller trace("not frag, no user") 313*4ee8f2c5SMilton Miller 314*4ee8f2c5SMilton Miller # If absoluteURI, it will have both, if abs_path neither 315*4ee8f2c5SMilton Miller if (("scheme" in split_url) != ("host" in split_url)) 316*4ee8f2c5SMilton Miller return 0 317*4ee8f2c5SMilton Miller trace("scheme host ok") 318*4ee8f2c5SMilton Miller 319*4ee8f2c5SMilton Miller if ("scheme" in split_url) { 320*4ee8f2c5SMilton Miller trace("original scheme is: >" split_url["scheme"] "<") 321*4ee8f2c5SMilton Miller scheme = unescape(split_url["scheme"]) 322*4ee8f2c5SMilton Miller trace("unescaped scheme is: >" scheme "<") 323*4ee8f2c5SMilton Miller # HTTP 2616 3.2.3 scheme MUST be case insensitive 324*4ee8f2c5SMilton Miller if (tolower(scheme) != "http") 325*4ee8f2c5SMilton Miller return 0 326*4ee8f2c5SMilton Miller trace("scheme is http") 327*4ee8f2c5SMilton Miller 328*4ee8f2c5SMilton Miller # 3.2.2 http always has a net_url host authority, host not empty 329*4ee8f2c5SMilton Miller if (!("host" in split_url)) 330*4ee8f2c5SMilton Miller return 0 331*4ee8f2c5SMilton Miller trace("host present >" split_url["host"] "<") 332*4ee8f2c5SMilton Miller 333*4ee8f2c5SMilton Miller # Authority name not empty 334*4ee8f2c5SMilton Miller if (split_url["host"] == "") 335*4ee8f2c5SMilton Miller return 0 336*4ee8f2c5SMilton Miller 337*4ee8f2c5SMilton Miller # 2616 3.2.3 empty path is / sole fixup: scheme://hostport 338*4ee8f2c5SMilton Miller if (split_url["path"] == "") 339*4ee8f2c5SMilton Miller split_url["path"] = "/" 340*4ee8f2c5SMilton Miller } 341*4ee8f2c5SMilton Miller 342*4ee8f2c5SMilton Miller trace("path is now >" split_url["path"] "<") 343*4ee8f2c5SMilton Miller trace("first path char is >" substr(split_url["path"], 1, 1) "<") 344*4ee8f2c5SMilton Miller 345*4ee8f2c5SMilton Miller # The path must be absolute. 346*4ee8f2c5SMilton Miller return substr(split_url["path"], 1, 1) == "/" 347*4ee8f2c5SMilton Miller} 348*4ee8f2c5SMilton Miller 349*4ee8f2c5SMilton Millerfunction location_header_ok(URI) 350*4ee8f2c5SMilton Miller{ 351*4ee8f2c5SMilton Miller # policy: all response URLs shall be https 352*4ee8f2c5SMilton Miller if (substr(URI, 1, 8) != "https://") 353*4ee8f2c5SMilton Miller return 0 354*4ee8f2c5SMilton Miller 355*4ee8f2c5SMilton Miller # The URL shall have been encoded 356*4ee8f2c5SMilton Miller if (bad_uric(URI)) 357*4ee8f2c5SMilton Miller return 0 358*4ee8f2c5SMilton Miller 359*4ee8f2c5SMilton Miller return 1 360*4ee8f2c5SMilton Miller} 361*4ee8f2c5SMilton Miller 362*4ee8f2c5SMilton Millerfunction response_needs_location(response) 363*4ee8f2c5SMilton Miller{ 364*4ee8f2c5SMilton Miller return (response ~ /^3/) || (response ~ /^201/) 365*4ee8f2c5SMilton Miller} 366*4ee8f2c5SMilton Miller 367*4ee8f2c5SMilton Millerfunction respond_and_exit(response, content, URI) 368*4ee8f2c5SMilton Miller{ 369*4ee8f2c5SMilton Miller # If the URI is given validate it should be sent and prepare header 370*4ee8f2c5SMilton Miller if (location_header_ok(URI) && response_needs_location(response)) 371*4ee8f2c5SMilton Miller location = CRLF "Location: " URI 372*4ee8f2c5SMilton Miller else 373*4ee8f2c5SMilton Miller location = "" 374*4ee8f2c5SMilton Miller 375*4ee8f2c5SMilton Miller if (response !~ /^[1-5][0-9][0-9] /) { 376*4ee8f2c5SMilton Miller trace( "DEBUG: response '" response "'\n" ) 377*4ee8f2c5SMilton Miller trace( "DEBUG: content: '" content"'\n" ) 378*4ee8f2c5SMilton Miller response = "500 Internal Server Error" 379*4ee8f2c5SMilton Miller content = response CRLF 380*4ee8f2c5SMilton Miller } 381*4ee8f2c5SMilton Miller 382*4ee8f2c5SMilton Miller content_length = sprintf("Content-Length: %d", length(content)) 383*4ee8f2c5SMilton Miller 384*4ee8f2c5SMilton Miller # RFC 2616 9.4 HEAD MUST NOT return message body. 385*4ee8f2c5SMilton Miller if (method == "HEAD") { 386*4ee8f2c5SMilton Miller content = "" 387*4ee8f2c5SMilton Miller } 388*4ee8f2c5SMilton Miller 389*4ee8f2c5SMilton Miller # Final trace before changing line endings visual seperation 390*4ee8f2c5SMilton Miller trace("") 391*4ee8f2c5SMilton Miller 392*4ee8f2c5SMilton Miller # Respond with protocol and response, prepared location from above, 393*4ee8f2c5SMilton Miller # and then the fixed response headers. 394*4ee8f2c5SMilton Miller 395*4ee8f2c5SMilton Miller # Separate header lines with CRLF but add nothing after the body 396*4ee8f2c5SMilton Miller OFS = CRLF 397*4ee8f2c5SMilton Miller ORS = "" 398*4ee8f2c5SMilton Miller 399*4ee8f2c5SMilton Miller print( "HTTP/1.1 " response location, 400*4ee8f2c5SMilton Miller content_length, 401*4ee8f2c5SMilton Miller "Content-Type: text/plain; charset=UTF-8", 402*4ee8f2c5SMilton Miller "X_Frame_Options: DENY", 403*4ee8f2c5SMilton Miller "Pragma: no-cache", 404*4ee8f2c5SMilton Miller "Cache_Control: no-Store,no-Cache", 405*4ee8f2c5SMilton Miller "X-XSS-Protection: 1; mode=block", 406*4ee8f2c5SMilton Miller "X-Content-Type-Options: nosniff", 407*4ee8f2c5SMilton Miller "Connection: close", 408*4ee8f2c5SMilton Miller "", 409*4ee8f2c5SMilton Miller content) 410*4ee8f2c5SMilton Miller 411*4ee8f2c5SMilton Miller # We told client to close the connection; also close this end. 412*4ee8f2c5SMilton Miller exit 0 413*4ee8f2c5SMilton Miller} 414*4ee8f2c5SMilton Miller 415*4ee8f2c5SMilton Miller# Respond with an error and close the connection to avoid synchronization. 416*4ee8f2c5SMilton Millerfunction respond_error(num) 417*4ee8f2c5SMilton Miller{ 418*4ee8f2c5SMilton Miller if (num in errors) 419*4ee8f2c5SMilton Miller if (num in msgtxt) 420*4ee8f2c5SMilton Miller respond_and_exit(errors[num], msgtxt[num] CRLF) 421*4ee8f2c5SMilton Miller else 422*4ee8f2c5SMilton Miller respond_and_exit(errors[num], errors[num] CRLF) 423*4ee8f2c5SMilton Miller else 424*4ee8f2c5SMilton Miller respond_and_exit(errors[500], "unknown error number " num CRLF) 425*4ee8f2c5SMilton Miller} 426*4ee8f2c5SMilton Miller 427*4ee8f2c5SMilton Miller# To generate a trace, set the tracefile or tracecmd variable with awk -v 428*4ee8f2c5SMilton Millerfunction trace(string) 429*4ee8f2c5SMilton Miller{ 430*4ee8f2c5SMilton Miller if (tracefile) 431*4ee8f2c5SMilton Miller print(string) > tracefile 432*4ee8f2c5SMilton Miller if (tracecmd) 433*4ee8f2c5SMilton Miller print(string) | tracecmd 434*4ee8f2c5SMilton Miller} 435*4ee8f2c5SMilton Miller 436*4ee8f2c5SMilton Miller 437*4ee8f2c5SMilton Miller 438*4ee8f2c5SMilton Miller########################################################### 439*4ee8f2c5SMilton Miller 440*4ee8f2c5SMilton Miller# BEGIN { 441*4ee8f2c5SMilton Miller# # The character classes as defined in rfc 2396 442*4ee8f2c5SMilton Miller# reserved = ";/?:@&=+$," 443*4ee8f2c5SMilton Miller# mark = "-_.!~*'()" 444*4ee8f2c5SMilton Miller# digit = "0123456789" 445*4ee8f2c5SMilton Miller# lower = "abcdefghijklmnopqrstuvwxyz" 446*4ee8f2c5SMilton Miller# upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 447*4ee8f2c5SMilton Miller# unreserved = lower upper digit mark 448*4ee8f2c5SMilton Miller# 449*4ee8f2c5SMilton Miller# control = 00-1F, 7F 450*4ee8f2c5SMilton Miller# space = " " 451*4ee8f2c5SMilton Miller# delims = "<>#%" dquote 452*4ee8f2c5SMilton Miller# unwise = "{}|\^[]`" 453*4ee8f2c5SMilton Miller# } 454*4ee8f2c5SMilton Miller 455*4ee8f2c5SMilton Miller################################################################ 456*4ee8f2c5SMilton Miller 457*4ee8f2c5SMilton Miller# Build a table to convert a hex character to an integer 458*4ee8f2c5SMilton Millerfunction make_hex2int(hex2int) { 459*4ee8f2c5SMilton Miller for(i =0; i < 10; i++) 460*4ee8f2c5SMilton Miller hex2int[i] = i 461*4ee8f2c5SMilton Miller for (i=10 ; i < 16; i++) { 462*4ee8f2c5SMilton Miller hex2int[substr("ABCDEF", i - 10 + 1, 1)] = i 463*4ee8f2c5SMilton Miller hex2int[substr("abcdef", i - 10 + 1, 1)] = i 464*4ee8f2c5SMilton Miller } 465*4ee8f2c5SMilton Miller} 466