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