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
13#include "common.h"
14#include "base64.h"
15#include "uuid.h"
16#include "httpread.h"
17#include "http_server.h"
18#include "wps_i.h"
19#include "wps_upnp.h"
20#include "wps_upnp_i.h"
21#include "upnp_xml.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 * "Files" that we serve via HTTP. The format of these files is given by
42 * WFA WPS specifications. Extra white space has been removed to save space.
43 */
44
45static const char wps_scpd_xml[] =
46"<?xml version=\"1.0\"?>\n"
47"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
48"<specVersion><major>1</major><minor>0</minor></specVersion>\n"
49"<actionList>\n"
50"<action>\n"
51"<name>GetDeviceInfo</name>\n"
52"<argumentList>\n"
53"<argument>\n"
54"<name>NewDeviceInfo</name>\n"
55"<direction>out</direction>\n"
56"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
57"</argument>\n"
58"</argumentList>\n"
59"</action>\n"
60"<action>\n"
61"<name>PutMessage</name>\n"
62"<argumentList>\n"
63"<argument>\n"
64"<name>NewInMessage</name>\n"
65"<direction>in</direction>\n"
66"<relatedStateVariable>InMessage</relatedStateVariable>\n"
67"</argument>\n"
68"<argument>\n"
69"<name>NewOutMessage</name>\n"
70"<direction>out</direction>\n"
71"<relatedStateVariable>OutMessage</relatedStateVariable>\n"
72"</argument>\n"
73"</argumentList>\n"
74"</action>\n"
75"<action>\n"
76"<name>PutWLANResponse</name>\n"
77"<argumentList>\n"
78"<argument>\n"
79"<name>NewMessage</name>\n"
80"<direction>in</direction>\n"
81"<relatedStateVariable>Message</relatedStateVariable>\n"
82"</argument>\n"
83"<argument>\n"
84"<name>NewWLANEventType</name>\n"
85"<direction>in</direction>\n"
86"<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
87"</argument>\n"
88"<argument>\n"
89"<name>NewWLANEventMAC</name>\n"
90"<direction>in</direction>\n"
91"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
92"</argument>\n"
93"</argumentList>\n"
94"</action>\n"
95"<action>\n"
96"<name>SetSelectedRegistrar</name>\n"
97"<argumentList>\n"
98"<argument>\n"
99"<name>NewMessage</name>\n"
100"<direction>in</direction>\n"
101"<relatedStateVariable>Message</relatedStateVariable>\n"
102"</argument>\n"
103"</argumentList>\n"
104"</action>\n"
105"</actionList>\n"
106"<serviceStateTable>\n"
107"<stateVariable sendEvents=\"no\">\n"
108"<name>Message</name>\n"
109"<dataType>bin.base64</dataType>\n"
110"</stateVariable>\n"
111"<stateVariable sendEvents=\"no\">\n"
112"<name>InMessage</name>\n"
113"<dataType>bin.base64</dataType>\n"
114"</stateVariable>\n"
115"<stateVariable sendEvents=\"no\">\n"
116"<name>OutMessage</name>\n"
117"<dataType>bin.base64</dataType>\n"
118"</stateVariable>\n"
119"<stateVariable sendEvents=\"no\">\n"
120"<name>DeviceInfo</name>\n"
121"<dataType>bin.base64</dataType>\n"
122"</stateVariable>\n"
123"<stateVariable sendEvents=\"yes\">\n"
124"<name>APStatus</name>\n"
125"<dataType>ui1</dataType>\n"
126"</stateVariable>\n"
127"<stateVariable sendEvents=\"yes\">\n"
128"<name>STAStatus</name>\n"
129"<dataType>ui1</dataType>\n"
130"</stateVariable>\n"
131"<stateVariable sendEvents=\"yes\">\n"
132"<name>WLANEvent</name>\n"
133"<dataType>bin.base64</dataType>\n"
134"</stateVariable>\n"
135"<stateVariable sendEvents=\"no\">\n"
136"<name>WLANEventType</name>\n"
137"<dataType>ui1</dataType>\n"
138"</stateVariable>\n"
139"<stateVariable sendEvents=\"no\">\n"
140"<name>WLANEventMAC</name>\n"
141"<dataType>string</dataType>\n"
142"</stateVariable>\n"
143"<stateVariable sendEvents=\"no\">\n"
144"<name>WLANResponse</name>\n"
145"<dataType>bin.base64</dataType>\n"
146"</stateVariable>\n"
147"</serviceStateTable>\n"
148"</scpd>\n"
149;
150
151
152static const char *wps_device_xml_prefix =
153	"<?xml version=\"1.0\"?>\n"
154	"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
155	"<specVersion>\n"
156	"<major>1</major>\n"
157	"<minor>0</minor>\n"
158	"</specVersion>\n"
159	"<device>\n"
160	"<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
161	"</deviceType>\n";
162
163static const char *wps_device_xml_postfix =
164	"<serviceList>\n"
165	"<service>\n"
166	"<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
167	"</serviceType>\n"
168	"<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
169	"\n"
170	"<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
171	"<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
172	"<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
173	"</service>\n"
174	"</serviceList>\n"
175	"</device>\n"
176	"</root>\n";
177
178
179/* format_wps_device_xml -- produce content of "file" wps_device.xml
180 * (UPNP_WPS_DEVICE_XML_FILE)
181 */
182static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
183				  struct upnp_wps_device_sm *sm,
184				  struct wpabuf *buf)
185{
186	const char *s;
187	char uuid_string[80];
188
189	wpabuf_put_str(buf, wps_device_xml_prefix);
190
191	/*
192	 * Add required fields with default values if not configured. Add
193	 * optional and recommended fields only if configured.
194	 */
195	s = iface->wps->friendly_name;
196	s = ((s && *s) ? s : "WPS Access Point");
197	xml_add_tagged_data(buf, "friendlyName", s);
198
199	s = iface->wps->dev.manufacturer;
200	s = ((s && *s) ? s : "");
201	xml_add_tagged_data(buf, "manufacturer", s);
202
203	if (iface->wps->manufacturer_url)
204		xml_add_tagged_data(buf, "manufacturerURL",
205				    iface->wps->manufacturer_url);
206
207	if (iface->wps->model_description)
208		xml_add_tagged_data(buf, "modelDescription",
209				    iface->wps->model_description);
210
211	s = iface->wps->dev.model_name;
212	s = ((s && *s) ? s : "");
213	xml_add_tagged_data(buf, "modelName", s);
214
215	if (iface->wps->dev.model_number)
216		xml_add_tagged_data(buf, "modelNumber",
217				    iface->wps->dev.model_number);
218
219	if (iface->wps->model_url)
220		xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
221
222	if (iface->wps->dev.serial_number)
223		xml_add_tagged_data(buf, "serialNumber",
224				    iface->wps->dev.serial_number);
225
226	uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
227	s = uuid_string;
228	/* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
229	 * easily...
230	 */
231	wpabuf_put_str(buf, "<UDN>uuid:");
232	xml_data_encode(buf, s, os_strlen(s));
233	wpabuf_put_str(buf, "</UDN>\n");
234
235	if (iface->wps->upc)
236		xml_add_tagged_data(buf, "UPC", iface->wps->upc);
237
238	wpabuf_put_str(buf, wps_device_xml_postfix);
239}
240
241
242static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
243{
244	wpabuf_put_str(buf, "HTTP/1.1 ");
245	switch (code) {
246	case HTTP_OK:
247		wpabuf_put_str(buf, "200 OK\r\n");
248		break;
249	case HTTP_BAD_REQUEST:
250		wpabuf_put_str(buf, "400 Bad request\r\n");
251		break;
252	case HTTP_PRECONDITION_FAILED:
253		wpabuf_put_str(buf, "412 Precondition failed\r\n");
254		break;
255	case HTTP_UNIMPLEMENTED:
256		wpabuf_put_str(buf, "501 Unimplemented\r\n");
257		break;
258	case HTTP_INTERNAL_SERVER_ERROR:
259	default:
260		wpabuf_put_str(buf, "500 Internal server error\r\n");
261		break;
262	}
263}
264
265
266static void http_put_date(struct wpabuf *buf)
267{
268	wpabuf_put_str(buf, "Date: ");
269	format_date(buf);
270	wpabuf_put_str(buf, "\r\n");
271}
272
273
274static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
275{
276	http_put_reply_code(buf, code);
277	wpabuf_put_str(buf, http_server_hdr);
278	wpabuf_put_str(buf, http_connection_close);
279	wpabuf_put_str(buf, "Content-Length: 0\r\n"
280		       "\r\n");
281}
282
283
284/* Given that we have received a header w/ GET, act upon it
285 *
286 * Format of GET (case-insensitive):
287 *
288 * First line must be:
289 *      GET /<file> HTTP/1.1
290 * Since we don't do anything fancy we just ignore other lines.
291 *
292 * Our response (if no error) which includes only required lines is:
293 * HTTP/1.1 200 OK
294 * Connection: close
295 * Content-Type: text/xml
296 * Date: <rfc1123-date>
297 *
298 * Header lines must end with \r\n
299 * Per RFC 2616, content-length: is not required but connection:close
300 * would appear to be required (given that we will be closing it!).
301 */
302static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
303				     struct http_request *hreq,
304				     const char *filename)
305{
306	struct wpabuf *buf; /* output buffer, allocated */
307	char *put_length_here;
308	char *body_start;
309	enum {
310		GET_DEVICE_XML_FILE,
311		GET_SCPD_XML_FILE
312	} req;
313	size_t extra_len = 0;
314	int body_length;
315	char len_buf[10];
316	struct upnp_wps_device_interface *iface;
317
318	iface = dl_list_first(&sm->interfaces,
319			      struct upnp_wps_device_interface, list);
320	if (iface == NULL) {
321		http_request_deinit(hreq);
322		return;
323	}
324
325	/*
326	 * It is not required that filenames be case insensitive but it is
327	 * allowed and cannot hurt here.
328	 */
329	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
330		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
331		req = GET_DEVICE_XML_FILE;
332		extra_len = 3000;
333		if (iface->wps->friendly_name)
334			extra_len += os_strlen(iface->wps->friendly_name);
335		if (iface->wps->manufacturer_url)
336			extra_len += os_strlen(iface->wps->manufacturer_url);
337		if (iface->wps->model_description)
338			extra_len += os_strlen(iface->wps->model_description);
339		if (iface->wps->model_url)
340			extra_len += os_strlen(iface->wps->model_url);
341		if (iface->wps->upc)
342			extra_len += os_strlen(iface->wps->upc);
343	} else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
344		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
345		req = GET_SCPD_XML_FILE;
346		extra_len = os_strlen(wps_scpd_xml);
347	} else {
348		/* File not found */
349		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
350			   filename);
351		buf = wpabuf_alloc(200);
352		if (buf == NULL) {
353			http_request_deinit(hreq);
354			return;
355		}
356		wpabuf_put_str(buf,
357			       "HTTP/1.1 404 Not Found\r\n"
358			       "Connection: close\r\n");
359
360		http_put_date(buf);
361
362		/* terminating empty line */
363		wpabuf_put_str(buf, "\r\n");
364
365		goto send_buf;
366	}
367
368	buf = wpabuf_alloc(1000 + extra_len);
369	if (buf == NULL) {
370		http_request_deinit(hreq);
371		return;
372	}
373
374	wpabuf_put_str(buf,
375		       "HTTP/1.1 200 OK\r\n"
376		       "Content-Type: text/xml; charset=\"utf-8\"\r\n");
377	wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
378	wpabuf_put_str(buf, "Connection: close\r\n");
379	wpabuf_put_str(buf, "Content-Length: ");
380	/*
381	 * We will paste the length in later, leaving some extra whitespace.
382	 * HTTP code is supposed to be tolerant of extra whitespace.
383	 */
384	put_length_here = wpabuf_put(buf, 0);
385	wpabuf_put_str(buf, "        \r\n");
386
387	http_put_date(buf);
388
389	/* terminating empty line */
390	wpabuf_put_str(buf, "\r\n");
391
392	body_start = wpabuf_put(buf, 0);
393
394	switch (req) {
395	case GET_DEVICE_XML_FILE:
396		format_wps_device_xml(iface, sm, buf);
397		break;
398	case GET_SCPD_XML_FILE:
399		wpabuf_put_str(buf, wps_scpd_xml);
400		break;
401	}
402
403	/* Now patch in the content length at the end */
404	body_length = (char *) wpabuf_put(buf, 0) - body_start;
405	os_snprintf(len_buf, 10, "%d", body_length);
406	os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
407
408send_buf:
409	http_request_send_and_deinit(hreq, buf);
410}
411
412
413static void wps_upnp_peer_del(struct upnp_wps_peer *peer)
414{
415	dl_list_del(&peer->list);
416	if (peer->wps)
417		wps_deinit(peer->wps);
418	os_free(peer);
419}
420
421
422static enum http_reply_code
423web_process_get_device_info(struct upnp_wps_device_sm *sm,
424			    struct wpabuf **reply, const char **replyname)
425{
426	static const char *name = "NewDeviceInfo";
427	struct wps_config cfg;
428	struct upnp_wps_device_interface *iface;
429	struct upnp_wps_peer *peer;
430
431	iface = dl_list_first(&sm->interfaces,
432			      struct upnp_wps_device_interface, list);
433
434	wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
435
436	if (!iface || iface->ctx->ap_pin == NULL)
437		return HTTP_INTERNAL_SERVER_ERROR;
438
439	peer = os_zalloc(sizeof(*peer));
440	if (!peer)
441		return HTTP_INTERNAL_SERVER_ERROR;
442
443	/*
444	 * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
445	 * registration over UPnP with the AP acting as an Enrollee. It should
446	 * be noted that this is frequently used just to get the device data,
447	 * i.e., there may not be any intent to actually complete the
448	 * registration.
449	 */
450
451	os_memset(&cfg, 0, sizeof(cfg));
452	cfg.wps = iface->wps;
453	cfg.pin = (u8 *) iface->ctx->ap_pin;
454	cfg.pin_len = os_strlen(iface->ctx->ap_pin);
455	peer->wps = wps_init(&cfg);
456	if (peer->wps) {
457		enum wsc_op_code op_code;
458		*reply = wps_get_msg(peer->wps, &op_code);
459		if (*reply == NULL) {
460			wps_deinit(peer->wps);
461			peer->wps = NULL;
462		}
463	} else
464		*reply = NULL;
465	if (*reply == NULL) {
466		wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
467		os_free(peer);
468		return HTTP_INTERNAL_SERVER_ERROR;
469	}
470
471	if (dl_list_len(&iface->peers) > 3) {
472		struct upnp_wps_peer *old;
473
474		old = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
475		if (old) {
476			wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session");
477			wps_upnp_peer_del(old);
478		}
479	}
480	dl_list_add_tail(&iface->peers, &peer->list);
481	/* TODO: Could schedule a timeout to free the entry */
482
483	*replyname = name;
484	return HTTP_OK;
485}
486
487
488static enum http_reply_code
489web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
490			struct wpabuf **reply, const char **replyname)
491{
492	struct wpabuf *msg;
493	static const char *name = "NewOutMessage";
494	enum http_reply_code ret;
495	enum wps_process_res res;
496	enum wsc_op_code op_code;
497	struct upnp_wps_device_interface *iface;
498	struct wps_parse_attr attr;
499	struct upnp_wps_peer *tmp, *peer;
500
501	iface = dl_list_first(&sm->interfaces,
502			      struct upnp_wps_device_interface, list);
503	if (!iface)
504		return HTTP_INTERNAL_SERVER_ERROR;
505
506	/*
507	 * PutMessage is used by external UPnP-based Registrar to perform WPS
508	 * operation with the access point itself; as compared with
509	 * PutWLANResponse which is for proxying.
510	 */
511	wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
512	msg = xml_get_base64_item(data, "NewInMessage", &ret);
513	if (msg == NULL)
514		return ret;
515
516	if (wps_parse_msg(msg, &attr)) {
517		wpa_printf(MSG_DEBUG,
518			   "WPS UPnP: Could not parse PutMessage - NewInMessage");
519		wpabuf_free(msg);
520		return HTTP_BAD_REQUEST;
521	}
522
523	/* Find a matching active peer session */
524	peer = NULL;
525	dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) {
526		if (!tmp->wps)
527			continue;
528		if (attr.enrollee_nonce &&
529		    os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce,
530			      WPS_NONCE_LEN) != 0)
531			continue; /* Enrollee nonce mismatch */
532		if (attr.msg_type &&
533		    *attr.msg_type != WPS_M2 &&
534		    *attr.msg_type != WPS_M2D &&
535		    attr.registrar_nonce &&
536		    os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce,
537			      WPS_NONCE_LEN) != 0)
538			continue; /* Registrar nonce mismatch */
539		peer = tmp;
540		break;
541	}
542	if (!peer) {
543		/*
544		  Try to use the first entry in case message could work with
545		 * it. The actual handler function will reject this, if needed.
546		 * This maintains older behavior where only a single peer entry
547		 * was supported.
548		 */
549		peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
550	}
551	if (!peer || !peer->wps) {
552		wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found");
553		wpabuf_free(msg);
554		return HTTP_BAD_REQUEST;
555	}
556
557	res = wps_process_msg(peer->wps, WSC_UPnP, msg);
558	if (res == WPS_FAILURE) {
559		*reply = NULL;
560		wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session");
561		wps_upnp_peer_del(peer);
562	} else {
563		*reply = wps_get_msg(peer->wps, &op_code);
564	}
565	wpabuf_free(msg);
566	if (*reply == NULL)
567		return HTTP_INTERNAL_SERVER_ERROR;
568	*replyname = name;
569	return HTTP_OK;
570}
571
572
573static enum http_reply_code
574web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
575			      struct wpabuf **reply, const char **replyname)
576{
577	struct wpabuf *msg;
578	enum http_reply_code ret;
579	u8 macaddr[ETH_ALEN];
580	int ev_type;
581	int type;
582	char *val;
583	struct upnp_wps_device_interface *iface;
584	int ok = 0;
585
586	/*
587	 * External UPnP-based Registrar is passing us a message to be proxied
588	 * over to a Wi-Fi -based client of ours.
589	 */
590
591	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
592	msg = xml_get_base64_item(data, "NewMessage", &ret);
593	if (msg == NULL) {
594		wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
595			   "from PutWLANResponse");
596		return ret;
597	}
598	val = xml_get_first_item(data, "NewWLANEventType");
599	if (val == NULL) {
600		wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
601			   "PutWLANResponse");
602		wpabuf_free(msg);
603		return UPNP_ARG_VALUE_INVALID;
604	}
605	ev_type = atol(val);
606	os_free(val);
607	val = xml_get_first_item(data, "NewWLANEventMAC");
608	if (val == NULL) {
609		wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
610			   "PutWLANResponse");
611		wpabuf_free(msg);
612		return UPNP_ARG_VALUE_INVALID;
613	}
614	if (hwaddr_aton(val, macaddr)) {
615		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
616			   "PutWLANResponse: '%s'", val);
617#ifdef CONFIG_WPS_STRICT
618		{
619			struct wps_parse_attr attr;
620			if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
621				wpabuf_free(msg);
622				os_free(val);
623				return UPNP_ARG_VALUE_INVALID;
624			}
625		}
626#endif /* CONFIG_WPS_STRICT */
627		if (hwaddr_aton2(val, macaddr) > 0) {
628			/*
629			 * At least some versions of Intel PROset seem to be
630			 * using dot-deliminated MAC address format here.
631			 */
632			wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
633				   "incorrect MAC address format in "
634				   "NewWLANEventMAC: %s -> " MACSTR,
635				   val, MAC2STR(macaddr));
636		} else {
637			wpabuf_free(msg);
638			os_free(val);
639			return UPNP_ARG_VALUE_INVALID;
640		}
641	}
642	os_free(val);
643	if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
644		struct wps_parse_attr attr;
645		if (wps_parse_msg(msg, &attr) < 0 ||
646		    attr.msg_type == NULL)
647			type = -1;
648		else
649			type = *attr.msg_type;
650		wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
651	} else
652		type = -1;
653	dl_list_for_each(iface, &sm->interfaces,
654			 struct upnp_wps_device_interface, list) {
655		if (iface->ctx->rx_req_put_wlan_response &&
656		    iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
657							 macaddr, msg, type)
658		    == 0)
659			ok = 1;
660	}
661
662	if (!ok) {
663		wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
664			   "rx_req_put_wlan_response");
665		wpabuf_free(msg);
666		return HTTP_INTERNAL_SERVER_ERROR;
667	}
668	wpabuf_free(msg);
669	*replyname = NULL;
670	*reply = NULL;
671	return HTTP_OK;
672}
673
674
675static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
676{
677	struct subscr_addr *a;
678
679	dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
680		if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
681			return 1;
682	}
683	return 0;
684}
685
686
687static struct subscription * find_er(struct upnp_wps_device_sm *sm,
688				     struct sockaddr_in *cli)
689{
690	struct subscription *s;
691	dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
692		if (find_er_addr(s, cli))
693			return s;
694	return NULL;
695}
696
697
698static enum http_reply_code
699web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
700				   struct sockaddr_in *cli, char *data,
701				   struct wpabuf **reply,
702				   const char **replyname)
703{
704	struct wpabuf *msg;
705	enum http_reply_code ret;
706	struct subscription *s;
707	struct upnp_wps_device_interface *iface;
708	int err = 0;
709
710	wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
711	s = find_er(sm, cli);
712	if (s == NULL) {
713		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
714			   "from unknown ER");
715		return UPNP_ACTION_FAILED;
716	}
717	msg = xml_get_base64_item(data, "NewMessage", &ret);
718	if (msg == NULL)
719		return ret;
720	dl_list_for_each(iface, &sm->interfaces,
721			 struct upnp_wps_device_interface, list) {
722		if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
723						   msg))
724			err = 1;
725	}
726	wpabuf_free(msg);
727	if (err)
728		return HTTP_INTERNAL_SERVER_ERROR;
729	*replyname = NULL;
730	*reply = NULL;
731	return HTTP_OK;
732}
733
734
735static const char *soap_prefix =
736	"<?xml version=\"1.0\"?>\n"
737	"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
738	"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
739	"<s:Body>\n";
740static const char *soap_postfix =
741	"</s:Body>\n</s:Envelope>\n";
742
743static const char *soap_error_prefix =
744	"<s:Fault>\n"
745	"<faultcode>s:Client</faultcode>\n"
746	"<faultstring>UPnPError</faultstring>\n"
747	"<detail>\n"
748	"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
749static const char *soap_error_postfix =
750	"<errorDescription>Error</errorDescription>\n"
751	"</UPnPError>\n"
752	"</detail>\n"
753	"</s:Fault>\n";
754
755static void web_connection_send_reply(struct http_request *req,
756				      enum http_reply_code ret,
757				      const char *action, int action_len,
758				      const struct wpabuf *reply,
759				      const char *replyname)
760{
761	struct wpabuf *buf;
762	char *replydata;
763	char *put_length_here = NULL;
764	char *body_start = NULL;
765
766	if (reply) {
767		size_t len;
768		replydata = (char *) base64_encode(wpabuf_head(reply),
769						   wpabuf_len(reply), &len);
770	} else
771		replydata = NULL;
772
773	/* Parameters of the response:
774	 *      action(action_len) -- action we are responding to
775	 *      replyname -- a name we need for the reply
776	 *      replydata -- NULL or null-terminated string
777	 */
778	buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
779			   (action_len > 0 ? action_len * 2 : 0));
780	if (buf == NULL) {
781		wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
782			   "POST");
783		os_free(replydata);
784		http_request_deinit(req);
785		return;
786	}
787
788	/*
789	 * Assuming we will be successful, put in the output header first.
790	 * Note: we do not keep connections alive (and httpread does
791	 * not support it)... therefore we must have Connection: close.
792	 */
793	if (ret == HTTP_OK) {
794		wpabuf_put_str(buf,
795			       "HTTP/1.1 200 OK\r\n"
796			       "Content-Type: text/xml; "
797			       "charset=\"utf-8\"\r\n");
798	} else {
799		wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
800	}
801	wpabuf_put_str(buf, http_connection_close);
802
803	wpabuf_put_str(buf, "Content-Length: ");
804	/*
805	 * We will paste the length in later, leaving some extra whitespace.
806	 * HTTP code is supposed to be tolerant of extra whitespace.
807	 */
808	put_length_here = wpabuf_put(buf, 0);
809	wpabuf_put_str(buf, "        \r\n");
810
811	http_put_date(buf);
812
813	/* terminating empty line */
814	wpabuf_put_str(buf, "\r\n");
815
816	body_start = wpabuf_put(buf, 0);
817
818	if (ret == HTTP_OK) {
819		wpabuf_put_str(buf, soap_prefix);
820		wpabuf_put_str(buf, "<u:");
821		wpabuf_put_data(buf, action, action_len);
822		wpabuf_put_str(buf, "Response xmlns:u=\"");
823		wpabuf_put_str(buf, urn_wfawlanconfig);
824		wpabuf_put_str(buf, "\">\n");
825		if (replydata && replyname) {
826			/* TODO: might possibly need to escape part of reply
827			 * data? ...
828			 * probably not, unlikely to have ampersand(&) or left
829			 * angle bracket (<) in it...
830			 */
831			wpabuf_printf(buf, "<%s>", replyname);
832			wpabuf_put_str(buf, replydata);
833			wpabuf_printf(buf, "</%s>\n", replyname);
834		}
835		wpabuf_put_str(buf, "</u:");
836		wpabuf_put_data(buf, action, action_len);
837		wpabuf_put_str(buf, "Response>\n");
838		wpabuf_put_str(buf, soap_postfix);
839	} else {
840		/* Error case */
841		wpabuf_put_str(buf, soap_prefix);
842		wpabuf_put_str(buf, soap_error_prefix);
843		wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
844		wpabuf_put_str(buf, soap_error_postfix);
845		wpabuf_put_str(buf, soap_postfix);
846	}
847	os_free(replydata);
848
849	/* Now patch in the content length at the end */
850	if (body_start && put_length_here) {
851		int body_length = (char *) wpabuf_put(buf, 0) - body_start;
852		char len_buf[10];
853		os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
854		os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
855	}
856
857	http_request_send_and_deinit(req, buf);
858}
859
860
861static const char * web_get_action(struct http_request *req,
862				   size_t *action_len)
863{
864	const char *match;
865	int match_len;
866	char *b;
867	char *action;
868
869	*action_len = 0;
870	/* The SOAPAction line of the header tells us what we want to do */
871	b = http_request_get_hdr_line(req, "SOAPAction:");
872	if (b == NULL)
873		return NULL;
874	if (*b == '"')
875		b++;
876	else
877		return NULL;
878	match = urn_wfawlanconfig;
879	match_len = os_strlen(urn_wfawlanconfig) - 1;
880	if (os_strncasecmp(b, match, match_len))
881		return NULL;
882	b += match_len;
883	/* skip over version */
884	while (isgraph(*b) && *b != '#')
885		b++;
886	if (*b != '#')
887		return NULL;
888	b++;
889	/* Following the sharp(#) should be the action and a double quote */
890	action = b;
891	while (isgraph(*b) && *b != '"')
892		b++;
893	if (*b != '"')
894		return NULL;
895	*action_len = b - action;
896	return action;
897}
898
899
900/* Given that we have received a header w/ POST, act upon it
901 *
902 * Format of POST (case-insensitive):
903 *
904 * First line must be:
905 *      POST /<file> HTTP/1.1
906 * Since we don't do anything fancy we just ignore other lines.
907 *
908 * Our response (if no error) which includes only required lines is:
909 * HTTP/1.1 200 OK
910 * Connection: close
911 * Content-Type: text/xml
912 * Date: <rfc1123-date>
913 *
914 * Header lines must end with \r\n
915 * Per RFC 2616, content-length: is not required but connection:close
916 * would appear to be required (given that we will be closing it!).
917 */
918static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
919				      struct sockaddr_in *cli,
920				      struct http_request *req,
921				      const char *filename)
922{
923	enum http_reply_code ret;
924	char *data = http_request_get_data(req); /* body of http msg */
925	const char *action = NULL;
926	size_t action_len = 0;
927	const char *replyname = NULL; /* argument name for the reply */
928	struct wpabuf *reply = NULL; /* data for the reply */
929
930	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
931		wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
932			   filename);
933		ret = HTTP_NOT_FOUND;
934		goto bad;
935	}
936
937	ret = UPNP_INVALID_ACTION;
938	action = web_get_action(req, &action_len);
939	if (action == NULL)
940		goto bad;
941
942	if (!os_strncasecmp("GetDeviceInfo", action, action_len))
943		ret = web_process_get_device_info(sm, &reply, &replyname);
944	else if (!os_strncasecmp("PutMessage", action, action_len))
945		ret = web_process_put_message(sm, data, &reply, &replyname);
946	else if (!os_strncasecmp("PutWLANResponse", action, action_len))
947		ret = web_process_put_wlan_response(sm, data, &reply,
948						    &replyname);
949	else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
950		ret = web_process_set_selected_registrar(sm, cli, data, &reply,
951							 &replyname);
952	else
953		wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
954
955bad:
956	if (ret != HTTP_OK)
957		wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
958	web_connection_send_reply(req, ret, action, action_len, reply,
959				  replyname);
960	wpabuf_free(reply);
961}
962
963
964/* Given that we have received a header w/ SUBSCRIBE, act upon it
965 *
966 * Format of SUBSCRIBE (case-insensitive):
967 *
968 * First line must be:
969 *      SUBSCRIBE /wps_event HTTP/1.1
970 *
971 * Our response (if no error) which includes only required lines is:
972 * HTTP/1.1 200 OK
973 * Server: xx, UPnP/1.0, xx
974 * SID: uuid:xxxxxxxxx
975 * Timeout: Second-<n>
976 * Content-Length: 0
977 * Date: xxxx
978 *
979 * Header lines must end with \r\n
980 * Per RFC 2616, content-length: is not required but connection:close
981 * would appear to be required (given that we will be closing it!).
982 */
983static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
984					   struct http_request *req,
985					   const char *filename)
986{
987	struct wpabuf *buf;
988	char *b;
989	char *hdr = http_request_get_hdr(req);
990	char *h;
991	char *match;
992	int match_len;
993	char *end;
994	int len;
995	int got_nt = 0;
996	u8 uuid[UUID_LEN];
997	int got_uuid = 0;
998	char *callback_urls = NULL;
999	struct subscription *s = NULL;
1000	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1001
1002	buf = wpabuf_alloc(1000);
1003	if (buf == NULL) {
1004		http_request_deinit(req);
1005		return;
1006	}
1007
1008	wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
1009			  (u8 *) hdr, os_strlen(hdr));
1010
1011	/* Parse/validate headers */
1012	h = hdr;
1013	/* First line: SUBSCRIBE /wps_event HTTP/1.1
1014	 * has already been parsed.
1015	 */
1016	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1017		ret = HTTP_PRECONDITION_FAILED;
1018		goto error;
1019	}
1020	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
1021	end = os_strchr(h, '\n');
1022
1023	while (end) {
1024		/* Option line by option line */
1025		h = end + 1;
1026		end = os_strchr(h, '\n');
1027		if (end == NULL)
1028			break; /* no unterminated lines allowed */
1029
1030		/* NT assures that it is our type of subscription;
1031		 * not used for a renewal.
1032		 **/
1033		match = "NT:";
1034		match_len = os_strlen(match);
1035		if (os_strncasecmp(h, match, match_len) == 0) {
1036			h += match_len;
1037			while (*h == ' ' || *h == '\t')
1038				h++;
1039			match = "upnp:event";
1040			match_len = os_strlen(match);
1041			if (os_strncasecmp(h, match, match_len) != 0) {
1042				ret = HTTP_BAD_REQUEST;
1043				goto error;
1044			}
1045			got_nt = 1;
1046			continue;
1047		}
1048		/* HOST should refer to us */
1049#if 0
1050		match = "HOST:";
1051		match_len = os_strlen(match);
1052		if (os_strncasecmp(h, match, match_len) == 0) {
1053			h += match_len;
1054			while (*h == ' ' || *h == '\t')
1055				h++;
1056			.....
1057		}
1058#endif
1059		/* CALLBACK gives one or more URLs for NOTIFYs
1060		 * to be sent as a result of the subscription.
1061		 * Each URL is enclosed in angle brackets.
1062		 */
1063		match = "CALLBACK:";
1064		match_len = os_strlen(match);
1065		if (os_strncasecmp(h, match, match_len) == 0) {
1066			h += match_len;
1067			while (*h == ' ' || *h == '\t')
1068				h++;
1069			len = end - h;
1070			os_free(callback_urls);
1071			callback_urls = dup_binstr(h, len);
1072			if (callback_urls == NULL) {
1073				ret = HTTP_INTERNAL_SERVER_ERROR;
1074				goto error;
1075			}
1076			if (len > 0 && callback_urls[len - 1] == '\r')
1077				callback_urls[len - 1] = '\0';
1078			continue;
1079		}
1080		/* SID is only for renewal */
1081		match = "SID:";
1082		match_len = os_strlen(match);
1083		if (os_strncasecmp(h, match, match_len) == 0) {
1084			h += match_len;
1085			while (*h == ' ' || *h == '\t')
1086				h++;
1087			match = "uuid:";
1088			match_len = os_strlen(match);
1089			if (os_strncasecmp(h, match, match_len) != 0) {
1090				ret = HTTP_BAD_REQUEST;
1091				goto error;
1092			}
1093			h += match_len;
1094			while (*h == ' ' || *h == '\t')
1095				h++;
1096			if (uuid_str2bin(h, uuid)) {
1097				ret = HTTP_BAD_REQUEST;
1098				goto error;
1099			}
1100			got_uuid = 1;
1101			continue;
1102		}
1103		/* TIMEOUT is requested timeout, but apparently we can
1104		 * just ignore this.
1105		 */
1106	}
1107
1108	if (got_uuid) {
1109		/* renewal */
1110		wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
1111		if (callback_urls) {
1112			ret = HTTP_BAD_REQUEST;
1113			goto error;
1114		}
1115		s = subscription_renew(sm, uuid);
1116		if (s == NULL) {
1117			char str[80];
1118			uuid_bin2str(uuid, str, sizeof(str));
1119			wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
1120				   "SID %s", str);
1121			ret = HTTP_PRECONDITION_FAILED;
1122			goto error;
1123		}
1124	} else if (callback_urls) {
1125		wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
1126		if (!got_nt) {
1127			ret = HTTP_PRECONDITION_FAILED;
1128			goto error;
1129		}
1130		s = subscription_start(sm, callback_urls);
1131		if (s == NULL) {
1132			ret = HTTP_INTERNAL_SERVER_ERROR;
1133			goto error;
1134		}
1135	} else {
1136		ret = HTTP_PRECONDITION_FAILED;
1137		goto error;
1138	}
1139
1140	/* success */
1141	http_put_reply_code(buf, HTTP_OK);
1142	wpabuf_put_str(buf, http_server_hdr);
1143	wpabuf_put_str(buf, http_connection_close);
1144	wpabuf_put_str(buf, "Content-Length: 0\r\n");
1145	wpabuf_put_str(buf, "SID: uuid:");
1146	/* subscription id */
1147	b = wpabuf_put(buf, 0);
1148	uuid_bin2str(s->uuid, b, 80);
1149	wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
1150	wpabuf_put(buf, os_strlen(b));
1151	wpabuf_put_str(buf, "\r\n");
1152	wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
1153	http_put_date(buf);
1154	/* And empty line to terminate header: */
1155	wpabuf_put_str(buf, "\r\n");
1156
1157	os_free(callback_urls);
1158	http_request_send_and_deinit(req, buf);
1159	return;
1160
1161error:
1162	/* Per UPnP spec:
1163	* Errors
1164	* Incompatible headers
1165	*   400 Bad Request. If SID header and one of NT or CALLBACK headers
1166	*     are present, the publisher must respond with HTTP error
1167	*     400 Bad Request.
1168	* Missing or invalid CALLBACK
1169	*   412 Precondition Failed. If CALLBACK header is missing or does not
1170	*     contain a valid HTTP URL, the publisher must respond with HTTP
1171	*     error 412 Precondition Failed.
1172	* Invalid NT
1173	*   412 Precondition Failed. If NT header does not equal upnp:event,
1174	*     the publisher must respond with HTTP error 412 Precondition
1175	*     Failed.
1176	* [For resubscription, use 412 if unknown uuid].
1177	* Unable to accept subscription
1178	*   5xx. If a publisher is not able to accept a subscription (such as
1179	*     due to insufficient resources), it must respond with a
1180	*     HTTP 500-series error code.
1181	*   599 Too many subscriptions (not a standard HTTP error)
1182	*/
1183	wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
1184	http_put_empty(buf, ret);
1185	http_request_send_and_deinit(req, buf);
1186	os_free(callback_urls);
1187}
1188
1189
1190/* Given that we have received a header w/ UNSUBSCRIBE, act upon it
1191 *
1192 * Format of UNSUBSCRIBE (case-insensitive):
1193 *
1194 * First line must be:
1195 *      UNSUBSCRIBE /wps_event HTTP/1.1
1196 *
1197 * Our response (if no error) which includes only required lines is:
1198 * HTTP/1.1 200 OK
1199 * Content-Length: 0
1200 *
1201 * Header lines must end with \r\n
1202 * Per RFC 2616, content-length: is not required but connection:close
1203 * would appear to be required (given that we will be closing it!).
1204 */
1205static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
1206					     struct http_request *req,
1207					     const char *filename)
1208{
1209	struct wpabuf *buf;
1210	char *hdr = http_request_get_hdr(req);
1211	char *h;
1212	char *match;
1213	int match_len;
1214	char *end;
1215	u8 uuid[UUID_LEN];
1216	int got_uuid = 0;
1217	struct subscription *s = NULL;
1218	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1219
1220	/* Parse/validate headers */
1221	h = hdr;
1222	/* First line: UNSUBSCRIBE /wps_event HTTP/1.1
1223	 * has already been parsed.
1224	 */
1225	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1226		ret = HTTP_PRECONDITION_FAILED;
1227		goto send_msg;
1228	}
1229	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
1230	end = os_strchr(h, '\n');
1231
1232	while (end) {
1233		/* Option line by option line */
1234		h = end + 1;
1235		end = os_strchr(h, '\n');
1236		if (end == NULL)
1237			break; /* no unterminated lines allowed */
1238
1239		/* HOST should refer to us */
1240#if 0
1241		match = "HOST:";
1242		match_len = os_strlen(match);
1243		if (os_strncasecmp(h, match, match_len) == 0) {
1244			h += match_len;
1245			while (*h == ' ' || *h == '\t')
1246				h++;
1247			.....
1248		}
1249#endif
1250		match = "SID:";
1251		match_len = os_strlen(match);
1252		if (os_strncasecmp(h, match, match_len) == 0) {
1253			h += match_len;
1254			while (*h == ' ' || *h == '\t')
1255				h++;
1256			match = "uuid:";
1257			match_len = os_strlen(match);
1258			if (os_strncasecmp(h, match, match_len) != 0) {
1259				ret = HTTP_BAD_REQUEST;
1260				goto send_msg;
1261			}
1262			h += match_len;
1263			while (*h == ' ' || *h == '\t')
1264				h++;
1265			if (uuid_str2bin(h, uuid)) {
1266				ret = HTTP_BAD_REQUEST;
1267				goto send_msg;
1268			}
1269			got_uuid = 1;
1270			continue;
1271		}
1272
1273		match = "NT:";
1274		match_len = os_strlen(match);
1275		if (os_strncasecmp(h, match, match_len) == 0) {
1276			ret = HTTP_BAD_REQUEST;
1277			goto send_msg;
1278		}
1279
1280		match = "CALLBACK:";
1281		match_len = os_strlen(match);
1282		if (os_strncasecmp(h, match, match_len) == 0) {
1283			ret = HTTP_BAD_REQUEST;
1284			goto send_msg;
1285		}
1286	}
1287
1288	if (got_uuid) {
1289		char str[80];
1290
1291		uuid_bin2str(uuid, str, sizeof(str));
1292
1293		s = subscription_find(sm, uuid);
1294		if (s) {
1295			struct subscr_addr *sa;
1296			sa = dl_list_first(&s->addr_list, struct subscr_addr,
1297					   list);
1298			wpa_printf(MSG_DEBUG,
1299				   "WPS UPnP: Unsubscribing %p (SID %s) %s",
1300				   s, str, (sa && sa->domain_and_port) ?
1301				   sa->domain_and_port : "-null-");
1302			dl_list_del(&s->list);
1303			subscription_destroy(s);
1304		} else {
1305			wpa_printf(MSG_INFO,
1306				   "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
1307				   str);
1308			ret = HTTP_PRECONDITION_FAILED;
1309			goto send_msg;
1310		}
1311	} else {
1312		wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
1313			   "found)");
1314		ret = HTTP_PRECONDITION_FAILED;
1315		goto send_msg;
1316	}
1317
1318	ret = HTTP_OK;
1319
1320send_msg:
1321	buf = wpabuf_alloc(200);
1322	if (buf == NULL) {
1323		http_request_deinit(req);
1324		return;
1325	}
1326	http_put_empty(buf, ret);
1327	http_request_send_and_deinit(req, buf);
1328}
1329
1330
1331/* Send error in response to unknown requests */
1332static void web_connection_unimplemented(struct http_request *req)
1333{
1334	struct wpabuf *buf;
1335	buf = wpabuf_alloc(200);
1336	if (buf == NULL) {
1337		http_request_deinit(req);
1338		return;
1339	}
1340	http_put_empty(buf, HTTP_UNIMPLEMENTED);
1341	http_request_send_and_deinit(req, buf);
1342}
1343
1344
1345
1346/* Called when we have gotten an apparently valid http request.
1347 */
1348static void web_connection_check_data(void *ctx, struct http_request *req)
1349{
1350	struct upnp_wps_device_sm *sm = ctx;
1351	enum httpread_hdr_type htype = http_request_get_type(req);
1352	char *filename = http_request_get_uri(req);
1353	struct sockaddr_in *cli = http_request_get_cli_addr(req);
1354
1355	if (!filename) {
1356		wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
1357		http_request_deinit(req);
1358		return;
1359	}
1360	/* Trim leading slashes from filename */
1361	while (*filename == '/')
1362		filename++;
1363
1364	wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
1365		   htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
1366
1367	switch (htype) {
1368	case HTTPREAD_HDR_TYPE_GET:
1369		web_connection_parse_get(sm, req, filename);
1370		break;
1371	case HTTPREAD_HDR_TYPE_POST:
1372		web_connection_parse_post(sm, cli, req, filename);
1373		break;
1374	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
1375		web_connection_parse_subscribe(sm, req, filename);
1376		break;
1377	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
1378		web_connection_parse_unsubscribe(sm, req, filename);
1379		break;
1380
1381		/* We are not required to support M-POST; just plain
1382		 * POST is supposed to work, so we only support that.
1383		 * If for some reason we need to support M-POST, it is
1384		 * mostly the same as POST, with small differences.
1385		 */
1386	default:
1387		/* Send 501 for anything else */
1388		web_connection_unimplemented(req);
1389		break;
1390	}
1391}
1392
1393
1394/*
1395 * Listening for web connections
1396 * We have a single TCP listening port, and hand off connections as we get
1397 * them.
1398 */
1399
1400void web_listener_stop(struct upnp_wps_device_sm *sm)
1401{
1402	http_server_deinit(sm->web_srv);
1403	sm->web_srv = NULL;
1404}
1405
1406
1407int web_listener_start(struct upnp_wps_device_sm *sm)
1408{
1409	struct in_addr addr;
1410	addr.s_addr = sm->ip_addr;
1411	sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
1412				       sm);
1413	if (sm->web_srv == NULL) {
1414		web_listener_stop(sm);
1415		return -1;
1416	}
1417	sm->web_port = http_server_get_port(sm->web_srv);
1418
1419	return 0;
1420}
1421