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
33static const char *urn_wfawlanconfig =
34	"urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
35static const char *http_server_hdr =
36	"Server: unspecified, UPnP/1.0, unspecified\r\n";
37static 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 */
48struct 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 */
121static 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 */
164static 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 */
204static 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 */
234static int
235upnp_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
277static 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
502static 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
513static 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 */
532static 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
591void 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
612static 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
636static 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
644static 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 */
672static 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
767send_buf:
768	send_wpabuf(c->sd, buf);
769	wpabuf_free(buf);
770}
771
772
773static 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
803static enum http_reply_code
804web_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
822static enum http_reply_code
823web_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
850static enum http_reply_code
851web_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
873static enum http_reply_code
874web_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
896static enum http_reply_code
897web_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
919static enum http_reply_code
920web_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
942static enum http_reply_code
943web_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
965static enum http_reply_code
966web_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
988static enum http_reply_code
989web_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
1047static enum http_reply_code
1048web_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
1071static enum http_reply_code
1072web_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
1094static enum http_reply_code
1095web_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
1117static enum http_reply_code
1118web_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
1140static enum http_reply_code
1141web_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
1163static 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";
1168static const char *soap_postfix =
1169	"</s:Body>\n</s:Envelope>\n";
1170
1171static 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";
1177static const char *soap_error_postfix =
1178	"<errorDescription>Error</errorDescription>\n"
1179	"</UPnPError>\n"
1180	"</detail>\n"
1181	"</s:Fault>\n";
1182
1183static 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
1290static 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 */
1352static 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
1412bad:
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 */
1440static 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
1607error:
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 */
1651static 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
1741send_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 */
1752static 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 */
1767static 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 */
1815static 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 */
1835static 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
1877fail:
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
1889void 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
1901static 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
1921int 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
1960fail:
1961	/* Error */
1962	web_listener_stop(sm);
1963	return -1;
1964}
1965