xref: /openbmc/phosphor-misc/http-redirect/http-redirect.awk (revision 4ee8f2c55b20dbf1987d056fe436168ee574b654)
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