diff options
Diffstat (limited to 'src/wps/wps_upnp_web.c')
-rw-r--r-- | src/wps/wps_upnp_web.c | 1046 |
1 files changed, 176 insertions, 870 deletions
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index b637454..9a6b36e 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -9,16 +9,16 @@ */ #include "includes.h" -#include <fcntl.h> #include "common.h" #include "base64.h" -#include "eloop.h" #include "uuid.h" #include "httpread.h" +#include "http_server.h" #include "wps_i.h" #include "wps_upnp.h" #include "wps_upnp_i.h" +#include "upnp_xml.h" /*************************************************************************** * Web connections (we serve pages of info about ourselves, handle @@ -38,238 +38,6 @@ static const char *http_connection_close = "Connection: close\r\n"; /* - * Incoming web connections are recorded in this struct. - * A web connection is a TCP connection to us, the server; - * it is called a "web connection" because we use http and serve - * data that looks like web pages. - * State information is need to track the connection until we figure - * out what they want and what we want to do about it. - */ -struct web_connection { - /* double linked list */ - struct web_connection *next; - struct web_connection *prev; - struct upnp_wps_device_sm *sm; /* parent */ - int sd; /* socket to read from */ - struct sockaddr_in cli_addr; - int sd_registered; /* nonzero if we must cancel registration */ - struct httpread *hread; /* state machine for reading socket */ - int n_rcvd_data; /* how much data read so far */ - int done; /* internal flag, set when we've finished */ -}; - - -/* - * XML parsing and formatting - * - * XML is a markup language based on unicode; usually (and in our case, - * always!) based on utf-8. utf-8 uses a variable number of bytes per - * character. utf-8 has the advantage that all non-ASCII unicode characters are - * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII - * characters are single ascii bytes, thus we can use typical text processing. - * - * (One other interesting thing about utf-8 is that it is possible to look at - * any random byte and determine if it is the first byte of a character as - * versus a continuation byte). - * - * The base syntax of XML uses a few ASCII punctionation characters; any - * characters that would appear in the payload data are rewritten using - * sequences, e.g., & for ampersand(&) and < for left angle bracket (<). - * Five such escapes total (more can be defined but that does not apply to our - * case). Thus we can safely parse for angle brackets etc. - * - * XML describes tree structures of tagged data, with each element beginning - * with an opening tag <label> and ending with a closing tag </label> with - * matching label. (There is also a self-closing tag <label/> which is supposed - * to be equivalent to <label></label>, i.e., no payload, but we are unlikely - * to see it for our purpose). - * - * Actually the opening tags are a little more complicated because they can - * contain "attributes" after the label (delimited by ascii space or tab chars) - * of the form attribute_label="value" or attribute_label='value'; as it turns - * out we do not have to read any of these attributes, just ignore them. - * - * Labels are any sequence of chars other than space, tab, right angle bracket - * (and ?), but may have an inner structure of <namespace><colon><plain_label>. - * As it turns out, we can ignore the namespaces, in fact we can ignore the - * entire tree hierarchy, because the plain labels we are looking for will be - * unique (not in general, but for this application). We do however have to be - * careful to skip over the namespaces. - * - * In generating XML we have to be more careful, but that is easy because - * everything we do is pretty canned. The only real care to take is to escape - * any special chars in our payload. - */ - -/** - * xml_next_tag - Advance to next tag - * @in: Input - * @out: OUT: start of tag just after '<' - * @out_tagname: OUT: start of name of tag, skipping namespace - * @end: OUT: one after tag - * Returns: 0 on success, 1 on failure - * - * A tag has form: - * <left angle bracket><...><right angle bracket> - * Within the angle brackets, there is an optional leading forward slash (which - * makes the tag an ending tag), then an optional leading label (followed by - * colon) and then the tag name itself. - * - * Note that angle brackets present in the original data must have been encoded - * as < and > so they will not trouble us. - */ -static int xml_next_tag(char *in, char **out, char **out_tagname, - char **end) -{ - while (*in && *in != '<') - in++; - if (*in != '<') - return 1; - *out = ++in; - if (*in == '/') - in++; - *out_tagname = in; /* maybe */ - while (isalnum(*in) || *in == '-') - in++; - if (*in == ':') - *out_tagname = ++in; - while (*in && *in != '>') - in++; - if (*in != '>') - return 1; - *end = ++in; - return 0; -} - - -/* xml_data_encode -- format data for xml file, escaping special characters. - * - * Note that we assume we are using utf8 both as input and as output! - * In utf8, characters may be classed as follows: - * 0xxxxxxx(2) -- 1 byte ascii char - * 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80 - * 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here) - * 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here) - * 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here) - * 10xxxxxx(2) -- extension byte (6 payload bits per byte) - * Some values implied by the above are however illegal because they - * do not represent unicode chars or are not the shortest encoding. - * Actually, we can almost entirely ignore the above and just do - * text processing same as for ascii text. - * - * XML is written with arbitrary unicode characters, except that five - * characters have special meaning and so must be escaped where they - * appear in payload data... which we do here. - */ -static void xml_data_encode(struct wpabuf *buf, const char *data, int len) -{ - int i; - for (i = 0; i < len; i++) { - u8 c = ((u8 *) data)[i]; - if (c == '<') { - wpabuf_put_str(buf, "<"); - continue; - } - if (c == '>') { - wpabuf_put_str(buf, ">"); - continue; - } - if (c == '&') { - wpabuf_put_str(buf, "&"); - continue; - } - if (c == '\'') { - wpabuf_put_str(buf, "'"); - continue; - } - if (c == '"') { - wpabuf_put_str(buf, """); - continue; - } - /* - * We could try to represent control characters using the - * sequence: &#x; where x is replaced by a hex numeral, but not - * clear why we would do this. - */ - wpabuf_put_u8(buf, c); - } -} - - -/* xml_add_tagged_data -- format tagged data as a new xml line. - * - * tag must not have any special chars. - * data may have special chars, which are escaped. - */ -static void xml_add_tagged_data(struct wpabuf *buf, const char *tag, - const char *data) -{ - wpabuf_printf(buf, "<%s>", tag); - xml_data_encode(buf, data, os_strlen(data)); - wpabuf_printf(buf, "</%s>\n", tag); -} - - -/* A POST body looks something like (per upnp spec): - * <?xml version="1.0"?> - * <s:Envelope - * xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" - * s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> - * <s:Body> - * <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v"> - * <argumentName>in arg value</argumentName> - * other in args and their values go here, if any - * </u:actionName> - * </s:Body> - * </s:Envelope> - * - * where : - * s: might be some other namespace name followed by colon - * u: might be some other namespace name followed by colon - * actionName will be replaced according to action requested - * schema following actionName will be WFA scheme instead - * argumentName will be actual argument name - * (in arg value) will be actual argument value - */ -static int -upnp_get_first_document_item(char *doc, const char *item, char **value) -{ - const char *match = item; - int match_len = os_strlen(item); - char *tag; - char *tagname; - char *end; - - *value = NULL; /* default, bad */ - - /* - * This is crude: ignore any possible tag name conflicts and go right - * to the first tag of this name. This should be ok for the limited - * domain of UPnP messages. - */ - for (;;) { - if (xml_next_tag(doc, &tag, &tagname, &end)) - return 1; - doc = end; - if (!os_strncasecmp(tagname, match, match_len) && - *tag != '/' && - (tagname[match_len] == '>' || - !isgraph(tagname[match_len]))) { - break; - } - } - end = doc; - while (*end && *end != '<') - end++; - *value = os_zalloc(1 + (end - doc)); - if (*value == NULL) - return 1; - os_memcpy(*value, doc, end - doc); - return 0; -} - - -/* * "Files" that we serve via HTTP. The format of these files is given by * WFA WPS specifications. Extra white space has been removed to save space. */ @@ -305,76 +73,6 @@ static const char wps_scpd_xml[] = "</argumentList>\n" "</action>\n" "<action>\n" -"<name>GetAPSettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewMessage</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>Message</relatedStateVariable>\n" -"</argument>\n" -"<argument>\n" -"<name>NewAPSettings</name>\n" -"<direction>out</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>SetAPSettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>APSettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>DelAPSettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewAPSettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>GetSTASettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewMessage</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>Message</relatedStateVariable>\n" -"</argument>\n" -"<argument>\n" -"<name>NewSTASettings</name>\n" -"<direction>out</direction>\n" -"<relatedStateVariable>STASettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>SetSTASettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewSTASettings</name>\n" -"<direction>out</direction>\n" -"<relatedStateVariable>STASettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>DelSTASettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewSTASettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>STASettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" "<name>PutWLANResponse</name>\n" "<argumentList>\n" "<argument>\n" @@ -404,46 +102,6 @@ static const char wps_scpd_xml[] = "</argument>\n" "</argumentList>\n" "</action>\n" -"<action>\n" -"<name>RebootAP</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewAPSettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>ResetAP</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewMessage</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>Message</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>RebootSTA</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewSTASettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>ResetSTA</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewMessage</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>Message</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" "</actionList>\n" "<serviceStateTable>\n" "<stateVariable sendEvents=\"no\">\n" @@ -462,18 +120,10 @@ static const char wps_scpd_xml[] = "<name>DeviceInfo</name>\n" "<dataType>bin.base64</dataType>\n" "</stateVariable>\n" -"<stateVariable sendEvents=\"no\">\n" -"<name>APSettings</name>\n" -"<dataType>bin.base64</dataType>\n" -"</stateVariable>\n" "<stateVariable sendEvents=\"yes\">\n" "<name>APStatus</name>\n" "<dataType>ui1</dataType>\n" "</stateVariable>\n" -"<stateVariable sendEvents=\"no\">\n" -"<name>STASettings</name>\n" -"<dataType>bin.base64</dataType>\n" -"</stateVariable>\n" "<stateVariable sendEvents=\"yes\">\n" "<name>STAStatus</name>\n" "<dataType>ui1</dataType>\n" @@ -588,27 +238,6 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm, } -void web_connection_stop(struct web_connection *c) -{ - struct upnp_wps_device_sm *sm = c->sm; - - httpread_destroy(c->hread); - c->hread = NULL; - close(c->sd); - c->sd = -1; - if (c->next == c) { - sm->web_connections = NULL; - } else { - if (sm->web_connections == c) - sm->web_connections = c->next; - c->next->prev = c->prev; - c->prev->next = c->next; - } - os_free(c); - sm->n_web_connections--; -} - - static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code) { wpabuf_put_str(buf, "HTTP/1.1 "); @@ -669,9 +298,9 @@ static void http_put_empty(struct wpabuf *buf, enum http_reply_code code) * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */ -static void web_connection_parse_get(struct web_connection *c, char *filename) +static void web_connection_parse_get(struct upnp_wps_device_sm *sm, + struct http_request *hreq, char *filename) { - struct upnp_wps_device_sm *sm = c->sm; struct wpabuf *buf; /* output buffer, allocated */ char *put_length_here; char *body_start; @@ -712,8 +341,10 @@ static void web_connection_parse_get(struct web_connection *c, char *filename) wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s", filename); buf = wpabuf_alloc(200); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(hreq); return; + } wpabuf_put_str(buf, "HTTP/1.1 404 Not Found\r\n" "Connection: close\r\n"); @@ -727,8 +358,10 @@ static void web_connection_parse_get(struct web_connection *c, char *filename) } buf = wpabuf_alloc(1000 + extra_len); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(hreq); return; + } wpabuf_put_str(buf, "HTTP/1.1 200 OK\r\n" @@ -765,38 +398,7 @@ static void web_connection_parse_get(struct web_connection *c, char *filename) os_memcpy(put_length_here, len_buf, os_strlen(len_buf)); send_buf: - send_wpabuf(c->sd, buf); - wpabuf_free(buf); -} - - -static struct wpabuf * web_get_item(char *data, const char *name, - enum http_reply_code *ret) -{ - char *msg; - struct wpabuf *buf; - unsigned char *decoded; - size_t len; - - if (upnp_get_first_document_item(data, name, &msg)) { - *ret = UPNP_ARG_VALUE_INVALID; - return NULL; - } - - decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len); - os_free(msg); - if (decoded == NULL) { - *ret = UPNP_OUT_OF_MEMORY; - return NULL; - } - - buf = wpabuf_alloc_ext_data(decoded, len); - if (buf == NULL) { - os_free(decoded); - *ret = UPNP_OUT_OF_MEMORY; - return NULL; - } - return buf; + http_request_send_and_deinit(hreq, buf); } @@ -805,11 +407,39 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, struct wpabuf **reply, const char **replyname) { static const char *name = "NewDeviceInfo"; + struct wps_config cfg; + struct upnp_wps_peer *peer = &sm->peer; wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); - if (sm->ctx->rx_req_get_device_info == NULL) + + if (sm->ctx->ap_pin == NULL) return HTTP_INTERNAL_SERVER_ERROR; - *reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer); + + /* + * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS + * registration over UPnP with the AP acting as an Enrollee. It should + * be noted that this is frequently used just to get the device data, + * i.e., there may not be any intent to actually complete the + * registration. + */ + + if (peer->wps) + wps_deinit(peer->wps); + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = sm->wps; + cfg.pin = (u8 *) sm->ctx->ap_pin; + cfg.pin_len = os_strlen(sm->ctx->ap_pin); + peer->wps = wps_init(&cfg); + if (peer->wps) { + enum wsc_op_code op_code; + *reply = wps_get_msg(peer->wps, &op_code); + if (*reply == NULL) { + wps_deinit(peer->wps); + peer->wps = NULL; + } + } else + *reply = NULL; if (*reply == NULL) { wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo"); return HTTP_INTERNAL_SERVER_ERROR; @@ -826,6 +456,8 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, struct wpabuf *msg; static const char *name = "NewOutMessage"; enum http_reply_code ret; + enum wps_process_res res; + enum wsc_op_code op_code; /* * PutMessage is used by external UPnP-based Registrar to perform WPS @@ -833,104 +465,14 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, * PutWLANResponse which is for proxying. */ wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage"); - if (sm->ctx->rx_req_put_message == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - msg = web_get_item(data, "NewInMessage", &ret); - if (msg == NULL) - return ret; - *reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg); - wpabuf_free(msg); - if (*reply == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - *replyname = name; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - static const char *name = "NewAPSettings"; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings"); - if (sm->ctx->rx_req_get_ap_settings == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - msg = web_get_item(data, "NewMessage", &ret); - if (msg == NULL) - return ret; - *reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg); - wpabuf_free(msg); - if (*reply == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - *replyname = name; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings"); - msg = web_get_item(data, "NewAPSettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_set_ap_settings || - sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings"); - msg = web_get_item(data, "NewAPSettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_del_ap_settings || - sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - static const char *name = "NewSTASettings"; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings"); - if (sm->ctx->rx_req_get_sta_settings == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - msg = web_get_item(data, "NewMessage", &ret); + msg = xml_get_base64_item(data, "NewInMessage", &ret); if (msg == NULL) return ret; - *reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg); + res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg); + if (res == WPS_FAILURE) + *reply = NULL; + else + *reply = wps_get_msg(sm->peer.wps, &op_code); wpabuf_free(msg); if (*reply == NULL) return HTTP_INTERNAL_SERVER_ERROR; @@ -940,52 +482,6 @@ web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data, static enum http_reply_code -web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings"); - msg = web_get_item(data, "NewSTASettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_set_sta_settings || - sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings"); - msg = web_get_item(data, "NewSTASettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_del_sta_settings || - sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, struct wpabuf **reply, const char **replyname) { @@ -1002,22 +498,45 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, */ wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse"); - msg = web_get_item(data, "NewMessage", &ret); - if (msg == NULL) + msg = xml_get_base64_item(data, "NewMessage", &ret); + if (msg == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage " + "from PutWLANResponse"); return ret; - if (upnp_get_first_document_item(data, "NewWLANEventType", &val)) { + } + val = xml_get_first_item(data, "NewWLANEventType"); + if (val == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in " + "PutWLANResponse"); wpabuf_free(msg); return UPNP_ARG_VALUE_INVALID; } ev_type = atol(val); os_free(val); - val = NULL; - if (upnp_get_first_document_item(data, "NewWLANEventMAC", &val) || - hwaddr_aton(val, macaddr)) { + val = xml_get_first_item(data, "NewWLANEventMAC"); + if (val == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in " + "PutWLANResponse"); wpabuf_free(msg); - os_free(val); return UPNP_ARG_VALUE_INVALID; } + if (hwaddr_aton(val, macaddr)) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in " + "PutWLANResponse: '%s'", val); + if (hwaddr_aton2(val, macaddr) > 0) { + /* + * At least some versions of Intel PROset seem to be + * using dot-deliminated MAC address format here. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow " + "incorrect MAC address format in " + "NewWLANEventMAC"); + } else { + wpabuf_free(msg); + os_free(val); + return UPNP_ARG_VALUE_INVALID; + } + } os_free(val); if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) { struct wps_parse_attr attr; @@ -1044,112 +563,50 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, } -static enum http_reply_code -web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, - const char **replyname) +static int find_er_addr(struct subscription *s, struct sockaddr_in *cli) { - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); - msg = web_get_item(data, "NewMessage", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_set_selected_registrar || - sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - + struct subscr_addr *a; -static enum http_reply_code -web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP"); - msg = web_get_item(data, "NewAPSettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_reboot_ap || - sm->ctx->rx_req_reboot_ap(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; + dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) { + if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr) + return 1; } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; + return 0; } -static enum http_reply_code -web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) +static struct subscription * find_er(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli) { - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP"); - msg = web_get_item(data, "NewMessage", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_reset_ap || - sm->ctx->rx_req_reset_ap(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; + struct subscription *s; + dl_list_for_each(s, &sm->subscriptions, struct subscription, list) + if (find_er_addr(s, cli)) + return s; + return NULL; } static enum http_reply_code -web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) +web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli, char *data, + struct wpabuf **reply, + const char **replyname) { struct wpabuf *msg; enum http_reply_code ret; + struct subscription *s; - wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA"); - msg = web_get_item(data, "NewSTASettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_reboot_sta || - sm->ctx->rx_req_reboot_sta(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; + wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); + s = find_er(sm, cli); + if (s == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar " + "from unknown ER"); + return UPNP_ACTION_FAILED; } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA"); - msg = web_get_item(data, "NewMessage", &ret); + msg = xml_get_base64_item(data, "NewMessage", &ret); if (msg == NULL) return ret; - if (!sm->ctx->rx_req_reset_sta || - sm->ctx->rx_req_reset_sta(sm->priv, msg)) { + if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) { wpabuf_free(msg); return HTTP_INTERNAL_SERVER_ERROR; } @@ -1180,7 +637,7 @@ static const char *soap_error_postfix = "</detail>\n" "</s:Fault>\n"; -static void web_connection_send_reply(struct web_connection *c, +static void web_connection_send_reply(struct http_request *req, enum http_reply_code ret, const char *action, int action_len, const struct wpabuf *reply, @@ -1208,8 +665,8 @@ static void web_connection_send_reply(struct web_connection *c, if (buf == NULL) { wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to " "POST"); - wpabuf_free(buf); os_free(replydata); + http_request_deinit(req); return; } @@ -1282,13 +739,12 @@ static void web_connection_send_reply(struct web_connection *c, os_memcpy(put_length_here, len_buf, os_strlen(len_buf)); } - send_wpabuf(c->sd, buf); - wpabuf_free(buf); + http_request_send_and_deinit(req, buf); } -static const char * web_get_action(struct web_connection *c, - const char *filename, size_t *action_len) +static const char * web_get_action(struct http_request *req, + size_t *action_len) { const char *match; int match_len; @@ -1296,13 +752,8 @@ static const char * web_get_action(struct web_connection *c, char *action; *action_len = 0; - if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) { - wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s", - filename); - return NULL; - } /* The SOAPAction line of the header tells us what we want to do */ - b = httpread_hdr_line_get(c->hread, "SOAPAction:"); + b = http_request_get_hdr_line(req, "SOAPAction:"); if (b == NULL) return NULL; if (*b == '"') @@ -1349,70 +800,47 @@ static const char * web_get_action(struct web_connection *c, * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */ -static void web_connection_parse_post(struct web_connection *c, +static void web_connection_parse_post(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli, + struct http_request *req, const char *filename) { enum http_reply_code ret; - struct upnp_wps_device_sm *sm = c->sm; - char *data = httpread_data_get(c->hread); /* body of http msg */ - const char *action; - size_t action_len; + char *data = http_request_get_data(req); /* body of http msg */ + const char *action = NULL; + size_t action_len = 0; const char *replyname = NULL; /* argument name for the reply */ struct wpabuf *reply = NULL; /* data for the reply */ + if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) { + wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s", + filename); + ret = HTTP_NOT_FOUND; + goto bad; + } + ret = UPNP_INVALID_ACTION; - action = web_get_action(c, filename, &action_len); + action = web_get_action(req, &action_len); if (action == NULL) goto bad; - /* - * There are quite a few possible actions. Although we appear to - * support them all here, not all of them are necessarily supported by - * callbacks at higher levels. - */ if (!os_strncasecmp("GetDeviceInfo", action, action_len)) ret = web_process_get_device_info(sm, &reply, &replyname); else if (!os_strncasecmp("PutMessage", action, action_len)) ret = web_process_put_message(sm, data, &reply, &replyname); - else if (!os_strncasecmp("GetAPSettings", action, action_len)) - ret = web_process_get_ap_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("SetAPSettings", action, action_len)) - ret = web_process_set_ap_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("DelAPSettings", action, action_len)) - ret = web_process_del_ap_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("GetSTASettings", action, action_len)) - ret = web_process_get_sta_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("SetSTASettings", action, action_len)) - ret = web_process_set_sta_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("DelSTASettings", action, action_len)) - ret = web_process_del_sta_settings(sm, data, &reply, - &replyname); else if (!os_strncasecmp("PutWLANResponse", action, action_len)) ret = web_process_put_wlan_response(sm, data, &reply, &replyname); else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len)) - ret = web_process_set_selected_registrar(sm, data, &reply, + ret = web_process_set_selected_registrar(sm, cli, data, &reply, &replyname); - else if (!os_strncasecmp("RebootAP", action, action_len)) - ret = web_process_reboot_ap(sm, data, &reply, &replyname); - else if (!os_strncasecmp("ResetAP", action, action_len)) - ret = web_process_reset_ap(sm, data, &reply, &replyname); - else if (!os_strncasecmp("RebootSTA", action, action_len)) - ret = web_process_reboot_sta(sm, data, &reply, &replyname); - else if (!os_strncasecmp("ResetSTA", action, action_len)) - ret = web_process_reset_sta(sm, data, &reply, &replyname); else wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type"); bad: if (ret != HTTP_OK) wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret); - web_connection_send_reply(c, ret, action, action_len, reply, + web_connection_send_reply(req, ret, action, action_len, reply, replyname); wpabuf_free(reply); } @@ -1437,13 +865,13 @@ bad: * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */ -static void web_connection_parse_subscribe(struct web_connection *c, +static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, + struct http_request *req, const char *filename) { - struct upnp_wps_device_sm *sm = c->sm; struct wpabuf *buf; char *b; - char *hdr = httpread_hdr_get(c->hread); + char *hdr = http_request_get_hdr(req); char *h; char *match; int match_len; @@ -1457,8 +885,10 @@ static void web_connection_parse_subscribe(struct web_connection *c, enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR; buf = wpabuf_alloc(1000); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(req); return; + } /* Parse/validate headers */ h = hdr; @@ -1599,9 +1029,8 @@ static void web_connection_parse_subscribe(struct web_connection *c, /* And empty line to terminate header: */ wpabuf_put_str(buf, "\r\n"); - send_wpabuf(c->sd, buf); - wpabuf_free(buf); os_free(callback_urls); + http_request_send_and_deinit(req, buf); return; error: @@ -1627,8 +1056,7 @@ error: * 599 Too many subscriptions (not a standard HTTP error) */ http_put_empty(buf, ret); - send_wpabuf(c->sd, buf); - wpabuf_free(buf); + http_request_send_and_deinit(req, buf); os_free(callback_urls); } @@ -1648,12 +1076,12 @@ error: * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */ -static void web_connection_parse_unsubscribe(struct web_connection *c, +static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, + struct http_request *req, const char *filename) { - struct upnp_wps_device_sm *sm = c->sm; struct wpabuf *buf; - char *hdr = httpread_hdr_get(c->hread); + char *hdr = http_request_get_hdr(req); char *h; char *match; int match_len; @@ -1721,12 +1149,13 @@ static void web_connection_parse_unsubscribe(struct web_connection *c, if (got_uuid) { s = subscription_find(sm, uuid); if (s) { + struct subscr_addr *sa; + sa = dl_list_first(&s->addr_list, struct subscr_addr, + list); wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s", - s, - (s && s->addr_list && - s->addr_list->domain_and_port) ? - s->addr_list->domain_and_port : "-null-"); - subscription_unlink(s); + s, (sa && sa->domain_and_port) ? + sa->domain_and_port : "-null-"); + dl_list_del(&s->list); subscription_destroy(s); } } else { @@ -1740,40 +1169,42 @@ static void web_connection_parse_unsubscribe(struct web_connection *c, send_msg: buf = wpabuf_alloc(200); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(req); return; + } http_put_empty(buf, ret); - send_wpabuf(c->sd, buf); - wpabuf_free(buf); + http_request_send_and_deinit(req, buf); } /* Send error in response to unknown requests */ -static void web_connection_unimplemented(struct web_connection *c) +static void web_connection_unimplemented(struct http_request *req) { struct wpabuf *buf; buf = wpabuf_alloc(200); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(req); return; + } http_put_empty(buf, HTTP_UNIMPLEMENTED); - send_wpabuf(c->sd, buf); - wpabuf_free(buf); + http_request_send_and_deinit(req, buf); } /* Called when we have gotten an apparently valid http request. */ -static void web_connection_check_data(struct web_connection *c) +static void web_connection_check_data(void *ctx, struct http_request *req) { - struct httpread *hread = c->hread; - enum httpread_hdr_type htype = httpread_hdr_type_get(hread); - /* char *data = httpread_data_get(hread); */ - char *filename = httpread_uri_get(hread); + struct upnp_wps_device_sm *sm = ctx; + enum httpread_hdr_type htype = http_request_get_type(req); + char *filename = http_request_get_uri(req); + struct sockaddr_in *cli = http_request_get_cli_addr(req); - c->done = 1; if (!filename) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI"); + http_request_deinit(req); return; } /* Trim leading slashes from filename */ @@ -1781,22 +1212,22 @@ static void web_connection_check_data(struct web_connection *c) filename++; wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d", - htype, inet_ntoa(c->cli_addr.sin_addr), - htons(c->cli_addr.sin_port)); + htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port)); switch (htype) { case HTTPREAD_HDR_TYPE_GET: - web_connection_parse_get(c, filename); + web_connection_parse_get(sm, req, filename); break; case HTTPREAD_HDR_TYPE_POST: - web_connection_parse_post(c, filename); + web_connection_parse_post(sm, cli, req, filename); break; case HTTPREAD_HDR_TYPE_SUBSCRIBE: - web_connection_parse_subscribe(c, filename); + web_connection_parse_subscribe(sm, req, filename); break; case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: - web_connection_parse_unsubscribe(c, filename); + web_connection_parse_unsubscribe(sm, req, filename); break; + /* We are not required to support M-POST; just plain * POST is supposed to work, so we only support that. * If for some reason we need to support M-POST, it is @@ -1804,82 +1235,12 @@ static void web_connection_check_data(struct web_connection *c) */ default: /* Send 501 for anything else */ - web_connection_unimplemented(c); + web_connection_unimplemented(req); break; } } - -/* called back when we have gotten request */ -static void web_connection_got_file_handler(struct httpread *handle, - void *cookie, - enum httpread_event en) -{ - struct web_connection *c = cookie; - - if (en == HTTPREAD_EVENT_FILE_READY) - web_connection_check_data(c); - web_connection_stop(c); -} - - -/* web_connection_start - Start web connection - * @sm: WPS UPnP state machine from upnp_wps_device_init() - * @sd: Socket descriptor - * @addr: Client address - * - * The socket descriptor sd is handed over for ownership by the WPS UPnP - * state machine. - */ -static void web_connection_start(struct upnp_wps_device_sm *sm, - int sd, struct sockaddr_in *addr) -{ - struct web_connection *c = NULL; - - /* if too many connections, bail */ - if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) { - close(sd); - return; - } - - c = os_zalloc(sizeof(*c)); - if (c == NULL) - return; - os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr)); - c->sm = sm; - c->sd = sd; -#if 0 - /* - * Setting non-blocking should not be necessary for read, and can mess - * up sending where blocking might be better. - */ - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) - break; -#endif - c->hread = httpread_create(c->sd, web_connection_got_file_handler, - c /* cookie */, - WEB_CONNECTION_MAX_READ, - WEB_CONNECTION_TIMEOUT_SEC); - if (c->hread == NULL) - goto fail; - if (sm->web_connections) { - c->next = sm->web_connections; - c->prev = c->next->prev; - c->prev->next = c; - c->next->prev = c; - } else { - sm->web_connections = c->next = c->prev = c; - } - sm->n_web_connections++; - return; - -fail: - if (c) - web_connection_stop(c); -} - - /* * Listening for web connections * We have a single TCP listening port, and hand off connections as we get @@ -1888,77 +1249,22 @@ fail: void web_listener_stop(struct upnp_wps_device_sm *sm) { - if (sm->web_sd_registered) { - sm->web_sd_registered = 0; - eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ); - } - if (sm->web_sd >= 0) - close(sm->web_sd); - sm->web_sd = -1; -} - - -static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx) -{ - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - struct upnp_wps_device_sm *sm = sock_ctx; - int new_sd; - - /* Create state for new connection */ - /* Remember so we can cancel if need be */ - new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len); - if (new_sd < 0) { - wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept " - "errno=%d (%s) web_sd=%d", - errno, strerror(errno), sm->web_sd); - return; - } - web_connection_start(sm, new_sd, &addr); + http_server_deinit(sm->web_srv); + sm->web_srv = NULL; } int web_listener_start(struct upnp_wps_device_sm *sm) { - struct sockaddr_in addr; - int port; - - sm->web_sd = socket(AF_INET, SOCK_STREAM, 0); - if (sm->web_sd < 0) - goto fail; - if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - port = 49152; /* first non-reserved port */ - for (;;) { - os_memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = sm->ip_addr; - addr.sin_port = htons(port); - if (bind(sm->web_sd, (struct sockaddr *) &addr, - sizeof(addr)) == 0) - break; - if (errno == EADDRINUSE) { - /* search for unused port */ - if (++port == 65535) - goto fail; - continue; - } - goto fail; + struct in_addr addr; + addr.s_addr = sm->ip_addr; + sm->web_srv = http_server_init(&addr, -1, web_connection_check_data, + sm); + if (sm->web_srv == NULL) { + web_listener_stop(sm); + return -1; } - if (listen(sm->web_sd, 10 /* max backlog */) != 0) - goto fail; - if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ, - web_listener_handler, NULL, sm)) - goto fail; - sm->web_sd_registered = 1; - sm->web_port = port; + sm->web_port = http_server_get_port(sm->web_srv); return 0; - -fail: - /* Error */ - web_listener_stop(sm); - return -1; } |