Home | History | Annotate | Download | only in wps
      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 (at) 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 
     33 static const char *urn_wfawlanconfig =
     34 	"urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
     35 static const char *http_server_hdr =
     36 	"Server: unspecified, UPnP/1.0, unspecified\r\n";
     37 static const char *http_connection_close =
     38 	"Connection: close\r\n";
     39 
     40 /*
     41  * "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 
     45 static 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 
    152 static 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 
    163 static 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  */
    182 static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
    183 				  struct wpabuf *buf)
    184 {
    185 	const char *s;
    186 	char uuid_string[80];
    187 	struct upnp_wps_device_interface *iface;
    188 
    189 	iface = dl_list_first(&sm->interfaces,
    190 			      struct upnp_wps_device_interface, list);
    191 
    192 	wpabuf_put_str(buf, wps_device_xml_prefix);
    193 
    194 	/*
    195 	 * Add required fields with default values if not configured. Add
    196 	 * optional and recommended fields only if configured.
    197 	 */
    198 	s = iface->wps->friendly_name;
    199 	s = ((s && *s) ? s : "WPS Access Point");
    200 	xml_add_tagged_data(buf, "friendlyName", s);
    201 
    202 	s = iface->wps->dev.manufacturer;
    203 	s = ((s && *s) ? s : "");
    204 	xml_add_tagged_data(buf, "manufacturer", s);
    205 
    206 	if (iface->wps->manufacturer_url)
    207 		xml_add_tagged_data(buf, "manufacturerURL",
    208 				    iface->wps->manufacturer_url);
    209 
    210 	if (iface->wps->model_description)
    211 		xml_add_tagged_data(buf, "modelDescription",
    212 				    iface->wps->model_description);
    213 
    214 	s = iface->wps->dev.model_name;
    215 	s = ((s && *s) ? s : "");
    216 	xml_add_tagged_data(buf, "modelName", s);
    217 
    218 	if (iface->wps->dev.model_number)
    219 		xml_add_tagged_data(buf, "modelNumber",
    220 				    iface->wps->dev.model_number);
    221 
    222 	if (iface->wps->model_url)
    223 		xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
    224 
    225 	if (iface->wps->dev.serial_number)
    226 		xml_add_tagged_data(buf, "serialNumber",
    227 				    iface->wps->dev.serial_number);
    228 
    229 	uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
    230 	s = uuid_string;
    231 	/* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
    232 	 * easily...
    233 	 */
    234 	wpabuf_put_str(buf, "<UDN>uuid:");
    235 	xml_data_encode(buf, s, os_strlen(s));
    236 	wpabuf_put_str(buf, "</UDN>\n");
    237 
    238 	if (iface->wps->upc)
    239 		xml_add_tagged_data(buf, "UPC", iface->wps->upc);
    240 
    241 	wpabuf_put_str(buf, wps_device_xml_postfix);
    242 }
    243 
    244 
    245 static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
    246 {
    247 	wpabuf_put_str(buf, "HTTP/1.1 ");
    248 	switch (code) {
    249 	case HTTP_OK:
    250 		wpabuf_put_str(buf, "200 OK\r\n");
    251 		break;
    252 	case HTTP_BAD_REQUEST:
    253 		wpabuf_put_str(buf, "400 Bad request\r\n");
    254 		break;
    255 	case HTTP_PRECONDITION_FAILED:
    256 		wpabuf_put_str(buf, "412 Precondition failed\r\n");
    257 		break;
    258 	case HTTP_UNIMPLEMENTED:
    259 		wpabuf_put_str(buf, "501 Unimplemented\r\n");
    260 		break;
    261 	case HTTP_INTERNAL_SERVER_ERROR:
    262 	default:
    263 		wpabuf_put_str(buf, "500 Internal server error\r\n");
    264 		break;
    265 	}
    266 }
    267 
    268 
    269 static void http_put_date(struct wpabuf *buf)
    270 {
    271 	wpabuf_put_str(buf, "Date: ");
    272 	format_date(buf);
    273 	wpabuf_put_str(buf, "\r\n");
    274 }
    275 
    276 
    277 static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
    278 {
    279 	http_put_reply_code(buf, code);
    280 	wpabuf_put_str(buf, http_server_hdr);
    281 	wpabuf_put_str(buf, http_connection_close);
    282 	wpabuf_put_str(buf, "Content-Length: 0\r\n"
    283 		       "\r\n");
    284 }
    285 
    286 
    287 /* Given that we have received a header w/ GET, act upon it
    288  *
    289  * Format of GET (case-insensitive):
    290  *
    291  * First line must be:
    292  *      GET /<file> HTTP/1.1
    293  * Since we don't do anything fancy we just ignore other lines.
    294  *
    295  * Our response (if no error) which includes only required lines is:
    296  * HTTP/1.1 200 OK
    297  * Connection: close
    298  * Content-Type: text/xml
    299  * Date: <rfc1123-date>
    300  *
    301  * Header lines must end with \r\n
    302  * Per RFC 2616, content-length: is not required but connection:close
    303  * would appear to be required (given that we will be closing it!).
    304  */
    305 static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
    306 				     struct http_request *hreq, char *filename)
    307 {
    308 	struct wpabuf *buf; /* output buffer, allocated */
    309 	char *put_length_here;
    310 	char *body_start;
    311 	enum {
    312 		GET_DEVICE_XML_FILE,
    313 		GET_SCPD_XML_FILE
    314 	} req;
    315 	size_t extra_len = 0;
    316 	int body_length;
    317 	char len_buf[10];
    318 	struct upnp_wps_device_interface *iface;
    319 
    320 	iface = dl_list_first(&sm->interfaces,
    321 			      struct upnp_wps_device_interface, list);
    322 
    323 	/*
    324 	 * It is not required that filenames be case insensitive but it is
    325 	 * allowed and cannot hurt here.
    326 	 */
    327 	if (filename == NULL)
    328 		filename = "(null)"; /* just in case */
    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(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 
    408 send_buf:
    409 	http_request_send_and_deinit(hreq, buf);
    410 }
    411 
    412 
    413 static enum http_reply_code
    414 web_process_get_device_info(struct upnp_wps_device_sm *sm,
    415 			    struct wpabuf **reply, const char **replyname)
    416 {
    417 	static const char *name = "NewDeviceInfo";
    418 	struct wps_config cfg;
    419 	struct upnp_wps_device_interface *iface;
    420 	struct upnp_wps_peer *peer;
    421 
    422 	iface = dl_list_first(&sm->interfaces,
    423 			      struct upnp_wps_device_interface, list);
    424 	peer = &iface->peer;
    425 
    426 	wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
    427 
    428 	if (iface->ctx->ap_pin == NULL)
    429 		return HTTP_INTERNAL_SERVER_ERROR;
    430 
    431 	/*
    432 	 * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
    433 	 * registration over UPnP with the AP acting as an Enrollee. It should
    434 	 * be noted that this is frequently used just to get the device data,
    435 	 * i.e., there may not be any intent to actually complete the
    436 	 * registration.
    437 	 */
    438 
    439 	if (peer->wps)
    440 		wps_deinit(peer->wps);
    441 
    442 	os_memset(&cfg, 0, sizeof(cfg));
    443 	cfg.wps = iface->wps;
    444 	cfg.pin = (u8 *) iface->ctx->ap_pin;
    445 	cfg.pin_len = os_strlen(iface->ctx->ap_pin);
    446 	peer->wps = wps_init(&cfg);
    447 	if (peer->wps) {
    448 		enum wsc_op_code op_code;
    449 		*reply = wps_get_msg(peer->wps, &op_code);
    450 		if (*reply == NULL) {
    451 			wps_deinit(peer->wps);
    452 			peer->wps = NULL;
    453 		}
    454 	} else
    455 		*reply = NULL;
    456 	if (*reply == NULL) {
    457 		wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
    458 		return HTTP_INTERNAL_SERVER_ERROR;
    459 	}
    460 	*replyname = name;
    461 	return HTTP_OK;
    462 }
    463 
    464 
    465 static enum http_reply_code
    466 web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
    467 			struct wpabuf **reply, const char **replyname)
    468 {
    469 	struct wpabuf *msg;
    470 	static const char *name = "NewOutMessage";
    471 	enum http_reply_code ret;
    472 	enum wps_process_res res;
    473 	enum wsc_op_code op_code;
    474 	struct upnp_wps_device_interface *iface;
    475 
    476 	iface = dl_list_first(&sm->interfaces,
    477 			      struct upnp_wps_device_interface, list);
    478 
    479 	/*
    480 	 * PutMessage is used by external UPnP-based Registrar to perform WPS
    481 	 * operation with the access point itself; as compared with
    482 	 * PutWLANResponse which is for proxying.
    483 	 */
    484 	wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
    485 	msg = xml_get_base64_item(data, "NewInMessage", &ret);
    486 	if (msg == NULL)
    487 		return ret;
    488 	res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
    489 	if (res == WPS_FAILURE)
    490 		*reply = NULL;
    491 	else
    492 		*reply = wps_get_msg(iface->peer.wps, &op_code);
    493 	wpabuf_free(msg);
    494 	if (*reply == NULL)
    495 		return HTTP_INTERNAL_SERVER_ERROR;
    496 	*replyname = name;
    497 	return HTTP_OK;
    498 }
    499 
    500 
    501 static enum http_reply_code
    502 web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
    503 			      struct wpabuf **reply, const char **replyname)
    504 {
    505 	struct wpabuf *msg;
    506 	enum http_reply_code ret;
    507 	u8 macaddr[ETH_ALEN];
    508 	int ev_type;
    509 	int type;
    510 	char *val;
    511 	struct upnp_wps_device_interface *iface;
    512 	int ok = 0;
    513 
    514 	/*
    515 	 * External UPnP-based Registrar is passing us a message to be proxied
    516 	 * over to a Wi-Fi -based client of ours.
    517 	 */
    518 
    519 	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
    520 	msg = xml_get_base64_item(data, "NewMessage", &ret);
    521 	if (msg == NULL) {
    522 		wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
    523 			   "from PutWLANResponse");
    524 		return ret;
    525 	}
    526 	val = xml_get_first_item(data, "NewWLANEventType");
    527 	if (val == NULL) {
    528 		wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
    529 			   "PutWLANResponse");
    530 		wpabuf_free(msg);
    531 		return UPNP_ARG_VALUE_INVALID;
    532 	}
    533 	ev_type = atol(val);
    534 	os_free(val);
    535 	val = xml_get_first_item(data, "NewWLANEventMAC");
    536 	if (val == NULL) {
    537 		wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
    538 			   "PutWLANResponse");
    539 		wpabuf_free(msg);
    540 		return UPNP_ARG_VALUE_INVALID;
    541 	}
    542 	if (hwaddr_aton(val, macaddr)) {
    543 		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
    544 			   "PutWLANResponse: '%s'", val);
    545 #ifdef CONFIG_WPS_STRICT
    546 		{
    547 			struct wps_parse_attr attr;
    548 			if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
    549 				wpabuf_free(msg);
    550 				os_free(val);
    551 				return UPNP_ARG_VALUE_INVALID;
    552 			}
    553 		}
    554 #endif /* CONFIG_WPS_STRICT */
    555 		if (hwaddr_aton2(val, macaddr) > 0) {
    556 			/*
    557 			 * At least some versions of Intel PROset seem to be
    558 			 * using dot-deliminated MAC address format here.
    559 			 */
    560 			wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
    561 				   "incorrect MAC address format in "
    562 				   "NewWLANEventMAC: %s -> " MACSTR,
    563 				   val, MAC2STR(macaddr));
    564 		} else {
    565 			wpabuf_free(msg);
    566 			os_free(val);
    567 			return UPNP_ARG_VALUE_INVALID;
    568 		}
    569 	}
    570 	os_free(val);
    571 	if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
    572 		struct wps_parse_attr attr;
    573 		if (wps_parse_msg(msg, &attr) < 0 ||
    574 		    attr.msg_type == NULL)
    575 			type = -1;
    576 		else
    577 			type = *attr.msg_type;
    578 		wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
    579 	} else
    580 		type = -1;
    581 	dl_list_for_each(iface, &sm->interfaces,
    582 			 struct upnp_wps_device_interface, list) {
    583 		if (iface->ctx->rx_req_put_wlan_response &&
    584 		    iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
    585 							 macaddr, msg, type)
    586 		    == 0)
    587 			ok = 1;
    588 	}
    589 
    590 	if (!ok) {
    591 		wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
    592 			   "rx_req_put_wlan_response");
    593 		wpabuf_free(msg);
    594 		return HTTP_INTERNAL_SERVER_ERROR;
    595 	}
    596 	wpabuf_free(msg);
    597 	*replyname = NULL;
    598 	*reply = NULL;
    599 	return HTTP_OK;
    600 }
    601 
    602 
    603 static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
    604 {
    605 	struct subscr_addr *a;
    606 
    607 	dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
    608 		if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
    609 			return 1;
    610 	}
    611 	return 0;
    612 }
    613 
    614 
    615 static struct subscription * find_er(struct upnp_wps_device_sm *sm,
    616 				     struct sockaddr_in *cli)
    617 {
    618 	struct subscription *s;
    619 	dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
    620 		if (find_er_addr(s, cli))
    621 			return s;
    622 	return NULL;
    623 }
    624 
    625 
    626 static enum http_reply_code
    627 web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
    628 				   struct sockaddr_in *cli, char *data,
    629 				   struct wpabuf **reply,
    630 				   const char **replyname)
    631 {
    632 	struct wpabuf *msg;
    633 	enum http_reply_code ret;
    634 	struct subscription *s;
    635 	struct upnp_wps_device_interface *iface;
    636 	int err = 0;
    637 
    638 	wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
    639 	s = find_er(sm, cli);
    640 	if (s == NULL) {
    641 		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
    642 			   "from unknown ER");
    643 		return UPNP_ACTION_FAILED;
    644 	}
    645 	msg = xml_get_base64_item(data, "NewMessage", &ret);
    646 	if (msg == NULL)
    647 		return ret;
    648 	dl_list_for_each(iface, &sm->interfaces,
    649 			 struct upnp_wps_device_interface, list) {
    650 		if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
    651 						   msg))
    652 			err = 1;
    653 	}
    654 	wpabuf_free(msg);
    655 	if (err)
    656 		return HTTP_INTERNAL_SERVER_ERROR;
    657 	*replyname = NULL;
    658 	*reply = NULL;
    659 	return HTTP_OK;
    660 }
    661 
    662 
    663 static const char *soap_prefix =
    664 	"<?xml version=\"1.0\"?>\n"
    665 	"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
    666 	"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
    667 	"<s:Body>\n";
    668 static const char *soap_postfix =
    669 	"</s:Body>\n</s:Envelope>\n";
    670 
    671 static const char *soap_error_prefix =
    672 	"<s:Fault>\n"
    673 	"<faultcode>s:Client</faultcode>\n"
    674 	"<faultstring>UPnPError</faultstring>\n"
    675 	"<detail>\n"
    676 	"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
    677 static const char *soap_error_postfix =
    678 	"<errorDescription>Error</errorDescription>\n"
    679 	"</UPnPError>\n"
    680 	"</detail>\n"
    681 	"</s:Fault>\n";
    682 
    683 static void web_connection_send_reply(struct http_request *req,
    684 				      enum http_reply_code ret,
    685 				      const char *action, int action_len,
    686 				      const struct wpabuf *reply,
    687 				      const char *replyname)
    688 {
    689 	struct wpabuf *buf;
    690 	char *replydata;
    691 	char *put_length_here = NULL;
    692 	char *body_start = NULL;
    693 
    694 	if (reply) {
    695 		size_t len;
    696 		replydata = (char *) base64_encode(wpabuf_head(reply),
    697 						   wpabuf_len(reply), &len);
    698 	} else
    699 		replydata = NULL;
    700 
    701 	/* Parameters of the response:
    702 	 *      action(action_len) -- action we are responding to
    703 	 *      replyname -- a name we need for the reply
    704 	 *      replydata -- NULL or null-terminated string
    705 	 */
    706 	buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
    707 			   (action_len > 0 ? action_len * 2 : 0));
    708 	if (buf == NULL) {
    709 		wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
    710 			   "POST");
    711 		os_free(replydata);
    712 		http_request_deinit(req);
    713 		return;
    714 	}
    715 
    716 	/*
    717 	 * Assuming we will be successful, put in the output header first.
    718 	 * Note: we do not keep connections alive (and httpread does
    719 	 * not support it)... therefore we must have Connection: close.
    720 	 */
    721 	if (ret == HTTP_OK) {
    722 		wpabuf_put_str(buf,
    723 			       "HTTP/1.1 200 OK\r\n"
    724 			       "Content-Type: text/xml; "
    725 			       "charset=\"utf-8\"\r\n");
    726 	} else {
    727 		wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
    728 	}
    729 	wpabuf_put_str(buf, http_connection_close);
    730 
    731 	wpabuf_put_str(buf, "Content-Length: ");
    732 	/*
    733 	 * We will paste the length in later, leaving some extra whitespace.
    734 	 * HTTP code is supposed to be tolerant of extra whitespace.
    735 	 */
    736 	put_length_here = wpabuf_put(buf, 0);
    737 	wpabuf_put_str(buf, "        \r\n");
    738 
    739 	http_put_date(buf);
    740 
    741 	/* terminating empty line */
    742 	wpabuf_put_str(buf, "\r\n");
    743 
    744 	body_start = wpabuf_put(buf, 0);
    745 
    746 	if (ret == HTTP_OK) {
    747 		wpabuf_put_str(buf, soap_prefix);
    748 		wpabuf_put_str(buf, "<u:");
    749 		wpabuf_put_data(buf, action, action_len);
    750 		wpabuf_put_str(buf, "Response xmlns:u=\"");
    751 		wpabuf_put_str(buf, urn_wfawlanconfig);
    752 		wpabuf_put_str(buf, "\">\n");
    753 		if (replydata && replyname) {
    754 			/* TODO: might possibly need to escape part of reply
    755 			 * data? ...
    756 			 * probably not, unlikely to have ampersand(&) or left
    757 			 * angle bracket (<) in it...
    758 			 */
    759 			wpabuf_printf(buf, "<%s>", replyname);
    760 			wpabuf_put_str(buf, replydata);
    761 			wpabuf_printf(buf, "</%s>\n", replyname);
    762 		}
    763 		wpabuf_put_str(buf, "</u:");
    764 		wpabuf_put_data(buf, action, action_len);
    765 		wpabuf_put_str(buf, "Response>\n");
    766 		wpabuf_put_str(buf, soap_postfix);
    767 	} else {
    768 		/* Error case */
    769 		wpabuf_put_str(buf, soap_prefix);
    770 		wpabuf_put_str(buf, soap_error_prefix);
    771 		wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
    772 		wpabuf_put_str(buf, soap_error_postfix);
    773 		wpabuf_put_str(buf, soap_postfix);
    774 	}
    775 	os_free(replydata);
    776 
    777 	/* Now patch in the content length at the end */
    778 	if (body_start && put_length_here) {
    779 		int body_length = (char *) wpabuf_put(buf, 0) - body_start;
    780 		char len_buf[10];
    781 		os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
    782 		os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
    783 	}
    784 
    785 	http_request_send_and_deinit(req, buf);
    786 }
    787 
    788 
    789 static const char * web_get_action(struct http_request *req,
    790 				   size_t *action_len)
    791 {
    792 	const char *match;
    793 	int match_len;
    794 	char *b;
    795 	char *action;
    796 
    797 	*action_len = 0;
    798 	/* The SOAPAction line of the header tells us what we want to do */
    799 	b = http_request_get_hdr_line(req, "SOAPAction:");
    800 	if (b == NULL)
    801 		return NULL;
    802 	if (*b == '"')
    803 		b++;
    804 	else
    805 		return NULL;
    806 	match = urn_wfawlanconfig;
    807 	match_len = os_strlen(urn_wfawlanconfig) - 1;
    808 	if (os_strncasecmp(b, match, match_len))
    809 		return NULL;
    810 	b += match_len;
    811 	/* skip over version */
    812 	while (isgraph(*b) && *b != '#')
    813 		b++;
    814 	if (*b != '#')
    815 		return NULL;
    816 	b++;
    817 	/* Following the sharp(#) should be the action and a double quote */
    818 	action = b;
    819 	while (isgraph(*b) && *b != '"')
    820 		b++;
    821 	if (*b != '"')
    822 		return NULL;
    823 	*action_len = b - action;
    824 	return action;
    825 }
    826 
    827 
    828 /* Given that we have received a header w/ POST, act upon it
    829  *
    830  * Format of POST (case-insensitive):
    831  *
    832  * First line must be:
    833  *      POST /<file> HTTP/1.1
    834  * Since we don't do anything fancy we just ignore other lines.
    835  *
    836  * Our response (if no error) which includes only required lines is:
    837  * HTTP/1.1 200 OK
    838  * Connection: close
    839  * Content-Type: text/xml
    840  * Date: <rfc1123-date>
    841  *
    842  * Header lines must end with \r\n
    843  * Per RFC 2616, content-length: is not required but connection:close
    844  * would appear to be required (given that we will be closing it!).
    845  */
    846 static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
    847 				      struct sockaddr_in *cli,
    848 				      struct http_request *req,
    849 				      const char *filename)
    850 {
    851 	enum http_reply_code ret;
    852 	char *data = http_request_get_data(req); /* body of http msg */
    853 	const char *action = NULL;
    854 	size_t action_len = 0;
    855 	const char *replyname = NULL; /* argument name for the reply */
    856 	struct wpabuf *reply = NULL; /* data for the reply */
    857 
    858 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
    859 		wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
    860 			   filename);
    861 		ret = HTTP_NOT_FOUND;
    862 		goto bad;
    863 	}
    864 
    865 	ret = UPNP_INVALID_ACTION;
    866 	action = web_get_action(req, &action_len);
    867 	if (action == NULL)
    868 		goto bad;
    869 
    870 	if (!os_strncasecmp("GetDeviceInfo", action, action_len))
    871 		ret = web_process_get_device_info(sm, &reply, &replyname);
    872 	else if (!os_strncasecmp("PutMessage", action, action_len))
    873 		ret = web_process_put_message(sm, data, &reply, &replyname);
    874 	else if (!os_strncasecmp("PutWLANResponse", action, action_len))
    875 		ret = web_process_put_wlan_response(sm, data, &reply,
    876 						    &replyname);
    877 	else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
    878 		ret = web_process_set_selected_registrar(sm, cli, data, &reply,
    879 							 &replyname);
    880 	else
    881 		wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
    882 
    883 bad:
    884 	if (ret != HTTP_OK)
    885 		wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
    886 	web_connection_send_reply(req, ret, action, action_len, reply,
    887 				  replyname);
    888 	wpabuf_free(reply);
    889 }
    890 
    891 
    892 /* Given that we have received a header w/ SUBSCRIBE, act upon it
    893  *
    894  * Format of SUBSCRIBE (case-insensitive):
    895  *
    896  * First line must be:
    897  *      SUBSCRIBE /wps_event HTTP/1.1
    898  *
    899  * Our response (if no error) which includes only required lines is:
    900  * HTTP/1.1 200 OK
    901  * Server: xx, UPnP/1.0, xx
    902  * SID: uuid:xxxxxxxxx
    903  * Timeout: Second-<n>
    904  * Content-Length: 0
    905  * Date: xxxx
    906  *
    907  * Header lines must end with \r\n
    908  * Per RFC 2616, content-length: is not required but connection:close
    909  * would appear to be required (given that we will be closing it!).
    910  */
    911 static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
    912 					   struct http_request *req,
    913 					   const char *filename)
    914 {
    915 	struct wpabuf *buf;
    916 	char *b;
    917 	char *hdr = http_request_get_hdr(req);
    918 	char *h;
    919 	char *match;
    920 	int match_len;
    921 	char *end;
    922 	int len;
    923 	int got_nt = 0;
    924 	u8 uuid[UUID_LEN];
    925 	int got_uuid = 0;
    926 	char *callback_urls = NULL;
    927 	struct subscription *s = NULL;
    928 	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
    929 
    930 	buf = wpabuf_alloc(1000);
    931 	if (buf == NULL) {
    932 		http_request_deinit(req);
    933 		return;
    934 	}
    935 
    936 	wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
    937 			  (u8 *) hdr, os_strlen(hdr));
    938 
    939 	/* Parse/validate headers */
    940 	h = hdr;
    941 	/* First line: SUBSCRIBE /wps_event HTTP/1.1
    942 	 * has already been parsed.
    943 	 */
    944 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
    945 		ret = HTTP_PRECONDITION_FAILED;
    946 		goto error;
    947 	}
    948 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
    949 	end = os_strchr(h, '\n');
    950 
    951 	for (; end != NULL; h = end + 1) {
    952 		/* Option line by option line */
    953 		h = end + 1;
    954 		end = os_strchr(h, '\n');
    955 		if (end == NULL)
    956 			break; /* no unterminated lines allowed */
    957 
    958 		/* NT assures that it is our type of subscription;
    959 		 * not used for a renewal.
    960 		 **/
    961 		match = "NT:";
    962 		match_len = os_strlen(match);
    963 		if (os_strncasecmp(h, match, match_len) == 0) {
    964 			h += match_len;
    965 			while (*h == ' ' || *h == '\t')
    966 				h++;
    967 			match = "upnp:event";
    968 			match_len = os_strlen(match);
    969 			if (os_strncasecmp(h, match, match_len) != 0) {
    970 				ret = HTTP_BAD_REQUEST;
    971 				goto error;
    972 			}
    973 			got_nt = 1;
    974 			continue;
    975 		}
    976 		/* HOST should refer to us */
    977 #if 0
    978 		match = "HOST:";
    979 		match_len = os_strlen(match);
    980 		if (os_strncasecmp(h, match, match_len) == 0) {
    981 			h += match_len;
    982 			while (*h == ' ' || *h == '\t')
    983 				h++;
    984 			.....
    985 		}
    986 #endif
    987 		/* CALLBACK gives one or more URLs for NOTIFYs
    988 		 * to be sent as a result of the subscription.
    989 		 * Each URL is enclosed in angle brackets.
    990 		 */
    991 		match = "CALLBACK:";
    992 		match_len = os_strlen(match);
    993 		if (os_strncasecmp(h, match, match_len) == 0) {
    994 			h += match_len;
    995 			while (*h == ' ' || *h == '\t')
    996 				h++;
    997 			len = end - h;
    998 			os_free(callback_urls);
    999 			callback_urls = dup_binstr(h, len);
   1000 			if (callback_urls == NULL) {
   1001 				ret = HTTP_INTERNAL_SERVER_ERROR;
   1002 				goto error;
   1003 			}
   1004 			continue;
   1005 		}
   1006 		/* SID is only for renewal */
   1007 		match = "SID:";
   1008 		match_len = os_strlen(match);
   1009 		if (os_strncasecmp(h, match, match_len) == 0) {
   1010 			h += match_len;
   1011 			while (*h == ' ' || *h == '\t')
   1012 				h++;
   1013 			match = "uuid:";
   1014 			match_len = os_strlen(match);
   1015 			if (os_strncasecmp(h, match, match_len) != 0) {
   1016 				ret = HTTP_BAD_REQUEST;
   1017 				goto error;
   1018 			}
   1019 			h += match_len;
   1020 			while (*h == ' ' || *h == '\t')
   1021 				h++;
   1022 			if (uuid_str2bin(h, uuid)) {
   1023 				ret = HTTP_BAD_REQUEST;
   1024 				goto error;
   1025 			}
   1026 			got_uuid = 1;
   1027 			continue;
   1028 		}
   1029 		/* TIMEOUT is requested timeout, but apparently we can
   1030 		 * just ignore this.
   1031 		 */
   1032 	}
   1033 
   1034 	if (got_uuid) {
   1035 		/* renewal */
   1036 		wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
   1037 		if (callback_urls) {
   1038 			ret = HTTP_BAD_REQUEST;
   1039 			goto error;
   1040 		}
   1041 		s = subscription_renew(sm, uuid);
   1042 		if (s == NULL) {
   1043 			char str[80];
   1044 			uuid_bin2str(uuid, str, sizeof(str));
   1045 			wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
   1046 				   "SID %s", str);
   1047 			ret = HTTP_PRECONDITION_FAILED;
   1048 			goto error;
   1049 		}
   1050 	} else if (callback_urls) {
   1051 		wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
   1052 		if (!got_nt) {
   1053 			ret = HTTP_PRECONDITION_FAILED;
   1054 			goto error;
   1055 		}
   1056 		s = subscription_start(sm, callback_urls);
   1057 		if (s == NULL) {
   1058 			ret = HTTP_INTERNAL_SERVER_ERROR;
   1059 			goto error;
   1060 		}
   1061 	} else {
   1062 		ret = HTTP_PRECONDITION_FAILED;
   1063 		goto error;
   1064 	}
   1065 
   1066 	/* success */
   1067 	http_put_reply_code(buf, HTTP_OK);
   1068 	wpabuf_put_str(buf, http_server_hdr);
   1069 	wpabuf_put_str(buf, http_connection_close);
   1070 	wpabuf_put_str(buf, "Content-Length: 0\r\n");
   1071 	wpabuf_put_str(buf, "SID: uuid:");
   1072 	/* subscription id */
   1073 	b = wpabuf_put(buf, 0);
   1074 	uuid_bin2str(s->uuid, b, 80);
   1075 	wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
   1076 	wpabuf_put(buf, os_strlen(b));
   1077 	wpabuf_put_str(buf, "\r\n");
   1078 	wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
   1079 	http_put_date(buf);
   1080 	/* And empty line to terminate header: */
   1081 	wpabuf_put_str(buf, "\r\n");
   1082 
   1083 	os_free(callback_urls);
   1084 	http_request_send_and_deinit(req, buf);
   1085 	return;
   1086 
   1087 error:
   1088 	/* Per UPnP spec:
   1089 	* Errors
   1090 	* Incompatible headers
   1091 	*   400 Bad Request. If SID header and one of NT or CALLBACK headers
   1092 	*     are present, the publisher must respond with HTTP error
   1093 	*     400 Bad Request.
   1094 	* Missing or invalid CALLBACK
   1095 	*   412 Precondition Failed. If CALLBACK header is missing or does not
   1096 	*     contain a valid HTTP URL, the publisher must respond with HTTP
   1097 	*     error 412 Precondition Failed.
   1098 	* Invalid NT
   1099 	*   412 Precondition Failed. If NT header does not equal upnp:event,
   1100 	*     the publisher must respond with HTTP error 412 Precondition
   1101 	*     Failed.
   1102 	* [For resubscription, use 412 if unknown uuid].
   1103 	* Unable to accept subscription
   1104 	*   5xx. If a publisher is not able to accept a subscription (such as
   1105 	*     due to insufficient resources), it must respond with a
   1106 	*     HTTP 500-series error code.
   1107 	*   599 Too many subscriptions (not a standard HTTP error)
   1108 	*/
   1109 	wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
   1110 	http_put_empty(buf, ret);
   1111 	http_request_send_and_deinit(req, buf);
   1112 	os_free(callback_urls);
   1113 }
   1114 
   1115 
   1116 /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
   1117  *
   1118  * Format of UNSUBSCRIBE (case-insensitive):
   1119  *
   1120  * First line must be:
   1121  *      UNSUBSCRIBE /wps_event HTTP/1.1
   1122  *
   1123  * Our response (if no error) which includes only required lines is:
   1124  * HTTP/1.1 200 OK
   1125  * Content-Length: 0
   1126  *
   1127  * Header lines must end with \r\n
   1128  * Per RFC 2616, content-length: is not required but connection:close
   1129  * would appear to be required (given that we will be closing it!).
   1130  */
   1131 static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
   1132 					     struct http_request *req,
   1133 					     const char *filename)
   1134 {
   1135 	struct wpabuf *buf;
   1136 	char *hdr = http_request_get_hdr(req);
   1137 	char *h;
   1138 	char *match;
   1139 	int match_len;
   1140 	char *end;
   1141 	u8 uuid[UUID_LEN];
   1142 	int got_uuid = 0;
   1143 	struct subscription *s = NULL;
   1144 	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
   1145 
   1146 	/* Parse/validate headers */
   1147 	h = hdr;
   1148 	/* First line: UNSUBSCRIBE /wps_event HTTP/1.1
   1149 	 * has already been parsed.
   1150 	 */
   1151 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
   1152 		ret = HTTP_PRECONDITION_FAILED;
   1153 		goto send_msg;
   1154 	}
   1155 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
   1156 	end = os_strchr(h, '\n');
   1157 
   1158 	for (; end != NULL; h = end + 1) {
   1159 		/* Option line by option line */
   1160 		h = end + 1;
   1161 		end = os_strchr(h, '\n');
   1162 		if (end == NULL)
   1163 			break; /* no unterminated lines allowed */
   1164 
   1165 		/* HOST should refer to us */
   1166 #if 0
   1167 		match = "HOST:";
   1168 		match_len = os_strlen(match);
   1169 		if (os_strncasecmp(h, match, match_len) == 0) {
   1170 			h += match_len;
   1171 			while (*h == ' ' || *h == '\t')
   1172 				h++;
   1173 			.....
   1174 		}
   1175 #endif
   1176 		/* SID is only for renewal */
   1177 		match = "SID:";
   1178 		match_len = os_strlen(match);
   1179 		if (os_strncasecmp(h, match, match_len) == 0) {
   1180 			h += match_len;
   1181 			while (*h == ' ' || *h == '\t')
   1182 				h++;
   1183 			match = "uuid:";
   1184 			match_len = os_strlen(match);
   1185 			if (os_strncasecmp(h, match, match_len) != 0) {
   1186 				ret = HTTP_BAD_REQUEST;
   1187 				goto send_msg;
   1188 			}
   1189 			h += match_len;
   1190 			while (*h == ' ' || *h == '\t')
   1191 				h++;
   1192 			if (uuid_str2bin(h, uuid)) {
   1193 				ret = HTTP_BAD_REQUEST;
   1194 				goto send_msg;
   1195 			}
   1196 			got_uuid = 1;
   1197 			continue;
   1198 		}
   1199 	}
   1200 
   1201 	if (got_uuid) {
   1202 		s = subscription_find(sm, uuid);
   1203 		if (s) {
   1204 			struct subscr_addr *sa;
   1205 			sa = dl_list_first(&s->addr_list, struct subscr_addr,
   1206 					   list);
   1207 			wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
   1208 				   s, (sa && sa->domain_and_port) ?
   1209 				   sa->domain_and_port : "-null-");
   1210 			dl_list_del(&s->list);
   1211 			subscription_destroy(s);
   1212 		}
   1213 	} else {
   1214 		wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
   1215 			   "found)");
   1216 		ret = HTTP_PRECONDITION_FAILED;
   1217 		goto send_msg;
   1218 	}
   1219 
   1220 	ret = HTTP_OK;
   1221 
   1222 send_msg:
   1223 	buf = wpabuf_alloc(200);
   1224 	if (buf == NULL) {
   1225 		http_request_deinit(req);
   1226 		return;
   1227 	}
   1228 	http_put_empty(buf, ret);
   1229 	http_request_send_and_deinit(req, buf);
   1230 }
   1231 
   1232 
   1233 /* Send error in response to unknown requests */
   1234 static void web_connection_unimplemented(struct http_request *req)
   1235 {
   1236 	struct wpabuf *buf;
   1237 	buf = wpabuf_alloc(200);
   1238 	if (buf == NULL) {
   1239 		http_request_deinit(req);
   1240 		return;
   1241 	}
   1242 	http_put_empty(buf, HTTP_UNIMPLEMENTED);
   1243 	http_request_send_and_deinit(req, buf);
   1244 }
   1245 
   1246 
   1247 
   1248 /* Called when we have gotten an apparently valid http request.
   1249  */
   1250 static void web_connection_check_data(void *ctx, struct http_request *req)
   1251 {
   1252 	struct upnp_wps_device_sm *sm = ctx;
   1253 	enum httpread_hdr_type htype = http_request_get_type(req);
   1254 	char *filename = http_request_get_uri(req);
   1255 	struct sockaddr_in *cli = http_request_get_cli_addr(req);
   1256 
   1257 	if (!filename) {
   1258 		wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
   1259 		http_request_deinit(req);
   1260 		return;
   1261 	}
   1262 	/* Trim leading slashes from filename */
   1263 	while (*filename == '/')
   1264 		filename++;
   1265 
   1266 	wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
   1267 		   htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
   1268 
   1269 	switch (htype) {
   1270 	case HTTPREAD_HDR_TYPE_GET:
   1271 		web_connection_parse_get(sm, req, filename);
   1272 		break;
   1273 	case HTTPREAD_HDR_TYPE_POST:
   1274 		web_connection_parse_post(sm, cli, req, filename);
   1275 		break;
   1276 	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
   1277 		web_connection_parse_subscribe(sm, req, filename);
   1278 		break;
   1279 	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
   1280 		web_connection_parse_unsubscribe(sm, req, filename);
   1281 		break;
   1282 
   1283 		/* We are not required to support M-POST; just plain
   1284 		 * POST is supposed to work, so we only support that.
   1285 		 * If for some reason we need to support M-POST, it is
   1286 		 * mostly the same as POST, with small differences.
   1287 		 */
   1288 	default:
   1289 		/* Send 501 for anything else */
   1290 		web_connection_unimplemented(req);
   1291 		break;
   1292 	}
   1293 }
   1294 
   1295 
   1296 /*
   1297  * Listening for web connections
   1298  * We have a single TCP listening port, and hand off connections as we get
   1299  * them.
   1300  */
   1301 
   1302 void web_listener_stop(struct upnp_wps_device_sm *sm)
   1303 {
   1304 	http_server_deinit(sm->web_srv);
   1305 	sm->web_srv = NULL;
   1306 }
   1307 
   1308 
   1309 int web_listener_start(struct upnp_wps_device_sm *sm)
   1310 {
   1311 	struct in_addr addr;
   1312 	addr.s_addr = sm->ip_addr;
   1313 	sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
   1314 				       sm);
   1315 	if (sm->web_srv == NULL) {
   1316 		web_listener_stop(sm);
   1317 		return -1;
   1318 	}
   1319 	sm->web_port = http_server_get_port(sm->web_srv);
   1320 
   1321 	return 0;
   1322 }
   1323