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