LCOV - code coverage report
Current view: top level - src/wps - wps_upnp_web.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1443382998 Lines: 509 555 91.7 %
Date: 2015-09-27 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /*
       2             :  * UPnP WPS Device - Web connections
       3             :  * Copyright (c) 2000-2003 Intel Corporation
       4             :  * Copyright (c) 2006-2007 Sony Corporation
       5             :  * Copyright (c) 2008-2009 Atheros Communications
       6             :  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
       7             :  *
       8             :  * See wps_upnp.c for more details on licensing and code history.
       9             :  */
      10             : 
      11             : #include "includes.h"
      12             : 
      13             : #include "common.h"
      14             : #include "base64.h"
      15             : #include "uuid.h"
      16             : #include "httpread.h"
      17             : #include "http_server.h"
      18             : #include "wps_i.h"
      19             : #include "wps_upnp.h"
      20             : #include "wps_upnp_i.h"
      21             : #include "upnp_xml.h"
      22             : 
      23             : /***************************************************************************
      24             :  * Web connections (we serve pages of info about ourselves, handle
      25             :  * requests, etc. etc.).
      26             :  **************************************************************************/
      27             : 
      28             : #define WEB_CONNECTION_TIMEOUT_SEC 30   /* Drop web connection after t.o. */
      29             : #define WEB_CONNECTION_MAX_READ 8000    /* Max we'll read for TCP request */
      30             : #define MAX_WEB_CONNECTIONS 10          /* max simultaneous web connects */
      31             : 
      32             : 
      33             : static const char *urn_wfawlanconfig =
      34             :         "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
      35             : static const char *http_server_hdr =
      36             :         "Server: unspecified, UPnP/1.0, unspecified\r\n";
      37             : static const char *http_connection_close =
      38             :         "Connection: close\r\n";
      39             : 
      40             : /*
      41             :  * "Files" that we serve via HTTP. The format of these files is given by
      42             :  * WFA WPS specifications. Extra white space has been removed to save space.
      43             :  */
      44             : 
      45             : static const char wps_scpd_xml[] =
      46             : "<?xml version=\"1.0\"?>\n"
      47             : "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
      48             : "<specVersion><major>1</major><minor>0</minor></specVersion>\n"
      49             : "<actionList>\n"
      50             : "<action>\n"
      51             : "<name>GetDeviceInfo</name>\n"
      52             : "<argumentList>\n"
      53             : "<argument>\n"
      54             : "<name>NewDeviceInfo</name>\n"
      55             : "<direction>out</direction>\n"
      56             : "<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
      57             : "</argument>\n"
      58             : "</argumentList>\n"
      59             : "</action>\n"
      60             : "<action>\n"
      61             : "<name>PutMessage</name>\n"
      62             : "<argumentList>\n"
      63             : "<argument>\n"
      64             : "<name>NewInMessage</name>\n"
      65             : "<direction>in</direction>\n"
      66             : "<relatedStateVariable>InMessage</relatedStateVariable>\n"
      67             : "</argument>\n"
      68             : "<argument>\n"
      69             : "<name>NewOutMessage</name>\n"
      70             : "<direction>out</direction>\n"
      71             : "<relatedStateVariable>OutMessage</relatedStateVariable>\n"
      72             : "</argument>\n"
      73             : "</argumentList>\n"
      74             : "</action>\n"
      75             : "<action>\n"
      76             : "<name>PutWLANResponse</name>\n"
      77             : "<argumentList>\n"
      78             : "<argument>\n"
      79             : "<name>NewMessage</name>\n"
      80             : "<direction>in</direction>\n"
      81             : "<relatedStateVariable>Message</relatedStateVariable>\n"
      82             : "</argument>\n"
      83             : "<argument>\n"
      84             : "<name>NewWLANEventType</name>\n"
      85             : "<direction>in</direction>\n"
      86             : "<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
      87             : "</argument>\n"
      88             : "<argument>\n"
      89             : "<name>NewWLANEventMAC</name>\n"
      90             : "<direction>in</direction>\n"
      91             : "<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
      92             : "</argument>\n"
      93             : "</argumentList>\n"
      94             : "</action>\n"
      95             : "<action>\n"
      96             : "<name>SetSelectedRegistrar</name>\n"
      97             : "<argumentList>\n"
      98             : "<argument>\n"
      99             : "<name>NewMessage</name>\n"
     100             : "<direction>in</direction>\n"
     101             : "<relatedStateVariable>Message</relatedStateVariable>\n"
     102             : "</argument>\n"
     103             : "</argumentList>\n"
     104             : "</action>\n"
     105             : "</actionList>\n"
     106             : "<serviceStateTable>\n"
     107             : "<stateVariable sendEvents=\"no\">\n"
     108             : "<name>Message</name>\n"
     109             : "<dataType>bin.base64</dataType>\n"
     110             : "</stateVariable>\n"
     111             : "<stateVariable sendEvents=\"no\">\n"
     112             : "<name>InMessage</name>\n"
     113             : "<dataType>bin.base64</dataType>\n"
     114             : "</stateVariable>\n"
     115             : "<stateVariable sendEvents=\"no\">\n"
     116             : "<name>OutMessage</name>\n"
     117             : "<dataType>bin.base64</dataType>\n"
     118             : "</stateVariable>\n"
     119             : "<stateVariable sendEvents=\"no\">\n"
     120             : "<name>DeviceInfo</name>\n"
     121             : "<dataType>bin.base64</dataType>\n"
     122             : "</stateVariable>\n"
     123             : "<stateVariable sendEvents=\"yes\">\n"
     124             : "<name>APStatus</name>\n"
     125             : "<dataType>ui1</dataType>\n"
     126             : "</stateVariable>\n"
     127             : "<stateVariable sendEvents=\"yes\">\n"
     128             : "<name>STAStatus</name>\n"
     129             : "<dataType>ui1</dataType>\n"
     130             : "</stateVariable>\n"
     131             : "<stateVariable sendEvents=\"yes\">\n"
     132             : "<name>WLANEvent</name>\n"
     133             : "<dataType>bin.base64</dataType>\n"
     134             : "</stateVariable>\n"
     135             : "<stateVariable sendEvents=\"no\">\n"
     136             : "<name>WLANEventType</name>\n"
     137             : "<dataType>ui1</dataType>\n"
     138             : "</stateVariable>\n"
     139             : "<stateVariable sendEvents=\"no\">\n"
     140             : "<name>WLANEventMAC</name>\n"
     141             : "<dataType>string</dataType>\n"
     142             : "</stateVariable>\n"
     143             : "<stateVariable sendEvents=\"no\">\n"
     144             : "<name>WLANResponse</name>\n"
     145             : "<dataType>bin.base64</dataType>\n"
     146             : "</stateVariable>\n"
     147             : "</serviceStateTable>\n"
     148             : "</scpd>\n"
     149             : ;
     150             : 
     151             : 
     152             : static const char *wps_device_xml_prefix =
     153             :         "<?xml version=\"1.0\"?>\n"
     154             :         "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
     155             :         "<specVersion>\n"
     156             :         "<major>1</major>\n"
     157             :         "<minor>0</minor>\n"
     158             :         "</specVersion>\n"
     159             :         "<device>\n"
     160             :         "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
     161             :         "</deviceType>\n";
     162             : 
     163             : static const char *wps_device_xml_postfix =
     164             :         "<serviceList>\n"
     165             :         "<service>\n"
     166             :         "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
     167             :         "</serviceType>\n"
     168             :         "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
     169             :         "\n"
     170             :         "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
     171             :         "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
     172             :         "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
     173             :         "</service>\n"
     174             :         "</serviceList>\n"
     175             :         "</device>\n"
     176             :         "</root>\n";
     177             : 
     178             : 
     179             : /* format_wps_device_xml -- produce content of "file" wps_device.xml
     180             :  * (UPNP_WPS_DEVICE_XML_FILE)
     181             :  */
     182          30 : static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
     183             :                                   struct upnp_wps_device_sm *sm,
     184             :                                   struct wpabuf *buf)
     185             : {
     186             :         const char *s;
     187             :         char uuid_string[80];
     188             : 
     189          30 :         wpabuf_put_str(buf, wps_device_xml_prefix);
     190             : 
     191             :         /*
     192             :          * Add required fields with default values if not configured. Add
     193             :          * optional and recommended fields only if configured.
     194             :          */
     195          30 :         s = iface->wps->friendly_name;
     196          30 :         s = ((s && *s) ? s : "WPS Access Point");
     197          30 :         xml_add_tagged_data(buf, "friendlyName", s);
     198             : 
     199          30 :         s = iface->wps->dev.manufacturer;
     200          30 :         s = ((s && *s) ? s : "");
     201          30 :         xml_add_tagged_data(buf, "manufacturer", s);
     202             : 
     203          30 :         if (iface->wps->manufacturer_url)
     204           6 :                 xml_add_tagged_data(buf, "manufacturerURL",
     205           6 :                                     iface->wps->manufacturer_url);
     206             : 
     207          30 :         if (iface->wps->model_description)
     208           6 :                 xml_add_tagged_data(buf, "modelDescription",
     209           6 :                                     iface->wps->model_description);
     210             : 
     211          30 :         s = iface->wps->dev.model_name;
     212          30 :         s = ((s && *s) ? s : "");
     213          30 :         xml_add_tagged_data(buf, "modelName", s);
     214             : 
     215          30 :         if (iface->wps->dev.model_number)
     216          30 :                 xml_add_tagged_data(buf, "modelNumber",
     217          30 :                                     iface->wps->dev.model_number);
     218             : 
     219          30 :         if (iface->wps->model_url)
     220           6 :                 xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
     221             : 
     222          30 :         if (iface->wps->dev.serial_number)
     223          30 :                 xml_add_tagged_data(buf, "serialNumber",
     224          30 :                                     iface->wps->dev.serial_number);
     225             : 
     226          30 :         uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
     227          30 :         s = uuid_string;
     228             :         /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
     229             :          * easily...
     230             :          */
     231          30 :         wpabuf_put_str(buf, "<UDN>uuid:");
     232          30 :         xml_data_encode(buf, s, os_strlen(s));
     233          30 :         wpabuf_put_str(buf, "</UDN>\n");
     234             : 
     235          30 :         if (iface->wps->upc)
     236           6 :                 xml_add_tagged_data(buf, "UPC", iface->wps->upc);
     237             : 
     238          30 :         wpabuf_put_str(buf, wps_device_xml_postfix);
     239          30 : }
     240             : 
     241             : 
     242          95 : static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
     243             : {
     244          95 :         wpabuf_put_str(buf, "HTTP/1.1 ");
     245          95 :         switch (code) {
     246             :         case HTTP_OK:
     247          60 :                 wpabuf_put_str(buf, "200 OK\r\n");
     248          60 :                 break;
     249             :         case HTTP_BAD_REQUEST:
     250           8 :                 wpabuf_put_str(buf, "400 Bad request\r\n");
     251           8 :                 break;
     252             :         case HTTP_PRECONDITION_FAILED:
     253           8 :                 wpabuf_put_str(buf, "412 Precondition failed\r\n");
     254           8 :                 break;
     255             :         case HTTP_UNIMPLEMENTED:
     256          11 :                 wpabuf_put_str(buf, "501 Unimplemented\r\n");
     257          11 :                 break;
     258             :         case HTTP_INTERNAL_SERVER_ERROR:
     259             :         default:
     260           8 :                 wpabuf_put_str(buf, "500 Internal server error\r\n");
     261           8 :                 break;
     262             :         }
     263          95 : }
     264             : 
     265             : 
     266         244 : static void http_put_date(struct wpabuf *buf)
     267             : {
     268         244 :         wpabuf_put_str(buf, "Date: ");
     269         244 :         format_date(buf);
     270         244 :         wpabuf_put_str(buf, "\r\n");
     271         244 : }
     272             : 
     273             : 
     274          56 : static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
     275             : {
     276          56 :         http_put_reply_code(buf, code);
     277          56 :         wpabuf_put_str(buf, http_server_hdr);
     278          56 :         wpabuf_put_str(buf, http_connection_close);
     279          56 :         wpabuf_put_str(buf, "Content-Length: 0\r\n"
     280             :                        "\r\n");
     281          56 : }
     282             : 
     283             : 
     284             : /* Given that we have received a header w/ GET, act upon it
     285             :  *
     286             :  * Format of GET (case-insensitive):
     287             :  *
     288             :  * First line must be:
     289             :  *      GET /<file> HTTP/1.1
     290             :  * Since we don't do anything fancy we just ignore other lines.
     291             :  *
     292             :  * Our response (if no error) which includes only required lines is:
     293             :  * HTTP/1.1 200 OK
     294             :  * Connection: close
     295             :  * Content-Type: text/xml
     296             :  * Date: <rfc1123-date>
     297             :  *
     298             :  * Header lines must end with \r\n
     299             :  * Per RFC 2616, content-length: is not required but connection:close
     300             :  * would appear to be required (given that we will be closing it!).
     301             :  */
     302          32 : static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
     303             :                                      struct http_request *hreq, char *filename)
     304             : {
     305             :         struct wpabuf *buf; /* output buffer, allocated */
     306             :         char *put_length_here;
     307             :         char *body_start;
     308             :         enum {
     309             :                 GET_DEVICE_XML_FILE,
     310             :                 GET_SCPD_XML_FILE
     311             :         } req;
     312          32 :         size_t extra_len = 0;
     313             :         int body_length;
     314             :         char len_buf[10];
     315             :         struct upnp_wps_device_interface *iface;
     316             : 
     317          32 :         iface = dl_list_first(&sm->interfaces,
     318             :                               struct upnp_wps_device_interface, list);
     319          32 :         if (iface == NULL) {
     320           0 :                 http_request_deinit(hreq);
     321           0 :                 return;
     322             :         }
     323             : 
     324             :         /*
     325             :          * It is not required that filenames be case insensitive but it is
     326             :          * allowed and cannot hurt here.
     327             :          */
     328          32 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
     329          30 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
     330          30 :                 req = GET_DEVICE_XML_FILE;
     331          30 :                 extra_len = 3000;
     332          30 :                 if (iface->wps->friendly_name)
     333           9 :                         extra_len += os_strlen(iface->wps->friendly_name);
     334          30 :                 if (iface->wps->manufacturer_url)
     335           6 :                         extra_len += os_strlen(iface->wps->manufacturer_url);
     336          30 :                 if (iface->wps->model_description)
     337           6 :                         extra_len += os_strlen(iface->wps->model_description);
     338          30 :                 if (iface->wps->model_url)
     339           6 :                         extra_len += os_strlen(iface->wps->model_url);
     340          30 :                 if (iface->wps->upc)
     341           6 :                         extra_len += os_strlen(iface->wps->upc);
     342           2 :         } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
     343           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
     344           1 :                 req = GET_SCPD_XML_FILE;
     345           1 :                 extra_len = os_strlen(wps_scpd_xml);
     346             :         } else {
     347             :                 /* File not found */
     348           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
     349             :                            filename);
     350           1 :                 buf = wpabuf_alloc(200);
     351           1 :                 if (buf == NULL) {
     352           0 :                         http_request_deinit(hreq);
     353           0 :                         return;
     354             :                 }
     355           1 :                 wpabuf_put_str(buf,
     356             :                                "HTTP/1.1 404 Not Found\r\n"
     357             :                                "Connection: close\r\n");
     358             : 
     359           1 :                 http_put_date(buf);
     360             : 
     361             :                 /* terminating empty line */
     362           1 :                 wpabuf_put_str(buf, "\r\n");
     363             : 
     364           1 :                 goto send_buf;
     365             :         }
     366             : 
     367          31 :         buf = wpabuf_alloc(1000 + extra_len);
     368          31 :         if (buf == NULL) {
     369           0 :                 http_request_deinit(hreq);
     370           0 :                 return;
     371             :         }
     372             : 
     373          31 :         wpabuf_put_str(buf,
     374             :                        "HTTP/1.1 200 OK\r\n"
     375             :                        "Content-Type: text/xml; charset=\"utf-8\"\r\n");
     376          31 :         wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
     377          31 :         wpabuf_put_str(buf, "Connection: close\r\n");
     378          31 :         wpabuf_put_str(buf, "Content-Length: ");
     379             :         /*
     380             :          * We will paste the length in later, leaving some extra whitespace.
     381             :          * HTTP code is supposed to be tolerant of extra whitespace.
     382             :          */
     383          31 :         put_length_here = wpabuf_put(buf, 0);
     384          31 :         wpabuf_put_str(buf, "        \r\n");
     385             : 
     386          31 :         http_put_date(buf);
     387             : 
     388             :         /* terminating empty line */
     389          31 :         wpabuf_put_str(buf, "\r\n");
     390             : 
     391          31 :         body_start = wpabuf_put(buf, 0);
     392             : 
     393          31 :         switch (req) {
     394             :         case GET_DEVICE_XML_FILE:
     395          30 :                 format_wps_device_xml(iface, sm, buf);
     396          30 :                 break;
     397             :         case GET_SCPD_XML_FILE:
     398           1 :                 wpabuf_put_str(buf, wps_scpd_xml);
     399           1 :                 break;
     400             :         }
     401             : 
     402             :         /* Now patch in the content length at the end */
     403          31 :         body_length = (char *) wpabuf_put(buf, 0) - body_start;
     404          31 :         os_snprintf(len_buf, 10, "%d", body_length);
     405          31 :         os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
     406             : 
     407             : send_buf:
     408          32 :         http_request_send_and_deinit(hreq, buf);
     409             : }
     410             : 
     411             : 
     412             : static enum http_reply_code
     413          41 : web_process_get_device_info(struct upnp_wps_device_sm *sm,
     414             :                             struct wpabuf **reply, const char **replyname)
     415             : {
     416             :         static const char *name = "NewDeviceInfo";
     417             :         struct wps_config cfg;
     418             :         struct upnp_wps_device_interface *iface;
     419             :         struct upnp_wps_peer *peer;
     420             : 
     421          41 :         iface = dl_list_first(&sm->interfaces,
     422             :                               struct upnp_wps_device_interface, list);
     423             : 
     424          41 :         wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
     425             : 
     426          41 :         if (!iface || iface->ctx->ap_pin == NULL)
     427           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     428             : 
     429          41 :         peer = &iface->peer;
     430             : 
     431             :         /*
     432             :          * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
     433             :          * registration over UPnP with the AP acting as an Enrollee. It should
     434             :          * be noted that this is frequently used just to get the device data,
     435             :          * i.e., there may not be any intent to actually complete the
     436             :          * registration.
     437             :          */
     438             : 
     439          41 :         if (peer->wps)
     440          17 :                 wps_deinit(peer->wps);
     441             : 
     442          41 :         os_memset(&cfg, 0, sizeof(cfg));
     443          41 :         cfg.wps = iface->wps;
     444          41 :         cfg.pin = (u8 *) iface->ctx->ap_pin;
     445          41 :         cfg.pin_len = os_strlen(iface->ctx->ap_pin);
     446          41 :         peer->wps = wps_init(&cfg);
     447          41 :         if (peer->wps) {
     448             :                 enum wsc_op_code op_code;
     449          41 :                 *reply = wps_get_msg(peer->wps, &op_code);
     450          41 :                 if (*reply == NULL) {
     451           0 :                         wps_deinit(peer->wps);
     452           0 :                         peer->wps = NULL;
     453             :                 }
     454             :         } else
     455           0 :                 *reply = NULL;
     456          41 :         if (*reply == NULL) {
     457           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
     458           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     459             :         }
     460          41 :         *replyname = name;
     461          41 :         return HTTP_OK;
     462             : }
     463             : 
     464             : 
     465             : static enum http_reply_code
     466          43 : web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
     467             :                         struct wpabuf **reply, const char **replyname)
     468             : {
     469             :         struct wpabuf *msg;
     470             :         static const char *name = "NewOutMessage";
     471             :         enum http_reply_code ret;
     472             :         enum wps_process_res res;
     473             :         enum wsc_op_code op_code;
     474             :         struct upnp_wps_device_interface *iface;
     475             : 
     476          43 :         iface = dl_list_first(&sm->interfaces,
     477             :                               struct upnp_wps_device_interface, list);
     478          43 :         if (!iface)
     479           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     480             : 
     481             :         /*
     482             :          * PutMessage is used by external UPnP-based Registrar to perform WPS
     483             :          * operation with the access point itself; as compared with
     484             :          * PutWLANResponse which is for proxying.
     485             :          */
     486          43 :         wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
     487          43 :         msg = xml_get_base64_item(data, "NewInMessage", &ret);
     488          43 :         if (msg == NULL)
     489           1 :                 return ret;
     490          42 :         res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
     491          42 :         if (res == WPS_FAILURE)
     492           9 :                 *reply = NULL;
     493             :         else
     494          33 :                 *reply = wps_get_msg(iface->peer.wps, &op_code);
     495          42 :         wpabuf_free(msg);
     496          42 :         if (*reply == NULL)
     497           9 :                 return HTTP_INTERNAL_SERVER_ERROR;
     498          33 :         *replyname = name;
     499          33 :         return HTTP_OK;
     500             : }
     501             : 
     502             : 
     503             : static enum http_reply_code
     504          41 : web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
     505             :                               struct wpabuf **reply, const char **replyname)
     506             : {
     507             :         struct wpabuf *msg;
     508             :         enum http_reply_code ret;
     509             :         u8 macaddr[ETH_ALEN];
     510             :         int ev_type;
     511             :         int type;
     512             :         char *val;
     513             :         struct upnp_wps_device_interface *iface;
     514          41 :         int ok = 0;
     515             : 
     516             :         /*
     517             :          * External UPnP-based Registrar is passing us a message to be proxied
     518             :          * over to a Wi-Fi -based client of ours.
     519             :          */
     520             : 
     521          41 :         wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
     522          41 :         msg = xml_get_base64_item(data, "NewMessage", &ret);
     523          41 :         if (msg == NULL) {
     524           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
     525             :                            "from PutWLANResponse");
     526           1 :                 return ret;
     527             :         }
     528          40 :         val = xml_get_first_item(data, "NewWLANEventType");
     529          40 :         if (val == NULL) {
     530           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
     531             :                            "PutWLANResponse");
     532           0 :                 wpabuf_free(msg);
     533           0 :                 return UPNP_ARG_VALUE_INVALID;
     534             :         }
     535          40 :         ev_type = atol(val);
     536          40 :         os_free(val);
     537          40 :         val = xml_get_first_item(data, "NewWLANEventMAC");
     538          40 :         if (val == NULL) {
     539           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
     540             :                            "PutWLANResponse");
     541           0 :                 wpabuf_free(msg);
     542           0 :                 return UPNP_ARG_VALUE_INVALID;
     543             :         }
     544          40 :         if (hwaddr_aton(val, macaddr)) {
     545           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
     546             :                            "PutWLANResponse: '%s'", val);
     547             : #ifdef CONFIG_WPS_STRICT
     548             :                 {
     549             :                         struct wps_parse_attr attr;
     550             :                         if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
     551             :                                 wpabuf_free(msg);
     552             :                                 os_free(val);
     553             :                                 return UPNP_ARG_VALUE_INVALID;
     554             :                         }
     555             :                 }
     556             : #endif /* CONFIG_WPS_STRICT */
     557           0 :                 if (hwaddr_aton2(val, macaddr) > 0) {
     558             :                         /*
     559             :                          * At least some versions of Intel PROset seem to be
     560             :                          * using dot-deliminated MAC address format here.
     561             :                          */
     562           0 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
     563             :                                    "incorrect MAC address format in "
     564             :                                    "NewWLANEventMAC: %s -> " MACSTR,
     565           0 :                                    val, MAC2STR(macaddr));
     566             :                 } else {
     567           0 :                         wpabuf_free(msg);
     568           0 :                         os_free(val);
     569           0 :                         return UPNP_ARG_VALUE_INVALID;
     570             :                 }
     571             :         }
     572          40 :         os_free(val);
     573          40 :         if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
     574             :                 struct wps_parse_attr attr;
     575          80 :                 if (wps_parse_msg(msg, &attr) < 0 ||
     576          40 :                     attr.msg_type == NULL)
     577           0 :                         type = -1;
     578             :                 else
     579          40 :                         type = *attr.msg_type;
     580          40 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
     581             :         } else
     582           0 :                 type = -1;
     583          80 :         dl_list_for_each(iface, &sm->interfaces,
     584             :                          struct upnp_wps_device_interface, list) {
     585          80 :                 if (iface->ctx->rx_req_put_wlan_response &&
     586          40 :                     iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
     587             :                                                          macaddr, msg, type)
     588             :                     == 0)
     589          40 :                         ok = 1;
     590             :         }
     591             : 
     592          40 :         if (!ok) {
     593           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
     594             :                            "rx_req_put_wlan_response");
     595           0 :                 wpabuf_free(msg);
     596           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     597             :         }
     598          40 :         wpabuf_free(msg);
     599          40 :         *replyname = NULL;
     600          40 :         *reply = NULL;
     601          40 :         return HTTP_OK;
     602             : }
     603             : 
     604             : 
     605          36 : static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
     606             : {
     607             :         struct subscr_addr *a;
     608             : 
     609          36 :         dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
     610          36 :                 if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
     611          36 :                         return 1;
     612             :         }
     613           0 :         return 0;
     614             : }
     615             : 
     616             : 
     617          37 : static struct subscription * find_er(struct upnp_wps_device_sm *sm,
     618             :                                      struct sockaddr_in *cli)
     619             : {
     620             :         struct subscription *s;
     621          37 :         dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
     622          36 :                 if (find_er_addr(s, cli))
     623          36 :                         return s;
     624           1 :         return NULL;
     625             : }
     626             : 
     627             : 
     628             : static enum http_reply_code
     629          37 : web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
     630             :                                    struct sockaddr_in *cli, char *data,
     631             :                                    struct wpabuf **reply,
     632             :                                    const char **replyname)
     633             : {
     634             :         struct wpabuf *msg;
     635             :         enum http_reply_code ret;
     636             :         struct subscription *s;
     637             :         struct upnp_wps_device_interface *iface;
     638          37 :         int err = 0;
     639             : 
     640          37 :         wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
     641          37 :         s = find_er(sm, cli);
     642          37 :         if (s == NULL) {
     643           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
     644             :                            "from unknown ER");
     645           1 :                 return UPNP_ACTION_FAILED;
     646             :         }
     647          36 :         msg = xml_get_base64_item(data, "NewMessage", &ret);
     648          36 :         if (msg == NULL)
     649           0 :                 return ret;
     650          72 :         dl_list_for_each(iface, &sm->interfaces,
     651             :                          struct upnp_wps_device_interface, list) {
     652          36 :                 if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
     653             :                                                    msg))
     654           1 :                         err = 1;
     655             :         }
     656          36 :         wpabuf_free(msg);
     657          36 :         if (err)
     658           1 :                 return HTTP_INTERNAL_SERVER_ERROR;
     659          35 :         *replyname = NULL;
     660          35 :         *reply = NULL;
     661          35 :         return HTTP_OK;
     662             : }
     663             : 
     664             : 
     665             : static const char *soap_prefix =
     666             :         "<?xml version=\"1.0\"?>\n"
     667             :         "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
     668             :         "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
     669             :         "<s:Body>\n";
     670             : static const char *soap_postfix =
     671             :         "</s:Body>\n</s:Envelope>\n";
     672             : 
     673             : static const char *soap_error_prefix =
     674             :         "<s:Fault>\n"
     675             :         "<faultcode>s:Client</faultcode>\n"
     676             :         "<faultstring>UPnPError</faultstring>\n"
     677             :         "<detail>\n"
     678             :         "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
     679             : static const char *soap_error_postfix =
     680             :         "<errorDescription>Error</errorDescription>\n"
     681             :         "</UPnPError>\n"
     682             :         "</detail>\n"
     683             :         "</s:Fault>\n";
     684             : 
     685         173 : static void web_connection_send_reply(struct http_request *req,
     686             :                                       enum http_reply_code ret,
     687             :                                       const char *action, int action_len,
     688             :                                       const struct wpabuf *reply,
     689             :                                       const char *replyname)
     690             : {
     691             :         struct wpabuf *buf;
     692             :         char *replydata;
     693         173 :         char *put_length_here = NULL;
     694         173 :         char *body_start = NULL;
     695             : 
     696         173 :         if (reply) {
     697             :                 size_t len;
     698          74 :                 replydata = (char *) base64_encode(wpabuf_head(reply),
     699             :                                                    wpabuf_len(reply), &len);
     700             :         } else
     701          99 :                 replydata = NULL;
     702             : 
     703             :         /* Parameters of the response:
     704             :          *      action(action_len) -- action we are responding to
     705             :          *      replyname -- a name we need for the reply
     706             :          *      replydata -- NULL or null-terminated string
     707             :          */
     708         336 :         buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
     709         163 :                            (action_len > 0 ? action_len * 2 : 0));
     710         173 :         if (buf == NULL) {
     711           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
     712             :                            "POST");
     713           0 :                 os_free(replydata);
     714           0 :                 http_request_deinit(req);
     715         173 :                 return;
     716             :         }
     717             : 
     718             :         /*
     719             :          * Assuming we will be successful, put in the output header first.
     720             :          * Note: we do not keep connections alive (and httpread does
     721             :          * not support it)... therefore we must have Connection: close.
     722             :          */
     723         173 :         if (ret == HTTP_OK) {
     724         149 :                 wpabuf_put_str(buf,
     725             :                                "HTTP/1.1 200 OK\r\n"
     726             :                                "Content-Type: text/xml; "
     727             :                                "charset=\"utf-8\"\r\n");
     728             :         } else {
     729          24 :                 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
     730             :         }
     731         173 :         wpabuf_put_str(buf, http_connection_close);
     732             : 
     733         173 :         wpabuf_put_str(buf, "Content-Length: ");
     734             :         /*
     735             :          * We will paste the length in later, leaving some extra whitespace.
     736             :          * HTTP code is supposed to be tolerant of extra whitespace.
     737             :          */
     738         173 :         put_length_here = wpabuf_put(buf, 0);
     739         173 :         wpabuf_put_str(buf, "        \r\n");
     740             : 
     741         173 :         http_put_date(buf);
     742             : 
     743             :         /* terminating empty line */
     744         173 :         wpabuf_put_str(buf, "\r\n");
     745             : 
     746         173 :         body_start = wpabuf_put(buf, 0);
     747             : 
     748         173 :         if (ret == HTTP_OK) {
     749         149 :                 wpabuf_put_str(buf, soap_prefix);
     750         149 :                 wpabuf_put_str(buf, "<u:");
     751         149 :                 wpabuf_put_data(buf, action, action_len);
     752         149 :                 wpabuf_put_str(buf, "Response xmlns:u=\"");
     753         149 :                 wpabuf_put_str(buf, urn_wfawlanconfig);
     754         149 :                 wpabuf_put_str(buf, "\">\n");
     755         149 :                 if (replydata && replyname) {
     756             :                         /* TODO: might possibly need to escape part of reply
     757             :                          * data? ...
     758             :                          * probably not, unlikely to have ampersand(&) or left
     759             :                          * angle bracket (<) in it...
     760             :                          */
     761          74 :                         wpabuf_printf(buf, "<%s>", replyname);
     762          74 :                         wpabuf_put_str(buf, replydata);
     763          74 :                         wpabuf_printf(buf, "</%s>\n", replyname);
     764             :                 }
     765         149 :                 wpabuf_put_str(buf, "</u:");
     766         149 :                 wpabuf_put_data(buf, action, action_len);
     767         149 :                 wpabuf_put_str(buf, "Response>\n");
     768         149 :                 wpabuf_put_str(buf, soap_postfix);
     769             :         } else {
     770             :                 /* Error case */
     771          24 :                 wpabuf_put_str(buf, soap_prefix);
     772          24 :                 wpabuf_put_str(buf, soap_error_prefix);
     773          24 :                 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
     774          24 :                 wpabuf_put_str(buf, soap_error_postfix);
     775          24 :                 wpabuf_put_str(buf, soap_postfix);
     776             :         }
     777         173 :         os_free(replydata);
     778             : 
     779             :         /* Now patch in the content length at the end */
     780         173 :         if (body_start && put_length_here) {
     781         173 :                 int body_length = (char *) wpabuf_put(buf, 0) - body_start;
     782             :                 char len_buf[10];
     783         173 :                 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
     784         173 :                 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
     785             :         }
     786             : 
     787         173 :         http_request_send_and_deinit(req, buf);
     788             : }
     789             : 
     790             : 
     791         169 : static const char * web_get_action(struct http_request *req,
     792             :                                    size_t *action_len)
     793             : {
     794             :         const char *match;
     795             :         int match_len;
     796             :         char *b;
     797             :         char *action;
     798             : 
     799         169 :         *action_len = 0;
     800             :         /* The SOAPAction line of the header tells us what we want to do */
     801         169 :         b = http_request_get_hdr_line(req, "SOAPAction:");
     802         169 :         if (b == NULL)
     803           1 :                 return NULL;
     804         168 :         if (*b == '"')
     805         166 :                 b++;
     806             :         else
     807           2 :                 return NULL;
     808         166 :         match = urn_wfawlanconfig;
     809         166 :         match_len = os_strlen(urn_wfawlanconfig) - 1;
     810         166 :         if (os_strncasecmp(b, match, match_len))
     811           1 :                 return NULL;
     812         165 :         b += match_len;
     813             :         /* skip over version */
     814         498 :         while (isgraph(*b) && *b != '#')
     815         168 :                 b++;
     816         165 :         if (*b != '#')
     817           1 :                 return NULL;
     818         164 :         b++;
     819             :         /* Following the sharp(#) should be the action and a double quote */
     820         164 :         action = b;
     821        2662 :         while (isgraph(*b) && *b != '"')
     822        2334 :                 b++;
     823         164 :         if (*b != '"')
     824           1 :                 return NULL;
     825         163 :         *action_len = b - action;
     826         163 :         return action;
     827             : }
     828             : 
     829             : 
     830             : /* Given that we have received a header w/ POST, act upon it
     831             :  *
     832             :  * Format of POST (case-insensitive):
     833             :  *
     834             :  * First line must be:
     835             :  *      POST /<file> HTTP/1.1
     836             :  * Since we don't do anything fancy we just ignore other lines.
     837             :  *
     838             :  * Our response (if no error) which includes only required lines is:
     839             :  * HTTP/1.1 200 OK
     840             :  * Connection: close
     841             :  * Content-Type: text/xml
     842             :  * Date: <rfc1123-date>
     843             :  *
     844             :  * Header lines must end with \r\n
     845             :  * Per RFC 2616, content-length: is not required but connection:close
     846             :  * would appear to be required (given that we will be closing it!).
     847             :  */
     848         173 : static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
     849             :                                       struct sockaddr_in *cli,
     850             :                                       struct http_request *req,
     851             :                                       const char *filename)
     852             : {
     853             :         enum http_reply_code ret;
     854         173 :         char *data = http_request_get_data(req); /* body of http msg */
     855         173 :         const char *action = NULL;
     856         173 :         size_t action_len = 0;
     857         173 :         const char *replyname = NULL; /* argument name for the reply */
     858         173 :         struct wpabuf *reply = NULL; /* data for the reply */
     859             : 
     860         173 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
     861           4 :                 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
     862             :                            filename);
     863           4 :                 ret = HTTP_NOT_FOUND;
     864           4 :                 goto bad;
     865             :         }
     866             : 
     867         169 :         ret = UPNP_INVALID_ACTION;
     868         169 :         action = web_get_action(req, &action_len);
     869         169 :         if (action == NULL)
     870           6 :                 goto bad;
     871             : 
     872         163 :         if (!os_strncasecmp("GetDeviceInfo", action, action_len))
     873          41 :                 ret = web_process_get_device_info(sm, &reply, &replyname);
     874         122 :         else if (!os_strncasecmp("PutMessage", action, action_len))
     875          43 :                 ret = web_process_put_message(sm, data, &reply, &replyname);
     876          79 :         else if (!os_strncasecmp("PutWLANResponse", action, action_len))
     877          41 :                 ret = web_process_put_wlan_response(sm, data, &reply,
     878             :                                                     &replyname);
     879          38 :         else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
     880          37 :                 ret = web_process_set_selected_registrar(sm, cli, data, &reply,
     881             :                                                          &replyname);
     882             :         else
     883           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
     884             : 
     885             : bad:
     886         173 :         if (ret != HTTP_OK)
     887          24 :                 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
     888         173 :         web_connection_send_reply(req, ret, action, action_len, reply,
     889             :                                   replyname);
     890         173 :         wpabuf_free(reply);
     891         173 : }
     892             : 
     893             : 
     894             : /* Given that we have received a header w/ SUBSCRIBE, act upon it
     895             :  *
     896             :  * Format of SUBSCRIBE (case-insensitive):
     897             :  *
     898             :  * First line must be:
     899             :  *      SUBSCRIBE /wps_event HTTP/1.1
     900             :  *
     901             :  * Our response (if no error) which includes only required lines is:
     902             :  * HTTP/1.1 200 OK
     903             :  * Server: xx, UPnP/1.0, xx
     904             :  * SID: uuid:xxxxxxxxx
     905             :  * Timeout: Second-<n>
     906             :  * Content-Length: 0
     907             :  * Date: xxxx
     908             :  *
     909             :  * Header lines must end with \r\n
     910             :  * Per RFC 2616, content-length: is not required but connection:close
     911             :  * would appear to be required (given that we will be closing it!).
     912             :  */
     913          55 : static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
     914             :                                            struct http_request *req,
     915             :                                            const char *filename)
     916             : {
     917             :         struct wpabuf *buf;
     918             :         char *b;
     919          55 :         char *hdr = http_request_get_hdr(req);
     920             :         char *h;
     921             :         char *match;
     922             :         int match_len;
     923             :         char *end;
     924             :         int len;
     925          55 :         int got_nt = 0;
     926             :         u8 uuid[UUID_LEN];
     927          55 :         int got_uuid = 0;
     928          55 :         char *callback_urls = NULL;
     929          55 :         struct subscription *s = NULL;
     930          55 :         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
     931             : 
     932          55 :         buf = wpabuf_alloc(1000);
     933          55 :         if (buf == NULL) {
     934           0 :                 http_request_deinit(req);
     935           0 :                 return;
     936             :         }
     937             : 
     938          55 :         wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
     939             :                           (u8 *) hdr, os_strlen(hdr));
     940             : 
     941             :         /* Parse/validate headers */
     942          55 :         h = hdr;
     943             :         /* First line: SUBSCRIBE /wps_event HTTP/1.1
     944             :          * has already been parsed.
     945             :          */
     946          55 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
     947           1 :                 ret = HTTP_PRECONDITION_FAILED;
     948           1 :                 goto error;
     949             :         }
     950          54 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
     951          54 :         end = os_strchr(h, '\n');
     952             : 
     953         432 :         while (end) {
     954             :                 /* Option line by option line */
     955         378 :                 h = end + 1;
     956         378 :                 end = os_strchr(h, '\n');
     957         378 :                 if (end == NULL)
     958          51 :                         break; /* no unterminated lines allowed */
     959             : 
     960             :                 /* NT assures that it is our type of subscription;
     961             :                  * not used for a renewal.
     962             :                  **/
     963         327 :                 match = "NT:";
     964         327 :                 match_len = os_strlen(match);
     965         327 :                 if (os_strncasecmp(h, match, match_len) == 0) {
     966          53 :                         h += match_len;
     967         159 :                         while (*h == ' ' || *h == '\t')
     968          53 :                                 h++;
     969          53 :                         match = "upnp:event";
     970          53 :                         match_len = os_strlen(match);
     971          53 :                         if (os_strncasecmp(h, match, match_len) != 0) {
     972           1 :                                 ret = HTTP_BAD_REQUEST;
     973           1 :                                 goto error;
     974             :                         }
     975          52 :                         got_nt = 1;
     976          52 :                         continue;
     977             :                 }
     978             :                 /* HOST should refer to us */
     979             : #if 0
     980             :                 match = "HOST:";
     981             :                 match_len = os_strlen(match);
     982             :                 if (os_strncasecmp(h, match, match_len) == 0) {
     983             :                         h += match_len;
     984             :                         while (*h == ' ' || *h == '\t')
     985             :                                 h++;
     986             :                         .....
     987             :                 }
     988             : #endif
     989             :                 /* CALLBACK gives one or more URLs for NOTIFYs
     990             :                  * to be sent as a result of the subscription.
     991             :                  * Each URL is enclosed in angle brackets.
     992             :                  */
     993         274 :                 match = "CALLBACK:";
     994         274 :                 match_len = os_strlen(match);
     995         274 :                 if (os_strncasecmp(h, match, match_len) == 0) {
     996          48 :                         h += match_len;
     997         145 :                         while (*h == ' ' || *h == '\t')
     998          49 :                                 h++;
     999          48 :                         len = end - h;
    1000          48 :                         os_free(callback_urls);
    1001          48 :                         callback_urls = dup_binstr(h, len);
    1002          48 :                         if (callback_urls == NULL) {
    1003           0 :                                 ret = HTTP_INTERNAL_SERVER_ERROR;
    1004           0 :                                 goto error;
    1005             :                         }
    1006          48 :                         if (len > 0 && callback_urls[len - 1] == '\r')
    1007          48 :                                 callback_urls[len - 1] = '\0';
    1008          48 :                         continue;
    1009             :                 }
    1010             :                 /* SID is only for renewal */
    1011         226 :                 match = "SID:";
    1012         226 :                 match_len = os_strlen(match);
    1013         226 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1014           6 :                         h += match_len;
    1015          18 :                         while (*h == ' ' || *h == '\t')
    1016           6 :                                 h++;
    1017           6 :                         match = "uuid:";
    1018           6 :                         match_len = os_strlen(match);
    1019           6 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1020           1 :                                 ret = HTTP_BAD_REQUEST;
    1021           1 :                                 goto error;
    1022             :                         }
    1023           5 :                         h += match_len;
    1024          14 :                         while (*h == ' ' || *h == '\t')
    1025           4 :                                 h++;
    1026           5 :                         if (uuid_str2bin(h, uuid)) {
    1027           1 :                                 ret = HTTP_BAD_REQUEST;
    1028           1 :                                 goto error;
    1029             :                         }
    1030           4 :                         got_uuid = 1;
    1031           4 :                         continue;
    1032             :                 }
    1033             :                 /* TIMEOUT is requested timeout, but apparently we can
    1034             :                  * just ignore this.
    1035             :                  */
    1036             :         }
    1037             : 
    1038          51 :         if (got_uuid) {
    1039             :                 /* renewal */
    1040           4 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
    1041           4 :                 if (callback_urls) {
    1042           1 :                         ret = HTTP_BAD_REQUEST;
    1043           1 :                         goto error;
    1044             :                 }
    1045           3 :                 s = subscription_renew(sm, uuid);
    1046           3 :                 if (s == NULL) {
    1047             :                         char str[80];
    1048           1 :                         uuid_bin2str(uuid, str, sizeof(str));
    1049           1 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
    1050             :                                    "SID %s", str);
    1051           1 :                         ret = HTTP_PRECONDITION_FAILED;
    1052           1 :                         goto error;
    1053             :                 }
    1054          47 :         } else if (callback_urls) {
    1055          46 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
    1056          46 :                 if (!got_nt) {
    1057           1 :                         ret = HTTP_PRECONDITION_FAILED;
    1058           1 :                         goto error;
    1059             :                 }
    1060          45 :                 s = subscription_start(sm, callback_urls);
    1061          45 :                 if (s == NULL) {
    1062           8 :                         ret = HTTP_INTERNAL_SERVER_ERROR;
    1063           8 :                         goto error;
    1064             :                 }
    1065             :         } else {
    1066           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1067           1 :                 goto error;
    1068             :         }
    1069             : 
    1070             :         /* success */
    1071          39 :         http_put_reply_code(buf, HTTP_OK);
    1072          39 :         wpabuf_put_str(buf, http_server_hdr);
    1073          39 :         wpabuf_put_str(buf, http_connection_close);
    1074          39 :         wpabuf_put_str(buf, "Content-Length: 0\r\n");
    1075          39 :         wpabuf_put_str(buf, "SID: uuid:");
    1076             :         /* subscription id */
    1077          39 :         b = wpabuf_put(buf, 0);
    1078          39 :         uuid_bin2str(s->uuid, b, 80);
    1079          39 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
    1080          39 :         wpabuf_put(buf, os_strlen(b));
    1081          39 :         wpabuf_put_str(buf, "\r\n");
    1082          39 :         wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
    1083          39 :         http_put_date(buf);
    1084             :         /* And empty line to terminate header: */
    1085          39 :         wpabuf_put_str(buf, "\r\n");
    1086             : 
    1087          39 :         os_free(callback_urls);
    1088          39 :         http_request_send_and_deinit(req, buf);
    1089          39 :         return;
    1090             : 
    1091             : error:
    1092             :         /* Per UPnP spec:
    1093             :         * Errors
    1094             :         * Incompatible headers
    1095             :         *   400 Bad Request. If SID header and one of NT or CALLBACK headers
    1096             :         *     are present, the publisher must respond with HTTP error
    1097             :         *     400 Bad Request.
    1098             :         * Missing or invalid CALLBACK
    1099             :         *   412 Precondition Failed. If CALLBACK header is missing or does not
    1100             :         *     contain a valid HTTP URL, the publisher must respond with HTTP
    1101             :         *     error 412 Precondition Failed.
    1102             :         * Invalid NT
    1103             :         *   412 Precondition Failed. If NT header does not equal upnp:event,
    1104             :         *     the publisher must respond with HTTP error 412 Precondition
    1105             :         *     Failed.
    1106             :         * [For resubscription, use 412 if unknown uuid].
    1107             :         * Unable to accept subscription
    1108             :         *   5xx. If a publisher is not able to accept a subscription (such as
    1109             :         *     due to insufficient resources), it must respond with a
    1110             :         *     HTTP 500-series error code.
    1111             :         *   599 Too many subscriptions (not a standard HTTP error)
    1112             :         */
    1113          16 :         wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
    1114          16 :         http_put_empty(buf, ret);
    1115          16 :         http_request_send_and_deinit(req, buf);
    1116          16 :         os_free(callback_urls);
    1117             : }
    1118             : 
    1119             : 
    1120             : /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
    1121             :  *
    1122             :  * Format of UNSUBSCRIBE (case-insensitive):
    1123             :  *
    1124             :  * First line must be:
    1125             :  *      UNSUBSCRIBE /wps_event HTTP/1.1
    1126             :  *
    1127             :  * Our response (if no error) which includes only required lines is:
    1128             :  * HTTP/1.1 200 OK
    1129             :  * Content-Length: 0
    1130             :  *
    1131             :  * Header lines must end with \r\n
    1132             :  * Per RFC 2616, content-length: is not required but connection:close
    1133             :  * would appear to be required (given that we will be closing it!).
    1134             :  */
    1135          29 : static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
    1136             :                                              struct http_request *req,
    1137             :                                              const char *filename)
    1138             : {
    1139             :         struct wpabuf *buf;
    1140          29 :         char *hdr = http_request_get_hdr(req);
    1141             :         char *h;
    1142             :         char *match;
    1143             :         int match_len;
    1144             :         char *end;
    1145             :         u8 uuid[UUID_LEN];
    1146          29 :         int got_uuid = 0;
    1147          29 :         struct subscription *s = NULL;
    1148          29 :         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
    1149             : 
    1150             :         /* Parse/validate headers */
    1151          29 :         h = hdr;
    1152             :         /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
    1153             :          * has already been parsed.
    1154             :          */
    1155          29 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
    1156           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1157           1 :                 goto send_msg;
    1158             :         }
    1159          28 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
    1160          28 :         end = os_strchr(h, '\n');
    1161             : 
    1162         150 :         while (end) {
    1163             :                 /* Option line by option line */
    1164         122 :                 h = end + 1;
    1165         122 :                 end = os_strchr(h, '\n');
    1166         122 :                 if (end == NULL)
    1167          24 :                         break; /* no unterminated lines allowed */
    1168             : 
    1169             :                 /* HOST should refer to us */
    1170             : #if 0
    1171             :                 match = "HOST:";
    1172             :                 match_len = os_strlen(match);
    1173             :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1174             :                         h += match_len;
    1175             :                         while (*h == ' ' || *h == '\t')
    1176             :                                 h++;
    1177             :                         .....
    1178             :                 }
    1179             : #endif
    1180          98 :                 match = "SID:";
    1181          98 :                 match_len = os_strlen(match);
    1182          98 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1183          25 :                         h += match_len;
    1184          79 :                         while (*h == ' ' || *h == '\t')
    1185          29 :                                 h++;
    1186          25 :                         match = "uuid:";
    1187          25 :                         match_len = os_strlen(match);
    1188          25 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1189           1 :                                 ret = HTTP_BAD_REQUEST;
    1190           1 :                                 goto send_msg;
    1191             :                         }
    1192          24 :                         h += match_len;
    1193          51 :                         while (*h == ' ' || *h == '\t')
    1194           3 :                                 h++;
    1195          24 :                         if (uuid_str2bin(h, uuid)) {
    1196           1 :                                 ret = HTTP_BAD_REQUEST;
    1197           1 :                                 goto send_msg;
    1198             :                         }
    1199          23 :                         got_uuid = 1;
    1200          23 :                         continue;
    1201             :                 }
    1202             : 
    1203          73 :                 match = "NT:";
    1204          73 :                 match_len = os_strlen(match);
    1205          73 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1206           1 :                         ret = HTTP_BAD_REQUEST;
    1207           1 :                         goto send_msg;
    1208             :                 }
    1209             : 
    1210          72 :                 match = "CALLBACK:";
    1211          72 :                 match_len = os_strlen(match);
    1212          72 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1213           1 :                         ret = HTTP_BAD_REQUEST;
    1214           1 :                         goto send_msg;
    1215             :                 }
    1216             :         }
    1217             : 
    1218          24 :         if (got_uuid) {
    1219             :                 char str[80];
    1220             : 
    1221          23 :                 uuid_bin2str(uuid, str, sizeof(str));
    1222             : 
    1223          23 :                 s = subscription_find(sm, uuid);
    1224          23 :                 if (s) {
    1225             :                         struct subscr_addr *sa;
    1226          21 :                         sa = dl_list_first(&s->addr_list, struct subscr_addr,
    1227             :                                            list);
    1228          42 :                         wpa_printf(MSG_DEBUG,
    1229             :                                    "WPS UPnP: Unsubscribing %p (SID %s) %s",
    1230          21 :                                    s, str, (sa && sa->domain_and_port) ?
    1231             :                                    sa->domain_and_port : "-null-");
    1232          21 :                         dl_list_del(&s->list);
    1233          21 :                         subscription_destroy(s);
    1234             :                 } else {
    1235           2 :                         wpa_printf(MSG_INFO,
    1236             :                                    "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
    1237             :                                    str);
    1238           2 :                         ret = HTTP_PRECONDITION_FAILED;
    1239           2 :                         goto send_msg;
    1240             :                 }
    1241             :         } else {
    1242           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
    1243             :                            "found)");
    1244           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1245           1 :                 goto send_msg;
    1246             :         }
    1247             : 
    1248          21 :         ret = HTTP_OK;
    1249             : 
    1250             : send_msg:
    1251          29 :         buf = wpabuf_alloc(200);
    1252          29 :         if (buf == NULL) {
    1253           0 :                 http_request_deinit(req);
    1254          29 :                 return;
    1255             :         }
    1256          29 :         http_put_empty(buf, ret);
    1257          29 :         http_request_send_and_deinit(req, buf);
    1258             : }
    1259             : 
    1260             : 
    1261             : /* Send error in response to unknown requests */
    1262          11 : static void web_connection_unimplemented(struct http_request *req)
    1263             : {
    1264             :         struct wpabuf *buf;
    1265          11 :         buf = wpabuf_alloc(200);
    1266          11 :         if (buf == NULL) {
    1267           0 :                 http_request_deinit(req);
    1268          11 :                 return;
    1269             :         }
    1270          11 :         http_put_empty(buf, HTTP_UNIMPLEMENTED);
    1271          11 :         http_request_send_and_deinit(req, buf);
    1272             : }
    1273             : 
    1274             : 
    1275             : 
    1276             : /* Called when we have gotten an apparently valid http request.
    1277             :  */
    1278         300 : static void web_connection_check_data(void *ctx, struct http_request *req)
    1279             : {
    1280         300 :         struct upnp_wps_device_sm *sm = ctx;
    1281         300 :         enum httpread_hdr_type htype = http_request_get_type(req);
    1282         300 :         char *filename = http_request_get_uri(req);
    1283         300 :         struct sockaddr_in *cli = http_request_get_cli_addr(req);
    1284             : 
    1285         300 :         if (!filename) {
    1286           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
    1287           0 :                 http_request_deinit(req);
    1288         300 :                 return;
    1289             :         }
    1290             :         /* Trim leading slashes from filename */
    1291         884 :         while (*filename == '/')
    1292         284 :                 filename++;
    1293             : 
    1294         300 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
    1295         300 :                    htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
    1296             : 
    1297         300 :         switch (htype) {
    1298             :         case HTTPREAD_HDR_TYPE_GET:
    1299          32 :                 web_connection_parse_get(sm, req, filename);
    1300          32 :                 break;
    1301             :         case HTTPREAD_HDR_TYPE_POST:
    1302         173 :                 web_connection_parse_post(sm, cli, req, filename);
    1303         173 :                 break;
    1304             :         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
    1305          55 :                 web_connection_parse_subscribe(sm, req, filename);
    1306          55 :                 break;
    1307             :         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
    1308          29 :                 web_connection_parse_unsubscribe(sm, req, filename);
    1309          29 :                 break;
    1310             : 
    1311             :                 /* We are not required to support M-POST; just plain
    1312             :                  * POST is supposed to work, so we only support that.
    1313             :                  * If for some reason we need to support M-POST, it is
    1314             :                  * mostly the same as POST, with small differences.
    1315             :                  */
    1316             :         default:
    1317             :                 /* Send 501 for anything else */
    1318          11 :                 web_connection_unimplemented(req);
    1319          11 :                 break;
    1320             :         }
    1321             : }
    1322             : 
    1323             : 
    1324             : /*
    1325             :  * Listening for web connections
    1326             :  * We have a single TCP listening port, and hand off connections as we get
    1327             :  * them.
    1328             :  */
    1329             : 
    1330          37 : void web_listener_stop(struct upnp_wps_device_sm *sm)
    1331             : {
    1332          37 :         http_server_deinit(sm->web_srv);
    1333          37 :         sm->web_srv = NULL;
    1334          37 : }
    1335             : 
    1336             : 
    1337          36 : int web_listener_start(struct upnp_wps_device_sm *sm)
    1338             : {
    1339             :         struct in_addr addr;
    1340          36 :         addr.s_addr = sm->ip_addr;
    1341          36 :         sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
    1342             :                                        sm);
    1343          36 :         if (sm->web_srv == NULL) {
    1344           0 :                 web_listener_stop(sm);
    1345           0 :                 return -1;
    1346             :         }
    1347          36 :         sm->web_port = http_server_get_port(sm->web_srv);
    1348             : 
    1349          36 :         return 0;
    1350             : }

Generated by: LCOV version 1.10