xref: /plan9/sys/src/libhttpd/parsereq.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 #include <u.h>
2 #include <libc.h>
3 #include <bin.h>
4 #include <httpd.h>
5 
6 typedef struct Strings		Strings;
7 
8 struct Strings
9 {
10 	char	*s1;
11 	char	*s2;
12 };
13 
14 static	char*		abspath(HConnect *cc, char *origpath, char *curdir);
15 static	int		getc(HConnect*);
16 static	char*		getword(HConnect*);
17 static	Strings		parseuri(HConnect *c, char*);
18 static	Strings		stripmagic(char*);
19 static	Strings		stripsearch(char*);
20 
21 /*
22  * parse the next request line
23  * returns:
24  *	1 ok
25  *	0 eof
26  *	-1 error
27  */
28 int
29 hparsereq(HConnect *c, int timeout)
30 {
31 	Strings ss;
32 	char *vs, *v, *search, *uri, *origuri, *extra;
33 
34 	if(c->bin != nil){
35 		hfail(c, HInternal);
36 		return -1;
37 	}
38 
39 	/*
40 	 * serve requests until a magic request.
41 	 * later requests have to come quickly.
42 	 * only works for http/1.1 or later.
43 	 */
44 	alarm(timeout);
45 	if(!hgethead(c, 0))
46 		return 0;
47 	alarm(0);
48 	c->reqtime = time(nil);
49 	c->req.meth = getword(c);
50 	if(c->req.meth == nil){
51 		hfail(c, HSyntax);
52 		return -1;
53 	}
54 	uri = getword(c);
55 	if(uri == nil || strlen(uri) == 0){
56 		hfail(c, HSyntax);
57 		return -1;
58 	}
59 	v = getword(c);
60 	if(v == nil){
61 		if(strcmp(c->req.meth, "GET") != 0){
62 			hfail(c, HUnimp, c->req.meth);
63 			return -1;
64 		}
65 		c->req.vermaj = 0;
66 		c->req.vermin = 9;
67 	}else{
68 		vs = v;
69 		if(strncmp(vs, "HTTP/", 5) != 0){
70 			hfail(c, HUnkVers, vs);
71 			return -1;
72 		}
73 		vs += 5;
74 		c->req.vermaj = strtoul(vs, &vs, 10);
75 		if(*vs != '.' || c->req.vermaj != 1){
76 			hfail(c, HUnkVers, vs);
77 			return -1;
78 		}
79 		vs++;
80 		c->req.vermin = strtoul(vs, &vs, 10);
81 		if(*vs != '\0'){
82 			hfail(c, HUnkVers, vs);
83 			return -1;
84 		}
85 
86 		extra = getword(c);
87 		if(extra != nil){
88 			hfail(c, HSyntax);
89 			return -1;
90 		}
91 	}
92 
93 	/*
94 	 * the fragment is not supposed to be sent
95 	 * strip it 'cause some clients send it
96 	 */
97 	origuri = uri;
98 	uri = strchr(origuri, '#');
99 	if(uri != nil)
100 		*uri = 0;
101 
102 	/*
103 	 * http/1.1 requires the server to accept absolute
104 	 * or relative uri's.  convert to relative with an absolute path
105 	 */
106 	if(http11(c)){
107 		ss = parseuri(c, origuri);
108 		uri = ss.s1;
109 		c->req.urihost = ss.s2;
110 		if(uri == nil){
111 			hfail(c, HBadReq, uri);
112 			return -1;
113 		}
114 		origuri = uri;
115 	}
116 
117 	/*
118 	 * munge uri for search, protection, and magic
119 	 */
120 	ss = stripsearch(origuri);
121 	origuri = ss.s1;
122 	search = ss.s2;
123 	uri = hurlunesc(c, origuri);
124 	uri = abspath(c, uri, "/");
125 	if(uri == nil || uri[0] == '\0'){
126 		hfail(c, HNotFound, "no object specified");
127 		return -1;
128 	}
129 
130 	c->req.uri = uri;
131 	c->req.search = search;
132 
133 	return 1;
134 }
135 
136 static Strings
137 parseuri(HConnect *c, char *uri)
138 {
139 	Strings ss;
140 	char *urihost, *p;
141 
142 	urihost = nil;
143 	if(uri[0] != '/'){
144 		if(cistrncmp(uri, "http://", 7) != 0){
145 			ss.s1 = nil;
146 			ss.s2 = nil;
147 			return ss;
148 		}
149 		uri += 5;	/* skip http: */
150 	}
151 
152 	/*
153 	 * anything starting with // is a host name or number
154 	 * hostnames constists of letters, digits, - and .
155 	 * for now, just ignore any port given
156 	 */
157 	if(uri[0] == '/' && uri[1] == '/'){
158 		urihost = uri + 2;
159 		p = strchr(urihost, '/');
160 		if(p == nil)
161 			uri = hstrdup(c, "/");
162 		else{
163 			uri = hstrdup(c, p);
164 			*p = '\0';
165 		}
166 		p = strchr(urihost, ':');
167 		if(p != nil)
168 			*p = '\0';
169 	}
170 
171 	if(uri[0] != '/' || uri[1] == '/'){
172 		ss.s1 = nil;
173 		ss.s2 = nil;
174 		return ss;
175 	}
176 
177 	ss.s1 = uri;
178 	ss.s2 = hlower(urihost);
179 	return ss;
180 }
181 static Strings
182 stripsearch(char *uri)
183 {
184 	Strings ss;
185 	char *search;
186 
187 	search = strchr(uri, '?');
188 	if(search != nil)
189 		*search++ = 0;
190 	ss.s1 = uri;
191 	ss.s2 = search;
192 	return ss;
193 }
194 
195 /*
196  *  to circumscribe the accessible files we have to eliminate ..'s
197  *  and resolve all names from the root.
198  */
199 static char*
200 abspath(HConnect *cc, char *origpath, char *curdir)
201 {
202 	char *p, *sp, *path, *work, *rpath;
203 	int len, n, c;
204 
205 	if(curdir == nil)
206 		curdir = "/";
207 	if(origpath == nil)
208 		origpath = "";
209 	work = hstrdup(cc, origpath);
210 	path = work;
211 
212 	/*
213 	 * remove any really special characters
214 	 */
215 	for(sp = "`;| "; *sp; sp++){
216 		p = strchr(path, *sp);
217 		if(p)
218 			*p = 0;
219 	}
220 
221 	len = strlen(curdir) + strlen(path) + 2 + UTFmax;
222 	if(len < 10)
223 		len = 10;
224 	rpath = halloc(cc, len);
225 	if(*path == '/')
226 		rpath[0] = 0;
227 	else
228 		strcpy(rpath, curdir);
229 	n = strlen(rpath);
230 
231 	while(path){
232 		p = strchr(path, '/');
233 		if(p)
234 			*p++ = 0;
235 		if(strcmp(path, "..") == 0){
236 			while(n > 1){
237 				n--;
238 				c = rpath[n];
239 				rpath[n] = 0;
240 				if(c == '/')
241 					break;
242 			}
243 		}else if(strcmp(path, ".") == 0){
244 			;
245 		}else if(n == 1)
246 			n += snprint(rpath+n, len-n, "%s", path);
247 		else
248 			n += snprint(rpath+n, len-n, "/%s", path);
249 		path = p;
250 	}
251 
252 	if(strncmp(rpath, "/bin/", 5) == 0)
253 		strcpy(rpath, "/");
254 	return rpath;
255 }
256 
257 static char*
258 getword(HConnect *c)
259 {
260 	char *buf;
261 	int ch, n;
262 
263 	while((ch = getc(c)) == ' ' || ch == '\t' || ch == '\r')
264 		;
265 	if(ch == '\n')
266 		return nil;
267 	n = 0;
268 	buf = halloc(c, 1);
269 	for(;;){
270 		switch(ch){
271 		case ' ':
272 		case '\t':
273 		case '\r':
274 		case '\n':
275 			buf[n] = '\0';
276 			return hstrdup(c, buf);
277 		}
278 
279 		if(n < HMaxWord-1){
280 			buf = bingrow(&c->bin, buf, n, n + 1, 0);
281 			if(buf == nil)
282 				return nil;
283 			buf[n++] = ch;
284 		}
285 		ch = getc(c);
286 	}
287 	return nil;
288 }
289 
290 static int
291 getc(HConnect *c)
292 {
293 	if(c->hpos < c->hstop)
294 		return *c->hpos++;
295 	return '\n';
296 }
297