2 * UPnP WPS Device - Web connections
3 * Copyright (c) 2000-2003 Intel Corporation
4 * Copyright (c) 2006-2007 Sony Corporation
5 * Copyright (c) 2008-2009 Atheros Communications
6 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
8 * See wps_upnp.c for more details on licensing and code history.
17 #include "http_server.h"
20 #include "wps_upnp_i.h"
23 /***************************************************************************
24 * Web connections (we serve pages of info about ourselves, handle
25 * requests, etc. etc.).
26 **************************************************************************/
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 */
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";
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.
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"
51 "<name>GetDeviceInfo</name>\n"
54 "<name>NewDeviceInfo</name>\n"
55 "<direction>out</direction>\n"
56 "<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
61 "<name>PutMessage</name>\n"
64 "<name>NewInMessage</name>\n"
65 "<direction>in</direction>\n"
66 "<relatedStateVariable>InMessage</relatedStateVariable>\n"
69 "<name>NewOutMessage</name>\n"
70 "<direction>out</direction>\n"
71 "<relatedStateVariable>OutMessage</relatedStateVariable>\n"
76 "<name>PutWLANResponse</name>\n"
79 "<name>NewMessage</name>\n"
80 "<direction>in</direction>\n"
81 "<relatedStateVariable>Message</relatedStateVariable>\n"
84 "<name>NewWLANEventType</name>\n"
85 "<direction>in</direction>\n"
86 "<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
89 "<name>NewWLANEventMAC</name>\n"
90 "<direction>in</direction>\n"
91 "<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
96 "<name>SetSelectedRegistrar</name>\n"
99 "<name>NewMessage</name>\n"
100 "<direction>in</direction>\n"
101 "<relatedStateVariable>Message</relatedStateVariable>\n"
106 "<serviceStateTable>\n"
107 "<stateVariable sendEvents=\"no\">\n"
108 "<name>Message</name>\n"
109 "<dataType>bin.base64</dataType>\n"
111 "<stateVariable sendEvents=\"no\">\n"
112 "<name>InMessage</name>\n"
113 "<dataType>bin.base64</dataType>\n"
115 "<stateVariable sendEvents=\"no\">\n"
116 "<name>OutMessage</name>\n"
117 "<dataType>bin.base64</dataType>\n"
119 "<stateVariable sendEvents=\"no\">\n"
120 "<name>DeviceInfo</name>\n"
121 "<dataType>bin.base64</dataType>\n"
123 "<stateVariable sendEvents=\"yes\">\n"
124 "<name>APStatus</name>\n"
125 "<dataType>ui1</dataType>\n"
127 "<stateVariable sendEvents=\"yes\">\n"
128 "<name>STAStatus</name>\n"
129 "<dataType>ui1</dataType>\n"
131 "<stateVariable sendEvents=\"yes\">\n"
132 "<name>WLANEvent</name>\n"
133 "<dataType>bin.base64</dataType>\n"
135 "<stateVariable sendEvents=\"no\">\n"
136 "<name>WLANEventType</name>\n"
137 "<dataType>ui1</dataType>\n"
139 "<stateVariable sendEvents=\"no\">\n"
140 "<name>WLANEventMAC</name>\n"
141 "<dataType>string</dataType>\n"
143 "<stateVariable sendEvents=\"no\">\n"
144 "<name>WLANResponse</name>\n"
145 "<dataType>bin.base64</dataType>\n"
147 "</serviceStateTable>\n"
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"
160 "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
163 static const char *wps_device_xml_postfix =
166 "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
168 "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
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"
179 /* format_wps_device_xml -- produce content of "file" wps_device.xml
180 * (UPNP_WPS_DEVICE_XML_FILE)
182 static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
186 char uuid_string[80];
187 struct upnp_wps_device_interface *iface;
189 iface = dl_list_first(&sm->interfaces,
190 struct upnp_wps_device_interface, list);
192 wpabuf_put_str(buf, wps_device_xml_prefix);
195 * Add required fields with default values if not configured. Add
196 * optional and recommended fields only if configured.
198 s = iface->wps->friendly_name;
199 s = ((s && *s) ? s : "WPS Access Point");
200 xml_add_tagged_data(buf, "friendlyName", s);
202 s = iface->wps->dev.manufacturer;
203 s = ((s && *s) ? s : "");
204 xml_add_tagged_data(buf, "manufacturer", s);
206 if (iface->wps->manufacturer_url)
207 xml_add_tagged_data(buf, "manufacturerURL",
208 iface->wps->manufacturer_url);
210 if (iface->wps->model_description)
211 xml_add_tagged_data(buf, "modelDescription",
212 iface->wps->model_description);
214 s = iface->wps->dev.model_name;
215 s = ((s && *s) ? s : "");
216 xml_add_tagged_data(buf, "modelName", s);
218 if (iface->wps->dev.model_number)
219 xml_add_tagged_data(buf, "modelNumber",
220 iface->wps->dev.model_number);
222 if (iface->wps->model_url)
223 xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
225 if (iface->wps->dev.serial_number)
226 xml_add_tagged_data(buf, "serialNumber",
227 iface->wps->dev.serial_number);
229 uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
231 /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
234 wpabuf_put_str(buf, "<UDN>uuid:");
235 xml_data_encode(buf, s, os_strlen(s));
236 wpabuf_put_str(buf, "</UDN>\n");
239 xml_add_tagged_data(buf, "UPC", iface->wps->upc);
241 wpabuf_put_str(buf, wps_device_xml_postfix);
245 static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
247 wpabuf_put_str(buf, "HTTP/1.1 ");
250 wpabuf_put_str(buf, "200 OK\r\n");
252 case HTTP_BAD_REQUEST:
253 wpabuf_put_str(buf, "400 Bad request\r\n");
255 case HTTP_PRECONDITION_FAILED:
256 wpabuf_put_str(buf, "412 Precondition failed\r\n");
258 case HTTP_UNIMPLEMENTED:
259 wpabuf_put_str(buf, "501 Unimplemented\r\n");
261 case HTTP_INTERNAL_SERVER_ERROR:
263 wpabuf_put_str(buf, "500 Internal server error\r\n");
269 static void http_put_date(struct wpabuf *buf)
271 wpabuf_put_str(buf, "Date: ");
273 wpabuf_put_str(buf, "\r\n");
277 static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
279 http_put_reply_code(buf, code);
280 wpabuf_put_str(buf, http_server_hdr);
281 wpabuf_put_str(buf, http_connection_close);
282 wpabuf_put_str(buf, "Content-Length: 0\r\n"
287 /* Given that we have received a header w/ GET, act upon it
289 * Format of GET (case-insensitive):
291 * First line must be:
292 * GET /<file> HTTP/1.1
293 * Since we don't do anything fancy we just ignore other lines.
295 * Our response (if no error) which includes only required lines is:
298 * Content-Type: text/xml
299 * Date: <rfc1123-date>
301 * Header lines must end with \r\n
302 * Per RFC 2616, content-length: is not required but connection:close
303 * would appear to be required (given that we will be closing it!).
305 static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
306 struct http_request *hreq, char *filename)
308 struct wpabuf *buf; /* output buffer, allocated */
309 char *put_length_here;
315 size_t extra_len = 0;
318 struct upnp_wps_device_interface *iface;
320 iface = dl_list_first(&sm->interfaces,
321 struct upnp_wps_device_interface, list);
324 * It is not required that filenames be case insensitive but it is
325 * allowed and cannot hurt here.
327 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
328 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
329 req = GET_DEVICE_XML_FILE;
331 if (iface->wps->friendly_name)
332 extra_len += os_strlen(iface->wps->friendly_name);
333 if (iface->wps->manufacturer_url)
334 extra_len += os_strlen(iface->wps->manufacturer_url);
335 if (iface->wps->model_description)
336 extra_len += os_strlen(iface->wps->model_description);
337 if (iface->wps->model_url)
338 extra_len += os_strlen(iface->wps->model_url);
340 extra_len += os_strlen(iface->wps->upc);
341 } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
342 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
343 req = GET_SCPD_XML_FILE;
344 extra_len = os_strlen(wps_scpd_xml);
347 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
349 buf = wpabuf_alloc(200);
351 http_request_deinit(hreq);
355 "HTTP/1.1 404 Not Found\r\n"
356 "Connection: close\r\n");
360 /* terminating empty line */
361 wpabuf_put_str(buf, "\r\n");
366 buf = wpabuf_alloc(1000 + extra_len);
368 http_request_deinit(hreq);
373 "HTTP/1.1 200 OK\r\n"
374 "Content-Type: text/xml; charset=\"utf-8\"\r\n");
375 wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
376 wpabuf_put_str(buf, "Connection: close\r\n");
377 wpabuf_put_str(buf, "Content-Length: ");
379 * We will paste the length in later, leaving some extra whitespace.
380 * HTTP code is supposed to be tolerant of extra whitespace.
382 put_length_here = wpabuf_put(buf, 0);
383 wpabuf_put_str(buf, " \r\n");
387 /* terminating empty line */
388 wpabuf_put_str(buf, "\r\n");
390 body_start = wpabuf_put(buf, 0);
393 case GET_DEVICE_XML_FILE:
394 format_wps_device_xml(sm, buf);
396 case GET_SCPD_XML_FILE:
397 wpabuf_put_str(buf, wps_scpd_xml);
401 /* Now patch in the content length at the end */
402 body_length = (char *) wpabuf_put(buf, 0) - body_start;
403 os_snprintf(len_buf, 10, "%d", body_length);
404 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
407 http_request_send_and_deinit(hreq, buf);
411 static enum http_reply_code
412 web_process_get_device_info(struct upnp_wps_device_sm *sm,
413 struct wpabuf **reply, const char **replyname)
415 static const char *name = "NewDeviceInfo";
416 struct wps_config cfg;
417 struct upnp_wps_device_interface *iface;
418 struct upnp_wps_peer *peer;
420 iface = dl_list_first(&sm->interfaces,
421 struct upnp_wps_device_interface, list);
424 wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
426 if (iface->ctx->ap_pin == NULL)
427 return HTTP_INTERNAL_SERVER_ERROR;
430 * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
431 * registration over UPnP with the AP acting as an Enrollee. It should
432 * be noted that this is frequently used just to get the device data,
433 * i.e., there may not be any intent to actually complete the
438 wps_deinit(peer->wps);
440 os_memset(&cfg, 0, sizeof(cfg));
441 cfg.wps = iface->wps;
442 cfg.pin = (u8 *) iface->ctx->ap_pin;
443 cfg.pin_len = os_strlen(iface->ctx->ap_pin);
444 peer->wps = wps_init(&cfg);
446 enum wsc_op_code op_code;
447 *reply = wps_get_msg(peer->wps, &op_code);
448 if (*reply == NULL) {
449 wps_deinit(peer->wps);
454 if (*reply == NULL) {
455 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
456 return HTTP_INTERNAL_SERVER_ERROR;
463 static enum http_reply_code
464 web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
465 struct wpabuf **reply, const char **replyname)
468 static const char *name = "NewOutMessage";
469 enum http_reply_code ret;
470 enum wps_process_res res;
471 enum wsc_op_code op_code;
472 struct upnp_wps_device_interface *iface;
474 iface = dl_list_first(&sm->interfaces,
475 struct upnp_wps_device_interface, list);
478 * PutMessage is used by external UPnP-based Registrar to perform WPS
479 * operation with the access point itself; as compared with
480 * PutWLANResponse which is for proxying.
482 wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
483 msg = xml_get_base64_item(data, "NewInMessage", &ret);
486 res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
487 if (res == WPS_FAILURE)
490 *reply = wps_get_msg(iface->peer.wps, &op_code);
493 return HTTP_INTERNAL_SERVER_ERROR;
499 static enum http_reply_code
500 web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
501 struct wpabuf **reply, const char **replyname)
504 enum http_reply_code ret;
505 u8 macaddr[ETH_ALEN];
509 struct upnp_wps_device_interface *iface;
513 * External UPnP-based Registrar is passing us a message to be proxied
514 * over to a Wi-Fi -based client of ours.
517 wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
518 msg = xml_get_base64_item(data, "NewMessage", &ret);
520 wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
521 "from PutWLANResponse");
524 val = xml_get_first_item(data, "NewWLANEventType");
526 wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
529 return UPNP_ARG_VALUE_INVALID;
533 val = xml_get_first_item(data, "NewWLANEventMAC");
535 wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
538 return UPNP_ARG_VALUE_INVALID;
540 if (hwaddr_aton(val, macaddr)) {
541 wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
542 "PutWLANResponse: '%s'", val);
543 #ifdef CONFIG_WPS_STRICT
545 struct wps_parse_attr attr;
546 if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
549 return UPNP_ARG_VALUE_INVALID;
552 #endif /* CONFIG_WPS_STRICT */
553 if (hwaddr_aton2(val, macaddr) > 0) {
555 * At least some versions of Intel PROset seem to be
556 * using dot-deliminated MAC address format here.
558 wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
559 "incorrect MAC address format in "
560 "NewWLANEventMAC: %s -> " MACSTR,
561 val, MAC2STR(macaddr));
565 return UPNP_ARG_VALUE_INVALID;
569 if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
570 struct wps_parse_attr attr;
571 if (wps_parse_msg(msg, &attr) < 0 ||
572 attr.msg_type == NULL)
575 type = *attr.msg_type;
576 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
579 dl_list_for_each(iface, &sm->interfaces,
580 struct upnp_wps_device_interface, list) {
581 if (iface->ctx->rx_req_put_wlan_response &&
582 iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
589 wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
590 "rx_req_put_wlan_response");
592 return HTTP_INTERNAL_SERVER_ERROR;
601 static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
603 struct subscr_addr *a;
605 dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
606 if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
613 static struct subscription * find_er(struct upnp_wps_device_sm *sm,
614 struct sockaddr_in *cli)
616 struct subscription *s;
617 dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
618 if (find_er_addr(s, cli))
624 static enum http_reply_code
625 web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
626 struct sockaddr_in *cli, char *data,
627 struct wpabuf **reply,
628 const char **replyname)
631 enum http_reply_code ret;
632 struct subscription *s;
633 struct upnp_wps_device_interface *iface;
636 wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
637 s = find_er(sm, cli);
639 wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
641 return UPNP_ACTION_FAILED;
643 msg = xml_get_base64_item(data, "NewMessage", &ret);
646 dl_list_for_each(iface, &sm->interfaces,
647 struct upnp_wps_device_interface, list) {
648 if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
654 return HTTP_INTERNAL_SERVER_ERROR;
661 static const char *soap_prefix =
662 "<?xml version=\"1.0\"?>\n"
663 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
664 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
666 static const char *soap_postfix =
667 "</s:Body>\n</s:Envelope>\n";
669 static const char *soap_error_prefix =
671 "<faultcode>s:Client</faultcode>\n"
672 "<faultstring>UPnPError</faultstring>\n"
674 "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
675 static const char *soap_error_postfix =
676 "<errorDescription>Error</errorDescription>\n"
681 static void web_connection_send_reply(struct http_request *req,
682 enum http_reply_code ret,
683 const char *action, int action_len,
684 const struct wpabuf *reply,
685 const char *replyname)
689 char *put_length_here = NULL;
690 char *body_start = NULL;
694 replydata = (char *) base64_encode(wpabuf_head(reply),
695 wpabuf_len(reply), &len);
699 /* Parameters of the response:
700 * action(action_len) -- action we are responding to
701 * replyname -- a name we need for the reply
702 * replydata -- NULL or null-terminated string
704 buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
705 (action_len > 0 ? action_len * 2 : 0));
707 wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
710 http_request_deinit(req);
715 * Assuming we will be successful, put in the output header first.
716 * Note: we do not keep connections alive (and httpread does
717 * not support it)... therefore we must have Connection: close.
719 if (ret == HTTP_OK) {
721 "HTTP/1.1 200 OK\r\n"
722 "Content-Type: text/xml; "
723 "charset=\"utf-8\"\r\n");
725 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
727 wpabuf_put_str(buf, http_connection_close);
729 wpabuf_put_str(buf, "Content-Length: ");
731 * We will paste the length in later, leaving some extra whitespace.
732 * HTTP code is supposed to be tolerant of extra whitespace.
734 put_length_here = wpabuf_put(buf, 0);
735 wpabuf_put_str(buf, " \r\n");
739 /* terminating empty line */
740 wpabuf_put_str(buf, "\r\n");
742 body_start = wpabuf_put(buf, 0);
744 if (ret == HTTP_OK) {
745 wpabuf_put_str(buf, soap_prefix);
746 wpabuf_put_str(buf, "<u:");
747 wpabuf_put_data(buf, action, action_len);
748 wpabuf_put_str(buf, "Response xmlns:u=\"");
749 wpabuf_put_str(buf, urn_wfawlanconfig);
750 wpabuf_put_str(buf, "\">\n");
751 if (replydata && replyname) {
752 /* TODO: might possibly need to escape part of reply
754 * probably not, unlikely to have ampersand(&) or left
755 * angle bracket (<) in it...
757 wpabuf_printf(buf, "<%s>", replyname);
758 wpabuf_put_str(buf, replydata);
759 wpabuf_printf(buf, "</%s>\n", replyname);
761 wpabuf_put_str(buf, "</u:");
762 wpabuf_put_data(buf, action, action_len);
763 wpabuf_put_str(buf, "Response>\n");
764 wpabuf_put_str(buf, soap_postfix);
767 wpabuf_put_str(buf, soap_prefix);
768 wpabuf_put_str(buf, soap_error_prefix);
769 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
770 wpabuf_put_str(buf, soap_error_postfix);
771 wpabuf_put_str(buf, soap_postfix);
775 /* Now patch in the content length at the end */
776 if (body_start && put_length_here) {
777 int body_length = (char *) wpabuf_put(buf, 0) - body_start;
779 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
780 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
783 http_request_send_and_deinit(req, buf);
787 static const char * web_get_action(struct http_request *req,
796 /* The SOAPAction line of the header tells us what we want to do */
797 b = http_request_get_hdr_line(req, "SOAPAction:");
804 match = urn_wfawlanconfig;
805 match_len = os_strlen(urn_wfawlanconfig) - 1;
806 if (os_strncasecmp(b, match, match_len))
809 /* skip over version */
810 while (isgraph(*b) && *b != '#')
815 /* Following the sharp(#) should be the action and a double quote */
817 while (isgraph(*b) && *b != '"')
821 *action_len = b - action;
826 /* Given that we have received a header w/ POST, act upon it
828 * Format of POST (case-insensitive):
830 * First line must be:
831 * POST /<file> HTTP/1.1
832 * Since we don't do anything fancy we just ignore other lines.
834 * Our response (if no error) which includes only required lines is:
837 * Content-Type: text/xml
838 * Date: <rfc1123-date>
840 * Header lines must end with \r\n
841 * Per RFC 2616, content-length: is not required but connection:close
842 * would appear to be required (given that we will be closing it!).
844 static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
845 struct sockaddr_in *cli,
846 struct http_request *req,
847 const char *filename)
849 enum http_reply_code ret;
850 char *data = http_request_get_data(req); /* body of http msg */
851 const char *action = NULL;
852 size_t action_len = 0;
853 const char *replyname = NULL; /* argument name for the reply */
854 struct wpabuf *reply = NULL; /* data for the reply */
856 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
857 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
859 ret = HTTP_NOT_FOUND;
863 ret = UPNP_INVALID_ACTION;
864 action = web_get_action(req, &action_len);
868 if (!os_strncasecmp("GetDeviceInfo", action, action_len))
869 ret = web_process_get_device_info(sm, &reply, &replyname);
870 else if (!os_strncasecmp("PutMessage", action, action_len))
871 ret = web_process_put_message(sm, data, &reply, &replyname);
872 else if (!os_strncasecmp("PutWLANResponse", action, action_len))
873 ret = web_process_put_wlan_response(sm, data, &reply,
875 else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
876 ret = web_process_set_selected_registrar(sm, cli, data, &reply,
879 wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
883 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
884 web_connection_send_reply(req, ret, action, action_len, reply,
890 /* Given that we have received a header w/ SUBSCRIBE, act upon it
892 * Format of SUBSCRIBE (case-insensitive):
894 * First line must be:
895 * SUBSCRIBE /wps_event HTTP/1.1
897 * Our response (if no error) which includes only required lines is:
899 * Server: xx, UPnP/1.0, xx
900 * SID: uuid:xxxxxxxxx
901 * Timeout: Second-<n>
905 * Header lines must end with \r\n
906 * Per RFC 2616, content-length: is not required but connection:close
907 * would appear to be required (given that we will be closing it!).
909 static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
910 struct http_request *req,
911 const char *filename)
915 char *hdr = http_request_get_hdr(req);
924 char *callback_urls = NULL;
925 struct subscription *s = NULL;
926 enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
928 buf = wpabuf_alloc(1000);
930 http_request_deinit(req);
934 wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
935 (u8 *) hdr, os_strlen(hdr));
937 /* Parse/validate headers */
939 /* First line: SUBSCRIBE /wps_event HTTP/1.1
940 * has already been parsed.
942 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
943 ret = HTTP_PRECONDITION_FAILED;
946 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
947 end = os_strchr(h, '\n');
950 /* Option line by option line */
952 end = os_strchr(h, '\n');
954 break; /* no unterminated lines allowed */
956 /* NT assures that it is our type of subscription;
957 * not used for a renewal.
960 match_len = os_strlen(match);
961 if (os_strncasecmp(h, match, match_len) == 0) {
963 while (*h == ' ' || *h == '\t')
965 match = "upnp:event";
966 match_len = os_strlen(match);
967 if (os_strncasecmp(h, match, match_len) != 0) {
968 ret = HTTP_BAD_REQUEST;
974 /* HOST should refer to us */
977 match_len = os_strlen(match);
978 if (os_strncasecmp(h, match, match_len) == 0) {
980 while (*h == ' ' || *h == '\t')
985 /* CALLBACK gives one or more URLs for NOTIFYs
986 * to be sent as a result of the subscription.
987 * Each URL is enclosed in angle brackets.
990 match_len = os_strlen(match);
991 if (os_strncasecmp(h, match, match_len) == 0) {
993 while (*h == ' ' || *h == '\t')
996 os_free(callback_urls);
997 callback_urls = dup_binstr(h, len);
998 if (callback_urls == NULL) {
999 ret = HTTP_INTERNAL_SERVER_ERROR;
1004 /* SID is only for renewal */
1006 match_len = os_strlen(match);
1007 if (os_strncasecmp(h, match, match_len) == 0) {
1009 while (*h == ' ' || *h == '\t')
1012 match_len = os_strlen(match);
1013 if (os_strncasecmp(h, match, match_len) != 0) {
1014 ret = HTTP_BAD_REQUEST;
1018 while (*h == ' ' || *h == '\t')
1020 if (uuid_str2bin(h, uuid)) {
1021 ret = HTTP_BAD_REQUEST;
1027 /* TIMEOUT is requested timeout, but apparently we can
1034 wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
1035 if (callback_urls) {
1036 ret = HTTP_BAD_REQUEST;
1039 s = subscription_renew(sm, uuid);
1042 uuid_bin2str(uuid, str, sizeof(str));
1043 wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
1045 ret = HTTP_PRECONDITION_FAILED;
1048 } else if (callback_urls) {
1049 wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
1051 ret = HTTP_PRECONDITION_FAILED;
1054 s = subscription_start(sm, callback_urls);
1056 ret = HTTP_INTERNAL_SERVER_ERROR;
1060 ret = HTTP_PRECONDITION_FAILED;
1065 http_put_reply_code(buf, HTTP_OK);
1066 wpabuf_put_str(buf, http_server_hdr);
1067 wpabuf_put_str(buf, http_connection_close);
1068 wpabuf_put_str(buf, "Content-Length: 0\r\n");
1069 wpabuf_put_str(buf, "SID: uuid:");
1070 /* subscription id */
1071 b = wpabuf_put(buf, 0);
1072 uuid_bin2str(s->uuid, b, 80);
1073 wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
1074 wpabuf_put(buf, os_strlen(b));
1075 wpabuf_put_str(buf, "\r\n");
1076 wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
1078 /* And empty line to terminate header: */
1079 wpabuf_put_str(buf, "\r\n");
1081 os_free(callback_urls);
1082 http_request_send_and_deinit(req, buf);
1088 * Incompatible headers
1089 * 400 Bad Request. If SID header and one of NT or CALLBACK headers
1090 * are present, the publisher must respond with HTTP error
1092 * Missing or invalid CALLBACK
1093 * 412 Precondition Failed. If CALLBACK header is missing or does not
1094 * contain a valid HTTP URL, the publisher must respond with HTTP
1095 * error 412 Precondition Failed.
1097 * 412 Precondition Failed. If NT header does not equal upnp:event,
1098 * the publisher must respond with HTTP error 412 Precondition
1100 * [For resubscription, use 412 if unknown uuid].
1101 * Unable to accept subscription
1102 * 5xx. If a publisher is not able to accept a subscription (such as
1103 * due to insufficient resources), it must respond with a
1104 * HTTP 500-series error code.
1105 * 599 Too many subscriptions (not a standard HTTP error)
1107 wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
1108 http_put_empty(buf, ret);
1109 http_request_send_and_deinit(req, buf);
1110 os_free(callback_urls);
1114 /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
1116 * Format of UNSUBSCRIBE (case-insensitive):
1118 * First line must be:
1119 * UNSUBSCRIBE /wps_event HTTP/1.1
1121 * Our response (if no error) which includes only required lines is:
1125 * Header lines must end with \r\n
1126 * Per RFC 2616, content-length: is not required but connection:close
1127 * would appear to be required (given that we will be closing it!).
1129 static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
1130 struct http_request *req,
1131 const char *filename)
1134 char *hdr = http_request_get_hdr(req);
1141 struct subscription *s = NULL;
1142 enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1144 /* Parse/validate headers */
1146 /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
1147 * has already been parsed.
1149 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1150 ret = HTTP_PRECONDITION_FAILED;
1153 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
1154 end = os_strchr(h, '\n');
1157 /* Option line by option line */
1159 end = os_strchr(h, '\n');
1161 break; /* no unterminated lines allowed */
1163 /* HOST should refer to us */
1166 match_len = os_strlen(match);
1167 if (os_strncasecmp(h, match, match_len) == 0) {
1169 while (*h == ' ' || *h == '\t')
1175 match_len = os_strlen(match);
1176 if (os_strncasecmp(h, match, match_len) == 0) {
1178 while (*h == ' ' || *h == '\t')
1181 match_len = os_strlen(match);
1182 if (os_strncasecmp(h, match, match_len) != 0) {
1183 ret = HTTP_BAD_REQUEST;
1187 while (*h == ' ' || *h == '\t')
1189 if (uuid_str2bin(h, uuid)) {
1190 ret = HTTP_BAD_REQUEST;
1198 match_len = os_strlen(match);
1199 if (os_strncasecmp(h, match, match_len) == 0) {
1200 ret = HTTP_BAD_REQUEST;
1204 match = "CALLBACK:";
1205 match_len = os_strlen(match);
1206 if (os_strncasecmp(h, match, match_len) == 0) {
1207 ret = HTTP_BAD_REQUEST;
1213 s = subscription_find(sm, uuid);
1215 struct subscr_addr *sa;
1216 sa = dl_list_first(&s->addr_list, struct subscr_addr,
1218 wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
1219 s, (sa && sa->domain_and_port) ?
1220 sa->domain_and_port : "-null-");
1221 dl_list_del(&s->list);
1222 subscription_destroy(s);
1224 wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
1225 ret = HTTP_PRECONDITION_FAILED;
1229 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
1231 ret = HTTP_PRECONDITION_FAILED;
1238 buf = wpabuf_alloc(200);
1240 http_request_deinit(req);
1243 http_put_empty(buf, ret);
1244 http_request_send_and_deinit(req, buf);
1248 /* Send error in response to unknown requests */
1249 static void web_connection_unimplemented(struct http_request *req)
1252 buf = wpabuf_alloc(200);
1254 http_request_deinit(req);
1257 http_put_empty(buf, HTTP_UNIMPLEMENTED);
1258 http_request_send_and_deinit(req, buf);
1263 /* Called when we have gotten an apparently valid http request.
1265 static void web_connection_check_data(void *ctx, struct http_request *req)
1267 struct upnp_wps_device_sm *sm = ctx;
1268 enum httpread_hdr_type htype = http_request_get_type(req);
1269 char *filename = http_request_get_uri(req);
1270 struct sockaddr_in *cli = http_request_get_cli_addr(req);
1273 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
1274 http_request_deinit(req);
1277 /* Trim leading slashes from filename */
1278 while (*filename == '/')
1281 wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
1282 htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
1285 case HTTPREAD_HDR_TYPE_GET:
1286 web_connection_parse_get(sm, req, filename);
1288 case HTTPREAD_HDR_TYPE_POST:
1289 web_connection_parse_post(sm, cli, req, filename);
1291 case HTTPREAD_HDR_TYPE_SUBSCRIBE:
1292 web_connection_parse_subscribe(sm, req, filename);
1294 case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
1295 web_connection_parse_unsubscribe(sm, req, filename);
1298 /* We are not required to support M-POST; just plain
1299 * POST is supposed to work, so we only support that.
1300 * If for some reason we need to support M-POST, it is
1301 * mostly the same as POST, with small differences.
1304 /* Send 501 for anything else */
1305 web_connection_unimplemented(req);
1312 * Listening for web connections
1313 * We have a single TCP listening port, and hand off connections as we get
1317 void web_listener_stop(struct upnp_wps_device_sm *sm)
1319 http_server_deinit(sm->web_srv);
1324 int web_listener_start(struct upnp_wps_device_sm *sm)
1326 struct in_addr addr;
1327 addr.s_addr = sm->ip_addr;
1328 sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
1330 if (sm->web_srv == NULL) {
1331 web_listener_stop(sm);
1334 sm->web_port = http_server_get_port(sm->web_srv);