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
);