• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * UPnP WPS Device - Web connections
3  * Copyright (c) 2000-2003 Intel Corporation
4  * Copyright (c) 2006-2007 Sony Corporation
5  * Copyright (c) 2008-2009 Atheros Communications
6  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
7  *
8  * See wps_upnp.c for more details on licensing and code history.
9  */
10 
11 #include "includes.h"
12 #include <fcntl.h>
13 
14 #include "common.h"
15 #include "base64.h"
16 #include "eloop.h"
17 #include "uuid.h"
18 #include "httpread.h"
19 #include "wps_i.h"
20 #include "wps_upnp.h"
21 #include "wps_upnp_i.h"
22 
23 /***************************************************************************
24  * Web connections (we serve pages of info about ourselves, handle
25  * requests, etc. etc.).
26  **************************************************************************/
27 
28 #define WEB_CONNECTION_TIMEOUT_SEC 30   /* Drop web connection after t.o. */
29 #define WEB_CONNECTION_MAX_READ 8000    /* Max we'll read for TCP request */
30 #define MAX_WEB_CONNECTIONS 10          /* max simultaneous web connects */
31 
32 
33 static const char *urn_wfawlanconfig =
34 	"urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
35 static const char *http_server_hdr =
36 	"Server: unspecified, UPnP/1.0, unspecified\r\n";
37 static const char *http_connection_close =
38 	"Connection: close\r\n";
39 
40 /*
41  * Incoming web connections are recorded in this struct.
42  * A web connection is a TCP connection to us, the server;
43  * it is called a "web connection" because we use http and serve
44  * data that looks like web pages.
45  * State information is need to track the connection until we figure
46  * out what they want and what we want to do about it.
47  */
48 struct web_connection {
49 	/* double linked list */
50 	struct web_connection *next;
51 	struct web_connection *prev;
52 	struct upnp_wps_device_sm *sm; /* parent */
53 	int sd; /* socket to read from */
54 	struct sockaddr_in cli_addr;
55 	int sd_registered; /* nonzero if we must cancel registration */
56 	struct httpread *hread; /* state machine for reading socket */
57 	int n_rcvd_data; /* how much data read so far */
58 	int done; /* internal flag, set when we've finished */
59 };
60 
61 
62 /*
63  * XML parsing and formatting
64  *
65  * XML is a markup language based on unicode; usually (and in our case,
66  * always!) based on utf-8. utf-8 uses a variable number of bytes per
67  * character. utf-8 has the advantage that all non-ASCII unicode characters are
68  * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
69  * characters are single ascii bytes, thus we can use typical text processing.
70  *
71  * (One other interesting thing about utf-8 is that it is possible to look at
72  * any random byte and determine if it is the first byte of a character as
73  * versus a continuation byte).
74  *
75  * The base syntax of XML uses a few ASCII punctionation characters; any
76  * characters that would appear in the payload data are rewritten using
77  * sequences, e.g., &amp; for ampersand(&) and &lt for left angle bracket (<).
78  * Five such escapes total (more can be defined but that does not apply to our
79  * case). Thus we can safely parse for angle brackets etc.
80  *
81  * XML describes tree structures of tagged data, with each element beginning
82  * with an opening tag <label> and ending with a closing tag </label> with
83  * matching label. (There is also a self-closing tag <label/> which is supposed
84  * to be equivalent to <label></label>, i.e., no payload, but we are unlikely
85  * to see it for our purpose).
86  *
87  * Actually the opening tags are a little more complicated because they can
88  * contain "attributes" after the label (delimited by ascii space or tab chars)
89  * of the form attribute_label="value" or attribute_label='value'; as it turns
90  * out we do not have to read any of these attributes, just ignore them.
91  *
92  * Labels are any sequence of chars other than space, tab, right angle bracket
93  * (and ?), but may have an inner structure of <namespace><colon><plain_label>.
94  * As it turns out, we can ignore the namespaces, in fact we can ignore the
95  * entire tree hierarchy, because the plain labels we are looking for will be
96  * unique (not in general, but for this application). We do however have to be
97  * careful to skip over the namespaces.
98  *
99  * In generating XML we have to be more careful, but that is easy because
100  * everything we do is pretty canned. The only real care to take is to escape
101  * any special chars in our payload.
102  */
103 
104 /**
105  * xml_next_tag - Advance to next tag
106  * @in: Input
107  * @out: OUT: start of tag just after '<'
108  * @out_tagname: OUT: start of name of tag, skipping namespace
109  * @end: OUT: one after tag
110  * Returns: 0 on success, 1 on failure
111  *
112  * A tag has form:
113  *     <left angle bracket><...><right angle bracket>
114  * Within the angle brackets, there is an optional leading forward slash (which
115  * makes the tag an ending tag), then an optional leading label (followed by
116  * colon) and then the tag name itself.
117  *
118  * Note that angle brackets present in the original data must have been encoded
119  * as &lt; and &gt; so they will not trouble us.
120  */
xml_next_tag(char * in,char ** out,char ** out_tagname,char ** end)121 static int xml_next_tag(char *in, char **out, char **out_tagname,
122 			char **end)
123 {
124 	while (*in && *in != '<')
125 		in++;
126 	if (*in != '<')
127 		return 1;
128 	*out = ++in;
129 	if (*in == '/')
130 		in++;
131 	*out_tagname = in; /* maybe */
132 	while (isalnum(*in) || *in == '-')
133 		in++;
134 	if (*in == ':')
135 		*out_tagname = ++in;
136 	while (*in && *in != '>')
137 		in++;
138 	if (*in != '>')
139 		return 1;
140 	*end = ++in;
141 	return 0;
142 }
143 
144 
145 /* xml_data_encode -- format data for xml file, escaping special characters.
146  *
147  * Note that we assume we are using utf8 both as input and as output!
148  * In utf8, characters may be classed as follows:
149  *     0xxxxxxx(2) -- 1 byte ascii char
150  *     11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
151  *         110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
152  *         1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
153  *         11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
154  *      10xxxxxx(2) -- extension byte (6 payload bits per byte)
155  *      Some values implied by the above are however illegal because they
156  *      do not represent unicode chars or are not the shortest encoding.
157  * Actually, we can almost entirely ignore the above and just do
158  * text processing same as for ascii text.
159  *
160  * XML is written with arbitrary unicode characters, except that five
161  * characters have special meaning and so must be escaped where they
162  * appear in payload data... which we do here.
163  */
xml_data_encode(struct wpabuf * buf,const char * data,int len)164 static void xml_data_encode(struct wpabuf *buf, const char *data, int len)
165 {
166 	int i;
167 	for (i = 0; i < len; i++) {
168 		u8 c = ((u8 *) data)[i];
169 		if (c == '<') {
170 			wpabuf_put_str(buf, "&lt;");
171 			continue;
172 		}
173 		if (c == '>') {
174 			wpabuf_put_str(buf, "&gt;");
175 			continue;
176 		}
177 		if (c == '&') {
178 			wpabuf_put_str(buf, "&amp;");
179 			continue;
180 		}
181 		if (c == '\'') {
182 			wpabuf_put_str(buf, "&apos;");
183 			continue;
184 		}
185 		if (c == '"') {
186 			wpabuf_put_str(buf, "&quot;");
187 			continue;
188 		}
189 		/*
190 		 * We could try to represent control characters using the
191 		 * sequence: &#x; where x is replaced by a hex numeral, but not
192 		 * clear why we would do this.
193 		 */
194 		wpabuf_put_u8(buf, c);
195 	}
196 }
197 
198 
199 /* xml_add_tagged_data -- format tagged data as a new xml line.
200  *
201  * tag must not have any special chars.
202  * data may have special chars, which are escaped.
203  */
xml_add_tagged_data(struct wpabuf * buf,const char * tag,const char * data)204 static void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
205 				const char *data)
206 {
207 	wpabuf_printf(buf, "<%s>", tag);
208 	xml_data_encode(buf, data, os_strlen(data));
209 	wpabuf_printf(buf, "</%s>\n", tag);
210 }
211 
212 
213 /* A POST body looks something like (per upnp spec):
214  * <?xml version="1.0"?>
215  * <s:Envelope
216  *     xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
217  *     s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
218  *   <s:Body>
219  *     <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
220  *       <argumentName>in arg value</argumentName>
221  *       other in args and their values go here, if any
222  *     </u:actionName>
223  *   </s:Body>
224  * </s:Envelope>
225  *
226  * where :
227  *      s: might be some other namespace name followed by colon
228  *      u: might be some other namespace name followed by colon
229  *      actionName will be replaced according to action requested
230  *      schema following actionName will be WFA scheme instead
231  *      argumentName will be actual argument name
232  *      (in arg value) will be actual argument value
233  */
234 static int
upnp_get_first_document_item(char * doc,const char * item,char ** value)235 upnp_get_first_document_item(char *doc, const char *item, char **value)
236 {
237 	const char *match = item;
238 	int match_len = os_strlen(item);
239 	char *tag;
240 	char *tagname;
241 	char *end;
242 
243 	*value = NULL;          /* default, bad */
244 
245 	/*
246 	 * This is crude: ignore any possible tag name conflicts and go right
247 	 * to the first tag of this name. This should be ok for the limited
248 	 * domain of UPnP messages.
249 	 */
250 	for (;;) {
251 		if (xml_next_tag(doc, &tag, &tagname, &end))
252 			return 1;
253 		doc = end;
254 		if (!os_strncasecmp(tagname, match, match_len) &&
255 		    *tag != '/' &&
256 		    (tagname[match_len] == '>' ||
257 		     !isgraph(tagname[match_len]))) {
258 			break;
259 		}
260 	}
261 	end = doc;
262 	while (*end && *end != '<')
263 		end++;
264 	*value = os_zalloc(1 + (end - doc));
265 	if (*value == NULL)
266 		return 1;
267 	os_memcpy(*value, doc, end - doc);
268 	return 0;
269 }
270 
271 
272 /*
273  * "Files" that we serve via HTTP. The format of these files is given by
274  * WFA WPS specifications. Extra white space has been removed to save space.
275  */
276 
277 static const char wps_scpd_xml[] =
278 "<?xml version=\"1.0\"?>\n"
279 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
280 "<specVersion><major>1</major><minor>0</minor></specVersion>\n"
281 "<actionList>\n"
282 "<action>\n"
283 "<name>GetDeviceInfo</name>\n"
284 "<argumentList>\n"
285 "<argument>\n"
286 "<name>NewDeviceInfo</name>\n"
287 "<direction>out</direction>\n"
288 "<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
289 "</argument>\n"
290 "</argumentList>\n"
291 "</action>\n"
292 "<action>\n"
293 "<name>PutMessage</name>\n"
294 "<argumentList>\n"
295 "<argument>\n"
296 "<name>NewInMessage</name>\n"
297 "<direction>in</direction>\n"
298 "<relatedStateVariable>InMessage</relatedStateVariable>\n"
299 "</argument>\n"
300 "<argument>\n"
301 "<name>NewOutMessage</name>\n"
302 "<direction>out</direction>\n"
303 "<relatedStateVariable>OutMessage</relatedStateVariable>\n"
304 "</argument>\n"
305 "</argumentList>\n"
306 "</action>\n"
307 "<action>\n"
308 "<name>GetAPSettings</name>\n"
309 "<argumentList>\n"
310 "<argument>\n"
311 "<name>NewMessage</name>\n"
312 "<direction>in</direction>\n"
313 "<relatedStateVariable>Message</relatedStateVariable>\n"
314 "</argument>\n"
315 "<argument>\n"
316 "<name>NewAPSettings</name>\n"
317 "<direction>out</direction>\n"
318 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
319 "</argument>\n"
320 "</argumentList>\n"
321 "</action>\n"
322 "<action>\n"
323 "<name>SetAPSettings</name>\n"
324 "<argumentList>\n"
325 "<argument>\n"
326 "<name>APSettings</name>\n"
327 "<direction>in</direction>\n"
328 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
329 "</argument>\n"
330 "</argumentList>\n"
331 "</action>\n"
332 "<action>\n"
333 "<name>DelAPSettings</name>\n"
334 "<argumentList>\n"
335 "<argument>\n"
336 "<name>NewAPSettings</name>\n"
337 "<direction>in</direction>\n"
338 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
339 "</argument>\n"
340 "</argumentList>\n"
341 "</action>\n"
342 "<action>\n"
343 "<name>GetSTASettings</name>\n"
344 "<argumentList>\n"
345 "<argument>\n"
346 "<name>NewMessage</name>\n"
347 "<direction>in</direction>\n"
348 "<relatedStateVariable>Message</relatedStateVariable>\n"
349 "</argument>\n"
350 "<argument>\n"
351 "<name>NewSTASettings</name>\n"
352 "<direction>out</direction>\n"
353 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
354 "</argument>\n"
355 "</argumentList>\n"
356 "</action>\n"
357 "<action>\n"
358 "<name>SetSTASettings</name>\n"
359 "<argumentList>\n"
360 "<argument>\n"
361 "<name>NewSTASettings</name>\n"
362 "<direction>out</direction>\n"
363 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
364 "</argument>\n"
365 "</argumentList>\n"
366 "</action>\n"
367 "<action>\n"
368 "<name>DelSTASettings</name>\n"
369 "<argumentList>\n"
370 "<argument>\n"
371 "<name>NewSTASettings</name>\n"
372 "<direction>in</direction>\n"
373 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
374 "</argument>\n"
375 "</argumentList>\n"
376 "</action>\n"
377 "<action>\n"
378 "<name>PutWLANResponse</name>\n"
379 "<argumentList>\n"
380 "<argument>\n"
381 "<name>NewMessage</name>\n"
382 "<direction>in</direction>\n"
383 "<relatedStateVariable>Message</relatedStateVariable>\n"
384 "</argument>\n"
385 "<argument>\n"
386 "<name>NewWLANEventType</name>\n"
387 "<direction>in</direction>\n"
388 "<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
389 "</argument>\n"
390 "<argument>\n"
391 "<name>NewWLANEventMAC</name>\n"
392 "<direction>in</direction>\n"
393 "<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
394 "</argument>\n"
395 "</argumentList>\n"
396 "</action>\n"
397 "<action>\n"
398 "<name>SetSelectedRegistrar</name>\n"
399 "<argumentList>\n"
400 "<argument>\n"
401 "<name>NewMessage</name>\n"
402 "<direction>in</direction>\n"
403 "<relatedStateVariable>Message</relatedStateVariable>\n"
404 "</argument>\n"
405 "</argumentList>\n"
406 "</action>\n"
407 "<action>\n"
408 "<name>RebootAP</name>\n"
409 "<argumentList>\n"
410 "<argument>\n"
411 "<name>NewAPSettings</name>\n"
412 "<direction>in</direction>\n"
413 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
414 "</argument>\n"
415 "</argumentList>\n"
416 "</action>\n"
417 "<action>\n"
418 "<name>ResetAP</name>\n"
419 "<argumentList>\n"
420 "<argument>\n"
421 "<name>NewMessage</name>\n"
422 "<direction>in</direction>\n"
423 "<relatedStateVariable>Message</relatedStateVariable>\n"
424 "</argument>\n"
425 "</argumentList>\n"
426 "</action>\n"
427 "<action>\n"
428 "<name>RebootSTA</name>\n"
429 "<argumentList>\n"
430 "<argument>\n"
431 "<name>NewSTASettings</name>\n"
432 "<direction>in</direction>\n"
433 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
434 "</argument>\n"
435 "</argumentList>\n"
436 "</action>\n"
437 "<action>\n"
438 "<name>ResetSTA</name>\n"
439 "<argumentList>\n"
440 "<argument>\n"
441 "<name>NewMessage</name>\n"
442 "<direction>in</direction>\n"
443 "<relatedStateVariable>Message</relatedStateVariable>\n"
444 "</argument>\n"
445 "</argumentList>\n"
446 "</action>\n"
447 "</actionList>\n"
448 "<serviceStateTable>\n"
449 "<stateVariable sendEvents=\"no\">\n"
450 "<name>Message</name>\n"
451 "<dataType>bin.base64</dataType>\n"
452 "</stateVariable>\n"
453 "<stateVariable sendEvents=\"no\">\n"
454 "<name>InMessage</name>\n"
455 "<dataType>bin.base64</dataType>\n"
456 "</stateVariable>\n"
457 "<stateVariable sendEvents=\"no\">\n"
458 "<name>OutMessage</name>\n"
459 "<dataType>bin.base64</dataType>\n"
460 "</stateVariable>\n"
461 "<stateVariable sendEvents=\"no\">\n"
462 "<name>DeviceInfo</name>\n"
463 "<dataType>bin.base64</dataType>\n"
464 "</stateVariable>\n"
465 "<stateVariable sendEvents=\"no\">\n"
466 "<name>APSettings</name>\n"
467 "<dataType>bin.base64</dataType>\n"
468 "</stateVariable>\n"
469 "<stateVariable sendEvents=\"yes\">\n"
470 "<name>APStatus</name>\n"
471 "<dataType>ui1</dataType>\n"
472 "</stateVariable>\n"
473 "<stateVariable sendEvents=\"no\">\n"
474 "<name>STASettings</name>\n"
475 "<dataType>bin.base64</dataType>\n"
476 "</stateVariable>\n"
477 "<stateVariable sendEvents=\"yes\">\n"
478 "<name>STAStatus</name>\n"
479 "<dataType>ui1</dataType>\n"
480 "</stateVariable>\n"
481 "<stateVariable sendEvents=\"yes\">\n"
482 "<name>WLANEvent</name>\n"
483 "<dataType>bin.base64</dataType>\n"
484 "</stateVariable>\n"
485 "<stateVariable sendEvents=\"no\">\n"
486 "<name>WLANEventType</name>\n"
487 "<dataType>ui1</dataType>\n"
488 "</stateVariable>\n"
489 "<stateVariable sendEvents=\"no\">\n"
490 "<name>WLANEventMAC</name>\n"
491 "<dataType>string</dataType>\n"
492 "</stateVariable>\n"
493 "<stateVariable sendEvents=\"no\">\n"
494 "<name>WLANResponse</name>\n"
495 "<dataType>bin.base64</dataType>\n"
496 "</stateVariable>\n"
497 "</serviceStateTable>\n"
498 "</scpd>\n"
499 ;
500 
501 
502 static const char *wps_device_xml_prefix =
503 	"<?xml version=\"1.0\"?>\n"
504 	"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
505 	"<specVersion>\n"
506 	"<major>1</major>\n"
507 	"<minor>0</minor>\n"
508 	"</specVersion>\n"
509 	"<device>\n"
510 	"<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
511 	"</deviceType>\n";
512 
513 static const char *wps_device_xml_postfix =
514 	"<serviceList>\n"
515 	"<service>\n"
516 	"<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
517 	"</serviceType>\n"
518 	"<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
519 	"\n"
520 	"<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
521 	"<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
522 	"<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
523 	"</service>\n"
524 	"</serviceList>\n"
525 	"</device>\n"
526 	"</root>\n";
527 
528 
529 /* format_wps_device_xml -- produce content of "file" wps_device.xml
530  * (UPNP_WPS_DEVICE_XML_FILE)
531  */
format_wps_device_xml(struct upnp_wps_device_sm * sm,struct wpabuf * buf)532 static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
533 				  struct wpabuf *buf)
534 {
535 	const char *s;
536 	char uuid_string[80];
537 
538 	wpabuf_put_str(buf, wps_device_xml_prefix);
539 
540 	/*
541 	 * Add required fields with default values if not configured. Add
542 	 * optional and recommended fields only if configured.
543 	 */
544 	s = sm->wps->friendly_name;
545 	s = ((s && *s) ? s : "WPS Access Point");
546 	xml_add_tagged_data(buf, "friendlyName", s);
547 
548 	s = sm->wps->dev.manufacturer;
549 	s = ((s && *s) ? s : "");
550 	xml_add_tagged_data(buf, "manufacturer", s);
551 
552 	if (sm->wps->manufacturer_url)
553 		xml_add_tagged_data(buf, "manufacturerURL",
554 				    sm->wps->manufacturer_url);
555 
556 	if (sm->wps->model_description)
557 		xml_add_tagged_data(buf, "modelDescription",
558 				    sm->wps->model_description);
559 
560 	s = sm->wps->dev.model_name;
561 	s = ((s && *s) ? s : "");
562 	xml_add_tagged_data(buf, "modelName", s);
563 
564 	if (sm->wps->dev.model_number)
565 		xml_add_tagged_data(buf, "modelNumber",
566 				    sm->wps->dev.model_number);
567 
568 	if (sm->wps->model_url)
569 		xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
570 
571 	if (sm->wps->dev.serial_number)
572 		xml_add_tagged_data(buf, "serialNumber",
573 				    sm->wps->dev.serial_number);
574 
575 	uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
576 	s = uuid_string;
577 	/* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
578 	 * easily...
579 	 */
580 	wpabuf_put_str(buf, "<UDN>uuid:");
581 	xml_data_encode(buf, s, os_strlen(s));
582 	wpabuf_put_str(buf, "</UDN>\n");
583 
584 	if (sm->wps->upc)
585 		xml_add_tagged_data(buf, "UPC", sm->wps->upc);
586 
587 	wpabuf_put_str(buf, wps_device_xml_postfix);
588 }
589 
590 
web_connection_stop(struct web_connection * c)591 void web_connection_stop(struct web_connection *c)
592 {
593 	struct upnp_wps_device_sm *sm = c->sm;
594 
595 	httpread_destroy(c->hread);
596 	c->hread = NULL;
597 	close(c->sd);
598 	c->sd = -1;
599 	if (c->next == c) {
600 		sm->web_connections = NULL;
601 	} else {
602 		if (sm->web_connections == c)
603 			sm->web_connections = c->next;
604 		c->next->prev = c->prev;
605 		c->prev->next = c->next;
606 	}
607 	os_free(c);
608 	sm->n_web_connections--;
609 }
610 
611 
http_put_reply_code(struct wpabuf * buf,enum http_reply_code code)612 static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
613 {
614 	wpabuf_put_str(buf, "HTTP/1.1 ");
615 	switch (code) {
616 	case HTTP_OK:
617 		wpabuf_put_str(buf, "200 OK\r\n");
618 		break;
619 	case HTTP_BAD_REQUEST:
620 		wpabuf_put_str(buf, "400 Bad request\r\n");
621 		break;
622 	case HTTP_PRECONDITION_FAILED:
623 		wpabuf_put_str(buf, "412 Precondition failed\r\n");
624 		break;
625 	case HTTP_UNIMPLEMENTED:
626 		wpabuf_put_str(buf, "501 Unimplemented\r\n");
627 		break;
628 	case HTTP_INTERNAL_SERVER_ERROR:
629 	default:
630 		wpabuf_put_str(buf, "500 Internal server error\r\n");
631 		break;
632 	}
633 }
634 
635 
http_put_date(struct wpabuf * buf)636 static void http_put_date(struct wpabuf *buf)
637 {
638 	wpabuf_put_str(buf, "Date: ");
639 	format_date(buf);
640 	wpabuf_put_str(buf, "\r\n");
641 }
642 
643 
http_put_empty(struct wpabuf * buf,enum http_reply_code code)644 static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
645 {
646 	http_put_reply_code(buf, code);
647 	wpabuf_put_str(buf, http_server_hdr);
648 	wpabuf_put_str(buf, http_connection_close);
649 	wpabuf_put_str(buf, "Content-Length: 0\r\n"
650 		       "\r\n");
651 }
652 
653 
654 /* Given that we have received a header w/ GET, act upon it
655  *
656  * Format of GET (case-insensitive):
657  *
658  * First line must be:
659  *      GET /<file> HTTP/1.1
660  * Since we don't do anything fancy we just ignore other lines.
661  *
662  * Our response (if no error) which includes only required lines is:
663  * HTTP/1.1 200 OK
664  * Connection: close
665  * Content-Type: text/xml
666  * Date: <rfc1123-date>
667  *
668  * Header lines must end with \r\n
669  * Per RFC 2616, content-length: is not required but connection:close
670  * would appear to be required (given that we will be closing it!).
671  */
web_connection_parse_get(struct web_connection * c,char * filename)672 static void web_connection_parse_get(struct web_connection *c, char *filename)
673 {
674 	struct upnp_wps_device_sm *sm = c->sm;
675 	struct wpabuf *buf; /* output buffer, allocated */
676 	char *put_length_here;
677 	char *body_start;
678 	enum {
679 		GET_DEVICE_XML_FILE,
680 		GET_SCPD_XML_FILE
681 	} req;
682 	size_t extra_len = 0;
683 	int body_length;
684 	char len_buf[10];
685 
686 	/*
687 	 * It is not required that filenames be case insensitive but it is
688 	 * allowed and cannot hurt here.
689 	 */
690 	if (filename == NULL)
691 		filename = "(null)"; /* just in case */
692 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
693 		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
694 		req = GET_DEVICE_XML_FILE;
695 		extra_len = 3000;
696 		if (sm->wps->friendly_name)
697 			extra_len += os_strlen(sm->wps->friendly_name);
698 		if (sm->wps->manufacturer_url)
699 			extra_len += os_strlen(sm->wps->manufacturer_url);
700 		if (sm->wps->model_description)
701 			extra_len += os_strlen(sm->wps->model_description);
702 		if (sm->wps->model_url)
703 			extra_len += os_strlen(sm->wps->model_url);
704 		if (sm->wps->upc)
705 			extra_len += os_strlen(sm->wps->upc);
706 	} else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
707 		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
708 		req = GET_SCPD_XML_FILE;
709 		extra_len = os_strlen(wps_scpd_xml);
710 	} else {
711 		/* File not found */
712 		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
713 			   filename);
714 		buf = wpabuf_alloc(200);
715 		if (buf == NULL)
716 			return;
717 		wpabuf_put_str(buf,
718 			       "HTTP/1.1 404 Not Found\r\n"
719 			       "Connection: close\r\n");
720 
721 		http_put_date(buf);
722 
723 		/* terminating empty line */
724 		wpabuf_put_str(buf, "\r\n");
725 
726 		goto send_buf;
727 	}
728 
729 	buf = wpabuf_alloc(1000 + extra_len);
730 	if (buf == NULL)
731 		return;
732 
733 	wpabuf_put_str(buf,
734 		       "HTTP/1.1 200 OK\r\n"
735 		       "Content-Type: text/xml; charset=\"utf-8\"\r\n");
736 	wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
737 	wpabuf_put_str(buf, "Connection: close\r\n");
738 	wpabuf_put_str(buf, "Content-Length: ");
739 	/*
740 	 * We will paste the length in later, leaving some extra whitespace.
741 	 * HTTP code is supposed to be tolerant of extra whitespace.
742 	 */
743 	put_length_here = wpabuf_put(buf, 0);
744 	wpabuf_put_str(buf, "        \r\n");
745 
746 	http_put_date(buf);
747 
748 	/* terminating empty line */
749 	wpabuf_put_str(buf, "\r\n");
750 
751 	body_start = wpabuf_put(buf, 0);
752 
753 	switch (req) {
754 	case GET_DEVICE_XML_FILE:
755 		format_wps_device_xml(sm, buf);
756 		break;
757 	case GET_SCPD_XML_FILE:
758 		wpabuf_put_str(buf, wps_scpd_xml);
759 		break;
760 	}
761 
762 	/* Now patch in the content length at the end */
763 	body_length = (char *) wpabuf_put(buf, 0) - body_start;
764 	os_snprintf(len_buf, 10, "%d", body_length);
765 	os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
766 
767 send_buf:
768 	send_wpabuf(c->sd, buf);
769 	wpabuf_free(buf);
770 }
771 
772 
web_get_item(char * data,const char * name,enum http_reply_code * ret)773 static struct wpabuf * web_get_item(char *data, const char *name,
774 				    enum http_reply_code *ret)
775 {
776 	char *msg;
777 	struct wpabuf *buf;
778 	unsigned char *decoded;
779 	size_t len;
780 
781 	if (upnp_get_first_document_item(data, name, &msg)) {
782 		*ret = UPNP_ARG_VALUE_INVALID;
783 		return NULL;
784 	}
785 
786 	decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
787 	os_free(msg);
788 	if (decoded == NULL) {
789 		*ret = UPNP_OUT_OF_MEMORY;
790 		return NULL;
791 	}
792 
793 	buf = wpabuf_alloc_ext_data(decoded, len);
794 	if (buf == NULL) {
795 		os_free(decoded);
796 		*ret = UPNP_OUT_OF_MEMORY;
797 		return NULL;
798 	}
799 	return buf;
800 }
801 
802 
803 static enum http_reply_code
web_process_get_device_info(struct upnp_wps_device_sm * sm,struct wpabuf ** reply,const char ** replyname)804 web_process_get_device_info(struct upnp_wps_device_sm *sm,
805 			    struct wpabuf **reply, const char **replyname)
806 {
807 	static const char *name = "NewDeviceInfo";
808 
809 	wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
810 	if (sm->ctx->rx_req_get_device_info == NULL)
811 		return HTTP_INTERNAL_SERVER_ERROR;
812 	*reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer);
813 	if (*reply == NULL) {
814 		wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
815 		return HTTP_INTERNAL_SERVER_ERROR;
816 	}
817 	*replyname = name;
818 	return HTTP_OK;
819 }
820 
821 
822 static enum http_reply_code
web_process_put_message(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)823 web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
824 			struct wpabuf **reply, const char **replyname)
825 {
826 	struct wpabuf *msg;
827 	static const char *name = "NewOutMessage";
828 	enum http_reply_code ret;
829 
830 	/*
831 	 * PutMessage is used by external UPnP-based Registrar to perform WPS
832 	 * operation with the access point itself; as compared with
833 	 * PutWLANResponse which is for proxying.
834 	 */
835 	wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
836 	if (sm->ctx->rx_req_put_message == NULL)
837 		return HTTP_INTERNAL_SERVER_ERROR;
838 	msg = web_get_item(data, "NewInMessage", &ret);
839 	if (msg == NULL)
840 		return ret;
841 	*reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg);
842 	wpabuf_free(msg);
843 	if (*reply == NULL)
844 		return HTTP_INTERNAL_SERVER_ERROR;
845 	*replyname = name;
846 	return HTTP_OK;
847 }
848 
849 
850 static enum http_reply_code
web_process_get_ap_settings(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)851 web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data,
852 			    struct wpabuf **reply, const char **replyname)
853 {
854 	struct wpabuf *msg;
855 	static const char *name = "NewAPSettings";
856 	enum http_reply_code ret;
857 
858 	wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings");
859 	if (sm->ctx->rx_req_get_ap_settings == NULL)
860 		return HTTP_INTERNAL_SERVER_ERROR;
861 	msg = web_get_item(data, "NewMessage", &ret);
862 	if (msg == NULL)
863 		return ret;
864 	*reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg);
865 	wpabuf_free(msg);
866 	if (*reply == NULL)
867 		return HTTP_INTERNAL_SERVER_ERROR;
868 	*replyname = name;
869 	return HTTP_OK;
870 }
871 
872 
873 static enum http_reply_code
web_process_set_ap_settings(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)874 web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data,
875 			    struct wpabuf **reply, const char **replyname)
876 {
877 	struct wpabuf *msg;
878 	enum http_reply_code ret;
879 
880 	wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings");
881 	msg = web_get_item(data, "NewAPSettings", &ret);
882 	if (msg == NULL)
883 		return ret;
884 	if (!sm->ctx->rx_req_set_ap_settings ||
885 	    sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) {
886 		wpabuf_free(msg);
887 		return HTTP_INTERNAL_SERVER_ERROR;
888 	}
889 	wpabuf_free(msg);
890 	*replyname = NULL;
891 	*reply = NULL;
892 	return HTTP_OK;
893 }
894 
895 
896 static enum http_reply_code
web_process_del_ap_settings(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)897 web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data,
898 			    struct wpabuf **reply, const char **replyname)
899 {
900 	struct wpabuf *msg;
901 	enum http_reply_code ret;
902 
903 	wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings");
904 	msg = web_get_item(data, "NewAPSettings", &ret);
905 	if (msg == NULL)
906 		return ret;
907 	if (!sm->ctx->rx_req_del_ap_settings ||
908 	    sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) {
909 		wpabuf_free(msg);
910 		return HTTP_INTERNAL_SERVER_ERROR;
911 	}
912 	wpabuf_free(msg);
913 	*replyname = NULL;
914 	*reply = NULL;
915 	return HTTP_OK;
916 }
917 
918 
919 static enum http_reply_code
web_process_get_sta_settings(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)920 web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data,
921 			     struct wpabuf **reply, const char **replyname)
922 {
923 	struct wpabuf *msg;
924 	static const char *name = "NewSTASettings";
925 	enum http_reply_code ret;
926 
927 	wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings");
928 	if (sm->ctx->rx_req_get_sta_settings == NULL)
929 		return HTTP_INTERNAL_SERVER_ERROR;
930 	msg = web_get_item(data, "NewMessage", &ret);
931 	if (msg == NULL)
932 		return ret;
933 	*reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg);
934 	wpabuf_free(msg);
935 	if (*reply == NULL)
936 		return HTTP_INTERNAL_SERVER_ERROR;
937 	*replyname = name;
938 	return HTTP_OK;
939 }
940 
941 
942 static enum http_reply_code
web_process_set_sta_settings(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)943 web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data,
944 			     struct wpabuf **reply, const char **replyname)
945 {
946 	struct wpabuf *msg;
947 	enum http_reply_code ret;
948 
949 	wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings");
950 	msg = web_get_item(data, "NewSTASettings", &ret);
951 	if (msg == NULL)
952 		return ret;
953 	if (!sm->ctx->rx_req_set_sta_settings ||
954 	    sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) {
955 		wpabuf_free(msg);
956 		return HTTP_INTERNAL_SERVER_ERROR;
957 	}
958 	wpabuf_free(msg);
959 	*replyname = NULL;
960 	*reply = NULL;
961 	return HTTP_OK;
962 }
963 
964 
965 static enum http_reply_code
web_process_del_sta_settings(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)966 web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data,
967 			     struct wpabuf **reply, const char **replyname)
968 {
969 	struct wpabuf *msg;
970 	enum http_reply_code ret;
971 
972 	wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings");
973 	msg = web_get_item(data, "NewSTASettings", &ret);
974 	if (msg == NULL)
975 		return ret;
976 	if (!sm->ctx->rx_req_del_sta_settings ||
977 	    sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) {
978 		wpabuf_free(msg);
979 		return HTTP_INTERNAL_SERVER_ERROR;
980 	}
981 	wpabuf_free(msg);
982 	*replyname = NULL;
983 	*reply = NULL;
984 	return HTTP_OK;
985 }
986 
987 
988 static enum http_reply_code
web_process_put_wlan_response(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)989 web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
990 			      struct wpabuf **reply, const char **replyname)
991 {
992 	struct wpabuf *msg;
993 	enum http_reply_code ret;
994 	u8 macaddr[ETH_ALEN];
995 	int ev_type;
996 	int type;
997 	char *val;
998 
999 	/*
1000 	 * External UPnP-based Registrar is passing us a message to be proxied
1001 	 * over to a Wi-Fi -based client of ours.
1002 	 */
1003 
1004 	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
1005 	msg = web_get_item(data, "NewMessage", &ret);
1006 	if (msg == NULL)
1007 		return ret;
1008 	if (upnp_get_first_document_item(data, "NewWLANEventType", &val)) {
1009 		wpabuf_free(msg);
1010 		return UPNP_ARG_VALUE_INVALID;
1011 	}
1012 	ev_type = atol(val);
1013 	os_free(val);
1014 	val = NULL;
1015 	if (upnp_get_first_document_item(data, "NewWLANEventMAC", &val) ||
1016 	    hwaddr_aton(val, macaddr)) {
1017 		wpabuf_free(msg);
1018 		os_free(val);
1019 		return UPNP_ARG_VALUE_INVALID;
1020 	}
1021 	os_free(val);
1022 	if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
1023 		struct wps_parse_attr attr;
1024 		if (wps_parse_msg(msg, &attr) < 0 ||
1025 		    attr.msg_type == NULL)
1026 			type = -1;
1027 		else
1028 			type = *attr.msg_type;
1029 		wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
1030 	} else
1031 		type = -1;
1032 	if (!sm->ctx->rx_req_put_wlan_response ||
1033 	    sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
1034 					      type)) {
1035 		wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
1036 			   "rx_req_put_wlan_response");
1037 		wpabuf_free(msg);
1038 		return HTTP_INTERNAL_SERVER_ERROR;
1039 	}
1040 	wpabuf_free(msg);
1041 	*replyname = NULL;
1042 	*reply = NULL;
1043 	return HTTP_OK;
1044 }
1045 
1046 
1047 static enum http_reply_code
web_process_set_selected_registrar(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)1048 web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data,
1049 				   struct wpabuf **reply,
1050 				   const char **replyname)
1051 {
1052 	struct wpabuf *msg;
1053 	enum http_reply_code ret;
1054 
1055 	wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
1056 	msg = web_get_item(data, "NewMessage", &ret);
1057 	if (msg == NULL)
1058 		return ret;
1059 	if (!sm->ctx->rx_req_set_selected_registrar ||
1060 	    sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) {
1061 		wpabuf_free(msg);
1062 		return HTTP_INTERNAL_SERVER_ERROR;
1063 	}
1064 	wpabuf_free(msg);
1065 	*replyname = NULL;
1066 	*reply = NULL;
1067 	return HTTP_OK;
1068 }
1069 
1070 
1071 static enum http_reply_code
web_process_reboot_ap(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)1072 web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data,
1073 		      struct wpabuf **reply, const char **replyname)
1074 {
1075 	struct wpabuf *msg;
1076 	enum http_reply_code ret;
1077 
1078 	wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP");
1079 	msg = web_get_item(data, "NewAPSettings", &ret);
1080 	if (msg == NULL)
1081 		return ret;
1082 	if (!sm->ctx->rx_req_reboot_ap ||
1083 	    sm->ctx->rx_req_reboot_ap(sm->priv, msg)) {
1084 		wpabuf_free(msg);
1085 		return HTTP_INTERNAL_SERVER_ERROR;
1086 	}
1087 	wpabuf_free(msg);
1088 	*replyname = NULL;
1089 	*reply = NULL;
1090 	return HTTP_OK;
1091 }
1092 
1093 
1094 static enum http_reply_code
web_process_reset_ap(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)1095 web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data,
1096 		     struct wpabuf **reply, const char **replyname)
1097 {
1098 	struct wpabuf *msg;
1099 	enum http_reply_code ret;
1100 
1101 	wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP");
1102 	msg = web_get_item(data, "NewMessage", &ret);
1103 	if (msg == NULL)
1104 		return ret;
1105 	if (!sm->ctx->rx_req_reset_ap ||
1106 	    sm->ctx->rx_req_reset_ap(sm->priv, msg)) {
1107 		wpabuf_free(msg);
1108 		return HTTP_INTERNAL_SERVER_ERROR;
1109 	}
1110 	wpabuf_free(msg);
1111 	*replyname = NULL;
1112 	*reply = NULL;
1113 	return HTTP_OK;
1114 }
1115 
1116 
1117 static enum http_reply_code
web_process_reboot_sta(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)1118 web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data,
1119 		       struct wpabuf **reply, const char **replyname)
1120 {
1121 	struct wpabuf *msg;
1122 	enum http_reply_code ret;
1123 
1124 	wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA");
1125 	msg = web_get_item(data, "NewSTASettings", &ret);
1126 	if (msg == NULL)
1127 		return ret;
1128 	if (!sm->ctx->rx_req_reboot_sta ||
1129 	    sm->ctx->rx_req_reboot_sta(sm->priv, msg)) {
1130 		wpabuf_free(msg);
1131 		return HTTP_INTERNAL_SERVER_ERROR;
1132 	}
1133 	wpabuf_free(msg);
1134 	*replyname = NULL;
1135 	*reply = NULL;
1136 	return HTTP_OK;
1137 }
1138 
1139 
1140 static enum http_reply_code
web_process_reset_sta(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)1141 web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data,
1142 		      struct wpabuf **reply, const char **replyname)
1143 {
1144 	struct wpabuf *msg;
1145 	enum http_reply_code ret;
1146 
1147 	wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA");
1148 	msg = web_get_item(data, "NewMessage", &ret);
1149 	if (msg == NULL)
1150 		return ret;
1151 	if (!sm->ctx->rx_req_reset_sta ||
1152 	    sm->ctx->rx_req_reset_sta(sm->priv, msg)) {
1153 		wpabuf_free(msg);
1154 		return HTTP_INTERNAL_SERVER_ERROR;
1155 	}
1156 	wpabuf_free(msg);
1157 	*replyname = NULL;
1158 	*reply = NULL;
1159 	return HTTP_OK;
1160 }
1161 
1162 
1163 static const char *soap_prefix =
1164 	"<?xml version=\"1.0\"?>\n"
1165 	"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
1166 	"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
1167 	"<s:Body>\n";
1168 static const char *soap_postfix =
1169 	"</s:Body>\n</s:Envelope>\n";
1170 
1171 static const char *soap_error_prefix =
1172 	"<s:Fault>\n"
1173 	"<faultcode>s:Client</faultcode>\n"
1174 	"<faultstring>UPnPError</faultstring>\n"
1175 	"<detail>\n"
1176 	"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
1177 static const char *soap_error_postfix =
1178 	"<errorDescription>Error</errorDescription>\n"
1179 	"</UPnPError>\n"
1180 	"</detail>\n"
1181 	"</s:Fault>\n";
1182 
web_connection_send_reply(struct web_connection * c,enum http_reply_code ret,const char * action,int action_len,const struct wpabuf * reply,const char * replyname)1183 static void web_connection_send_reply(struct web_connection *c,
1184 				      enum http_reply_code ret,
1185 				      const char *action, int action_len,
1186 				      const struct wpabuf *reply,
1187 				      const char *replyname)
1188 {
1189 	struct wpabuf *buf;
1190 	char *replydata;
1191 	char *put_length_here = NULL;
1192 	char *body_start = NULL;
1193 
1194 	if (reply) {
1195 		size_t len;
1196 		replydata = (char *) base64_encode(wpabuf_head(reply),
1197 						   wpabuf_len(reply), &len);
1198 	} else
1199 		replydata = NULL;
1200 
1201 	/* Parameters of the response:
1202 	 *      action(action_len) -- action we are responding to
1203 	 *      replyname -- a name we need for the reply
1204 	 *      replydata -- NULL or null-terminated string
1205 	 */
1206 	buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
1207 			   (action_len > 0 ? action_len * 2 : 0));
1208 	if (buf == NULL) {
1209 		wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
1210 			   "POST");
1211 		wpabuf_free(buf);
1212 		os_free(replydata);
1213 		return;
1214 	}
1215 
1216 	/*
1217 	 * Assuming we will be successful, put in the output header first.
1218 	 * Note: we do not keep connections alive (and httpread does
1219 	 * not support it)... therefore we must have Connection: close.
1220 	 */
1221 	if (ret == HTTP_OK) {
1222 		wpabuf_put_str(buf,
1223 			       "HTTP/1.1 200 OK\r\n"
1224 			       "Content-Type: text/xml; "
1225 			       "charset=\"utf-8\"\r\n");
1226 	} else {
1227 		wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
1228 	}
1229 	wpabuf_put_str(buf, http_connection_close);
1230 
1231 	wpabuf_put_str(buf, "Content-Length: ");
1232 	/*
1233 	 * We will paste the length in later, leaving some extra whitespace.
1234 	 * HTTP code is supposed to be tolerant of extra whitespace.
1235 	 */
1236 	put_length_here = wpabuf_put(buf, 0);
1237 	wpabuf_put_str(buf, "        \r\n");
1238 
1239 	http_put_date(buf);
1240 
1241 	/* terminating empty line */
1242 	wpabuf_put_str(buf, "\r\n");
1243 
1244 	body_start = wpabuf_put(buf, 0);
1245 
1246 	if (ret == HTTP_OK) {
1247 		wpabuf_put_str(buf, soap_prefix);
1248 		wpabuf_put_str(buf, "<u:");
1249 		wpabuf_put_data(buf, action, action_len);
1250 		wpabuf_put_str(buf, "Response xmlns:u=\"");
1251 		wpabuf_put_str(buf, urn_wfawlanconfig);
1252 		wpabuf_put_str(buf, "\">\n");
1253 		if (replydata && replyname) {
1254 			/* TODO: might possibly need to escape part of reply
1255 			 * data? ...
1256 			 * probably not, unlikely to have ampersand(&) or left
1257 			 * angle bracket (<) in it...
1258 			 */
1259 			wpabuf_printf(buf, "<%s>", replyname);
1260 			wpabuf_put_str(buf, replydata);
1261 			wpabuf_printf(buf, "</%s>\n", replyname);
1262 		}
1263 		wpabuf_put_str(buf, "</u:");
1264 		wpabuf_put_data(buf, action, action_len);
1265 		wpabuf_put_str(buf, "Response>\n");
1266 		wpabuf_put_str(buf, soap_postfix);
1267 	} else {
1268 		/* Error case */
1269 		wpabuf_put_str(buf, soap_prefix);
1270 		wpabuf_put_str(buf, soap_error_prefix);
1271 		wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
1272 		wpabuf_put_str(buf, soap_error_postfix);
1273 		wpabuf_put_str(buf, soap_postfix);
1274 	}
1275 	os_free(replydata);
1276 
1277 	/* Now patch in the content length at the end */
1278 	if (body_start && put_length_here) {
1279 		int body_length = (char *) wpabuf_put(buf, 0) - body_start;
1280 		char len_buf[10];
1281 		os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
1282 		os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
1283 	}
1284 
1285 	send_wpabuf(c->sd, buf);
1286 	wpabuf_free(buf);
1287 }
1288 
1289 
web_get_action(struct web_connection * c,const char * filename,size_t * action_len)1290 static const char * web_get_action(struct web_connection *c,
1291 				   const char *filename, size_t *action_len)
1292 {
1293 	const char *match;
1294 	int match_len;
1295 	char *b;
1296 	char *action;
1297 
1298 	*action_len = 0;
1299 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
1300 		wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
1301 			   filename);
1302 		return NULL;
1303 	}
1304 	/* The SOAPAction line of the header tells us what we want to do */
1305 	b = httpread_hdr_line_get(c->hread, "SOAPAction:");
1306 	if (b == NULL)
1307 		return NULL;
1308 	if (*b == '"')
1309 		b++;
1310 	else
1311 		return NULL;
1312 	match = urn_wfawlanconfig;
1313 	match_len = os_strlen(urn_wfawlanconfig) - 1;
1314 	if (os_strncasecmp(b, match, match_len))
1315 		return NULL;
1316 	b += match_len;
1317 	/* skip over version */
1318 	while (isgraph(*b) && *b != '#')
1319 		b++;
1320 	if (*b != '#')
1321 		return NULL;
1322 	b++;
1323 	/* Following the sharp(#) should be the action and a double quote */
1324 	action = b;
1325 	while (isgraph(*b) && *b != '"')
1326 		b++;
1327 	if (*b != '"')
1328 		return NULL;
1329 	*action_len = b - action;
1330 	return action;
1331 }
1332 
1333 
1334 /* Given that we have received a header w/ POST, act upon it
1335  *
1336  * Format of POST (case-insensitive):
1337  *
1338  * First line must be:
1339  *      POST /<file> HTTP/1.1
1340  * Since we don't do anything fancy we just ignore other lines.
1341  *
1342  * Our response (if no error) which includes only required lines is:
1343  * HTTP/1.1 200 OK
1344  * Connection: close
1345  * Content-Type: text/xml
1346  * Date: <rfc1123-date>
1347  *
1348  * Header lines must end with \r\n
1349  * Per RFC 2616, content-length: is not required but connection:close
1350  * would appear to be required (given that we will be closing it!).
1351  */
web_connection_parse_post(struct web_connection * c,const char * filename)1352 static void web_connection_parse_post(struct web_connection *c,
1353 				      const char *filename)
1354 {
1355 	enum http_reply_code ret;
1356 	struct upnp_wps_device_sm *sm = c->sm;
1357 	char *data = httpread_data_get(c->hread); /* body of http msg */
1358 	const char *action;
1359 	size_t action_len;
1360 	const char *replyname = NULL; /* argument name for the reply */
1361 	struct wpabuf *reply = NULL; /* data for the reply */
1362 
1363 	ret = UPNP_INVALID_ACTION;
1364 	action = web_get_action(c, filename, &action_len);
1365 	if (action == NULL)
1366 		goto bad;
1367 
1368 	/*
1369 	 * There are quite a few possible actions. Although we appear to
1370 	 * support them all here, not all of them are necessarily supported by
1371 	 * callbacks at higher levels.
1372 	 */
1373 	if (!os_strncasecmp("GetDeviceInfo", action, action_len))
1374 		ret = web_process_get_device_info(sm, &reply, &replyname);
1375 	else if (!os_strncasecmp("PutMessage", action, action_len))
1376 		ret = web_process_put_message(sm, data, &reply, &replyname);
1377 	else if (!os_strncasecmp("GetAPSettings", action, action_len))
1378 		ret = web_process_get_ap_settings(sm, data, &reply,
1379 						  &replyname);
1380 	else if (!os_strncasecmp("SetAPSettings", action, action_len))
1381 		ret = web_process_set_ap_settings(sm, data, &reply,
1382 						  &replyname);
1383 	else if (!os_strncasecmp("DelAPSettings", action, action_len))
1384 		ret = web_process_del_ap_settings(sm, data, &reply,
1385 						  &replyname);
1386 	else if (!os_strncasecmp("GetSTASettings", action, action_len))
1387 		ret = web_process_get_sta_settings(sm, data, &reply,
1388 						   &replyname);
1389 	else if (!os_strncasecmp("SetSTASettings", action, action_len))
1390 		ret = web_process_set_sta_settings(sm, data, &reply,
1391 						   &replyname);
1392 	else if (!os_strncasecmp("DelSTASettings", action, action_len))
1393 		ret = web_process_del_sta_settings(sm, data, &reply,
1394 						  &replyname);
1395 	else if (!os_strncasecmp("PutWLANResponse", action, action_len))
1396 		ret = web_process_put_wlan_response(sm, data, &reply,
1397 						    &replyname);
1398 	else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
1399 		ret = web_process_set_selected_registrar(sm, data, &reply,
1400 							 &replyname);
1401 	else if (!os_strncasecmp("RebootAP", action, action_len))
1402 		ret = web_process_reboot_ap(sm, data, &reply, &replyname);
1403 	else if (!os_strncasecmp("ResetAP", action, action_len))
1404 		ret = web_process_reset_ap(sm, data, &reply, &replyname);
1405 	else if (!os_strncasecmp("RebootSTA", action, action_len))
1406 		ret = web_process_reboot_sta(sm, data, &reply, &replyname);
1407 	else if (!os_strncasecmp("ResetSTA", action, action_len))
1408 		ret = web_process_reset_sta(sm, data, &reply, &replyname);
1409 	else
1410 		wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
1411 
1412 bad:
1413 	if (ret != HTTP_OK)
1414 		wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
1415 	web_connection_send_reply(c, ret, action, action_len, reply,
1416 				  replyname);
1417 	wpabuf_free(reply);
1418 }
1419 
1420 
1421 /* Given that we have received a header w/ SUBSCRIBE, act upon it
1422  *
1423  * Format of SUBSCRIBE (case-insensitive):
1424  *
1425  * First line must be:
1426  *      SUBSCRIBE /wps_event HTTP/1.1
1427  *
1428  * Our response (if no error) which includes only required lines is:
1429  * HTTP/1.1 200 OK
1430  * Server: xx, UPnP/1.0, xx
1431  * SID: uuid:xxxxxxxxx
1432  * Timeout: Second-<n>
1433  * Content-Length: 0
1434  * Date: xxxx
1435  *
1436  * Header lines must end with \r\n
1437  * Per RFC 2616, content-length: is not required but connection:close
1438  * would appear to be required (given that we will be closing it!).
1439  */
web_connection_parse_subscribe(struct web_connection * c,const char * filename)1440 static void web_connection_parse_subscribe(struct web_connection *c,
1441 					   const char *filename)
1442 {
1443 	struct upnp_wps_device_sm *sm = c->sm;
1444 	struct wpabuf *buf;
1445 	char *b;
1446 	char *hdr = httpread_hdr_get(c->hread);
1447 	char *h;
1448 	char *match;
1449 	int match_len;
1450 	char *end;
1451 	int len;
1452 	int got_nt = 0;
1453 	u8 uuid[UUID_LEN];
1454 	int got_uuid = 0;
1455 	char *callback_urls = NULL;
1456 	struct subscription *s = NULL;
1457 	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1458 
1459 	buf = wpabuf_alloc(1000);
1460 	if (buf == NULL)
1461 		return;
1462 
1463 	/* Parse/validate headers */
1464 	h = hdr;
1465 	/* First line: SUBSCRIBE /wps_event HTTP/1.1
1466 	 * has already been parsed.
1467 	 */
1468 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1469 		ret = HTTP_PRECONDITION_FAILED;
1470 		goto error;
1471 	}
1472 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
1473 	end = os_strchr(h, '\n');
1474 
1475 	for (; end != NULL; h = end + 1) {
1476 		/* Option line by option line */
1477 		h = end + 1;
1478 		end = os_strchr(h, '\n');
1479 		if (end == NULL)
1480 			break; /* no unterminated lines allowed */
1481 
1482 		/* NT assures that it is our type of subscription;
1483 		 * not used for a renewl.
1484 		 **/
1485 		match = "NT:";
1486 		match_len = os_strlen(match);
1487 		if (os_strncasecmp(h, match, match_len) == 0) {
1488 			h += match_len;
1489 			while (*h == ' ' || *h == '\t')
1490 				h++;
1491 			match = "upnp:event";
1492 			match_len = os_strlen(match);
1493 			if (os_strncasecmp(h, match, match_len) != 0) {
1494 				ret = HTTP_BAD_REQUEST;
1495 				goto error;
1496 			}
1497 			got_nt = 1;
1498 			continue;
1499 		}
1500 		/* HOST should refer to us */
1501 #if 0
1502 		match = "HOST:";
1503 		match_len = os_strlen(match);
1504 		if (os_strncasecmp(h, match, match_len) == 0) {
1505 			h += match_len;
1506 			while (*h == ' ' || *h == '\t')
1507 				h++;
1508 			.....
1509 		}
1510 #endif
1511 		/* CALLBACK gives one or more URLs for NOTIFYs
1512 		 * to be sent as a result of the subscription.
1513 		 * Each URL is enclosed in angle brackets.
1514 		 */
1515 		match = "CALLBACK:";
1516 		match_len = os_strlen(match);
1517 		if (os_strncasecmp(h, match, match_len) == 0) {
1518 			h += match_len;
1519 			while (*h == ' ' || *h == '\t')
1520 				h++;
1521 			len = end - h;
1522 			os_free(callback_urls);
1523 			callback_urls = os_malloc(len + 1);
1524 			if (callback_urls == NULL) {
1525 				ret = HTTP_INTERNAL_SERVER_ERROR;
1526 				goto error;
1527 			}
1528 			os_memcpy(callback_urls, h, len);
1529 			callback_urls[len] = 0;
1530 			continue;
1531 		}
1532 		/* SID is only for renewal */
1533 		match = "SID:";
1534 		match_len = os_strlen(match);
1535 		if (os_strncasecmp(h, match, match_len) == 0) {
1536 			h += match_len;
1537 			while (*h == ' ' || *h == '\t')
1538 				h++;
1539 			match = "uuid:";
1540 			match_len = os_strlen(match);
1541 			if (os_strncasecmp(h, match, match_len) != 0) {
1542 				ret = HTTP_BAD_REQUEST;
1543 				goto error;
1544 			}
1545 			h += match_len;
1546 			while (*h == ' ' || *h == '\t')
1547 				h++;
1548 			if (uuid_str2bin(h, uuid)) {
1549 				ret = HTTP_BAD_REQUEST;
1550 				goto error;
1551 			}
1552 			got_uuid = 1;
1553 			continue;
1554 		}
1555 		/* TIMEOUT is requested timeout, but apparently we can
1556 		 * just ignore this.
1557 		 */
1558 	}
1559 
1560 	if (got_uuid) {
1561 		/* renewal */
1562 		if (callback_urls) {
1563 			ret = HTTP_BAD_REQUEST;
1564 			goto error;
1565 		}
1566 		s = subscription_renew(sm, uuid);
1567 		if (s == NULL) {
1568 			ret = HTTP_PRECONDITION_FAILED;
1569 			goto error;
1570 		}
1571 	} else if (callback_urls) {
1572 		if (!got_nt) {
1573 			ret = HTTP_PRECONDITION_FAILED;
1574 			goto error;
1575 		}
1576 		s = subscription_start(sm, callback_urls);
1577 		if (s == NULL) {
1578 			ret = HTTP_INTERNAL_SERVER_ERROR;
1579 			goto error;
1580 		}
1581 	} else {
1582 		ret = HTTP_PRECONDITION_FAILED;
1583 		goto error;
1584 	}
1585 
1586 	/* success */
1587 	http_put_reply_code(buf, HTTP_OK);
1588 	wpabuf_put_str(buf, http_server_hdr);
1589 	wpabuf_put_str(buf, http_connection_close);
1590 	wpabuf_put_str(buf, "Content-Length: 0\r\n");
1591 	wpabuf_put_str(buf, "SID: uuid:");
1592 	/* subscription id */
1593 	b = wpabuf_put(buf, 0);
1594 	uuid_bin2str(s->uuid, b, 80);
1595 	wpabuf_put(buf, os_strlen(b));
1596 	wpabuf_put_str(buf, "\r\n");
1597 	wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
1598 	http_put_date(buf);
1599 	/* And empty line to terminate header: */
1600 	wpabuf_put_str(buf, "\r\n");
1601 
1602 	send_wpabuf(c->sd, buf);
1603 	wpabuf_free(buf);
1604 	os_free(callback_urls);
1605 	return;
1606 
1607 error:
1608 	/* Per UPnP spec:
1609 	* Errors
1610 	* Incompatible headers
1611 	*   400 Bad Request. If SID header and one of NT or CALLBACK headers
1612 	*     are present, the publisher must respond with HTTP error
1613 	*     400 Bad Request.
1614 	* Missing or invalid CALLBACK
1615 	*   412 Precondition Failed. If CALLBACK header is missing or does not
1616 	*     contain a valid HTTP URL, the publisher must respond with HTTP
1617 	*     error 412 Precondition Failed.
1618 	* Invalid NT
1619 	*   412 Precondition Failed. If NT header does not equal upnp:event,
1620 	*     the publisher must respond with HTTP error 412 Precondition
1621 	*     Failed.
1622 	* [For resubscription, use 412 if unknown uuid].
1623 	* Unable to accept subscription
1624 	*   5xx. If a publisher is not able to accept a subscription (such as
1625 	*     due to insufficient resources), it must respond with a
1626 	*     HTTP 500-series error code.
1627 	*   599 Too many subscriptions (not a standard HTTP error)
1628 	*/
1629 	http_put_empty(buf, ret);
1630 	send_wpabuf(c->sd, buf);
1631 	wpabuf_free(buf);
1632 	os_free(callback_urls);
1633 }
1634 
1635 
1636 /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
1637  *
1638  * Format of UNSUBSCRIBE (case-insensitive):
1639  *
1640  * First line must be:
1641  *      UNSUBSCRIBE /wps_event HTTP/1.1
1642  *
1643  * Our response (if no error) which includes only required lines is:
1644  * HTTP/1.1 200 OK
1645  * Content-Length: 0
1646  *
1647  * Header lines must end with \r\n
1648  * Per RFC 2616, content-length: is not required but connection:close
1649  * would appear to be required (given that we will be closing it!).
1650  */
web_connection_parse_unsubscribe(struct web_connection * c,const char * filename)1651 static void web_connection_parse_unsubscribe(struct web_connection *c,
1652 					     const char *filename)
1653 {
1654 	struct upnp_wps_device_sm *sm = c->sm;
1655 	struct wpabuf *buf;
1656 	char *hdr = httpread_hdr_get(c->hread);
1657 	char *h;
1658 	char *match;
1659 	int match_len;
1660 	char *end;
1661 	u8 uuid[UUID_LEN];
1662 	int got_uuid = 0;
1663 	struct subscription *s = NULL;
1664 	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1665 
1666 	/* Parse/validate headers */
1667 	h = hdr;
1668 	/* First line: UNSUBSCRIBE /wps_event HTTP/1.1
1669 	 * has already been parsed.
1670 	 */
1671 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1672 		ret = HTTP_PRECONDITION_FAILED;
1673 		goto send_msg;
1674 	}
1675 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
1676 	end = os_strchr(h, '\n');
1677 
1678 	for (; end != NULL; h = end + 1) {
1679 		/* Option line by option line */
1680 		h = end + 1;
1681 		end = os_strchr(h, '\n');
1682 		if (end == NULL)
1683 			break; /* no unterminated lines allowed */
1684 
1685 		/* HOST should refer to us */
1686 #if 0
1687 		match = "HOST:";
1688 		match_len = os_strlen(match);
1689 		if (os_strncasecmp(h, match, match_len) == 0) {
1690 			h += match_len;
1691 			while (*h == ' ' || *h == '\t')
1692 				h++;
1693 			.....
1694 		}
1695 #endif
1696 		/* SID is only for renewal */
1697 		match = "SID:";
1698 		match_len = os_strlen(match);
1699 		if (os_strncasecmp(h, match, match_len) == 0) {
1700 			h += match_len;
1701 			while (*h == ' ' || *h == '\t')
1702 				h++;
1703 			match = "uuid:";
1704 			match_len = os_strlen(match);
1705 			if (os_strncasecmp(h, match, match_len) != 0) {
1706 				ret = HTTP_BAD_REQUEST;
1707 				goto send_msg;
1708 			}
1709 			h += match_len;
1710 			while (*h == ' ' || *h == '\t')
1711 				h++;
1712 			if (uuid_str2bin(h, uuid)) {
1713 				ret = HTTP_BAD_REQUEST;
1714 				goto send_msg;
1715 			}
1716 			got_uuid = 1;
1717 			continue;
1718 		}
1719 	}
1720 
1721 	if (got_uuid) {
1722 		s = subscription_find(sm, uuid);
1723 		if (s) {
1724 			wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
1725 				   s,
1726 				   (s && s->addr_list &&
1727 				    s->addr_list->domain_and_port) ?
1728 				   s->addr_list->domain_and_port : "-null-");
1729 			subscription_unlink(s);
1730 			subscription_destroy(s);
1731 		}
1732 	} else {
1733 		wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
1734 			   "found)");
1735 		ret = HTTP_PRECONDITION_FAILED;
1736 		goto send_msg;
1737 	}
1738 
1739 	ret = HTTP_OK;
1740 
1741 send_msg:
1742 	buf = wpabuf_alloc(200);
1743 	if (buf == NULL)
1744 		return;
1745 	http_put_empty(buf, ret);
1746 	send_wpabuf(c->sd, buf);
1747 	wpabuf_free(buf);
1748 }
1749 
1750 
1751 /* Send error in response to unknown requests */
web_connection_unimplemented(struct web_connection * c)1752 static void web_connection_unimplemented(struct web_connection *c)
1753 {
1754 	struct wpabuf *buf;
1755 	buf = wpabuf_alloc(200);
1756 	if (buf == NULL)
1757 		return;
1758 	http_put_empty(buf, HTTP_UNIMPLEMENTED);
1759 	send_wpabuf(c->sd, buf);
1760 	wpabuf_free(buf);
1761 }
1762 
1763 
1764 
1765 /* Called when we have gotten an apparently valid http request.
1766  */
web_connection_check_data(struct web_connection * c)1767 static void web_connection_check_data(struct web_connection *c)
1768 {
1769 	struct httpread *hread = c->hread;
1770 	enum httpread_hdr_type htype = httpread_hdr_type_get(hread);
1771 	/* char *data = httpread_data_get(hread); */
1772 	char *filename = httpread_uri_get(hread);
1773 
1774 	c->done = 1;
1775 	if (!filename) {
1776 		wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
1777 		return;
1778 	}
1779 	/* Trim leading slashes from filename */
1780 	while (*filename == '/')
1781 		filename++;
1782 
1783 	wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
1784 		   htype, inet_ntoa(c->cli_addr.sin_addr),
1785 		   htons(c->cli_addr.sin_port));
1786 
1787 	switch (htype) {
1788 	case HTTPREAD_HDR_TYPE_GET:
1789 		web_connection_parse_get(c, filename);
1790 		break;
1791 	case HTTPREAD_HDR_TYPE_POST:
1792 		web_connection_parse_post(c, filename);
1793 		break;
1794 	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
1795 		web_connection_parse_subscribe(c, filename);
1796 		break;
1797 	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
1798 		web_connection_parse_unsubscribe(c, filename);
1799 		break;
1800 		/* We are not required to support M-POST; just plain
1801 		 * POST is supposed to work, so we only support that.
1802 		 * If for some reason we need to support M-POST, it is
1803 		 * mostly the same as POST, with small differences.
1804 		 */
1805 	default:
1806 		/* Send 501 for anything else */
1807 		web_connection_unimplemented(c);
1808 		break;
1809 	}
1810 }
1811 
1812 
1813 
1814 /* called back when we have gotten request */
web_connection_got_file_handler(struct httpread * handle,void * cookie,enum httpread_event en)1815 static void web_connection_got_file_handler(struct httpread *handle,
1816 					    void *cookie,
1817 					    enum httpread_event en)
1818 {
1819 	struct web_connection *c = cookie;
1820 
1821 	if (en == HTTPREAD_EVENT_FILE_READY)
1822 		web_connection_check_data(c);
1823 	web_connection_stop(c);
1824 }
1825 
1826 
1827 /* web_connection_start - Start web connection
1828  * @sm: WPS UPnP state machine from upnp_wps_device_init()
1829  * @sd: Socket descriptor
1830  * @addr: Client address
1831  *
1832  * The socket descriptor sd is handed over for ownership by the WPS UPnP
1833  * state machine.
1834  */
web_connection_start(struct upnp_wps_device_sm * sm,int sd,struct sockaddr_in * addr)1835 static void web_connection_start(struct upnp_wps_device_sm *sm,
1836 				 int sd, struct sockaddr_in *addr)
1837 {
1838 	struct web_connection *c = NULL;
1839 
1840 	/* if too many connections, bail */
1841 	if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) {
1842 		close(sd);
1843 		return;
1844 	}
1845 
1846 	c = os_zalloc(sizeof(*c));
1847 	if (c == NULL)
1848 		return;
1849 	os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr));
1850 	c->sm = sm;
1851 	c->sd = sd;
1852 #if 0
1853 	/*
1854 	 * Setting non-blocking should not be necessary for read, and can mess
1855 	 * up sending where blocking might be better.
1856 	 */
1857 	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
1858 		break;
1859 #endif
1860 	c->hread = httpread_create(c->sd, web_connection_got_file_handler,
1861 				   c /* cookie */,
1862 				   WEB_CONNECTION_MAX_READ,
1863 				   WEB_CONNECTION_TIMEOUT_SEC);
1864 	if (c->hread == NULL)
1865 		goto fail;
1866 	if (sm->web_connections) {
1867 		c->next = sm->web_connections;
1868 		c->prev = c->next->prev;
1869 		c->prev->next = c;
1870 		c->next->prev = c;
1871 	} else {
1872 		sm->web_connections = c->next = c->prev = c;
1873 	}
1874 	sm->n_web_connections++;
1875 	return;
1876 
1877 fail:
1878 	if (c)
1879 		web_connection_stop(c);
1880 }
1881 
1882 
1883 /*
1884  * Listening for web connections
1885  * We have a single TCP listening port, and hand off connections as we get
1886  * them.
1887  */
1888 
web_listener_stop(struct upnp_wps_device_sm * sm)1889 void web_listener_stop(struct upnp_wps_device_sm *sm)
1890 {
1891 	if (sm->web_sd_registered) {
1892 		sm->web_sd_registered = 0;
1893 		eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ);
1894 	}
1895 	if (sm->web_sd >= 0)
1896 		close(sm->web_sd);
1897 	sm->web_sd = -1;
1898 }
1899 
1900 
web_listener_handler(int sd,void * eloop_ctx,void * sock_ctx)1901 static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
1902 {
1903 	struct sockaddr_in addr;
1904 	socklen_t addr_len = sizeof(addr);
1905 	struct upnp_wps_device_sm *sm = sock_ctx;
1906 	int new_sd;
1907 
1908 	/* Create state for new connection */
1909 	/* Remember so we can cancel if need be */
1910 	new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len);
1911 	if (new_sd < 0) {
1912 		wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept "
1913 			   "errno=%d (%s) web_sd=%d",
1914 			   errno, strerror(errno), sm->web_sd);
1915 		return;
1916 	}
1917 	web_connection_start(sm, new_sd, &addr);
1918 }
1919 
1920 
web_listener_start(struct upnp_wps_device_sm * sm)1921 int web_listener_start(struct upnp_wps_device_sm *sm)
1922 {
1923 	struct sockaddr_in addr;
1924 	int port;
1925 
1926 	sm->web_sd = socket(AF_INET, SOCK_STREAM, 0);
1927 	if (sm->web_sd < 0)
1928 		goto fail;
1929 	if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0)
1930 		goto fail;
1931 	port = 49152;  /* first non-reserved port */
1932 	for (;;) {
1933 		os_memset(&addr, 0, sizeof(addr));
1934 		addr.sin_family = AF_INET;
1935 		addr.sin_addr.s_addr = sm->ip_addr;
1936 		addr.sin_port = htons(port);
1937 		if (bind(sm->web_sd, (struct sockaddr *) &addr,
1938 			 sizeof(addr)) == 0)
1939 			break;
1940 		if (errno == EADDRINUSE) {
1941 			/* search for unused port */
1942 			if (++port == 65535)
1943 				goto fail;
1944 			continue;
1945 		}
1946 		goto fail;
1947 	}
1948 	if (listen(sm->web_sd, 10 /* max backlog */) != 0)
1949 		goto fail;
1950 	if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0)
1951 		goto fail;
1952 	if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ,
1953 				web_listener_handler, NULL, sm))
1954 		goto fail;
1955 	sm->web_sd_registered = 1;
1956 	sm->web_port = port;
1957 
1958 	return 0;
1959 
1960 fail:
1961 	/* Error */
1962 	web_listener_stop(sm);
1963 	return -1;
1964 }
1965