16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer * UPnP WPS Device - Web connections
36d49e1aeSJan Lentfer * Copyright (c) 2000-2003 Intel Corporation
46d49e1aeSJan Lentfer * Copyright (c) 2006-2007 Sony Corporation
56d49e1aeSJan Lentfer * Copyright (c) 2008-2009 Atheros Communications
66d49e1aeSJan Lentfer * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
76d49e1aeSJan Lentfer *
86d49e1aeSJan Lentfer * See wps_upnp.c for more details on licensing and code history.
96d49e1aeSJan Lentfer */
106d49e1aeSJan Lentfer
116d49e1aeSJan Lentfer #include "includes.h"
126d49e1aeSJan Lentfer
136d49e1aeSJan Lentfer #include "common.h"
146d49e1aeSJan Lentfer #include "base64.h"
156d49e1aeSJan Lentfer #include "uuid.h"
166d49e1aeSJan Lentfer #include "httpread.h"
173ff40c12SJohn Marino #include "http_server.h"
186d49e1aeSJan Lentfer #include "wps_i.h"
196d49e1aeSJan Lentfer #include "wps_upnp.h"
206d49e1aeSJan Lentfer #include "wps_upnp_i.h"
213ff40c12SJohn Marino #include "upnp_xml.h"
226d49e1aeSJan Lentfer
236d49e1aeSJan Lentfer /***************************************************************************
246d49e1aeSJan Lentfer * Web connections (we serve pages of info about ourselves, handle
256d49e1aeSJan Lentfer * requests, etc. etc.).
266d49e1aeSJan Lentfer **************************************************************************/
276d49e1aeSJan Lentfer
286d49e1aeSJan Lentfer #define WEB_CONNECTION_TIMEOUT_SEC 30 /* Drop web connection after t.o. */
296d49e1aeSJan Lentfer #define WEB_CONNECTION_MAX_READ 8000 /* Max we'll read for TCP request */
306d49e1aeSJan Lentfer #define MAX_WEB_CONNECTIONS 10 /* max simultaneous web connects */
316d49e1aeSJan Lentfer
326d49e1aeSJan Lentfer
336d49e1aeSJan Lentfer static const char *urn_wfawlanconfig =
346d49e1aeSJan Lentfer "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
356d49e1aeSJan Lentfer static const char *http_server_hdr =
366d49e1aeSJan Lentfer "Server: unspecified, UPnP/1.0, unspecified\r\n";
376d49e1aeSJan Lentfer static const char *http_connection_close =
386d49e1aeSJan Lentfer "Connection: close\r\n";
396d49e1aeSJan Lentfer
406d49e1aeSJan Lentfer /*
416d49e1aeSJan Lentfer * "Files" that we serve via HTTP. The format of these files is given by
426d49e1aeSJan Lentfer * WFA WPS specifications. Extra white space has been removed to save space.
436d49e1aeSJan Lentfer */
446d49e1aeSJan Lentfer
456d49e1aeSJan Lentfer static const char wps_scpd_xml[] =
466d49e1aeSJan Lentfer "<?xml version=\"1.0\"?>\n"
476d49e1aeSJan Lentfer "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
486d49e1aeSJan Lentfer "<specVersion><major>1</major><minor>0</minor></specVersion>\n"
496d49e1aeSJan Lentfer "<actionList>\n"
506d49e1aeSJan Lentfer "<action>\n"
516d49e1aeSJan Lentfer "<name>GetDeviceInfo</name>\n"
526d49e1aeSJan Lentfer "<argumentList>\n"
536d49e1aeSJan Lentfer "<argument>\n"
546d49e1aeSJan Lentfer "<name>NewDeviceInfo</name>\n"
556d49e1aeSJan Lentfer "<direction>out</direction>\n"
566d49e1aeSJan Lentfer "<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
576d49e1aeSJan Lentfer "</argument>\n"
586d49e1aeSJan Lentfer "</argumentList>\n"
596d49e1aeSJan Lentfer "</action>\n"
606d49e1aeSJan Lentfer "<action>\n"
616d49e1aeSJan Lentfer "<name>PutMessage</name>\n"
626d49e1aeSJan Lentfer "<argumentList>\n"
636d49e1aeSJan Lentfer "<argument>\n"
646d49e1aeSJan Lentfer "<name>NewInMessage</name>\n"
656d49e1aeSJan Lentfer "<direction>in</direction>\n"
666d49e1aeSJan Lentfer "<relatedStateVariable>InMessage</relatedStateVariable>\n"
676d49e1aeSJan Lentfer "</argument>\n"
686d49e1aeSJan Lentfer "<argument>\n"
696d49e1aeSJan Lentfer "<name>NewOutMessage</name>\n"
706d49e1aeSJan Lentfer "<direction>out</direction>\n"
716d49e1aeSJan Lentfer "<relatedStateVariable>OutMessage</relatedStateVariable>\n"
726d49e1aeSJan Lentfer "</argument>\n"
736d49e1aeSJan Lentfer "</argumentList>\n"
746d49e1aeSJan Lentfer "</action>\n"
756d49e1aeSJan Lentfer "<action>\n"
766d49e1aeSJan Lentfer "<name>PutWLANResponse</name>\n"
776d49e1aeSJan Lentfer "<argumentList>\n"
786d49e1aeSJan Lentfer "<argument>\n"
796d49e1aeSJan Lentfer "<name>NewMessage</name>\n"
806d49e1aeSJan Lentfer "<direction>in</direction>\n"
816d49e1aeSJan Lentfer "<relatedStateVariable>Message</relatedStateVariable>\n"
826d49e1aeSJan Lentfer "</argument>\n"
836d49e1aeSJan Lentfer "<argument>\n"
846d49e1aeSJan Lentfer "<name>NewWLANEventType</name>\n"
856d49e1aeSJan Lentfer "<direction>in</direction>\n"
866d49e1aeSJan Lentfer "<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
876d49e1aeSJan Lentfer "</argument>\n"
886d49e1aeSJan Lentfer "<argument>\n"
896d49e1aeSJan Lentfer "<name>NewWLANEventMAC</name>\n"
906d49e1aeSJan Lentfer "<direction>in</direction>\n"
916d49e1aeSJan Lentfer "<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
926d49e1aeSJan Lentfer "</argument>\n"
936d49e1aeSJan Lentfer "</argumentList>\n"
946d49e1aeSJan Lentfer "</action>\n"
956d49e1aeSJan Lentfer "<action>\n"
966d49e1aeSJan Lentfer "<name>SetSelectedRegistrar</name>\n"
976d49e1aeSJan Lentfer "<argumentList>\n"
986d49e1aeSJan Lentfer "<argument>\n"
996d49e1aeSJan Lentfer "<name>NewMessage</name>\n"
1006d49e1aeSJan Lentfer "<direction>in</direction>\n"
1016d49e1aeSJan Lentfer "<relatedStateVariable>Message</relatedStateVariable>\n"
1026d49e1aeSJan Lentfer "</argument>\n"
1036d49e1aeSJan Lentfer "</argumentList>\n"
1046d49e1aeSJan Lentfer "</action>\n"
1056d49e1aeSJan Lentfer "</actionList>\n"
1066d49e1aeSJan Lentfer "<serviceStateTable>\n"
1076d49e1aeSJan Lentfer "<stateVariable sendEvents=\"no\">\n"
1086d49e1aeSJan Lentfer "<name>Message</name>\n"
1096d49e1aeSJan Lentfer "<dataType>bin.base64</dataType>\n"
1106d49e1aeSJan Lentfer "</stateVariable>\n"
1116d49e1aeSJan Lentfer "<stateVariable sendEvents=\"no\">\n"
1126d49e1aeSJan Lentfer "<name>InMessage</name>\n"
1136d49e1aeSJan Lentfer "<dataType>bin.base64</dataType>\n"
1146d49e1aeSJan Lentfer "</stateVariable>\n"
1156d49e1aeSJan Lentfer "<stateVariable sendEvents=\"no\">\n"
1166d49e1aeSJan Lentfer "<name>OutMessage</name>\n"
1176d49e1aeSJan Lentfer "<dataType>bin.base64</dataType>\n"
1186d49e1aeSJan Lentfer "</stateVariable>\n"
1196d49e1aeSJan Lentfer "<stateVariable sendEvents=\"no\">\n"
1206d49e1aeSJan Lentfer "<name>DeviceInfo</name>\n"
1216d49e1aeSJan Lentfer "<dataType>bin.base64</dataType>\n"
1226d49e1aeSJan Lentfer "</stateVariable>\n"
1236d49e1aeSJan Lentfer "<stateVariable sendEvents=\"yes\">\n"
1246d49e1aeSJan Lentfer "<name>APStatus</name>\n"
1256d49e1aeSJan Lentfer "<dataType>ui1</dataType>\n"
1266d49e1aeSJan Lentfer "</stateVariable>\n"
1276d49e1aeSJan Lentfer "<stateVariable sendEvents=\"yes\">\n"
1286d49e1aeSJan Lentfer "<name>STAStatus</name>\n"
1296d49e1aeSJan Lentfer "<dataType>ui1</dataType>\n"
1306d49e1aeSJan Lentfer "</stateVariable>\n"
1316d49e1aeSJan Lentfer "<stateVariable sendEvents=\"yes\">\n"
1326d49e1aeSJan Lentfer "<name>WLANEvent</name>\n"
1336d49e1aeSJan Lentfer "<dataType>bin.base64</dataType>\n"
1346d49e1aeSJan Lentfer "</stateVariable>\n"
1356d49e1aeSJan Lentfer "<stateVariable sendEvents=\"no\">\n"
1366d49e1aeSJan Lentfer "<name>WLANEventType</name>\n"
1376d49e1aeSJan Lentfer "<dataType>ui1</dataType>\n"
1386d49e1aeSJan Lentfer "</stateVariable>\n"
1396d49e1aeSJan Lentfer "<stateVariable sendEvents=\"no\">\n"
1406d49e1aeSJan Lentfer "<name>WLANEventMAC</name>\n"
1416d49e1aeSJan Lentfer "<dataType>string</dataType>\n"
1426d49e1aeSJan Lentfer "</stateVariable>\n"
1436d49e1aeSJan Lentfer "<stateVariable sendEvents=\"no\">\n"
1446d49e1aeSJan Lentfer "<name>WLANResponse</name>\n"
1456d49e1aeSJan Lentfer "<dataType>bin.base64</dataType>\n"
1466d49e1aeSJan Lentfer "</stateVariable>\n"
1476d49e1aeSJan Lentfer "</serviceStateTable>\n"
1486d49e1aeSJan Lentfer "</scpd>\n"
1496d49e1aeSJan Lentfer ;
1506d49e1aeSJan Lentfer
1516d49e1aeSJan Lentfer
1526d49e1aeSJan Lentfer static const char *wps_device_xml_prefix =
1536d49e1aeSJan Lentfer "<?xml version=\"1.0\"?>\n"
1546d49e1aeSJan Lentfer "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
1556d49e1aeSJan Lentfer "<specVersion>\n"
1566d49e1aeSJan Lentfer "<major>1</major>\n"
1576d49e1aeSJan Lentfer "<minor>0</minor>\n"
1586d49e1aeSJan Lentfer "</specVersion>\n"
1596d49e1aeSJan Lentfer "<device>\n"
1606d49e1aeSJan Lentfer "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
1616d49e1aeSJan Lentfer "</deviceType>\n";
1626d49e1aeSJan Lentfer
1636d49e1aeSJan Lentfer static const char *wps_device_xml_postfix =
1646d49e1aeSJan Lentfer "<serviceList>\n"
1656d49e1aeSJan Lentfer "<service>\n"
1666d49e1aeSJan Lentfer "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
1676d49e1aeSJan Lentfer "</serviceType>\n"
1686d49e1aeSJan Lentfer "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
1696d49e1aeSJan Lentfer "\n"
1706d49e1aeSJan Lentfer "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
1716d49e1aeSJan Lentfer "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
1726d49e1aeSJan Lentfer "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
1736d49e1aeSJan Lentfer "</service>\n"
1746d49e1aeSJan Lentfer "</serviceList>\n"
1756d49e1aeSJan Lentfer "</device>\n"
1766d49e1aeSJan Lentfer "</root>\n";
1776d49e1aeSJan Lentfer
1786d49e1aeSJan Lentfer
1796d49e1aeSJan Lentfer /* format_wps_device_xml -- produce content of "file" wps_device.xml
1806d49e1aeSJan Lentfer * (UPNP_WPS_DEVICE_XML_FILE)
1816d49e1aeSJan Lentfer */
format_wps_device_xml(struct upnp_wps_device_interface * iface,struct upnp_wps_device_sm * sm,struct wpabuf * buf)182*a1157835SDaniel Fojt static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
183*a1157835SDaniel Fojt struct upnp_wps_device_sm *sm,
1846d49e1aeSJan Lentfer struct wpabuf *buf)
1856d49e1aeSJan Lentfer {
1866d49e1aeSJan Lentfer const char *s;
1876d49e1aeSJan Lentfer char uuid_string[80];
1886d49e1aeSJan Lentfer
1896d49e1aeSJan Lentfer wpabuf_put_str(buf, wps_device_xml_prefix);
1906d49e1aeSJan Lentfer
1916d49e1aeSJan Lentfer /*
1926d49e1aeSJan Lentfer * Add required fields with default values if not configured. Add
1936d49e1aeSJan Lentfer * optional and recommended fields only if configured.
1946d49e1aeSJan Lentfer */
1953ff40c12SJohn Marino s = iface->wps->friendly_name;
1966d49e1aeSJan Lentfer s = ((s && *s) ? s : "WPS Access Point");
1976d49e1aeSJan Lentfer xml_add_tagged_data(buf, "friendlyName", s);
1986d49e1aeSJan Lentfer
1993ff40c12SJohn Marino s = iface->wps->dev.manufacturer;
2006d49e1aeSJan Lentfer s = ((s && *s) ? s : "");
2016d49e1aeSJan Lentfer xml_add_tagged_data(buf, "manufacturer", s);
2026d49e1aeSJan Lentfer
2033ff40c12SJohn Marino if (iface->wps->manufacturer_url)
2046d49e1aeSJan Lentfer xml_add_tagged_data(buf, "manufacturerURL",
2053ff40c12SJohn Marino iface->wps->manufacturer_url);
2066d49e1aeSJan Lentfer
2073ff40c12SJohn Marino if (iface->wps->model_description)
2086d49e1aeSJan Lentfer xml_add_tagged_data(buf, "modelDescription",
2093ff40c12SJohn Marino iface->wps->model_description);
2106d49e1aeSJan Lentfer
2113ff40c12SJohn Marino s = iface->wps->dev.model_name;
2126d49e1aeSJan Lentfer s = ((s && *s) ? s : "");
2136d49e1aeSJan Lentfer xml_add_tagged_data(buf, "modelName", s);
2146d49e1aeSJan Lentfer
2153ff40c12SJohn Marino if (iface->wps->dev.model_number)
2166d49e1aeSJan Lentfer xml_add_tagged_data(buf, "modelNumber",
2173ff40c12SJohn Marino iface->wps->dev.model_number);
2186d49e1aeSJan Lentfer
2193ff40c12SJohn Marino if (iface->wps->model_url)
2203ff40c12SJohn Marino xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
2216d49e1aeSJan Lentfer
2223ff40c12SJohn Marino if (iface->wps->dev.serial_number)
2236d49e1aeSJan Lentfer xml_add_tagged_data(buf, "serialNumber",
2243ff40c12SJohn Marino iface->wps->dev.serial_number);
2256d49e1aeSJan Lentfer
2263ff40c12SJohn Marino uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
2276d49e1aeSJan Lentfer s = uuid_string;
2286d49e1aeSJan Lentfer /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
2296d49e1aeSJan Lentfer * easily...
2306d49e1aeSJan Lentfer */
2316d49e1aeSJan Lentfer wpabuf_put_str(buf, "<UDN>uuid:");
2326d49e1aeSJan Lentfer xml_data_encode(buf, s, os_strlen(s));
2336d49e1aeSJan Lentfer wpabuf_put_str(buf, "</UDN>\n");
2346d49e1aeSJan Lentfer
2353ff40c12SJohn Marino if (iface->wps->upc)
2363ff40c12SJohn Marino xml_add_tagged_data(buf, "UPC", iface->wps->upc);
2376d49e1aeSJan Lentfer
2386d49e1aeSJan Lentfer wpabuf_put_str(buf, wps_device_xml_postfix);
2396d49e1aeSJan Lentfer }
2406d49e1aeSJan Lentfer
2416d49e1aeSJan Lentfer
http_put_reply_code(struct wpabuf * buf,enum http_reply_code code)2426d49e1aeSJan Lentfer static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
2436d49e1aeSJan Lentfer {
2446d49e1aeSJan Lentfer wpabuf_put_str(buf, "HTTP/1.1 ");
2456d49e1aeSJan Lentfer switch (code) {
2466d49e1aeSJan Lentfer case HTTP_OK:
2476d49e1aeSJan Lentfer wpabuf_put_str(buf, "200 OK\r\n");
2486d49e1aeSJan Lentfer break;
2496d49e1aeSJan Lentfer case HTTP_BAD_REQUEST:
2506d49e1aeSJan Lentfer wpabuf_put_str(buf, "400 Bad request\r\n");
2516d49e1aeSJan Lentfer break;
2526d49e1aeSJan Lentfer case HTTP_PRECONDITION_FAILED:
2536d49e1aeSJan Lentfer wpabuf_put_str(buf, "412 Precondition failed\r\n");
2546d49e1aeSJan Lentfer break;
2556d49e1aeSJan Lentfer case HTTP_UNIMPLEMENTED:
2566d49e1aeSJan Lentfer wpabuf_put_str(buf, "501 Unimplemented\r\n");
2576d49e1aeSJan Lentfer break;
2586d49e1aeSJan Lentfer case HTTP_INTERNAL_SERVER_ERROR:
2596d49e1aeSJan Lentfer default:
2606d49e1aeSJan Lentfer wpabuf_put_str(buf, "500 Internal server error\r\n");
2616d49e1aeSJan Lentfer break;
2626d49e1aeSJan Lentfer }
2636d49e1aeSJan Lentfer }
2646d49e1aeSJan Lentfer
2656d49e1aeSJan Lentfer
http_put_date(struct wpabuf * buf)2666d49e1aeSJan Lentfer static void http_put_date(struct wpabuf *buf)
2676d49e1aeSJan Lentfer {
2686d49e1aeSJan Lentfer wpabuf_put_str(buf, "Date: ");
2696d49e1aeSJan Lentfer format_date(buf);
2706d49e1aeSJan Lentfer wpabuf_put_str(buf, "\r\n");
2716d49e1aeSJan Lentfer }
2726d49e1aeSJan Lentfer
2736d49e1aeSJan Lentfer
http_put_empty(struct wpabuf * buf,enum http_reply_code code)2746d49e1aeSJan Lentfer static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
2756d49e1aeSJan Lentfer {
2766d49e1aeSJan Lentfer http_put_reply_code(buf, code);
2776d49e1aeSJan Lentfer wpabuf_put_str(buf, http_server_hdr);
2786d49e1aeSJan Lentfer wpabuf_put_str(buf, http_connection_close);
2796d49e1aeSJan Lentfer wpabuf_put_str(buf, "Content-Length: 0\r\n"
2806d49e1aeSJan Lentfer "\r\n");
2816d49e1aeSJan Lentfer }
2826d49e1aeSJan Lentfer
2836d49e1aeSJan Lentfer
2846d49e1aeSJan Lentfer /* Given that we have received a header w/ GET, act upon it
2856d49e1aeSJan Lentfer *
2866d49e1aeSJan Lentfer * Format of GET (case-insensitive):
2876d49e1aeSJan Lentfer *
2886d49e1aeSJan Lentfer * First line must be:
2896d49e1aeSJan Lentfer * GET /<file> HTTP/1.1
2906d49e1aeSJan Lentfer * Since we don't do anything fancy we just ignore other lines.
2916d49e1aeSJan Lentfer *
2926d49e1aeSJan Lentfer * Our response (if no error) which includes only required lines is:
2936d49e1aeSJan Lentfer * HTTP/1.1 200 OK
2946d49e1aeSJan Lentfer * Connection: close
2956d49e1aeSJan Lentfer * Content-Type: text/xml
2966d49e1aeSJan Lentfer * Date: <rfc1123-date>
2976d49e1aeSJan Lentfer *
2986d49e1aeSJan Lentfer * Header lines must end with \r\n
2996d49e1aeSJan Lentfer * Per RFC 2616, content-length: is not required but connection:close
3006d49e1aeSJan Lentfer * would appear to be required (given that we will be closing it!).
3016d49e1aeSJan Lentfer */
web_connection_parse_get(struct upnp_wps_device_sm * sm,struct http_request * hreq,const char * filename)3023ff40c12SJohn Marino static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
303*a1157835SDaniel Fojt struct http_request *hreq,
304*a1157835SDaniel Fojt const char *filename)
3056d49e1aeSJan Lentfer {
3066d49e1aeSJan Lentfer struct wpabuf *buf; /* output buffer, allocated */
3076d49e1aeSJan Lentfer char *put_length_here;
3086d49e1aeSJan Lentfer char *body_start;
3096d49e1aeSJan Lentfer enum {
3106d49e1aeSJan Lentfer GET_DEVICE_XML_FILE,
3116d49e1aeSJan Lentfer GET_SCPD_XML_FILE
3126d49e1aeSJan Lentfer } req;
3136d49e1aeSJan Lentfer size_t extra_len = 0;
3146d49e1aeSJan Lentfer int body_length;
3156d49e1aeSJan Lentfer char len_buf[10];
3163ff40c12SJohn Marino struct upnp_wps_device_interface *iface;
3173ff40c12SJohn Marino
3183ff40c12SJohn Marino iface = dl_list_first(&sm->interfaces,
3193ff40c12SJohn Marino struct upnp_wps_device_interface, list);
320*a1157835SDaniel Fojt if (iface == NULL) {
321*a1157835SDaniel Fojt http_request_deinit(hreq);
322*a1157835SDaniel Fojt return;
323*a1157835SDaniel Fojt }
3246d49e1aeSJan Lentfer
3256d49e1aeSJan Lentfer /*
3266d49e1aeSJan Lentfer * It is not required that filenames be case insensitive but it is
3276d49e1aeSJan Lentfer * allowed and cannot hurt here.
3286d49e1aeSJan Lentfer */
3296d49e1aeSJan Lentfer if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
3306d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
3316d49e1aeSJan Lentfer req = GET_DEVICE_XML_FILE;
3326d49e1aeSJan Lentfer extra_len = 3000;
3333ff40c12SJohn Marino if (iface->wps->friendly_name)
3343ff40c12SJohn Marino extra_len += os_strlen(iface->wps->friendly_name);
3353ff40c12SJohn Marino if (iface->wps->manufacturer_url)
3363ff40c12SJohn Marino extra_len += os_strlen(iface->wps->manufacturer_url);
3373ff40c12SJohn Marino if (iface->wps->model_description)
3383ff40c12SJohn Marino extra_len += os_strlen(iface->wps->model_description);
3393ff40c12SJohn Marino if (iface->wps->model_url)
3403ff40c12SJohn Marino extra_len += os_strlen(iface->wps->model_url);
3413ff40c12SJohn Marino if (iface->wps->upc)
3423ff40c12SJohn Marino extra_len += os_strlen(iface->wps->upc);
3436d49e1aeSJan Lentfer } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
3446d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
3456d49e1aeSJan Lentfer req = GET_SCPD_XML_FILE;
3466d49e1aeSJan Lentfer extra_len = os_strlen(wps_scpd_xml);
3476d49e1aeSJan Lentfer } else {
3486d49e1aeSJan Lentfer /* File not found */
3496d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
3506d49e1aeSJan Lentfer filename);
3516d49e1aeSJan Lentfer buf = wpabuf_alloc(200);
3523ff40c12SJohn Marino if (buf == NULL) {
3533ff40c12SJohn Marino http_request_deinit(hreq);
3546d49e1aeSJan Lentfer return;
3553ff40c12SJohn Marino }
3566d49e1aeSJan Lentfer wpabuf_put_str(buf,
3576d49e1aeSJan Lentfer "HTTP/1.1 404 Not Found\r\n"
3586d49e1aeSJan Lentfer "Connection: close\r\n");
3596d49e1aeSJan Lentfer
3606d49e1aeSJan Lentfer http_put_date(buf);
3616d49e1aeSJan Lentfer
3626d49e1aeSJan Lentfer /* terminating empty line */
3636d49e1aeSJan Lentfer wpabuf_put_str(buf, "\r\n");
3646d49e1aeSJan Lentfer
3656d49e1aeSJan Lentfer goto send_buf;
3666d49e1aeSJan Lentfer }
3676d49e1aeSJan Lentfer
3686d49e1aeSJan Lentfer buf = wpabuf_alloc(1000 + extra_len);
3693ff40c12SJohn Marino if (buf == NULL) {
3703ff40c12SJohn Marino http_request_deinit(hreq);
3716d49e1aeSJan Lentfer return;
3723ff40c12SJohn Marino }
3736d49e1aeSJan Lentfer
3746d49e1aeSJan Lentfer wpabuf_put_str(buf,
3756d49e1aeSJan Lentfer "HTTP/1.1 200 OK\r\n"
3766d49e1aeSJan Lentfer "Content-Type: text/xml; charset=\"utf-8\"\r\n");
3776d49e1aeSJan Lentfer wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
3786d49e1aeSJan Lentfer wpabuf_put_str(buf, "Connection: close\r\n");
3796d49e1aeSJan Lentfer wpabuf_put_str(buf, "Content-Length: ");
3806d49e1aeSJan Lentfer /*
3816d49e1aeSJan Lentfer * We will paste the length in later, leaving some extra whitespace.
3826d49e1aeSJan Lentfer * HTTP code is supposed to be tolerant of extra whitespace.
3836d49e1aeSJan Lentfer */
3846d49e1aeSJan Lentfer put_length_here = wpabuf_put(buf, 0);
3856d49e1aeSJan Lentfer wpabuf_put_str(buf, " \r\n");
3866d49e1aeSJan Lentfer
3876d49e1aeSJan Lentfer http_put_date(buf);
3886d49e1aeSJan Lentfer
3896d49e1aeSJan Lentfer /* terminating empty line */
3906d49e1aeSJan Lentfer wpabuf_put_str(buf, "\r\n");
3916d49e1aeSJan Lentfer
3926d49e1aeSJan Lentfer body_start = wpabuf_put(buf, 0);
3936d49e1aeSJan Lentfer
3946d49e1aeSJan Lentfer switch (req) {
3956d49e1aeSJan Lentfer case GET_DEVICE_XML_FILE:
396*a1157835SDaniel Fojt format_wps_device_xml(iface, sm, buf);
3976d49e1aeSJan Lentfer break;
3986d49e1aeSJan Lentfer case GET_SCPD_XML_FILE:
3996d49e1aeSJan Lentfer wpabuf_put_str(buf, wps_scpd_xml);
4006d49e1aeSJan Lentfer break;
4016d49e1aeSJan Lentfer }
4026d49e1aeSJan Lentfer
4036d49e1aeSJan Lentfer /* Now patch in the content length at the end */
4046d49e1aeSJan Lentfer body_length = (char *) wpabuf_put(buf, 0) - body_start;
4056d49e1aeSJan Lentfer os_snprintf(len_buf, 10, "%d", body_length);
4066d49e1aeSJan Lentfer os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
4076d49e1aeSJan Lentfer
4086d49e1aeSJan Lentfer send_buf:
4093ff40c12SJohn Marino http_request_send_and_deinit(hreq, buf);
4106d49e1aeSJan Lentfer }
4116d49e1aeSJan Lentfer
4126d49e1aeSJan Lentfer
wps_upnp_peer_del(struct upnp_wps_peer * peer)413*a1157835SDaniel Fojt static void wps_upnp_peer_del(struct upnp_wps_peer *peer)
414*a1157835SDaniel Fojt {
415*a1157835SDaniel Fojt dl_list_del(&peer->list);
416*a1157835SDaniel Fojt if (peer->wps)
417*a1157835SDaniel Fojt wps_deinit(peer->wps);
418*a1157835SDaniel Fojt os_free(peer);
419*a1157835SDaniel Fojt }
420*a1157835SDaniel Fojt
421*a1157835SDaniel Fojt
4226d49e1aeSJan Lentfer static enum http_reply_code
web_process_get_device_info(struct upnp_wps_device_sm * sm,struct wpabuf ** reply,const char ** replyname)4236d49e1aeSJan Lentfer web_process_get_device_info(struct upnp_wps_device_sm *sm,
4246d49e1aeSJan Lentfer struct wpabuf **reply, const char **replyname)
4256d49e1aeSJan Lentfer {
4266d49e1aeSJan Lentfer static const char *name = "NewDeviceInfo";
4273ff40c12SJohn Marino struct wps_config cfg;
4283ff40c12SJohn Marino struct upnp_wps_device_interface *iface;
4293ff40c12SJohn Marino struct upnp_wps_peer *peer;
4303ff40c12SJohn Marino
4313ff40c12SJohn Marino iface = dl_list_first(&sm->interfaces,
4323ff40c12SJohn Marino struct upnp_wps_device_interface, list);
4336d49e1aeSJan Lentfer
4346d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
4353ff40c12SJohn Marino
436*a1157835SDaniel Fojt if (!iface || iface->ctx->ap_pin == NULL)
437*a1157835SDaniel Fojt return HTTP_INTERNAL_SERVER_ERROR;
438*a1157835SDaniel Fojt
439*a1157835SDaniel Fojt peer = os_zalloc(sizeof(*peer));
440*a1157835SDaniel Fojt if (!peer)
4416d49e1aeSJan Lentfer return HTTP_INTERNAL_SERVER_ERROR;
4423ff40c12SJohn Marino
4433ff40c12SJohn Marino /*
4443ff40c12SJohn Marino * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
4453ff40c12SJohn Marino * registration over UPnP with the AP acting as an Enrollee. It should
4463ff40c12SJohn Marino * be noted that this is frequently used just to get the device data,
4473ff40c12SJohn Marino * i.e., there may not be any intent to actually complete the
4483ff40c12SJohn Marino * registration.
4493ff40c12SJohn Marino */
4503ff40c12SJohn Marino
4513ff40c12SJohn Marino os_memset(&cfg, 0, sizeof(cfg));
4523ff40c12SJohn Marino cfg.wps = iface->wps;
4533ff40c12SJohn Marino cfg.pin = (u8 *) iface->ctx->ap_pin;
4543ff40c12SJohn Marino cfg.pin_len = os_strlen(iface->ctx->ap_pin);
4553ff40c12SJohn Marino peer->wps = wps_init(&cfg);
4563ff40c12SJohn Marino if (peer->wps) {
4573ff40c12SJohn Marino enum wsc_op_code op_code;
4583ff40c12SJohn Marino *reply = wps_get_msg(peer->wps, &op_code);
4593ff40c12SJohn Marino if (*reply == NULL) {
4603ff40c12SJohn Marino wps_deinit(peer->wps);
4613ff40c12SJohn Marino peer->wps = NULL;
4623ff40c12SJohn Marino }
4633ff40c12SJohn Marino } else
4643ff40c12SJohn Marino *reply = NULL;
4656d49e1aeSJan Lentfer if (*reply == NULL) {
4666d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
467*a1157835SDaniel Fojt os_free(peer);
4686d49e1aeSJan Lentfer return HTTP_INTERNAL_SERVER_ERROR;
4696d49e1aeSJan Lentfer }
470*a1157835SDaniel Fojt
471*a1157835SDaniel Fojt if (dl_list_len(&iface->peers) > 3) {
472*a1157835SDaniel Fojt struct upnp_wps_peer *old;
473*a1157835SDaniel Fojt
474*a1157835SDaniel Fojt old = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
475*a1157835SDaniel Fojt if (old) {
476*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session");
477*a1157835SDaniel Fojt wps_upnp_peer_del(old);
478*a1157835SDaniel Fojt }
479*a1157835SDaniel Fojt }
480*a1157835SDaniel Fojt dl_list_add_tail(&iface->peers, &peer->list);
481*a1157835SDaniel Fojt /* TODO: Could schedule a timeout to free the entry */
482*a1157835SDaniel Fojt
4836d49e1aeSJan Lentfer *replyname = name;
4846d49e1aeSJan Lentfer return HTTP_OK;
4856d49e1aeSJan Lentfer }
4866d49e1aeSJan Lentfer
4876d49e1aeSJan Lentfer
4886d49e1aeSJan Lentfer static enum http_reply_code
web_process_put_message(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)4896d49e1aeSJan Lentfer web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
4906d49e1aeSJan Lentfer struct wpabuf **reply, const char **replyname)
4916d49e1aeSJan Lentfer {
4926d49e1aeSJan Lentfer struct wpabuf *msg;
4936d49e1aeSJan Lentfer static const char *name = "NewOutMessage";
4946d49e1aeSJan Lentfer enum http_reply_code ret;
4953ff40c12SJohn Marino enum wps_process_res res;
4963ff40c12SJohn Marino enum wsc_op_code op_code;
4973ff40c12SJohn Marino struct upnp_wps_device_interface *iface;
498*a1157835SDaniel Fojt struct wps_parse_attr attr;
499*a1157835SDaniel Fojt struct upnp_wps_peer *tmp, *peer;
5003ff40c12SJohn Marino
5013ff40c12SJohn Marino iface = dl_list_first(&sm->interfaces,
5023ff40c12SJohn Marino struct upnp_wps_device_interface, list);
503*a1157835SDaniel Fojt if (!iface)
504*a1157835SDaniel Fojt return HTTP_INTERNAL_SERVER_ERROR;
5056d49e1aeSJan Lentfer
5066d49e1aeSJan Lentfer /*
5076d49e1aeSJan Lentfer * PutMessage is used by external UPnP-based Registrar to perform WPS
5086d49e1aeSJan Lentfer * operation with the access point itself; as compared with
5096d49e1aeSJan Lentfer * PutWLANResponse which is for proxying.
5106d49e1aeSJan Lentfer */
5116d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
5123ff40c12SJohn Marino msg = xml_get_base64_item(data, "NewInMessage", &ret);
5136d49e1aeSJan Lentfer if (msg == NULL)
5146d49e1aeSJan Lentfer return ret;
515*a1157835SDaniel Fojt
516*a1157835SDaniel Fojt if (wps_parse_msg(msg, &attr)) {
517*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
518*a1157835SDaniel Fojt "WPS UPnP: Could not parse PutMessage - NewInMessage");
519*a1157835SDaniel Fojt wpabuf_free(msg);
520*a1157835SDaniel Fojt return HTTP_BAD_REQUEST;
521*a1157835SDaniel Fojt }
522*a1157835SDaniel Fojt
523*a1157835SDaniel Fojt /* Find a matching active peer session */
524*a1157835SDaniel Fojt peer = NULL;
525*a1157835SDaniel Fojt dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) {
526*a1157835SDaniel Fojt if (!tmp->wps)
527*a1157835SDaniel Fojt continue;
528*a1157835SDaniel Fojt if (attr.enrollee_nonce &&
529*a1157835SDaniel Fojt os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce,
530*a1157835SDaniel Fojt WPS_NONCE_LEN) != 0)
531*a1157835SDaniel Fojt continue; /* Enrollee nonce mismatch */
532*a1157835SDaniel Fojt if (attr.msg_type &&
533*a1157835SDaniel Fojt *attr.msg_type != WPS_M2 &&
534*a1157835SDaniel Fojt *attr.msg_type != WPS_M2D &&
535*a1157835SDaniel Fojt attr.registrar_nonce &&
536*a1157835SDaniel Fojt os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce,
537*a1157835SDaniel Fojt WPS_NONCE_LEN) != 0)
538*a1157835SDaniel Fojt continue; /* Registrar nonce mismatch */
539*a1157835SDaniel Fojt peer = tmp;
540*a1157835SDaniel Fojt break;
541*a1157835SDaniel Fojt }
542*a1157835SDaniel Fojt if (!peer) {
543*a1157835SDaniel Fojt /*
544*a1157835SDaniel Fojt Try to use the first entry in case message could work with
545*a1157835SDaniel Fojt * it. The actual handler function will reject this, if needed.
546*a1157835SDaniel Fojt * This maintains older behavior where only a single peer entry
547*a1157835SDaniel Fojt * was supported.
548*a1157835SDaniel Fojt */
549*a1157835SDaniel Fojt peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
550*a1157835SDaniel Fojt }
551*a1157835SDaniel Fojt if (!peer || !peer->wps) {
552*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found");
553*a1157835SDaniel Fojt wpabuf_free(msg);
554*a1157835SDaniel Fojt return HTTP_BAD_REQUEST;
555*a1157835SDaniel Fojt }
556*a1157835SDaniel Fojt
557*a1157835SDaniel Fojt res = wps_process_msg(peer->wps, WSC_UPnP, msg);
558*a1157835SDaniel Fojt if (res == WPS_FAILURE) {
5593ff40c12SJohn Marino *reply = NULL;
560*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session");
561*a1157835SDaniel Fojt wps_upnp_peer_del(peer);
562*a1157835SDaniel Fojt } else {
563*a1157835SDaniel Fojt *reply = wps_get_msg(peer->wps, &op_code);
564*a1157835SDaniel Fojt }
5656d49e1aeSJan Lentfer wpabuf_free(msg);
5666d49e1aeSJan Lentfer if (*reply == NULL)
5676d49e1aeSJan Lentfer return HTTP_INTERNAL_SERVER_ERROR;
5686d49e1aeSJan Lentfer *replyname = name;
5696d49e1aeSJan Lentfer return HTTP_OK;
5706d49e1aeSJan Lentfer }
5716d49e1aeSJan Lentfer
5726d49e1aeSJan Lentfer
5736d49e1aeSJan Lentfer static enum http_reply_code
web_process_put_wlan_response(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)5746d49e1aeSJan Lentfer web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
5756d49e1aeSJan Lentfer struct wpabuf **reply, const char **replyname)
5766d49e1aeSJan Lentfer {
5776d49e1aeSJan Lentfer struct wpabuf *msg;
5786d49e1aeSJan Lentfer enum http_reply_code ret;
5796d49e1aeSJan Lentfer u8 macaddr[ETH_ALEN];
5806d49e1aeSJan Lentfer int ev_type;
5816d49e1aeSJan Lentfer int type;
5826d49e1aeSJan Lentfer char *val;
5833ff40c12SJohn Marino struct upnp_wps_device_interface *iface;
5843ff40c12SJohn Marino int ok = 0;
5856d49e1aeSJan Lentfer
5866d49e1aeSJan Lentfer /*
5876d49e1aeSJan Lentfer * External UPnP-based Registrar is passing us a message to be proxied
5886d49e1aeSJan Lentfer * over to a Wi-Fi -based client of ours.
5896d49e1aeSJan Lentfer */
5906d49e1aeSJan Lentfer
5916d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
5923ff40c12SJohn Marino msg = xml_get_base64_item(data, "NewMessage", &ret);
5933ff40c12SJohn Marino if (msg == NULL) {
5943ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
5953ff40c12SJohn Marino "from PutWLANResponse");
5966d49e1aeSJan Lentfer return ret;
5973ff40c12SJohn Marino }
5983ff40c12SJohn Marino val = xml_get_first_item(data, "NewWLANEventType");
5993ff40c12SJohn Marino if (val == NULL) {
6003ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
6013ff40c12SJohn Marino "PutWLANResponse");
6026d49e1aeSJan Lentfer wpabuf_free(msg);
6036d49e1aeSJan Lentfer return UPNP_ARG_VALUE_INVALID;
6046d49e1aeSJan Lentfer }
6056d49e1aeSJan Lentfer ev_type = atol(val);
6066d49e1aeSJan Lentfer os_free(val);
6073ff40c12SJohn Marino val = xml_get_first_item(data, "NewWLANEventMAC");
6083ff40c12SJohn Marino if (val == NULL) {
6093ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
6103ff40c12SJohn Marino "PutWLANResponse");
6113ff40c12SJohn Marino wpabuf_free(msg);
6123ff40c12SJohn Marino return UPNP_ARG_VALUE_INVALID;
6133ff40c12SJohn Marino }
6143ff40c12SJohn Marino if (hwaddr_aton(val, macaddr)) {
6153ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
6163ff40c12SJohn Marino "PutWLANResponse: '%s'", val);
6173ff40c12SJohn Marino #ifdef CONFIG_WPS_STRICT
6183ff40c12SJohn Marino {
6193ff40c12SJohn Marino struct wps_parse_attr attr;
6203ff40c12SJohn Marino if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
6216d49e1aeSJan Lentfer wpabuf_free(msg);
6226d49e1aeSJan Lentfer os_free(val);
6236d49e1aeSJan Lentfer return UPNP_ARG_VALUE_INVALID;
6246d49e1aeSJan Lentfer }
6253ff40c12SJohn Marino }
6263ff40c12SJohn Marino #endif /* CONFIG_WPS_STRICT */
6273ff40c12SJohn Marino if (hwaddr_aton2(val, macaddr) > 0) {
6283ff40c12SJohn Marino /*
6293ff40c12SJohn Marino * At least some versions of Intel PROset seem to be
6303ff40c12SJohn Marino * using dot-deliminated MAC address format here.
6313ff40c12SJohn Marino */
6323ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
6333ff40c12SJohn Marino "incorrect MAC address format in "
6343ff40c12SJohn Marino "NewWLANEventMAC: %s -> " MACSTR,
6353ff40c12SJohn Marino val, MAC2STR(macaddr));
6363ff40c12SJohn Marino } else {
6373ff40c12SJohn Marino wpabuf_free(msg);
6383ff40c12SJohn Marino os_free(val);
6393ff40c12SJohn Marino return UPNP_ARG_VALUE_INVALID;
6403ff40c12SJohn Marino }
6413ff40c12SJohn Marino }
6426d49e1aeSJan Lentfer os_free(val);
6436d49e1aeSJan Lentfer if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
6446d49e1aeSJan Lentfer struct wps_parse_attr attr;
6456d49e1aeSJan Lentfer if (wps_parse_msg(msg, &attr) < 0 ||
6466d49e1aeSJan Lentfer attr.msg_type == NULL)
6476d49e1aeSJan Lentfer type = -1;
6486d49e1aeSJan Lentfer else
6496d49e1aeSJan Lentfer type = *attr.msg_type;
6506d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
6516d49e1aeSJan Lentfer } else
6526d49e1aeSJan Lentfer type = -1;
6533ff40c12SJohn Marino dl_list_for_each(iface, &sm->interfaces,
6543ff40c12SJohn Marino struct upnp_wps_device_interface, list) {
6553ff40c12SJohn Marino if (iface->ctx->rx_req_put_wlan_response &&
6563ff40c12SJohn Marino iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
6573ff40c12SJohn Marino macaddr, msg, type)
6583ff40c12SJohn Marino == 0)
6593ff40c12SJohn Marino ok = 1;
6603ff40c12SJohn Marino }
6613ff40c12SJohn Marino
6623ff40c12SJohn Marino if (!ok) {
6636d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
6646d49e1aeSJan Lentfer "rx_req_put_wlan_response");
6656d49e1aeSJan Lentfer wpabuf_free(msg);
6666d49e1aeSJan Lentfer return HTTP_INTERNAL_SERVER_ERROR;
6676d49e1aeSJan Lentfer }
6686d49e1aeSJan Lentfer wpabuf_free(msg);
6696d49e1aeSJan Lentfer *replyname = NULL;
6706d49e1aeSJan Lentfer *reply = NULL;
6716d49e1aeSJan Lentfer return HTTP_OK;
6726d49e1aeSJan Lentfer }
6736d49e1aeSJan Lentfer
6746d49e1aeSJan Lentfer
find_er_addr(struct subscription * s,struct sockaddr_in * cli)6753ff40c12SJohn Marino static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
6763ff40c12SJohn Marino {
6773ff40c12SJohn Marino struct subscr_addr *a;
6783ff40c12SJohn Marino
6793ff40c12SJohn Marino dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
6803ff40c12SJohn Marino if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
6813ff40c12SJohn Marino return 1;
6823ff40c12SJohn Marino }
6833ff40c12SJohn Marino return 0;
6843ff40c12SJohn Marino }
6853ff40c12SJohn Marino
6863ff40c12SJohn Marino
find_er(struct upnp_wps_device_sm * sm,struct sockaddr_in * cli)6873ff40c12SJohn Marino static struct subscription * find_er(struct upnp_wps_device_sm *sm,
6883ff40c12SJohn Marino struct sockaddr_in *cli)
6893ff40c12SJohn Marino {
6903ff40c12SJohn Marino struct subscription *s;
6913ff40c12SJohn Marino dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
6923ff40c12SJohn Marino if (find_er_addr(s, cli))
6933ff40c12SJohn Marino return s;
6943ff40c12SJohn Marino return NULL;
6953ff40c12SJohn Marino }
6963ff40c12SJohn Marino
6973ff40c12SJohn Marino
6986d49e1aeSJan Lentfer static enum http_reply_code
web_process_set_selected_registrar(struct upnp_wps_device_sm * sm,struct sockaddr_in * cli,char * data,struct wpabuf ** reply,const char ** replyname)6993ff40c12SJohn Marino web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
7003ff40c12SJohn Marino struct sockaddr_in *cli, char *data,
7016d49e1aeSJan Lentfer struct wpabuf **reply,
7026d49e1aeSJan Lentfer const char **replyname)
7036d49e1aeSJan Lentfer {
7046d49e1aeSJan Lentfer struct wpabuf *msg;
7056d49e1aeSJan Lentfer enum http_reply_code ret;
7063ff40c12SJohn Marino struct subscription *s;
7073ff40c12SJohn Marino struct upnp_wps_device_interface *iface;
7083ff40c12SJohn Marino int err = 0;
7096d49e1aeSJan Lentfer
7106d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
7113ff40c12SJohn Marino s = find_er(sm, cli);
7123ff40c12SJohn Marino if (s == NULL) {
7133ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
7143ff40c12SJohn Marino "from unknown ER");
7153ff40c12SJohn Marino return UPNP_ACTION_FAILED;
7163ff40c12SJohn Marino }
7173ff40c12SJohn Marino msg = xml_get_base64_item(data, "NewMessage", &ret);
7186d49e1aeSJan Lentfer if (msg == NULL)
7196d49e1aeSJan Lentfer return ret;
7203ff40c12SJohn Marino dl_list_for_each(iface, &sm->interfaces,
7213ff40c12SJohn Marino struct upnp_wps_device_interface, list) {
7223ff40c12SJohn Marino if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
7233ff40c12SJohn Marino msg))
7243ff40c12SJohn Marino err = 1;
7253ff40c12SJohn Marino }
7266d49e1aeSJan Lentfer wpabuf_free(msg);
7273ff40c12SJohn Marino if (err)
7286d49e1aeSJan Lentfer return HTTP_INTERNAL_SERVER_ERROR;
7296d49e1aeSJan Lentfer *replyname = NULL;
7306d49e1aeSJan Lentfer *reply = NULL;
7316d49e1aeSJan Lentfer return HTTP_OK;
7326d49e1aeSJan Lentfer }
7336d49e1aeSJan Lentfer
7346d49e1aeSJan Lentfer
7356d49e1aeSJan Lentfer static const char *soap_prefix =
7366d49e1aeSJan Lentfer "<?xml version=\"1.0\"?>\n"
7376d49e1aeSJan Lentfer "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
7386d49e1aeSJan Lentfer "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
7396d49e1aeSJan Lentfer "<s:Body>\n";
7406d49e1aeSJan Lentfer static const char *soap_postfix =
7416d49e1aeSJan Lentfer "</s:Body>\n</s:Envelope>\n";
7426d49e1aeSJan Lentfer
7436d49e1aeSJan Lentfer static const char *soap_error_prefix =
7446d49e1aeSJan Lentfer "<s:Fault>\n"
7456d49e1aeSJan Lentfer "<faultcode>s:Client</faultcode>\n"
7466d49e1aeSJan Lentfer "<faultstring>UPnPError</faultstring>\n"
7476d49e1aeSJan Lentfer "<detail>\n"
7486d49e1aeSJan Lentfer "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
7496d49e1aeSJan Lentfer static const char *soap_error_postfix =
7506d49e1aeSJan Lentfer "<errorDescription>Error</errorDescription>\n"
7516d49e1aeSJan Lentfer "</UPnPError>\n"
7526d49e1aeSJan Lentfer "</detail>\n"
7536d49e1aeSJan Lentfer "</s:Fault>\n";
7546d49e1aeSJan Lentfer
web_connection_send_reply(struct http_request * req,enum http_reply_code ret,const char * action,int action_len,const struct wpabuf * reply,const char * replyname)7553ff40c12SJohn Marino static void web_connection_send_reply(struct http_request *req,
7566d49e1aeSJan Lentfer enum http_reply_code ret,
7576d49e1aeSJan Lentfer const char *action, int action_len,
7586d49e1aeSJan Lentfer const struct wpabuf *reply,
7596d49e1aeSJan Lentfer const char *replyname)
7606d49e1aeSJan Lentfer {
7616d49e1aeSJan Lentfer struct wpabuf *buf;
7626d49e1aeSJan Lentfer char *replydata;
7636d49e1aeSJan Lentfer char *put_length_here = NULL;
7646d49e1aeSJan Lentfer char *body_start = NULL;
7656d49e1aeSJan Lentfer
7666d49e1aeSJan Lentfer if (reply) {
7676d49e1aeSJan Lentfer size_t len;
7686d49e1aeSJan Lentfer replydata = (char *) base64_encode(wpabuf_head(reply),
7696d49e1aeSJan Lentfer wpabuf_len(reply), &len);
7706d49e1aeSJan Lentfer } else
7716d49e1aeSJan Lentfer replydata = NULL;
7726d49e1aeSJan Lentfer
7736d49e1aeSJan Lentfer /* Parameters of the response:
7746d49e1aeSJan Lentfer * action(action_len) -- action we are responding to
7756d49e1aeSJan Lentfer * replyname -- a name we need for the reply
7766d49e1aeSJan Lentfer * replydata -- NULL or null-terminated string
7776d49e1aeSJan Lentfer */
7786d49e1aeSJan Lentfer buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
7796d49e1aeSJan Lentfer (action_len > 0 ? action_len * 2 : 0));
7806d49e1aeSJan Lentfer if (buf == NULL) {
7816d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
7826d49e1aeSJan Lentfer "POST");
7836d49e1aeSJan Lentfer os_free(replydata);
7843ff40c12SJohn Marino http_request_deinit(req);
7856d49e1aeSJan Lentfer return;
7866d49e1aeSJan Lentfer }
7876d49e1aeSJan Lentfer
7886d49e1aeSJan Lentfer /*
7896d49e1aeSJan Lentfer * Assuming we will be successful, put in the output header first.
7906d49e1aeSJan Lentfer * Note: we do not keep connections alive (and httpread does
7916d49e1aeSJan Lentfer * not support it)... therefore we must have Connection: close.
7926d49e1aeSJan Lentfer */
7936d49e1aeSJan Lentfer if (ret == HTTP_OK) {
7946d49e1aeSJan Lentfer wpabuf_put_str(buf,
7956d49e1aeSJan Lentfer "HTTP/1.1 200 OK\r\n"
7966d49e1aeSJan Lentfer "Content-Type: text/xml; "
7976d49e1aeSJan Lentfer "charset=\"utf-8\"\r\n");
7986d49e1aeSJan Lentfer } else {
7996d49e1aeSJan Lentfer wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
8006d49e1aeSJan Lentfer }
8016d49e1aeSJan Lentfer wpabuf_put_str(buf, http_connection_close);
8026d49e1aeSJan Lentfer
8036d49e1aeSJan Lentfer wpabuf_put_str(buf, "Content-Length: ");
8046d49e1aeSJan Lentfer /*
8056d49e1aeSJan Lentfer * We will paste the length in later, leaving some extra whitespace.
8066d49e1aeSJan Lentfer * HTTP code is supposed to be tolerant of extra whitespace.
8076d49e1aeSJan Lentfer */
8086d49e1aeSJan Lentfer put_length_here = wpabuf_put(buf, 0);
8096d49e1aeSJan Lentfer wpabuf_put_str(buf, " \r\n");
8106d49e1aeSJan Lentfer
8116d49e1aeSJan Lentfer http_put_date(buf);
8126d49e1aeSJan Lentfer
8136d49e1aeSJan Lentfer /* terminating empty line */
8146d49e1aeSJan Lentfer wpabuf_put_str(buf, "\r\n");
8156d49e1aeSJan Lentfer
8166d49e1aeSJan Lentfer body_start = wpabuf_put(buf, 0);
8176d49e1aeSJan Lentfer
8186d49e1aeSJan Lentfer if (ret == HTTP_OK) {
8196d49e1aeSJan Lentfer wpabuf_put_str(buf, soap_prefix);
8206d49e1aeSJan Lentfer wpabuf_put_str(buf, "<u:");
8216d49e1aeSJan Lentfer wpabuf_put_data(buf, action, action_len);
8226d49e1aeSJan Lentfer wpabuf_put_str(buf, "Response xmlns:u=\"");
8236d49e1aeSJan Lentfer wpabuf_put_str(buf, urn_wfawlanconfig);
8246d49e1aeSJan Lentfer wpabuf_put_str(buf, "\">\n");
8256d49e1aeSJan Lentfer if (replydata && replyname) {
8266d49e1aeSJan Lentfer /* TODO: might possibly need to escape part of reply
8276d49e1aeSJan Lentfer * data? ...
8286d49e1aeSJan Lentfer * probably not, unlikely to have ampersand(&) or left
8296d49e1aeSJan Lentfer * angle bracket (<) in it...
8306d49e1aeSJan Lentfer */
8316d49e1aeSJan Lentfer wpabuf_printf(buf, "<%s>", replyname);
8326d49e1aeSJan Lentfer wpabuf_put_str(buf, replydata);
8336d49e1aeSJan Lentfer wpabuf_printf(buf, "</%s>\n", replyname);
8346d49e1aeSJan Lentfer }
8356d49e1aeSJan Lentfer wpabuf_put_str(buf, "</u:");
8366d49e1aeSJan Lentfer wpabuf_put_data(buf, action, action_len);
8376d49e1aeSJan Lentfer wpabuf_put_str(buf, "Response>\n");
8386d49e1aeSJan Lentfer wpabuf_put_str(buf, soap_postfix);
8396d49e1aeSJan Lentfer } else {
8406d49e1aeSJan Lentfer /* Error case */
8416d49e1aeSJan Lentfer wpabuf_put_str(buf, soap_prefix);
8426d49e1aeSJan Lentfer wpabuf_put_str(buf, soap_error_prefix);
8436d49e1aeSJan Lentfer wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
8446d49e1aeSJan Lentfer wpabuf_put_str(buf, soap_error_postfix);
8456d49e1aeSJan Lentfer wpabuf_put_str(buf, soap_postfix);
8466d49e1aeSJan Lentfer }
8476d49e1aeSJan Lentfer os_free(replydata);
8486d49e1aeSJan Lentfer
8496d49e1aeSJan Lentfer /* Now patch in the content length at the end */
8506d49e1aeSJan Lentfer if (body_start && put_length_here) {
8516d49e1aeSJan Lentfer int body_length = (char *) wpabuf_put(buf, 0) - body_start;
8526d49e1aeSJan Lentfer char len_buf[10];
8536d49e1aeSJan Lentfer os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
8546d49e1aeSJan Lentfer os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
8556d49e1aeSJan Lentfer }
8566d49e1aeSJan Lentfer
8573ff40c12SJohn Marino http_request_send_and_deinit(req, buf);
8586d49e1aeSJan Lentfer }
8596d49e1aeSJan Lentfer
8606d49e1aeSJan Lentfer
web_get_action(struct http_request * req,size_t * action_len)8613ff40c12SJohn Marino static const char * web_get_action(struct http_request *req,
8623ff40c12SJohn Marino size_t *action_len)
8636d49e1aeSJan Lentfer {
8646d49e1aeSJan Lentfer const char *match;
8656d49e1aeSJan Lentfer int match_len;
8666d49e1aeSJan Lentfer char *b;
8676d49e1aeSJan Lentfer char *action;
8686d49e1aeSJan Lentfer
8696d49e1aeSJan Lentfer *action_len = 0;
8706d49e1aeSJan Lentfer /* The SOAPAction line of the header tells us what we want to do */
8713ff40c12SJohn Marino b = http_request_get_hdr_line(req, "SOAPAction:");
8726d49e1aeSJan Lentfer if (b == NULL)
8736d49e1aeSJan Lentfer return NULL;
8746d49e1aeSJan Lentfer if (*b == '"')
8756d49e1aeSJan Lentfer b++;
8766d49e1aeSJan Lentfer else
8776d49e1aeSJan Lentfer return NULL;
8786d49e1aeSJan Lentfer match = urn_wfawlanconfig;
8796d49e1aeSJan Lentfer match_len = os_strlen(urn_wfawlanconfig) - 1;
8806d49e1aeSJan Lentfer if (os_strncasecmp(b, match, match_len))
8816d49e1aeSJan Lentfer return NULL;
8826d49e1aeSJan Lentfer b += match_len;
8836d49e1aeSJan Lentfer /* skip over version */
8846d49e1aeSJan Lentfer while (isgraph(*b) && *b != '#')
8856d49e1aeSJan Lentfer b++;
8866d49e1aeSJan Lentfer if (*b != '#')
8876d49e1aeSJan Lentfer return NULL;
8886d49e1aeSJan Lentfer b++;
8896d49e1aeSJan Lentfer /* Following the sharp(#) should be the action and a double quote */
8906d49e1aeSJan Lentfer action = b;
8916d49e1aeSJan Lentfer while (isgraph(*b) && *b != '"')
8926d49e1aeSJan Lentfer b++;
8936d49e1aeSJan Lentfer if (*b != '"')
8946d49e1aeSJan Lentfer return NULL;
8956d49e1aeSJan Lentfer *action_len = b - action;
8966d49e1aeSJan Lentfer return action;
8976d49e1aeSJan Lentfer }
8986d49e1aeSJan Lentfer
8996d49e1aeSJan Lentfer
9006d49e1aeSJan Lentfer /* Given that we have received a header w/ POST, act upon it
9016d49e1aeSJan Lentfer *
9026d49e1aeSJan Lentfer * Format of POST (case-insensitive):
9036d49e1aeSJan Lentfer *
9046d49e1aeSJan Lentfer * First line must be:
9056d49e1aeSJan Lentfer * POST /<file> HTTP/1.1
9066d49e1aeSJan Lentfer * Since we don't do anything fancy we just ignore other lines.
9076d49e1aeSJan Lentfer *
9086d49e1aeSJan Lentfer * Our response (if no error) which includes only required lines is:
9096d49e1aeSJan Lentfer * HTTP/1.1 200 OK
9106d49e1aeSJan Lentfer * Connection: close
9116d49e1aeSJan Lentfer * Content-Type: text/xml
9126d49e1aeSJan Lentfer * Date: <rfc1123-date>
9136d49e1aeSJan Lentfer *
9146d49e1aeSJan Lentfer * Header lines must end with \r\n
9156d49e1aeSJan Lentfer * Per RFC 2616, content-length: is not required but connection:close
9166d49e1aeSJan Lentfer * would appear to be required (given that we will be closing it!).
9176d49e1aeSJan Lentfer */
web_connection_parse_post(struct upnp_wps_device_sm * sm,struct sockaddr_in * cli,struct http_request * req,const char * filename)9183ff40c12SJohn Marino static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
9193ff40c12SJohn Marino struct sockaddr_in *cli,
9203ff40c12SJohn Marino struct http_request *req,
9216d49e1aeSJan Lentfer const char *filename)
9226d49e1aeSJan Lentfer {
9236d49e1aeSJan Lentfer enum http_reply_code ret;
9243ff40c12SJohn Marino char *data = http_request_get_data(req); /* body of http msg */
9253ff40c12SJohn Marino const char *action = NULL;
9263ff40c12SJohn Marino size_t action_len = 0;
9276d49e1aeSJan Lentfer const char *replyname = NULL; /* argument name for the reply */
9286d49e1aeSJan Lentfer struct wpabuf *reply = NULL; /* data for the reply */
9296d49e1aeSJan Lentfer
9303ff40c12SJohn Marino if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
9313ff40c12SJohn Marino wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
9323ff40c12SJohn Marino filename);
9333ff40c12SJohn Marino ret = HTTP_NOT_FOUND;
9343ff40c12SJohn Marino goto bad;
9353ff40c12SJohn Marino }
9363ff40c12SJohn Marino
9376d49e1aeSJan Lentfer ret = UPNP_INVALID_ACTION;
9383ff40c12SJohn Marino action = web_get_action(req, &action_len);
9396d49e1aeSJan Lentfer if (action == NULL)
9406d49e1aeSJan Lentfer goto bad;
9416d49e1aeSJan Lentfer
9426d49e1aeSJan Lentfer if (!os_strncasecmp("GetDeviceInfo", action, action_len))
9436d49e1aeSJan Lentfer ret = web_process_get_device_info(sm, &reply, &replyname);
9446d49e1aeSJan Lentfer else if (!os_strncasecmp("PutMessage", action, action_len))
9456d49e1aeSJan Lentfer ret = web_process_put_message(sm, data, &reply, &replyname);
9466d49e1aeSJan Lentfer else if (!os_strncasecmp("PutWLANResponse", action, action_len))
9476d49e1aeSJan Lentfer ret = web_process_put_wlan_response(sm, data, &reply,
9486d49e1aeSJan Lentfer &replyname);
9496d49e1aeSJan Lentfer else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
9503ff40c12SJohn Marino ret = web_process_set_selected_registrar(sm, cli, data, &reply,
9516d49e1aeSJan Lentfer &replyname);
9526d49e1aeSJan Lentfer else
9536d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
9546d49e1aeSJan Lentfer
9556d49e1aeSJan Lentfer bad:
9566d49e1aeSJan Lentfer if (ret != HTTP_OK)
9576d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
9583ff40c12SJohn Marino web_connection_send_reply(req, ret, action, action_len, reply,
9596d49e1aeSJan Lentfer replyname);
9606d49e1aeSJan Lentfer wpabuf_free(reply);
9616d49e1aeSJan Lentfer }
9626d49e1aeSJan Lentfer
9636d49e1aeSJan Lentfer
9646d49e1aeSJan Lentfer /* Given that we have received a header w/ SUBSCRIBE, act upon it
9656d49e1aeSJan Lentfer *
9666d49e1aeSJan Lentfer * Format of SUBSCRIBE (case-insensitive):
9676d49e1aeSJan Lentfer *
9686d49e1aeSJan Lentfer * First line must be:
9696d49e1aeSJan Lentfer * SUBSCRIBE /wps_event HTTP/1.1
9706d49e1aeSJan Lentfer *
9716d49e1aeSJan Lentfer * Our response (if no error) which includes only required lines is:
9726d49e1aeSJan Lentfer * HTTP/1.1 200 OK
9736d49e1aeSJan Lentfer * Server: xx, UPnP/1.0, xx
9746d49e1aeSJan Lentfer * SID: uuid:xxxxxxxxx
9756d49e1aeSJan Lentfer * Timeout: Second-<n>
9766d49e1aeSJan Lentfer * Content-Length: 0
9776d49e1aeSJan Lentfer * Date: xxxx
9786d49e1aeSJan Lentfer *
9796d49e1aeSJan Lentfer * Header lines must end with \r\n
9806d49e1aeSJan Lentfer * Per RFC 2616, content-length: is not required but connection:close
9816d49e1aeSJan Lentfer * would appear to be required (given that we will be closing it!).
9826d49e1aeSJan Lentfer */
web_connection_parse_subscribe(struct upnp_wps_device_sm * sm,struct http_request * req,const char * filename)9833ff40c12SJohn Marino static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
9843ff40c12SJohn Marino struct http_request *req,
9856d49e1aeSJan Lentfer const char *filename)
9866d49e1aeSJan Lentfer {
9876d49e1aeSJan Lentfer struct wpabuf *buf;
9886d49e1aeSJan Lentfer char *b;
9893ff40c12SJohn Marino char *hdr = http_request_get_hdr(req);
9906d49e1aeSJan Lentfer char *h;
9916d49e1aeSJan Lentfer char *match;
9926d49e1aeSJan Lentfer int match_len;
9936d49e1aeSJan Lentfer char *end;
9946d49e1aeSJan Lentfer int len;
9956d49e1aeSJan Lentfer int got_nt = 0;
9966d49e1aeSJan Lentfer u8 uuid[UUID_LEN];
9976d49e1aeSJan Lentfer int got_uuid = 0;
9986d49e1aeSJan Lentfer char *callback_urls = NULL;
9996d49e1aeSJan Lentfer struct subscription *s = NULL;
10006d49e1aeSJan Lentfer enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
10016d49e1aeSJan Lentfer
10026d49e1aeSJan Lentfer buf = wpabuf_alloc(1000);
10033ff40c12SJohn Marino if (buf == NULL) {
10043ff40c12SJohn Marino http_request_deinit(req);
10056d49e1aeSJan Lentfer return;
10063ff40c12SJohn Marino }
10073ff40c12SJohn Marino
10083ff40c12SJohn Marino wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
10093ff40c12SJohn Marino (u8 *) hdr, os_strlen(hdr));
10106d49e1aeSJan Lentfer
10116d49e1aeSJan Lentfer /* Parse/validate headers */
10126d49e1aeSJan Lentfer h = hdr;
10136d49e1aeSJan Lentfer /* First line: SUBSCRIBE /wps_event HTTP/1.1
10146d49e1aeSJan Lentfer * has already been parsed.
10156d49e1aeSJan Lentfer */
10166d49e1aeSJan Lentfer if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
10176d49e1aeSJan Lentfer ret = HTTP_PRECONDITION_FAILED;
10186d49e1aeSJan Lentfer goto error;
10196d49e1aeSJan Lentfer }
10206d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
10216d49e1aeSJan Lentfer end = os_strchr(h, '\n');
10226d49e1aeSJan Lentfer
1023*a1157835SDaniel Fojt while (end) {
10246d49e1aeSJan Lentfer /* Option line by option line */
10256d49e1aeSJan Lentfer h = end + 1;
10266d49e1aeSJan Lentfer end = os_strchr(h, '\n');
10276d49e1aeSJan Lentfer if (end == NULL)
10286d49e1aeSJan Lentfer break; /* no unterminated lines allowed */
10296d49e1aeSJan Lentfer
10306d49e1aeSJan Lentfer /* NT assures that it is our type of subscription;
10313ff40c12SJohn Marino * not used for a renewal.
10326d49e1aeSJan Lentfer **/
10336d49e1aeSJan Lentfer match = "NT:";
10346d49e1aeSJan Lentfer match_len = os_strlen(match);
10356d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) == 0) {
10366d49e1aeSJan Lentfer h += match_len;
10376d49e1aeSJan Lentfer while (*h == ' ' || *h == '\t')
10386d49e1aeSJan Lentfer h++;
10396d49e1aeSJan Lentfer match = "upnp:event";
10406d49e1aeSJan Lentfer match_len = os_strlen(match);
10416d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) != 0) {
10426d49e1aeSJan Lentfer ret = HTTP_BAD_REQUEST;
10436d49e1aeSJan Lentfer goto error;
10446d49e1aeSJan Lentfer }
10456d49e1aeSJan Lentfer got_nt = 1;
10466d49e1aeSJan Lentfer continue;
10476d49e1aeSJan Lentfer }
10486d49e1aeSJan Lentfer /* HOST should refer to us */
10496d49e1aeSJan Lentfer #if 0
10506d49e1aeSJan Lentfer match = "HOST:";
10516d49e1aeSJan Lentfer match_len = os_strlen(match);
10526d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) == 0) {
10536d49e1aeSJan Lentfer h += match_len;
10546d49e1aeSJan Lentfer while (*h == ' ' || *h == '\t')
10556d49e1aeSJan Lentfer h++;
10566d49e1aeSJan Lentfer .....
10576d49e1aeSJan Lentfer }
10586d49e1aeSJan Lentfer #endif
10596d49e1aeSJan Lentfer /* CALLBACK gives one or more URLs for NOTIFYs
10606d49e1aeSJan Lentfer * to be sent as a result of the subscription.
10616d49e1aeSJan Lentfer * Each URL is enclosed in angle brackets.
10626d49e1aeSJan Lentfer */
10636d49e1aeSJan Lentfer match = "CALLBACK:";
10646d49e1aeSJan Lentfer match_len = os_strlen(match);
10656d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) == 0) {
10666d49e1aeSJan Lentfer h += match_len;
10676d49e1aeSJan Lentfer while (*h == ' ' || *h == '\t')
10686d49e1aeSJan Lentfer h++;
10696d49e1aeSJan Lentfer len = end - h;
10706d49e1aeSJan Lentfer os_free(callback_urls);
10713ff40c12SJohn Marino callback_urls = dup_binstr(h, len);
10726d49e1aeSJan Lentfer if (callback_urls == NULL) {
10736d49e1aeSJan Lentfer ret = HTTP_INTERNAL_SERVER_ERROR;
10746d49e1aeSJan Lentfer goto error;
10756d49e1aeSJan Lentfer }
1076*a1157835SDaniel Fojt if (len > 0 && callback_urls[len - 1] == '\r')
1077*a1157835SDaniel Fojt callback_urls[len - 1] = '\0';
10786d49e1aeSJan Lentfer continue;
10796d49e1aeSJan Lentfer }
10806d49e1aeSJan Lentfer /* SID is only for renewal */
10816d49e1aeSJan Lentfer match = "SID:";
10826d49e1aeSJan Lentfer match_len = os_strlen(match);
10836d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) == 0) {
10846d49e1aeSJan Lentfer h += match_len;
10856d49e1aeSJan Lentfer while (*h == ' ' || *h == '\t')
10866d49e1aeSJan Lentfer h++;
10876d49e1aeSJan Lentfer match = "uuid:";
10886d49e1aeSJan Lentfer match_len = os_strlen(match);
10896d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) != 0) {
10906d49e1aeSJan Lentfer ret = HTTP_BAD_REQUEST;
10916d49e1aeSJan Lentfer goto error;
10926d49e1aeSJan Lentfer }
10936d49e1aeSJan Lentfer h += match_len;
10946d49e1aeSJan Lentfer while (*h == ' ' || *h == '\t')
10956d49e1aeSJan Lentfer h++;
10966d49e1aeSJan Lentfer if (uuid_str2bin(h, uuid)) {
10976d49e1aeSJan Lentfer ret = HTTP_BAD_REQUEST;
10986d49e1aeSJan Lentfer goto error;
10996d49e1aeSJan Lentfer }
11006d49e1aeSJan Lentfer got_uuid = 1;
11016d49e1aeSJan Lentfer continue;
11026d49e1aeSJan Lentfer }
11036d49e1aeSJan Lentfer /* TIMEOUT is requested timeout, but apparently we can
11046d49e1aeSJan Lentfer * just ignore this.
11056d49e1aeSJan Lentfer */
11066d49e1aeSJan Lentfer }
11076d49e1aeSJan Lentfer
11086d49e1aeSJan Lentfer if (got_uuid) {
11096d49e1aeSJan Lentfer /* renewal */
11103ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
11116d49e1aeSJan Lentfer if (callback_urls) {
11126d49e1aeSJan Lentfer ret = HTTP_BAD_REQUEST;
11136d49e1aeSJan Lentfer goto error;
11146d49e1aeSJan Lentfer }
11156d49e1aeSJan Lentfer s = subscription_renew(sm, uuid);
11166d49e1aeSJan Lentfer if (s == NULL) {
11173ff40c12SJohn Marino char str[80];
11183ff40c12SJohn Marino uuid_bin2str(uuid, str, sizeof(str));
11193ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
11203ff40c12SJohn Marino "SID %s", str);
11216d49e1aeSJan Lentfer ret = HTTP_PRECONDITION_FAILED;
11226d49e1aeSJan Lentfer goto error;
11236d49e1aeSJan Lentfer }
11246d49e1aeSJan Lentfer } else if (callback_urls) {
11253ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
11266d49e1aeSJan Lentfer if (!got_nt) {
11276d49e1aeSJan Lentfer ret = HTTP_PRECONDITION_FAILED;
11286d49e1aeSJan Lentfer goto error;
11296d49e1aeSJan Lentfer }
11306d49e1aeSJan Lentfer s = subscription_start(sm, callback_urls);
11316d49e1aeSJan Lentfer if (s == NULL) {
11326d49e1aeSJan Lentfer ret = HTTP_INTERNAL_SERVER_ERROR;
11336d49e1aeSJan Lentfer goto error;
11346d49e1aeSJan Lentfer }
11356d49e1aeSJan Lentfer } else {
11366d49e1aeSJan Lentfer ret = HTTP_PRECONDITION_FAILED;
11376d49e1aeSJan Lentfer goto error;
11386d49e1aeSJan Lentfer }
11396d49e1aeSJan Lentfer
11406d49e1aeSJan Lentfer /* success */
11416d49e1aeSJan Lentfer http_put_reply_code(buf, HTTP_OK);
11426d49e1aeSJan Lentfer wpabuf_put_str(buf, http_server_hdr);
11436d49e1aeSJan Lentfer wpabuf_put_str(buf, http_connection_close);
11446d49e1aeSJan Lentfer wpabuf_put_str(buf, "Content-Length: 0\r\n");
11456d49e1aeSJan Lentfer wpabuf_put_str(buf, "SID: uuid:");
11466d49e1aeSJan Lentfer /* subscription id */
11476d49e1aeSJan Lentfer b = wpabuf_put(buf, 0);
11486d49e1aeSJan Lentfer uuid_bin2str(s->uuid, b, 80);
11493ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
11506d49e1aeSJan Lentfer wpabuf_put(buf, os_strlen(b));
11516d49e1aeSJan Lentfer wpabuf_put_str(buf, "\r\n");
11526d49e1aeSJan Lentfer wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
11536d49e1aeSJan Lentfer http_put_date(buf);
11546d49e1aeSJan Lentfer /* And empty line to terminate header: */
11556d49e1aeSJan Lentfer wpabuf_put_str(buf, "\r\n");
11566d49e1aeSJan Lentfer
11576d49e1aeSJan Lentfer os_free(callback_urls);
11583ff40c12SJohn Marino http_request_send_and_deinit(req, buf);
11596d49e1aeSJan Lentfer return;
11606d49e1aeSJan Lentfer
11616d49e1aeSJan Lentfer error:
11626d49e1aeSJan Lentfer /* Per UPnP spec:
11636d49e1aeSJan Lentfer * Errors
11646d49e1aeSJan Lentfer * Incompatible headers
11656d49e1aeSJan Lentfer * 400 Bad Request. If SID header and one of NT or CALLBACK headers
11666d49e1aeSJan Lentfer * are present, the publisher must respond with HTTP error
11676d49e1aeSJan Lentfer * 400 Bad Request.
11686d49e1aeSJan Lentfer * Missing or invalid CALLBACK
11696d49e1aeSJan Lentfer * 412 Precondition Failed. If CALLBACK header is missing or does not
11706d49e1aeSJan Lentfer * contain a valid HTTP URL, the publisher must respond with HTTP
11716d49e1aeSJan Lentfer * error 412 Precondition Failed.
11726d49e1aeSJan Lentfer * Invalid NT
11736d49e1aeSJan Lentfer * 412 Precondition Failed. If NT header does not equal upnp:event,
11746d49e1aeSJan Lentfer * the publisher must respond with HTTP error 412 Precondition
11756d49e1aeSJan Lentfer * Failed.
11766d49e1aeSJan Lentfer * [For resubscription, use 412 if unknown uuid].
11776d49e1aeSJan Lentfer * Unable to accept subscription
11786d49e1aeSJan Lentfer * 5xx. If a publisher is not able to accept a subscription (such as
11796d49e1aeSJan Lentfer * due to insufficient resources), it must respond with a
11806d49e1aeSJan Lentfer * HTTP 500-series error code.
11816d49e1aeSJan Lentfer * 599 Too many subscriptions (not a standard HTTP error)
11826d49e1aeSJan Lentfer */
11833ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
11846d49e1aeSJan Lentfer http_put_empty(buf, ret);
11853ff40c12SJohn Marino http_request_send_and_deinit(req, buf);
11866d49e1aeSJan Lentfer os_free(callback_urls);
11876d49e1aeSJan Lentfer }
11886d49e1aeSJan Lentfer
11896d49e1aeSJan Lentfer
11906d49e1aeSJan Lentfer /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
11916d49e1aeSJan Lentfer *
11926d49e1aeSJan Lentfer * Format of UNSUBSCRIBE (case-insensitive):
11936d49e1aeSJan Lentfer *
11946d49e1aeSJan Lentfer * First line must be:
11956d49e1aeSJan Lentfer * UNSUBSCRIBE /wps_event HTTP/1.1
11966d49e1aeSJan Lentfer *
11976d49e1aeSJan Lentfer * Our response (if no error) which includes only required lines is:
11986d49e1aeSJan Lentfer * HTTP/1.1 200 OK
11996d49e1aeSJan Lentfer * Content-Length: 0
12006d49e1aeSJan Lentfer *
12016d49e1aeSJan Lentfer * Header lines must end with \r\n
12026d49e1aeSJan Lentfer * Per RFC 2616, content-length: is not required but connection:close
12036d49e1aeSJan Lentfer * would appear to be required (given that we will be closing it!).
12046d49e1aeSJan Lentfer */
web_connection_parse_unsubscribe(struct upnp_wps_device_sm * sm,struct http_request * req,const char * filename)12053ff40c12SJohn Marino static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
12063ff40c12SJohn Marino struct http_request *req,
12076d49e1aeSJan Lentfer const char *filename)
12086d49e1aeSJan Lentfer {
12096d49e1aeSJan Lentfer struct wpabuf *buf;
12103ff40c12SJohn Marino char *hdr = http_request_get_hdr(req);
12116d49e1aeSJan Lentfer char *h;
12126d49e1aeSJan Lentfer char *match;
12136d49e1aeSJan Lentfer int match_len;
12146d49e1aeSJan Lentfer char *end;
12156d49e1aeSJan Lentfer u8 uuid[UUID_LEN];
12166d49e1aeSJan Lentfer int got_uuid = 0;
12176d49e1aeSJan Lentfer struct subscription *s = NULL;
12186d49e1aeSJan Lentfer enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
12196d49e1aeSJan Lentfer
12206d49e1aeSJan Lentfer /* Parse/validate headers */
12216d49e1aeSJan Lentfer h = hdr;
12226d49e1aeSJan Lentfer /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
12236d49e1aeSJan Lentfer * has already been parsed.
12246d49e1aeSJan Lentfer */
12256d49e1aeSJan Lentfer if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
12266d49e1aeSJan Lentfer ret = HTTP_PRECONDITION_FAILED;
12276d49e1aeSJan Lentfer goto send_msg;
12286d49e1aeSJan Lentfer }
12296d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
12306d49e1aeSJan Lentfer end = os_strchr(h, '\n');
12316d49e1aeSJan Lentfer
1232*a1157835SDaniel Fojt while (end) {
12336d49e1aeSJan Lentfer /* Option line by option line */
12346d49e1aeSJan Lentfer h = end + 1;
12356d49e1aeSJan Lentfer end = os_strchr(h, '\n');
12366d49e1aeSJan Lentfer if (end == NULL)
12376d49e1aeSJan Lentfer break; /* no unterminated lines allowed */
12386d49e1aeSJan Lentfer
12396d49e1aeSJan Lentfer /* HOST should refer to us */
12406d49e1aeSJan Lentfer #if 0
12416d49e1aeSJan Lentfer match = "HOST:";
12426d49e1aeSJan Lentfer match_len = os_strlen(match);
12436d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) == 0) {
12446d49e1aeSJan Lentfer h += match_len;
12456d49e1aeSJan Lentfer while (*h == ' ' || *h == '\t')
12466d49e1aeSJan Lentfer h++;
12476d49e1aeSJan Lentfer .....
12486d49e1aeSJan Lentfer }
12496d49e1aeSJan Lentfer #endif
12506d49e1aeSJan Lentfer match = "SID:";
12516d49e1aeSJan Lentfer match_len = os_strlen(match);
12526d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) == 0) {
12536d49e1aeSJan Lentfer h += match_len;
12546d49e1aeSJan Lentfer while (*h == ' ' || *h == '\t')
12556d49e1aeSJan Lentfer h++;
12566d49e1aeSJan Lentfer match = "uuid:";
12576d49e1aeSJan Lentfer match_len = os_strlen(match);
12586d49e1aeSJan Lentfer if (os_strncasecmp(h, match, match_len) != 0) {
12596d49e1aeSJan Lentfer ret = HTTP_BAD_REQUEST;
12606d49e1aeSJan Lentfer goto send_msg;
12616d49e1aeSJan Lentfer }
12626d49e1aeSJan Lentfer h += match_len;
12636d49e1aeSJan Lentfer while (*h == ' ' || *h == '\t')
12646d49e1aeSJan Lentfer h++;
12656d49e1aeSJan Lentfer if (uuid_str2bin(h, uuid)) {
12666d49e1aeSJan Lentfer ret = HTTP_BAD_REQUEST;
12676d49e1aeSJan Lentfer goto send_msg;
12686d49e1aeSJan Lentfer }
12696d49e1aeSJan Lentfer got_uuid = 1;
12706d49e1aeSJan Lentfer continue;
12716d49e1aeSJan Lentfer }
1272*a1157835SDaniel Fojt
1273*a1157835SDaniel Fojt match = "NT:";
1274*a1157835SDaniel Fojt match_len = os_strlen(match);
1275*a1157835SDaniel Fojt if (os_strncasecmp(h, match, match_len) == 0) {
1276*a1157835SDaniel Fojt ret = HTTP_BAD_REQUEST;
1277*a1157835SDaniel Fojt goto send_msg;
1278*a1157835SDaniel Fojt }
1279*a1157835SDaniel Fojt
1280*a1157835SDaniel Fojt match = "CALLBACK:";
1281*a1157835SDaniel Fojt match_len = os_strlen(match);
1282*a1157835SDaniel Fojt if (os_strncasecmp(h, match, match_len) == 0) {
1283*a1157835SDaniel Fojt ret = HTTP_BAD_REQUEST;
1284*a1157835SDaniel Fojt goto send_msg;
1285*a1157835SDaniel Fojt }
12866d49e1aeSJan Lentfer }
12876d49e1aeSJan Lentfer
12886d49e1aeSJan Lentfer if (got_uuid) {
1289*a1157835SDaniel Fojt char str[80];
1290*a1157835SDaniel Fojt
1291*a1157835SDaniel Fojt uuid_bin2str(uuid, str, sizeof(str));
1292*a1157835SDaniel Fojt
12936d49e1aeSJan Lentfer s = subscription_find(sm, uuid);
12946d49e1aeSJan Lentfer if (s) {
12953ff40c12SJohn Marino struct subscr_addr *sa;
12963ff40c12SJohn Marino sa = dl_list_first(&s->addr_list, struct subscr_addr,
12973ff40c12SJohn Marino list);
1298*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
1299*a1157835SDaniel Fojt "WPS UPnP: Unsubscribing %p (SID %s) %s",
1300*a1157835SDaniel Fojt s, str, (sa && sa->domain_and_port) ?
13013ff40c12SJohn Marino sa->domain_and_port : "-null-");
13023ff40c12SJohn Marino dl_list_del(&s->list);
13036d49e1aeSJan Lentfer subscription_destroy(s);
1304*a1157835SDaniel Fojt } else {
1305*a1157835SDaniel Fojt wpa_printf(MSG_INFO,
1306*a1157835SDaniel Fojt "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
1307*a1157835SDaniel Fojt str);
1308*a1157835SDaniel Fojt ret = HTTP_PRECONDITION_FAILED;
1309*a1157835SDaniel Fojt goto send_msg;
13106d49e1aeSJan Lentfer }
13116d49e1aeSJan Lentfer } else {
13126d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
13136d49e1aeSJan Lentfer "found)");
13146d49e1aeSJan Lentfer ret = HTTP_PRECONDITION_FAILED;
13156d49e1aeSJan Lentfer goto send_msg;
13166d49e1aeSJan Lentfer }
13176d49e1aeSJan Lentfer
13186d49e1aeSJan Lentfer ret = HTTP_OK;
13196d49e1aeSJan Lentfer
13206d49e1aeSJan Lentfer send_msg:
13216d49e1aeSJan Lentfer buf = wpabuf_alloc(200);
13223ff40c12SJohn Marino if (buf == NULL) {
13233ff40c12SJohn Marino http_request_deinit(req);
13246d49e1aeSJan Lentfer return;
13253ff40c12SJohn Marino }
13266d49e1aeSJan Lentfer http_put_empty(buf, ret);
13273ff40c12SJohn Marino http_request_send_and_deinit(req, buf);
13286d49e1aeSJan Lentfer }
13296d49e1aeSJan Lentfer
13306d49e1aeSJan Lentfer
13316d49e1aeSJan Lentfer /* Send error in response to unknown requests */
web_connection_unimplemented(struct http_request * req)13323ff40c12SJohn Marino static void web_connection_unimplemented(struct http_request *req)
13336d49e1aeSJan Lentfer {
13346d49e1aeSJan Lentfer struct wpabuf *buf;
13356d49e1aeSJan Lentfer buf = wpabuf_alloc(200);
13363ff40c12SJohn Marino if (buf == NULL) {
13373ff40c12SJohn Marino http_request_deinit(req);
13386d49e1aeSJan Lentfer return;
13393ff40c12SJohn Marino }
13406d49e1aeSJan Lentfer http_put_empty(buf, HTTP_UNIMPLEMENTED);
13413ff40c12SJohn Marino http_request_send_and_deinit(req, buf);
13426d49e1aeSJan Lentfer }
13436d49e1aeSJan Lentfer
13446d49e1aeSJan Lentfer
13456d49e1aeSJan Lentfer
13466d49e1aeSJan Lentfer /* Called when we have gotten an apparently valid http request.
13476d49e1aeSJan Lentfer */
web_connection_check_data(void * ctx,struct http_request * req)13483ff40c12SJohn Marino static void web_connection_check_data(void *ctx, struct http_request *req)
13496d49e1aeSJan Lentfer {
13503ff40c12SJohn Marino struct upnp_wps_device_sm *sm = ctx;
13513ff40c12SJohn Marino enum httpread_hdr_type htype = http_request_get_type(req);
13523ff40c12SJohn Marino char *filename = http_request_get_uri(req);
13533ff40c12SJohn Marino struct sockaddr_in *cli = http_request_get_cli_addr(req);
13546d49e1aeSJan Lentfer
13556d49e1aeSJan Lentfer if (!filename) {
13566d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
13573ff40c12SJohn Marino http_request_deinit(req);
13586d49e1aeSJan Lentfer return;
13596d49e1aeSJan Lentfer }
13606d49e1aeSJan Lentfer /* Trim leading slashes from filename */
13616d49e1aeSJan Lentfer while (*filename == '/')
13626d49e1aeSJan Lentfer filename++;
13636d49e1aeSJan Lentfer
13646d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
13653ff40c12SJohn Marino htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
13666d49e1aeSJan Lentfer
13676d49e1aeSJan Lentfer switch (htype) {
13686d49e1aeSJan Lentfer case HTTPREAD_HDR_TYPE_GET:
13693ff40c12SJohn Marino web_connection_parse_get(sm, req, filename);
13706d49e1aeSJan Lentfer break;
13716d49e1aeSJan Lentfer case HTTPREAD_HDR_TYPE_POST:
13723ff40c12SJohn Marino web_connection_parse_post(sm, cli, req, filename);
13736d49e1aeSJan Lentfer break;
13746d49e1aeSJan Lentfer case HTTPREAD_HDR_TYPE_SUBSCRIBE:
13753ff40c12SJohn Marino web_connection_parse_subscribe(sm, req, filename);
13766d49e1aeSJan Lentfer break;
13776d49e1aeSJan Lentfer case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
13783ff40c12SJohn Marino web_connection_parse_unsubscribe(sm, req, filename);
13796d49e1aeSJan Lentfer break;
13803ff40c12SJohn Marino
13816d49e1aeSJan Lentfer /* We are not required to support M-POST; just plain
13826d49e1aeSJan Lentfer * POST is supposed to work, so we only support that.
13836d49e1aeSJan Lentfer * If for some reason we need to support M-POST, it is
13846d49e1aeSJan Lentfer * mostly the same as POST, with small differences.
13856d49e1aeSJan Lentfer */
13866d49e1aeSJan Lentfer default:
13876d49e1aeSJan Lentfer /* Send 501 for anything else */
13883ff40c12SJohn Marino web_connection_unimplemented(req);
13896d49e1aeSJan Lentfer break;
13906d49e1aeSJan Lentfer }
13916d49e1aeSJan Lentfer }
13926d49e1aeSJan Lentfer
13936d49e1aeSJan Lentfer
13946d49e1aeSJan Lentfer /*
13956d49e1aeSJan Lentfer * Listening for web connections
13966d49e1aeSJan Lentfer * We have a single TCP listening port, and hand off connections as we get
13976d49e1aeSJan Lentfer * them.
13986d49e1aeSJan Lentfer */
13996d49e1aeSJan Lentfer
web_listener_stop(struct upnp_wps_device_sm * sm)14006d49e1aeSJan Lentfer void web_listener_stop(struct upnp_wps_device_sm *sm)
14016d49e1aeSJan Lentfer {
14023ff40c12SJohn Marino http_server_deinit(sm->web_srv);
14033ff40c12SJohn Marino sm->web_srv = NULL;
14046d49e1aeSJan Lentfer }
14056d49e1aeSJan Lentfer
14066d49e1aeSJan Lentfer
web_listener_start(struct upnp_wps_device_sm * sm)14076d49e1aeSJan Lentfer int web_listener_start(struct upnp_wps_device_sm *sm)
14086d49e1aeSJan Lentfer {
14093ff40c12SJohn Marino struct in_addr addr;
14103ff40c12SJohn Marino addr.s_addr = sm->ip_addr;
14113ff40c12SJohn Marino sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
14123ff40c12SJohn Marino sm);
14133ff40c12SJohn Marino if (sm->web_srv == NULL) {
14146d49e1aeSJan Lentfer web_listener_stop(sm);
14156d49e1aeSJan Lentfer return -1;
14166d49e1aeSJan Lentfer }
14173ff40c12SJohn Marino sm->web_port = http_server_get_port(sm->web_srv);
14183ff40c12SJohn Marino
14193ff40c12SJohn Marino return 0;
14203ff40c12SJohn Marino }
1421