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.
21 #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 * Incoming web connections are recorded in this struct.
42 * A web connection is a TCP connection to us, the server;
43 * it is called a "web connection" because we use http and serve
44 * data that looks like web pages.
45 * State information is need to track the connection until we figure
46 * out what they want and what we want to do about it.
48 struct web_connection {
49 /* double linked list */
50 struct web_connection *next;
51 struct web_connection *prev;
52 struct upnp_wps_device_sm *sm; /* parent */
53 int sd; /* socket to read from */
54 struct sockaddr_in cli_addr;
55 int sd_registered; /* nonzero if we must cancel registration */
56 struct httpread *hread; /* state machine for reading socket */
57 int n_rcvd_data; /* how much data read so far */
58 int done; /* internal flag, set when we've finished */
63 * XML parsing and formatting
65 * XML is a markup language based on unicode; usually (and in our case,
66 * always!) based on utf-8. utf-8 uses a variable number of bytes per
67 * character. utf-8 has the advantage that all non-ASCII unicode characters are
68 * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
69 * characters are single ascii bytes, thus we can use typical text processing.
71 * (One other interesting thing about utf-8 is that it is possible to look at
72 * any random byte and determine if it is the first byte of a character as
73 * versus a continuation byte).
75 * The base syntax of XML uses a few ASCII punctionation characters; any
76 * characters that would appear in the payload data are rewritten using
77 * sequences, e.g., & for ampersand(&) and < for left angle bracket (<).
78 * Five such escapes total (more can be defined but that does not apply to our
79 * case). Thus we can safely parse for angle brackets etc.
81 * XML describes tree structures of tagged data, with each element beginning
82 * with an opening tag <label> and ending with a closing tag </label> with
83 * matching label. (There is also a self-closing tag <label/> which is supposed
84 * to be equivalent to <label></label>, i.e., no payload, but we are unlikely
85 * to see it for our purpose).
87 * Actually the opening tags are a little more complicated because they can
88 * contain "attributes" after the label (delimited by ascii space or tab chars)
89 * of the form attribute_label="value" or attribute_label='value'; as it turns
90 * out we do not have to read any of these attributes, just ignore them.
92 * Labels are any sequence of chars other than space, tab, right angle bracket
93 * (and ?), but may have an inner structure of <namespace><colon><plain_label>.
94 * As it turns out, we can ignore the namespaces, in fact we can ignore the
95 * entire tree hierarchy, because the plain labels we are looking for will be
96 * unique (not in general, but for this application). We do however have to be
97 * careful to skip over the namespaces.
99 * In generating XML we have to be more careful, but that is easy because
100 * everything we do is pretty canned. The only real care to take is to escape
101 * any special chars in our payload.
105 * xml_next_tag - Advance to next tag
107 * @out: OUT: start of tag just after '<'
108 * @out_tagname: OUT: start of name of tag, skipping namespace
109 * @end: OUT: one after tag
110 * Returns: 0 on success, 1 on failure
113 * <left angle bracket><...><right angle bracket>
114 * Within the angle brackets, there is an optional leading forward slash (which
115 * makes the tag an ending tag), then an optional leading label (followed by
116 * colon) and then the tag name itself.
118 * Note that angle brackets present in the original data must have been encoded
119 * as < and > so they will not trouble us.
121 static int xml_next_tag(char *in, char **out, char **out_tagname,
124 while (*in && *in != '<')
131 *out_tagname = in; /* maybe */
132 while (isalnum(*in) || *in == '-')
136 while (*in && *in != '>')
145 /* xml_data_encode -- format data for xml file, escaping special characters.
147 * Note that we assume we are using utf8 both as input and as output!
148 * In utf8, characters may be classed as follows:
149 * 0xxxxxxx(2) -- 1 byte ascii char
150 * 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
151 * 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
152 * 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
153 * 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
154 * 10xxxxxx(2) -- extension byte (6 payload bits per byte)
155 * Some values implied by the above are however illegal because they
156 * do not represent unicode chars or are not the shortest encoding.
157 * Actually, we can almost entirely ignore the above and just do
158 * text processing same as for ascii text.
160 * XML is written with arbitrary unicode characters, except that five
161 * characters have special meaning and so must be escaped where they
162 * appear in payload data... which we do here.
164 static void xml_data_encode(struct wpabuf *buf, const char *data, int len)
167 for (i = 0; i < len; i++) {
168 u8 c = ((u8 *) data)[i];
170 wpabuf_put_str(buf, "<");
174 wpabuf_put_str(buf, ">");
178 wpabuf_put_str(buf, "&");
182 wpabuf_put_str(buf, "'");
186 wpabuf_put_str(buf, """);
190 * We could try to represent control characters using the
191 * sequence: &#x; where x is replaced by a hex numeral, but not
192 * clear why we would do this.
194 wpabuf_put_u8(buf, c);
199 /* xml_add_tagged_data -- format tagged data as a new xml line.
201 * tag must not have any special chars.
202 * data may have special chars, which are escaped.
204 static void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
207 wpabuf_printf(buf, "<%s>", tag);
208 xml_data_encode(buf, data, os_strlen(data));
209 wpabuf_printf(buf, "</%s>\n", tag);
213 /* A POST body looks something like (per upnp spec):
214 * <?xml version="1.0"?>
216 * xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
217 * s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
219 * <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
220 * <argumentName>in arg value</argumentName>
221 * other in args and their values go here, if any
227 * s: might be some other namespace name followed by colon
228 * u: might be some other namespace name followed by colon
229 * actionName will be replaced according to action requested
230 * schema following actionName will be WFA scheme instead
231 * argumentName will be actual argument name
232 * (in arg value) will be actual argument value
235 upnp_get_first_document_item(char *doc, const char *item, char **value)
237 const char *match = item;
238 int match_len = os_strlen(item);
243 *value = NULL; /* default, bad */
246 * This is crude: ignore any possible tag name conflicts and go right
247 * to the first tag of this name. This should be ok for the limited
248 * domain of UPnP messages.
251 if (xml_next_tag(doc, &tag, &tagname, &end))
254 if (!os_strncasecmp(tagname, match, match_len) &&
256 (tagname[match_len] == '>' ||
257 !isgraph(tagname[match_len]))) {
262 while (*end && *end != '<')
264 *value = os_zalloc(1 + (end - doc));
267 os_memcpy(*value, doc, end - doc);
273 * "Files" that we serve via HTTP. The format of these files is given by
274 * WFA WPS specifications. Extra white space has been removed to save space.
277 static const char wps_scpd_xml[] =
278 "<?xml version=\"1.0\"?>\n"
279 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
280 "<specVersion><major>1</major><minor>0</minor></specVersion>\n"
283 "<name>GetDeviceInfo</name>\n"
286 "<name>NewDeviceInfo</name>\n"
287 "<direction>out</direction>\n"
288 "<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
293 "<name>PutMessage</name>\n"
296 "<name>NewInMessage</name>\n"
297 "<direction>in</direction>\n"
298 "<relatedStateVariable>InMessage</relatedStateVariable>\n"
301 "<name>NewOutMessage</name>\n"
302 "<direction>out</direction>\n"
303 "<relatedStateVariable>OutMessage</relatedStateVariable>\n"
308 "<name>GetAPSettings</name>\n"
311 "<name>NewMessage</name>\n"
312 "<direction>in</direction>\n"
313 "<relatedStateVariable>Message</relatedStateVariable>\n"
316 "<name>NewAPSettings</name>\n"
317 "<direction>out</direction>\n"
318 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
323 "<name>SetAPSettings</name>\n"
326 "<name>APSettings</name>\n"
327 "<direction>in</direction>\n"
328 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
333 "<name>DelAPSettings</name>\n"
336 "<name>NewAPSettings</name>\n"
337 "<direction>in</direction>\n"
338 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
343 "<name>GetSTASettings</name>\n"
346 "<name>NewMessage</name>\n"
347 "<direction>in</direction>\n"
348 "<relatedStateVariable>Message</relatedStateVariable>\n"
351 "<name>NewSTASettings</name>\n"
352 "<direction>out</direction>\n"
353 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
358 "<name>SetSTASettings</name>\n"
361 "<name>NewSTASettings</name>\n"
362 "<direction>out</direction>\n"
363 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
368 "<name>DelSTASettings</name>\n"
371 "<name>NewSTASettings</name>\n"
372 "<direction>in</direction>\n"
373 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
378 "<name>PutWLANResponse</name>\n"
381 "<name>NewMessage</name>\n"
382 "<direction>in</direction>\n"
383 "<relatedStateVariable>Message</relatedStateVariable>\n"
386 "<name>NewWLANEventType</name>\n"
387 "<direction>in</direction>\n"
388 "<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
391 "<name>NewWLANEventMAC</name>\n"
392 "<direction>in</direction>\n"
393 "<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
398 "<name>SetSelectedRegistrar</name>\n"
401 "<name>NewMessage</name>\n"
402 "<direction>in</direction>\n"
403 "<relatedStateVariable>Message</relatedStateVariable>\n"
408 "<name>RebootAP</name>\n"
411 "<name>NewAPSettings</name>\n"
412 "<direction>in</direction>\n"
413 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
418 "<name>ResetAP</name>\n"
421 "<name>NewMessage</name>\n"
422 "<direction>in</direction>\n"
423 "<relatedStateVariable>Message</relatedStateVariable>\n"
428 "<name>RebootSTA</name>\n"
431 "<name>NewSTASettings</name>\n"
432 "<direction>in</direction>\n"
433 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
438 "<name>ResetSTA</name>\n"
441 "<name>NewMessage</name>\n"
442 "<direction>in</direction>\n"
443 "<relatedStateVariable>Message</relatedStateVariable>\n"
448 "<serviceStateTable>\n"
449 "<stateVariable sendEvents=\"no\">\n"
450 "<name>Message</name>\n"
451 "<dataType>bin.base64</dataType>\n"
453 "<stateVariable sendEvents=\"no\">\n"
454 "<name>InMessage</name>\n"
455 "<dataType>bin.base64</dataType>\n"
457 "<stateVariable sendEvents=\"no\">\n"
458 "<name>OutMessage</name>\n"
459 "<dataType>bin.base64</dataType>\n"
461 "<stateVariable sendEvents=\"no\">\n"
462 "<name>DeviceInfo</name>\n"
463 "<dataType>bin.base64</dataType>\n"
465 "<stateVariable sendEvents=\"no\">\n"
466 "<name>APSettings</name>\n"
467 "<dataType>bin.base64</dataType>\n"
469 "<stateVariable sendEvents=\"yes\">\n"
470 "<name>APStatus</name>\n"
471 "<dataType>ui1</dataType>\n"
473 "<stateVariable sendEvents=\"no\">\n"
474 "<name>STASettings</name>\n"
475 "<dataType>bin.base64</dataType>\n"
477 "<stateVariable sendEvents=\"yes\">\n"
478 "<name>STAStatus</name>\n"
479 "<dataType>ui1</dataType>\n"
481 "<stateVariable sendEvents=\"yes\">\n"
482 "<name>WLANEvent</name>\n"
483 "<dataType>bin.base64</dataType>\n"
485 "<stateVariable sendEvents=\"no\">\n"
486 "<name>WLANEventType</name>\n"
487 "<dataType>ui1</dataType>\n"
489 "<stateVariable sendEvents=\"no\">\n"
490 "<name>WLANEventMAC</name>\n"
491 "<dataType>string</dataType>\n"
493 "<stateVariable sendEvents=\"no\">\n"
494 "<name>WLANResponse</name>\n"
495 "<dataType>bin.base64</dataType>\n"
497 "</serviceStateTable>\n"
502 static const char *wps_device_xml_prefix =
503 "<?xml version=\"1.0\"?>\n"
504 "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
510 "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
513 static const char *wps_device_xml_postfix =
516 "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
518 "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
520 "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
521 "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
522 "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
529 /* format_wps_device_xml -- produce content of "file" wps_device.xml
530 * (UPNP_WPS_DEVICE_XML_FILE)
532 static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
536 char uuid_string[80];
538 wpabuf_put_str(buf, wps_device_xml_prefix);
541 * Add required fields with default values if not configured. Add
542 * optional and recommended fields only if configured.
544 s = sm->wps->friendly_name;
545 s = ((s && *s) ? s : "WPS Access Point");
546 xml_add_tagged_data(buf, "friendlyName", s);
548 s = sm->wps->dev.manufacturer;
549 s = ((s && *s) ? s : "");
550 xml_add_tagged_data(buf, "manufacturer", s);
552 if (sm->wps->manufacturer_url)
553 xml_add_tagged_data(buf, "manufacturerURL",
554 sm->wps->manufacturer_url);
556 if (sm->wps->model_description)
557 xml_add_tagged_data(buf, "modelDescription",
558 sm->wps->model_description);
560 s = sm->wps->dev.model_name;
561 s = ((s && *s) ? s : "");
562 xml_add_tagged_data(buf, "modelName", s);
564 if (sm->wps->dev.model_number)
565 xml_add_tagged_data(buf, "modelNumber",
566 sm->wps->dev.model_number);
568 if (sm->wps->model_url)
569 xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
571 if (sm->wps->dev.serial_number)
572 xml_add_tagged_data(buf, "serialNumber",
573 sm->wps->dev.serial_number);
575 uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
577 /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
580 wpabuf_put_str(buf, "<UDN>uuid:");
581 xml_data_encode(buf, s, os_strlen(s));
582 wpabuf_put_str(buf, "</UDN>\n");
585 xml_add_tagged_data(buf, "UPC", sm->wps->upc);
587 wpabuf_put_str(buf, wps_device_xml_postfix);
591 void web_connection_stop(struct web_connection *c)
593 struct upnp_wps_device_sm *sm = c->sm;
595 httpread_destroy(c->hread);
600 sm->web_connections = NULL;
602 if (sm->web_connections == c)
603 sm->web_connections = c->next;
604 c->next->prev = c->prev;
605 c->prev->next = c->next;
608 sm->n_web_connections--;
612 static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
614 wpabuf_put_str(buf, "HTTP/1.1 ");
617 wpabuf_put_str(buf, "200 OK\r\n");
619 case HTTP_BAD_REQUEST:
620 wpabuf_put_str(buf, "400 Bad request\r\n");
622 case HTTP_PRECONDITION_FAILED:
623 wpabuf_put_str(buf, "412 Precondition failed\r\n");
625 case HTTP_UNIMPLEMENTED:
626 wpabuf_put_str(buf, "501 Unimplemented\r\n");
628 case HTTP_INTERNAL_SERVER_ERROR:
630 wpabuf_put_str(buf, "500 Internal server error\r\n");
636 static void http_put_date(struct wpabuf *buf)
638 wpabuf_put_str(buf, "Date: ");
640 wpabuf_put_str(buf, "\r\n");
644 static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
646 http_put_reply_code(buf, code);
647 wpabuf_put_str(buf, http_server_hdr);
648 wpabuf_put_str(buf, http_connection_close);
649 wpabuf_put_str(buf, "Content-Length: 0\r\n"
654 /* Given that we have received a header w/ GET, act upon it
656 * Format of GET (case-insensitive):
658 * First line must be:
659 * GET /<file> HTTP/1.1
660 * Since we don't do anything fancy we just ignore other lines.
662 * Our response (if no error) which includes only required lines is:
665 * Content-Type: text/xml
666 * Date: <rfc1123-date>
668 * Header lines must end with \r\n
669 * Per RFC 2616, content-length: is not required but connection:close
670 * would appear to be required (given that we will be closing it!).
672 static void web_connection_parse_get(struct web_connection *c, char *filename)
674 struct upnp_wps_device_sm *sm = c->sm;
675 struct wpabuf *buf; /* output buffer, allocated */
676 char *put_length_here;
682 size_t extra_len = 0;
687 * It is not required that filenames be case insensitive but it is
688 * allowed and cannot hurt here.
690 if (filename == NULL)
691 filename = "(null)"; /* just in case */
692 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
693 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
694 req = GET_DEVICE_XML_FILE;
696 if (sm->wps->friendly_name)
697 extra_len += os_strlen(sm->wps->friendly_name);
698 if (sm->wps->manufacturer_url)
699 extra_len += os_strlen(sm->wps->manufacturer_url);
700 if (sm->wps->model_description)
701 extra_len += os_strlen(sm->wps->model_description);
702 if (sm->wps->model_url)
703 extra_len += os_strlen(sm->wps->model_url);
705 extra_len += os_strlen(sm->wps->upc);
706 } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
707 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
708 req = GET_SCPD_XML_FILE;
709 extra_len = os_strlen(wps_scpd_xml);
712 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
714 buf = wpabuf_alloc(200);
718 "HTTP/1.1 404 Not Found\r\n"
719 "Connection: close\r\n");
723 /* terminating empty line */
724 wpabuf_put_str(buf, "\r\n");
729 buf = wpabuf_alloc(1000 + extra_len);
734 "HTTP/1.1 200 OK\r\n"
735 "Content-Type: text/xml; charset=\"utf-8\"\r\n");
736 wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
737 wpabuf_put_str(buf, "Connection: close\r\n");
738 wpabuf_put_str(buf, "Content-Length: ");
740 * We will paste the length in later, leaving some extra whitespace.
741 * HTTP code is supposed to be tolerant of extra whitespace.
743 put_length_here = wpabuf_put(buf, 0);
744 wpabuf_put_str(buf, " \r\n");
748 /* terminating empty line */
749 wpabuf_put_str(buf, "\r\n");
751 body_start = wpabuf_put(buf, 0);
754 case GET_DEVICE_XML_FILE:
755 format_wps_device_xml(sm, buf);
757 case GET_SCPD_XML_FILE:
758 wpabuf_put_str(buf, wps_scpd_xml);
762 /* Now patch in the content length at the end */
763 body_length = (char *) wpabuf_put(buf, 0) - body_start;
764 os_snprintf(len_buf, 10, "%d", body_length);
765 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
768 send_wpabuf(c->sd, buf);
773 static struct wpabuf * web_get_item(char *data, const char *name,
774 enum http_reply_code *ret)
778 unsigned char *decoded;
781 if (upnp_get_first_document_item(data, name, &msg)) {
782 *ret = UPNP_ARG_VALUE_INVALID;
786 decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
788 if (decoded == NULL) {
789 *ret = UPNP_OUT_OF_MEMORY;
793 buf = wpabuf_alloc_ext_data(decoded, len);
796 *ret = UPNP_OUT_OF_MEMORY;
803 static enum http_reply_code
804 web_process_get_device_info(struct upnp_wps_device_sm *sm,
805 struct wpabuf **reply, const char **replyname)
807 static const char *name = "NewDeviceInfo";
809 wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
810 if (sm->ctx->rx_req_get_device_info == NULL)
811 return HTTP_INTERNAL_SERVER_ERROR;
812 *reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer);
813 if (*reply == NULL) {
814 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
815 return HTTP_INTERNAL_SERVER_ERROR;
822 static enum http_reply_code
823 web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
824 struct wpabuf **reply, const char **replyname)
827 static const char *name = "NewOutMessage";
828 enum http_reply_code ret;
831 * PutMessage is used by external UPnP-based Registrar to perform WPS
832 * operation with the access point itself; as compared with
833 * PutWLANResponse which is for proxying.
835 wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
836 if (sm->ctx->rx_req_put_message == NULL)
837 return HTTP_INTERNAL_SERVER_ERROR;
838 msg = web_get_item(data, "NewInMessage", &ret);
841 *reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg);
844 return HTTP_INTERNAL_SERVER_ERROR;
850 static enum http_reply_code
851 web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data,
852 struct wpabuf **reply, const char **replyname)
855 static const char *name = "NewAPSettings";
856 enum http_reply_code ret;
858 wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings");
859 if (sm->ctx->rx_req_get_ap_settings == NULL)
860 return HTTP_INTERNAL_SERVER_ERROR;
861 msg = web_get_item(data, "NewMessage", &ret);
864 *reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg);
867 return HTTP_INTERNAL_SERVER_ERROR;
873 static enum http_reply_code
874 web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data,
875 struct wpabuf **reply, const char **replyname)
878 enum http_reply_code ret;
880 wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings");
881 msg = web_get_item(data, "NewAPSettings", &ret);
884 if (!sm->ctx->rx_req_set_ap_settings ||
885 sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) {
887 return HTTP_INTERNAL_SERVER_ERROR;
896 static enum http_reply_code
897 web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data,
898 struct wpabuf **reply, const char **replyname)
901 enum http_reply_code ret;
903 wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings");
904 msg = web_get_item(data, "NewAPSettings", &ret);
907 if (!sm->ctx->rx_req_del_ap_settings ||
908 sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) {
910 return HTTP_INTERNAL_SERVER_ERROR;
919 static enum http_reply_code
920 web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data,
921 struct wpabuf **reply, const char **replyname)
924 static const char *name = "NewSTASettings";
925 enum http_reply_code ret;
927 wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings");
928 if (sm->ctx->rx_req_get_sta_settings == NULL)
929 return HTTP_INTERNAL_SERVER_ERROR;
930 msg = web_get_item(data, "NewMessage", &ret);
933 *reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg);
936 return HTTP_INTERNAL_SERVER_ERROR;
942 static enum http_reply_code
943 web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data,
944 struct wpabuf **reply, const char **replyname)
947 enum http_reply_code ret;
949 wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings");
950 msg = web_get_item(data, "NewSTASettings", &ret);
953 if (!sm->ctx->rx_req_set_sta_settings ||
954 sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) {
956 return HTTP_INTERNAL_SERVER_ERROR;
965 static enum http_reply_code
966 web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data,
967 struct wpabuf **reply, const char **replyname)
970 enum http_reply_code ret;
972 wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings");
973 msg = web_get_item(data, "NewSTASettings", &ret);
976 if (!sm->ctx->rx_req_del_sta_settings ||
977 sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) {
979 return HTTP_INTERNAL_SERVER_ERROR;
988 static enum http_reply_code
989 web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
990 struct wpabuf **reply, const char **replyname)
993 enum http_reply_code ret;
994 u8 macaddr[ETH_ALEN];
1000 * External UPnP-based Registrar is passing us a message to be proxied
1001 * over to a Wi-Fi -based client of ours.
1004 wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
1005 msg = web_get_item(data, "NewMessage", &ret);
1008 if (upnp_get_first_document_item(data, "NewWLANEventType", &val)) {
1010 return UPNP_ARG_VALUE_INVALID;
1012 ev_type = atol(val);
1015 if (upnp_get_first_document_item(data, "NewWLANEventMAC", &val) ||
1016 hwaddr_aton(val, macaddr)) {
1019 return UPNP_ARG_VALUE_INVALID;
1022 if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
1023 struct wps_parse_attr attr;
1024 if (wps_parse_msg(msg, &attr) < 0 ||
1025 attr.msg_type == NULL)
1028 type = *attr.msg_type;
1029 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
1032 if (!sm->ctx->rx_req_put_wlan_response ||
1033 sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
1035 wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
1036 "rx_req_put_wlan_response");
1038 return HTTP_INTERNAL_SERVER_ERROR;
1047 static enum http_reply_code
1048 web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data,
1049 struct wpabuf **reply,
1050 const char **replyname)
1053 enum http_reply_code ret;
1055 wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
1056 msg = web_get_item(data, "NewMessage", &ret);
1059 if (!sm->ctx->rx_req_set_selected_registrar ||
1060 sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) {
1062 return HTTP_INTERNAL_SERVER_ERROR;
1071 static enum http_reply_code
1072 web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data,
1073 struct wpabuf **reply, const char **replyname)
1076 enum http_reply_code ret;
1078 wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP");
1079 msg = web_get_item(data, "NewAPSettings", &ret);
1082 if (!sm->ctx->rx_req_reboot_ap ||
1083 sm->ctx->rx_req_reboot_ap(sm->priv, msg)) {
1085 return HTTP_INTERNAL_SERVER_ERROR;
1094 static enum http_reply_code
1095 web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data,
1096 struct wpabuf **reply, const char **replyname)
1099 enum http_reply_code ret;
1101 wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP");
1102 msg = web_get_item(data, "NewMessage", &ret);
1105 if (!sm->ctx->rx_req_reset_ap ||
1106 sm->ctx->rx_req_reset_ap(sm->priv, msg)) {
1108 return HTTP_INTERNAL_SERVER_ERROR;
1117 static enum http_reply_code
1118 web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data,
1119 struct wpabuf **reply, const char **replyname)
1122 enum http_reply_code ret;
1124 wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA");
1125 msg = web_get_item(data, "NewSTASettings", &ret);
1128 if (!sm->ctx->rx_req_reboot_sta ||
1129 sm->ctx->rx_req_reboot_sta(sm->priv, msg)) {
1131 return HTTP_INTERNAL_SERVER_ERROR;
1140 static enum http_reply_code
1141 web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data,
1142 struct wpabuf **reply, const char **replyname)
1145 enum http_reply_code ret;
1147 wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA");
1148 msg = web_get_item(data, "NewMessage", &ret);
1151 if (!sm->ctx->rx_req_reset_sta ||
1152 sm->ctx->rx_req_reset_sta(sm->priv, msg)) {
1154 return HTTP_INTERNAL_SERVER_ERROR;
1163 static const char *soap_prefix =
1164 "<?xml version=\"1.0\"?>\n"
1165 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
1166 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
1168 static const char *soap_postfix =
1169 "</s:Body>\n</s:Envelope>\n";
1171 static const char *soap_error_prefix =
1173 "<faultcode>s:Client</faultcode>\n"
1174 "<faultstring>UPnPError</faultstring>\n"
1176 "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
1177 static const char *soap_error_postfix =
1178 "<errorDescription>Error</errorDescription>\n"
1183 static void web_connection_send_reply(struct web_connection *c,
1184 enum http_reply_code ret,
1185 const char *action, int action_len,
1186 const struct wpabuf *reply,
1187 const char *replyname)
1191 char *put_length_here = NULL;
1192 char *body_start = NULL;
1196 replydata = (char *) base64_encode(wpabuf_head(reply),
1197 wpabuf_len(reply), &len);
1201 /* Parameters of the response:
1202 * action(action_len) -- action we are responding to
1203 * replyname -- a name we need for the reply
1204 * replydata -- NULL or null-terminated string
1206 buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
1207 (action_len > 0 ? action_len * 2 : 0));
1209 wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
1217 * Assuming we will be successful, put in the output header first.
1218 * Note: we do not keep connections alive (and httpread does
1219 * not support it)... therefore we must have Connection: close.
1221 if (ret == HTTP_OK) {
1223 "HTTP/1.1 200 OK\r\n"
1224 "Content-Type: text/xml; "
1225 "charset=\"utf-8\"\r\n");
1227 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
1229 wpabuf_put_str(buf, http_connection_close);
1231 wpabuf_put_str(buf, "Content-Length: ");
1233 * We will paste the length in later, leaving some extra whitespace.
1234 * HTTP code is supposed to be tolerant of extra whitespace.
1236 put_length_here = wpabuf_put(buf, 0);
1237 wpabuf_put_str(buf, " \r\n");
1241 /* terminating empty line */
1242 wpabuf_put_str(buf, "\r\n");
1244 body_start = wpabuf_put(buf, 0);
1246 if (ret == HTTP_OK) {
1247 wpabuf_put_str(buf, soap_prefix);
1248 wpabuf_put_str(buf, "<u:");
1249 wpabuf_put_data(buf, action, action_len);
1250 wpabuf_put_str(buf, "Response xmlns:u=\"");
1251 wpabuf_put_str(buf, urn_wfawlanconfig);
1252 wpabuf_put_str(buf, "\">\n");
1253 if (replydata && replyname) {
1254 /* TODO: might possibly need to escape part of reply
1256 * probably not, unlikely to have ampersand(&) or left
1257 * angle bracket (<) in it...
1259 wpabuf_printf(buf, "<%s>", replyname);
1260 wpabuf_put_str(buf, replydata);
1261 wpabuf_printf(buf, "</%s>\n", replyname);
1263 wpabuf_put_str(buf, "</u:");
1264 wpabuf_put_data(buf, action, action_len);
1265 wpabuf_put_str(buf, "Response>\n");
1266 wpabuf_put_str(buf, soap_postfix);
1269 wpabuf_put_str(buf, soap_prefix);
1270 wpabuf_put_str(buf, soap_error_prefix);
1271 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
1272 wpabuf_put_str(buf, soap_error_postfix);
1273 wpabuf_put_str(buf, soap_postfix);
1277 /* Now patch in the content length at the end */
1278 if (body_start && put_length_here) {
1279 int body_length = (char *) wpabuf_put(buf, 0) - body_start;
1281 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
1282 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
1285 send_wpabuf(c->sd, buf);
1290 static const char * web_get_action(struct web_connection *c,
1291 const char *filename, size_t *action_len)
1299 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
1300 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
1304 /* The SOAPAction line of the header tells us what we want to do */
1305 b = httpread_hdr_line_get(c->hread, "SOAPAction:");
1312 match = urn_wfawlanconfig;
1313 match_len = os_strlen(urn_wfawlanconfig) - 1;
1314 if (os_strncasecmp(b, match, match_len))
1317 /* skip over version */
1318 while (isgraph(*b) && *b != '#')
1323 /* Following the sharp(#) should be the action and a double quote */
1325 while (isgraph(*b) && *b != '"')
1329 *action_len = b - action;
1334 /* Given that we have received a header w/ POST, act upon it
1336 * Format of POST (case-insensitive):
1338 * First line must be:
1339 * POST /<file> HTTP/1.1
1340 * Since we don't do anything fancy we just ignore other lines.
1342 * Our response (if no error) which includes only required lines is:
1345 * Content-Type: text/xml
1346 * Date: <rfc1123-date>
1348 * Header lines must end with \r\n
1349 * Per RFC 2616, content-length: is not required but connection:close
1350 * would appear to be required (given that we will be closing it!).
1352 static void web_connection_parse_post(struct web_connection *c,
1353 const char *filename)
1355 enum http_reply_code ret;
1356 struct upnp_wps_device_sm *sm = c->sm;
1357 char *data = httpread_data_get(c->hread); /* body of http msg */
1360 const char *replyname = NULL; /* argument name for the reply */
1361 struct wpabuf *reply = NULL; /* data for the reply */
1363 ret = UPNP_INVALID_ACTION;
1364 action = web_get_action(c, filename, &action_len);
1369 * There are quite a few possible actions. Although we appear to
1370 * support them all here, not all of them are necessarily supported by
1371 * callbacks at higher levels.
1373 if (!os_strncasecmp("GetDeviceInfo", action, action_len))
1374 ret = web_process_get_device_info(sm, &reply, &replyname);
1375 else if (!os_strncasecmp("PutMessage", action, action_len))
1376 ret = web_process_put_message(sm, data, &reply, &replyname);
1377 else if (!os_strncasecmp("GetAPSettings", action, action_len))
1378 ret = web_process_get_ap_settings(sm, data, &reply,
1380 else if (!os_strncasecmp("SetAPSettings", action, action_len))
1381 ret = web_process_set_ap_settings(sm, data, &reply,
1383 else if (!os_strncasecmp("DelAPSettings", action, action_len))
1384 ret = web_process_del_ap_settings(sm, data, &reply,
1386 else if (!os_strncasecmp("GetSTASettings", action, action_len))
1387 ret = web_process_get_sta_settings(sm, data, &reply,
1389 else if (!os_strncasecmp("SetSTASettings", action, action_len))
1390 ret = web_process_set_sta_settings(sm, data, &reply,
1392 else if (!os_strncasecmp("DelSTASettings", action, action_len))
1393 ret = web_process_del_sta_settings(sm, data, &reply,
1395 else if (!os_strncasecmp("PutWLANResponse", action, action_len))
1396 ret = web_process_put_wlan_response(sm, data, &reply,
1398 else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
1399 ret = web_process_set_selected_registrar(sm, data, &reply,
1401 else if (!os_strncasecmp("RebootAP", action, action_len))
1402 ret = web_process_reboot_ap(sm, data, &reply, &replyname);
1403 else if (!os_strncasecmp("ResetAP", action, action_len))
1404 ret = web_process_reset_ap(sm, data, &reply, &replyname);
1405 else if (!os_strncasecmp("RebootSTA", action, action_len))
1406 ret = web_process_reboot_sta(sm, data, &reply, &replyname);
1407 else if (!os_strncasecmp("ResetSTA", action, action_len))
1408 ret = web_process_reset_sta(sm, data, &reply, &replyname);
1410 wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
1414 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
1415 web_connection_send_reply(c, ret, action, action_len, reply,
1421 /* Given that we have received a header w/ SUBSCRIBE, act upon it
1423 * Format of SUBSCRIBE (case-insensitive):
1425 * First line must be:
1426 * SUBSCRIBE /wps_event HTTP/1.1
1428 * Our response (if no error) which includes only required lines is:
1430 * Server: xx, UPnP/1.0, xx
1431 * SID: uuid:xxxxxxxxx
1432 * Timeout: Second-<n>
1436 * Header lines must end with \r\n
1437 * Per RFC 2616, content-length: is not required but connection:close
1438 * would appear to be required (given that we will be closing it!).
1440 static void web_connection_parse_subscribe(struct web_connection *c,
1441 const char *filename)
1443 struct upnp_wps_device_sm *sm = c->sm;
1446 char *hdr = httpread_hdr_get(c->hread);
1455 char *callback_urls = NULL;
1456 struct subscription *s = NULL;
1457 enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1459 buf = wpabuf_alloc(1000);
1463 /* Parse/validate headers */
1465 /* First line: SUBSCRIBE /wps_event HTTP/1.1
1466 * has already been parsed.
1468 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1469 ret = HTTP_PRECONDITION_FAILED;
1472 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
1473 end = os_strchr(h, '\n');
1475 for (; end != NULL; h = end + 1) {
1476 /* Option line by option line */
1478 end = os_strchr(h, '\n');
1480 break; /* no unterminated lines allowed */
1482 /* NT assures that it is our type of subscription;
1483 * not used for a renewl.
1486 match_len = os_strlen(match);
1487 if (os_strncasecmp(h, match, match_len) == 0) {
1489 while (*h == ' ' || *h == '\t')
1491 match = "upnp:event";
1492 match_len = os_strlen(match);
1493 if (os_strncasecmp(h, match, match_len) != 0) {
1494 ret = HTTP_BAD_REQUEST;
1500 /* HOST should refer to us */
1503 match_len = os_strlen(match);
1504 if (os_strncasecmp(h, match, match_len) == 0) {
1506 while (*h == ' ' || *h == '\t')
1511 /* CALLBACK gives one or more URLs for NOTIFYs
1512 * to be sent as a result of the subscription.
1513 * Each URL is enclosed in angle brackets.
1515 match = "CALLBACK:";
1516 match_len = os_strlen(match);
1517 if (os_strncasecmp(h, match, match_len) == 0) {
1519 while (*h == ' ' || *h == '\t')
1522 os_free(callback_urls);
1523 callback_urls = os_malloc(len + 1);
1524 if (callback_urls == NULL) {
1525 ret = HTTP_INTERNAL_SERVER_ERROR;
1528 os_memcpy(callback_urls, h, len);
1529 callback_urls[len] = 0;
1532 /* SID is only for renewal */
1534 match_len = os_strlen(match);
1535 if (os_strncasecmp(h, match, match_len) == 0) {
1537 while (*h == ' ' || *h == '\t')
1540 match_len = os_strlen(match);
1541 if (os_strncasecmp(h, match, match_len) != 0) {
1542 ret = HTTP_BAD_REQUEST;
1546 while (*h == ' ' || *h == '\t')
1548 if (uuid_str2bin(h, uuid)) {
1549 ret = HTTP_BAD_REQUEST;
1555 /* TIMEOUT is requested timeout, but apparently we can
1562 if (callback_urls) {
1563 ret = HTTP_BAD_REQUEST;
1566 s = subscription_renew(sm, uuid);
1568 ret = HTTP_PRECONDITION_FAILED;
1571 } else if (callback_urls) {
1573 ret = HTTP_PRECONDITION_FAILED;
1576 s = subscription_start(sm, callback_urls);
1578 ret = HTTP_INTERNAL_SERVER_ERROR;
1582 ret = HTTP_PRECONDITION_FAILED;
1587 http_put_reply_code(buf, HTTP_OK);
1588 wpabuf_put_str(buf, http_server_hdr);
1589 wpabuf_put_str(buf, http_connection_close);
1590 wpabuf_put_str(buf, "Content-Length: 0\r\n");
1591 wpabuf_put_str(buf, "SID: uuid:");
1592 /* subscription id */
1593 b = wpabuf_put(buf, 0);
1594 uuid_bin2str(s->uuid, b, 80);
1595 wpabuf_put(buf, os_strlen(b));
1596 wpabuf_put_str(buf, "\r\n");
1597 wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
1599 /* And empty line to terminate header: */
1600 wpabuf_put_str(buf, "\r\n");
1602 send_wpabuf(c->sd, buf);
1604 os_free(callback_urls);
1610 * Incompatible headers
1611 * 400 Bad Request. If SID header and one of NT or CALLBACK headers
1612 * are present, the publisher must respond with HTTP error
1614 * Missing or invalid CALLBACK
1615 * 412 Precondition Failed. If CALLBACK header is missing or does not
1616 * contain a valid HTTP URL, the publisher must respond with HTTP
1617 * error 412 Precondition Failed.
1619 * 412 Precondition Failed. If NT header does not equal upnp:event,
1620 * the publisher must respond with HTTP error 412 Precondition
1622 * [For resubscription, use 412 if unknown uuid].
1623 * Unable to accept subscription
1624 * 5xx. If a publisher is not able to accept a subscription (such as
1625 * due to insufficient resources), it must respond with a
1626 * HTTP 500-series error code.
1627 * 599 Too many subscriptions (not a standard HTTP error)
1629 http_put_empty(buf, ret);
1630 send_wpabuf(c->sd, buf);
1632 os_free(callback_urls);
1636 /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
1638 * Format of UNSUBSCRIBE (case-insensitive):
1640 * First line must be:
1641 * UNSUBSCRIBE /wps_event HTTP/1.1
1643 * Our response (if no error) which includes only required lines is:
1647 * Header lines must end with \r\n
1648 * Per RFC 2616, content-length: is not required but connection:close
1649 * would appear to be required (given that we will be closing it!).
1651 static void web_connection_parse_unsubscribe(struct web_connection *c,
1652 const char *filename)
1654 struct upnp_wps_device_sm *sm = c->sm;
1656 char *hdr = httpread_hdr_get(c->hread);
1663 struct subscription *s = NULL;
1664 enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1666 /* Parse/validate headers */
1668 /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
1669 * has already been parsed.
1671 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1672 ret = HTTP_PRECONDITION_FAILED;
1675 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
1676 end = os_strchr(h, '\n');
1678 for (; end != NULL; h = end + 1) {
1679 /* Option line by option line */
1681 end = os_strchr(h, '\n');
1683 break; /* no unterminated lines allowed */
1685 /* HOST should refer to us */
1688 match_len = os_strlen(match);
1689 if (os_strncasecmp(h, match, match_len) == 0) {
1691 while (*h == ' ' || *h == '\t')
1696 /* SID is only for renewal */
1698 match_len = os_strlen(match);
1699 if (os_strncasecmp(h, match, match_len) == 0) {
1701 while (*h == ' ' || *h == '\t')
1704 match_len = os_strlen(match);
1705 if (os_strncasecmp(h, match, match_len) != 0) {
1706 ret = HTTP_BAD_REQUEST;
1710 while (*h == ' ' || *h == '\t')
1712 if (uuid_str2bin(h, uuid)) {
1713 ret = HTTP_BAD_REQUEST;
1722 s = subscription_find(sm, uuid);
1724 wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
1726 (s && s->addr_list &&
1727 s->addr_list->domain_and_port) ?
1728 s->addr_list->domain_and_port : "-null-");
1729 subscription_unlink(s);
1730 subscription_destroy(s);
1733 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
1735 ret = HTTP_PRECONDITION_FAILED;
1742 buf = wpabuf_alloc(200);
1745 http_put_empty(buf, ret);
1746 send_wpabuf(c->sd, buf);
1751 /* Send error in response to unknown requests */
1752 static void web_connection_unimplemented(struct web_connection *c)
1755 buf = wpabuf_alloc(200);
1758 http_put_empty(buf, HTTP_UNIMPLEMENTED);
1759 send_wpabuf(c->sd, buf);
1765 /* Called when we have gotten an apparently valid http request.
1767 static void web_connection_check_data(struct web_connection *c)
1769 struct httpread *hread = c->hread;
1770 enum httpread_hdr_type htype = httpread_hdr_type_get(hread);
1771 /* char *data = httpread_data_get(hread); */
1772 char *filename = httpread_uri_get(hread);
1776 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
1779 /* Trim leading slashes from filename */
1780 while (*filename == '/')
1783 wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
1784 htype, inet_ntoa(c->cli_addr.sin_addr),
1785 htons(c->cli_addr.sin_port));
1788 case HTTPREAD_HDR_TYPE_GET:
1789 web_connection_parse_get(c, filename);
1791 case HTTPREAD_HDR_TYPE_POST:
1792 web_connection_parse_post(c, filename);
1794 case HTTPREAD_HDR_TYPE_SUBSCRIBE:
1795 web_connection_parse_subscribe(c, filename);
1797 case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
1798 web_connection_parse_unsubscribe(c, filename);
1800 /* We are not required to support M-POST; just plain
1801 * POST is supposed to work, so we only support that.
1802 * If for some reason we need to support M-POST, it is
1803 * mostly the same as POST, with small differences.
1806 /* Send 501 for anything else */
1807 web_connection_unimplemented(c);
1814 /* called back when we have gotten request */
1815 static void web_connection_got_file_handler(struct httpread *handle,
1817 enum httpread_event en)
1819 struct web_connection *c = cookie;
1821 if (en == HTTPREAD_EVENT_FILE_READY)
1822 web_connection_check_data(c);
1823 web_connection_stop(c);
1827 /* web_connection_start - Start web connection
1828 * @sm: WPS UPnP state machine from upnp_wps_device_init()
1829 * @sd: Socket descriptor
1830 * @addr: Client address
1832 * The socket descriptor sd is handed over for ownership by the WPS UPnP
1835 static void web_connection_start(struct upnp_wps_device_sm *sm,
1836 int sd, struct sockaddr_in *addr)
1838 struct web_connection *c = NULL;
1840 /* if too many connections, bail */
1841 if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) {
1846 c = os_zalloc(sizeof(*c));
1849 os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr));
1854 * Setting non-blocking should not be necessary for read, and can mess
1855 * up sending where blocking might be better.
1857 if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
1860 c->hread = httpread_create(c->sd, web_connection_got_file_handler,
1862 WEB_CONNECTION_MAX_READ,
1863 WEB_CONNECTION_TIMEOUT_SEC);
1864 if (c->hread == NULL)
1866 if (sm->web_connections) {
1867 c->next = sm->web_connections;
1868 c->prev = c->next->prev;
1872 sm->web_connections = c->next = c->prev = c;
1874 sm->n_web_connections++;
1879 web_connection_stop(c);
1884 * Listening for web connections
1885 * We have a single TCP listening port, and hand off connections as we get
1889 void web_listener_stop(struct upnp_wps_device_sm *sm)
1891 if (sm->web_sd_registered) {
1892 sm->web_sd_registered = 0;
1893 eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ);
1895 if (sm->web_sd >= 0)
1901 static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
1903 struct sockaddr_in addr;
1904 socklen_t addr_len = sizeof(addr);
1905 struct upnp_wps_device_sm *sm = sock_ctx;
1908 /* Create state for new connection */
1909 /* Remember so we can cancel if need be */
1910 new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len);
1912 wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept "
1913 "errno=%d (%s) web_sd=%d",
1914 errno, strerror(errno), sm->web_sd);
1917 web_connection_start(sm, new_sd, &addr);
1921 int web_listener_start(struct upnp_wps_device_sm *sm)
1923 struct sockaddr_in addr;
1926 sm->web_sd = socket(AF_INET, SOCK_STREAM, 0);
1929 if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0)
1931 port = 49152; /* first non-reserved port */
1933 os_memset(&addr, 0, sizeof(addr));
1934 addr.sin_family = AF_INET;
1935 addr.sin_addr.s_addr = sm->ip_addr;
1936 addr.sin_port = htons(port);
1937 if (bind(sm->web_sd, (struct sockaddr *) &addr,
1940 if (errno == EADDRINUSE) {
1941 /* search for unused port */
1942 if (++port == 65535)
1948 if (listen(sm->web_sd, 10 /* max backlog */) != 0)
1950 if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0)
1952 if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ,
1953 web_listener_handler, NULL, sm))
1955 sm->web_sd_registered = 1;
1956 sm->web_port = port;
1962 web_listener_stop(sm);