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 1426431149 Lines: 500 552 90.6 %
Date: 2015-03-15 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          12 : 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          12 :         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          12 :         s = iface->wps->friendly_name;
     196          12 :         s = ((s && *s) ? s : "WPS Access Point");
     197          12 :         xml_add_tagged_data(buf, "friendlyName", s);
     198             : 
     199          12 :         s = iface->wps->dev.manufacturer;
     200          12 :         s = ((s && *s) ? s : "");
     201          12 :         xml_add_tagged_data(buf, "manufacturer", s);
     202             : 
     203          12 :         if (iface->wps->manufacturer_url)
     204           2 :                 xml_add_tagged_data(buf, "manufacturerURL",
     205           2 :                                     iface->wps->manufacturer_url);
     206             : 
     207          12 :         if (iface->wps->model_description)
     208           2 :                 xml_add_tagged_data(buf, "modelDescription",
     209           2 :                                     iface->wps->model_description);
     210             : 
     211          12 :         s = iface->wps->dev.model_name;
     212          12 :         s = ((s && *s) ? s : "");
     213          12 :         xml_add_tagged_data(buf, "modelName", s);
     214             : 
     215          12 :         if (iface->wps->dev.model_number)
     216          12 :                 xml_add_tagged_data(buf, "modelNumber",
     217          12 :                                     iface->wps->dev.model_number);
     218             : 
     219          12 :         if (iface->wps->model_url)
     220           2 :                 xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
     221             : 
     222          12 :         if (iface->wps->dev.serial_number)
     223          12 :                 xml_add_tagged_data(buf, "serialNumber",
     224          12 :                                     iface->wps->dev.serial_number);
     225             : 
     226          12 :         uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
     227          12 :         s = uuid_string;
     228             :         /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
     229             :          * easily...
     230             :          */
     231          12 :         wpabuf_put_str(buf, "<UDN>uuid:");
     232          12 :         xml_data_encode(buf, s, os_strlen(s));
     233          12 :         wpabuf_put_str(buf, "</UDN>\n");
     234             : 
     235          12 :         if (iface->wps->upc)
     236           2 :                 xml_add_tagged_data(buf, "UPC", iface->wps->upc);
     237             : 
     238          12 :         wpabuf_put_str(buf, wps_device_xml_postfix);
     239          12 : }
     240             : 
     241             : 
     242          41 : static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
     243             : {
     244          41 :         wpabuf_put_str(buf, "HTTP/1.1 ");
     245          41 :         switch (code) {
     246             :         case HTTP_OK:
     247          25 :                 wpabuf_put_str(buf, "200 OK\r\n");
     248          25 :                 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           7 :                 wpabuf_put_str(buf, "412 Precondition failed\r\n");
     254           7 :                 break;
     255             :         case HTTP_UNIMPLEMENTED:
     256           1 :                 wpabuf_put_str(buf, "501 Unimplemented\r\n");
     257           1 :                 break;
     258             :         case HTTP_INTERNAL_SERVER_ERROR:
     259             :         default:
     260           0 :                 wpabuf_put_str(buf, "500 Internal server error\r\n");
     261           0 :                 break;
     262             :         }
     263          41 : }
     264             : 
     265             : 
     266         101 : static void http_put_date(struct wpabuf *buf)
     267             : {
     268         101 :         wpabuf_put_str(buf, "Date: ");
     269         101 :         format_date(buf);
     270         101 :         wpabuf_put_str(buf, "\r\n");
     271         101 : }
     272             : 
     273             : 
     274          27 : static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
     275             : {
     276          27 :         http_put_reply_code(buf, code);
     277          27 :         wpabuf_put_str(buf, http_server_hdr);
     278          27 :         wpabuf_put_str(buf, http_connection_close);
     279          27 :         wpabuf_put_str(buf, "Content-Length: 0\r\n"
     280             :                        "\r\n");
     281          27 : }
     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          14 : 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          14 :         size_t extra_len = 0;
     313             :         int body_length;
     314             :         char len_buf[10];
     315             :         struct upnp_wps_device_interface *iface;
     316             : 
     317          14 :         iface = dl_list_first(&sm->interfaces,
     318             :                               struct upnp_wps_device_interface, list);
     319          14 :         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          14 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
     329          12 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
     330          12 :                 req = GET_DEVICE_XML_FILE;
     331          12 :                 extra_len = 3000;
     332          12 :                 if (iface->wps->friendly_name)
     333           2 :                         extra_len += os_strlen(iface->wps->friendly_name);
     334          12 :                 if (iface->wps->manufacturer_url)
     335           2 :                         extra_len += os_strlen(iface->wps->manufacturer_url);
     336          12 :                 if (iface->wps->model_description)
     337           2 :                         extra_len += os_strlen(iface->wps->model_description);
     338          12 :                 if (iface->wps->model_url)
     339           2 :                         extra_len += os_strlen(iface->wps->model_url);
     340          12 :                 if (iface->wps->upc)
     341           2 :                         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          13 :         buf = wpabuf_alloc(1000 + extra_len);
     368          13 :         if (buf == NULL) {
     369           0 :                 http_request_deinit(hreq);
     370           0 :                 return;
     371             :         }
     372             : 
     373          13 :         wpabuf_put_str(buf,
     374             :                        "HTTP/1.1 200 OK\r\n"
     375             :                        "Content-Type: text/xml; charset=\"utf-8\"\r\n");
     376          13 :         wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
     377          13 :         wpabuf_put_str(buf, "Connection: close\r\n");
     378          13 :         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          13 :         put_length_here = wpabuf_put(buf, 0);
     384          13 :         wpabuf_put_str(buf, "        \r\n");
     385             : 
     386          13 :         http_put_date(buf);
     387             : 
     388             :         /* terminating empty line */
     389          13 :         wpabuf_put_str(buf, "\r\n");
     390             : 
     391          13 :         body_start = wpabuf_put(buf, 0);
     392             : 
     393          13 :         switch (req) {
     394             :         case GET_DEVICE_XML_FILE:
     395          12 :                 format_wps_device_xml(iface, sm, buf);
     396          12 :                 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          13 :         body_length = (char *) wpabuf_put(buf, 0) - body_start;
     404          13 :         os_snprintf(len_buf, 10, "%d", body_length);
     405          13 :         os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
     406             : 
     407             : send_buf:
     408          14 :         http_request_send_and_deinit(hreq, buf);
     409             : }
     410             : 
     411             : 
     412             : static enum http_reply_code
     413          13 : 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          13 :         iface = dl_list_first(&sm->interfaces,
     422             :                               struct upnp_wps_device_interface, list);
     423             : 
     424          13 :         wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
     425             : 
     426          13 :         if (!iface || iface->ctx->ap_pin == NULL)
     427           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     428             : 
     429          13 :         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          13 :         if (peer->wps)
     440           2 :                 wps_deinit(peer->wps);
     441             : 
     442          13 :         os_memset(&cfg, 0, sizeof(cfg));
     443          13 :         cfg.wps = iface->wps;
     444          13 :         cfg.pin = (u8 *) iface->ctx->ap_pin;
     445          13 :         cfg.pin_len = os_strlen(iface->ctx->ap_pin);
     446          13 :         peer->wps = wps_init(&cfg);
     447          13 :         if (peer->wps) {
     448             :                 enum wsc_op_code op_code;
     449          13 :                 *reply = wps_get_msg(peer->wps, &op_code);
     450          13 :                 if (*reply == NULL) {
     451           0 :                         wps_deinit(peer->wps);
     452           0 :                         peer->wps = NULL;
     453             :                 }
     454             :         } else
     455           0 :                 *reply = NULL;
     456          13 :         if (*reply == NULL) {
     457           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
     458           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     459             :         }
     460          13 :         *replyname = name;
     461          13 :         return HTTP_OK;
     462             : }
     463             : 
     464             : 
     465             : static enum http_reply_code
     466           9 : 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           9 :         iface = dl_list_first(&sm->interfaces,
     477             :                               struct upnp_wps_device_interface, list);
     478           9 :         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           9 :         wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
     487           9 :         msg = xml_get_base64_item(data, "NewInMessage", &ret);
     488           9 :         if (msg == NULL)
     489           1 :                 return ret;
     490           8 :         res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
     491           8 :         if (res == WPS_FAILURE)
     492           1 :                 *reply = NULL;
     493             :         else
     494           7 :                 *reply = wps_get_msg(iface->peer.wps, &op_code);
     495           8 :         wpabuf_free(msg);
     496           8 :         if (*reply == NULL)
     497           1 :                 return HTTP_INTERNAL_SERVER_ERROR;
     498           7 :         *replyname = name;
     499           7 :         return HTTP_OK;
     500             : }
     501             : 
     502             : 
     503             : static enum http_reply_code
     504          24 : 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          24 :         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          24 :         wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
     522          24 :         msg = xml_get_base64_item(data, "NewMessage", &ret);
     523          24 :         if (msg == NULL) {
     524           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
     525             :                            "from PutWLANResponse");
     526           1 :                 return ret;
     527             :         }
     528          23 :         val = xml_get_first_item(data, "NewWLANEventType");
     529          23 :         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          23 :         ev_type = atol(val);
     536          23 :         os_free(val);
     537          23 :         val = xml_get_first_item(data, "NewWLANEventMAC");
     538          23 :         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          23 :         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          23 :         os_free(val);
     573          23 :         if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
     574             :                 struct wps_parse_attr attr;
     575          46 :                 if (wps_parse_msg(msg, &attr) < 0 ||
     576          23 :                     attr.msg_type == NULL)
     577           0 :                         type = -1;
     578             :                 else
     579          23 :                         type = *attr.msg_type;
     580          23 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
     581             :         } else
     582           0 :                 type = -1;
     583          46 :         dl_list_for_each(iface, &sm->interfaces,
     584             :                          struct upnp_wps_device_interface, list) {
     585          46 :                 if (iface->ctx->rx_req_put_wlan_response &&
     586          23 :                     iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
     587             :                                                          macaddr, msg, type)
     588             :                     == 0)
     589          23 :                         ok = 1;
     590             :         }
     591             : 
     592          23 :         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          23 :         wpabuf_free(msg);
     599          23 :         *replyname = NULL;
     600          23 :         *reply = NULL;
     601          23 :         return HTTP_OK;
     602             : }
     603             : 
     604             : 
     605          18 : static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
     606             : {
     607             :         struct subscr_addr *a;
     608             : 
     609          18 :         dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
     610          18 :                 if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
     611          18 :                         return 1;
     612             :         }
     613           0 :         return 0;
     614             : }
     615             : 
     616             : 
     617          19 : static struct subscription * find_er(struct upnp_wps_device_sm *sm,
     618             :                                      struct sockaddr_in *cli)
     619             : {
     620             :         struct subscription *s;
     621          19 :         dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
     622          18 :                 if (find_er_addr(s, cli))
     623          18 :                         return s;
     624           1 :         return NULL;
     625             : }
     626             : 
     627             : 
     628             : static enum http_reply_code
     629          19 : 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          19 :         int err = 0;
     639             : 
     640          19 :         wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
     641          19 :         s = find_er(sm, cli);
     642          19 :         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          18 :         msg = xml_get_base64_item(data, "NewMessage", &ret);
     648          18 :         if (msg == NULL)
     649           0 :                 return ret;
     650          36 :         dl_list_for_each(iface, &sm->interfaces,
     651             :                          struct upnp_wps_device_interface, list) {
     652          18 :                 if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
     653             :                                                    msg))
     654           0 :                         err = 1;
     655             :         }
     656          18 :         wpabuf_free(msg);
     657          18 :         if (err)
     658           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     659          18 :         *replyname = NULL;
     660          18 :         *reply = NULL;
     661          18 :         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          73 : 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          73 :         char *put_length_here = NULL;
     694          73 :         char *body_start = NULL;
     695             : 
     696          73 :         if (reply) {
     697             :                 size_t len;
     698          20 :                 replydata = (char *) base64_encode(wpabuf_head(reply),
     699             :                                                    wpabuf_len(reply), &len);
     700             :         } else
     701          53 :                 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         139 :         buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
     709          66 :                            (action_len > 0 ? action_len * 2 : 0));
     710          73 :         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          73 :                 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          73 :         if (ret == HTTP_OK) {
     724          61 :                 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          12 :                 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
     730             :         }
     731          73 :         wpabuf_put_str(buf, http_connection_close);
     732             : 
     733          73 :         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          73 :         put_length_here = wpabuf_put(buf, 0);
     739          73 :         wpabuf_put_str(buf, "        \r\n");
     740             : 
     741          73 :         http_put_date(buf);
     742             : 
     743             :         /* terminating empty line */
     744          73 :         wpabuf_put_str(buf, "\r\n");
     745             : 
     746          73 :         body_start = wpabuf_put(buf, 0);
     747             : 
     748          73 :         if (ret == HTTP_OK) {
     749          61 :                 wpabuf_put_str(buf, soap_prefix);
     750          61 :                 wpabuf_put_str(buf, "<u:");
     751          61 :                 wpabuf_put_data(buf, action, action_len);
     752          61 :                 wpabuf_put_str(buf, "Response xmlns:u=\"");
     753          61 :                 wpabuf_put_str(buf, urn_wfawlanconfig);
     754          61 :                 wpabuf_put_str(buf, "\">\n");
     755          61 :                 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          20 :                         wpabuf_printf(buf, "<%s>", replyname);
     762          20 :                         wpabuf_put_str(buf, replydata);
     763          20 :                         wpabuf_printf(buf, "</%s>\n", replyname);
     764             :                 }
     765          61 :                 wpabuf_put_str(buf, "</u:");
     766          61 :                 wpabuf_put_data(buf, action, action_len);
     767          61 :                 wpabuf_put_str(buf, "Response>\n");
     768          61 :                 wpabuf_put_str(buf, soap_postfix);
     769             :         } else {
     770             :                 /* Error case */
     771          12 :                 wpabuf_put_str(buf, soap_prefix);
     772          12 :                 wpabuf_put_str(buf, soap_error_prefix);
     773          12 :                 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
     774          12 :                 wpabuf_put_str(buf, soap_error_postfix);
     775          12 :                 wpabuf_put_str(buf, soap_postfix);
     776             :         }
     777          73 :         os_free(replydata);
     778             : 
     779             :         /* Now patch in the content length at the end */
     780          73 :         if (body_start && put_length_here) {
     781          73 :                 int body_length = (char *) wpabuf_put(buf, 0) - body_start;
     782             :                 char len_buf[10];
     783          73 :                 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
     784          73 :                 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
     785             :         }
     786             : 
     787          73 :         http_request_send_and_deinit(req, buf);
     788             : }
     789             : 
     790             : 
     791          72 : 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          72 :         *action_len = 0;
     800             :         /* The SOAPAction line of the header tells us what we want to do */
     801          72 :         b = http_request_get_hdr_line(req, "SOAPAction:");
     802          72 :         if (b == NULL)
     803           1 :                 return NULL;
     804          71 :         if (*b == '"')
     805          69 :                 b++;
     806             :         else
     807           2 :                 return NULL;
     808          69 :         match = urn_wfawlanconfig;
     809          69 :         match_len = os_strlen(urn_wfawlanconfig) - 1;
     810          69 :         if (os_strncasecmp(b, match, match_len))
     811           1 :                 return NULL;
     812          68 :         b += match_len;
     813             :         /* skip over version */
     814         207 :         while (isgraph(*b) && *b != '#')
     815          71 :                 b++;
     816          68 :         if (*b != '#')
     817           1 :                 return NULL;
     818          67 :         b++;
     819             :         /* Following the sharp(#) should be the action and a double quote */
     820          67 :         action = b;
     821        1149 :         while (isgraph(*b) && *b != '"')
     822        1015 :                 b++;
     823          67 :         if (*b != '"')
     824           1 :                 return NULL;
     825          66 :         *action_len = b - action;
     826          66 :         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          73 : 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          73 :         char *data = http_request_get_data(req); /* body of http msg */
     855          73 :         const char *action = NULL;
     856          73 :         size_t action_len = 0;
     857          73 :         const char *replyname = NULL; /* argument name for the reply */
     858          73 :         struct wpabuf *reply = NULL; /* data for the reply */
     859             : 
     860          73 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
     861           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
     862             :                            filename);
     863           1 :                 ret = HTTP_NOT_FOUND;
     864           1 :                 goto bad;
     865             :         }
     866             : 
     867          72 :         ret = UPNP_INVALID_ACTION;
     868          72 :         action = web_get_action(req, &action_len);
     869          72 :         if (action == NULL)
     870           6 :                 goto bad;
     871             : 
     872          66 :         if (!os_strncasecmp("GetDeviceInfo", action, action_len))
     873          13 :                 ret = web_process_get_device_info(sm, &reply, &replyname);
     874          53 :         else if (!os_strncasecmp("PutMessage", action, action_len))
     875           9 :                 ret = web_process_put_message(sm, data, &reply, &replyname);
     876          44 :         else if (!os_strncasecmp("PutWLANResponse", action, action_len))
     877          24 :                 ret = web_process_put_wlan_response(sm, data, &reply,
     878             :                                                     &replyname);
     879          20 :         else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
     880          19 :                 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          73 :         if (ret != HTTP_OK)
     887          12 :                 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
     888          73 :         web_connection_send_reply(req, ret, action, action_len, reply,
     889             :                                   replyname);
     890          73 :         wpabuf_free(reply);
     891          73 : }
     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          22 : 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          22 :         char *hdr = http_request_get_hdr(req);
     920             :         char *h;
     921             :         char *match;
     922             :         int match_len;
     923             :         char *end;
     924             :         int len;
     925          22 :         int got_nt = 0;
     926             :         u8 uuid[UUID_LEN];
     927          22 :         int got_uuid = 0;
     928          22 :         char *callback_urls = NULL;
     929          22 :         struct subscription *s = NULL;
     930          22 :         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
     931             : 
     932          22 :         buf = wpabuf_alloc(1000);
     933          22 :         if (buf == NULL) {
     934           0 :                 http_request_deinit(req);
     935           0 :                 return;
     936             :         }
     937             : 
     938          22 :         wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
     939             :                           (u8 *) hdr, os_strlen(hdr));
     940             : 
     941             :         /* Parse/validate headers */
     942          22 :         h = hdr;
     943             :         /* First line: SUBSCRIBE /wps_event HTTP/1.1
     944             :          * has already been parsed.
     945             :          */
     946          22 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
     947           1 :                 ret = HTTP_PRECONDITION_FAILED;
     948           1 :                 goto error;
     949             :         }
     950          21 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
     951          21 :         end = os_strchr(h, '\n');
     952             : 
     953         161 :         while (end) {
     954             :                 /* Option line by option line */
     955         140 :                 h = end + 1;
     956         140 :                 end = os_strchr(h, '\n');
     957         140 :                 if (end == NULL)
     958          18 :                         break; /* no unterminated lines allowed */
     959             : 
     960             :                 /* NT assures that it is our type of subscription;
     961             :                  * not used for a renewal.
     962             :                  **/
     963         122 :                 match = "NT:";
     964         122 :                 match_len = os_strlen(match);
     965         122 :                 if (os_strncasecmp(h, match, match_len) == 0) {
     966          20 :                         h += match_len;
     967          60 :                         while (*h == ' ' || *h == '\t')
     968          20 :                                 h++;
     969          20 :                         match = "upnp:event";
     970          20 :                         match_len = os_strlen(match);
     971          20 :                         if (os_strncasecmp(h, match, match_len) != 0) {
     972           1 :                                 ret = HTTP_BAD_REQUEST;
     973           1 :                                 goto error;
     974             :                         }
     975          19 :                         got_nt = 1;
     976          19 :                         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         102 :                 match = "CALLBACK:";
     994         102 :                 match_len = os_strlen(match);
     995         102 :                 if (os_strncasecmp(h, match, match_len) == 0) {
     996          15 :                         h += match_len;
     997          45 :                         while (*h == ' ' || *h == '\t')
     998          15 :                                 h++;
     999          15 :                         len = end - h;
    1000          15 :                         os_free(callback_urls);
    1001          15 :                         callback_urls = dup_binstr(h, len);
    1002          15 :                         if (callback_urls == NULL) {
    1003           0 :                                 ret = HTTP_INTERNAL_SERVER_ERROR;
    1004           0 :                                 goto error;
    1005             :                         }
    1006          15 :                         continue;
    1007             :                 }
    1008             :                 /* SID is only for renewal */
    1009          87 :                 match = "SID:";
    1010          87 :                 match_len = os_strlen(match);
    1011          87 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1012           6 :                         h += match_len;
    1013          18 :                         while (*h == ' ' || *h == '\t')
    1014           6 :                                 h++;
    1015           6 :                         match = "uuid:";
    1016           6 :                         match_len = os_strlen(match);
    1017           6 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1018           1 :                                 ret = HTTP_BAD_REQUEST;
    1019           1 :                                 goto error;
    1020             :                         }
    1021           5 :                         h += match_len;
    1022          14 :                         while (*h == ' ' || *h == '\t')
    1023           4 :                                 h++;
    1024           5 :                         if (uuid_str2bin(h, uuid)) {
    1025           1 :                                 ret = HTTP_BAD_REQUEST;
    1026           1 :                                 goto error;
    1027             :                         }
    1028           4 :                         got_uuid = 1;
    1029           4 :                         continue;
    1030             :                 }
    1031             :                 /* TIMEOUT is requested timeout, but apparently we can
    1032             :                  * just ignore this.
    1033             :                  */
    1034             :         }
    1035             : 
    1036          18 :         if (got_uuid) {
    1037             :                 /* renewal */
    1038           4 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
    1039           4 :                 if (callback_urls) {
    1040           1 :                         ret = HTTP_BAD_REQUEST;
    1041           1 :                         goto error;
    1042             :                 }
    1043           3 :                 s = subscription_renew(sm, uuid);
    1044           3 :                 if (s == NULL) {
    1045             :                         char str[80];
    1046           1 :                         uuid_bin2str(uuid, str, sizeof(str));
    1047           1 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
    1048             :                                    "SID %s", str);
    1049           1 :                         ret = HTTP_PRECONDITION_FAILED;
    1050           1 :                         goto error;
    1051             :                 }
    1052          14 :         } else if (callback_urls) {
    1053          13 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
    1054          13 :                 if (!got_nt) {
    1055           1 :                         ret = HTTP_PRECONDITION_FAILED;
    1056           1 :                         goto error;
    1057             :                 }
    1058          12 :                 s = subscription_start(sm, callback_urls);
    1059          12 :                 if (s == NULL) {
    1060           0 :                         ret = HTTP_INTERNAL_SERVER_ERROR;
    1061           0 :                         goto error;
    1062             :                 }
    1063             :         } else {
    1064           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1065           1 :                 goto error;
    1066             :         }
    1067             : 
    1068             :         /* success */
    1069          14 :         http_put_reply_code(buf, HTTP_OK);
    1070          14 :         wpabuf_put_str(buf, http_server_hdr);
    1071          14 :         wpabuf_put_str(buf, http_connection_close);
    1072          14 :         wpabuf_put_str(buf, "Content-Length: 0\r\n");
    1073          14 :         wpabuf_put_str(buf, "SID: uuid:");
    1074             :         /* subscription id */
    1075          14 :         b = wpabuf_put(buf, 0);
    1076          14 :         uuid_bin2str(s->uuid, b, 80);
    1077          14 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
    1078          14 :         wpabuf_put(buf, os_strlen(b));
    1079          14 :         wpabuf_put_str(buf, "\r\n");
    1080          14 :         wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
    1081          14 :         http_put_date(buf);
    1082             :         /* And empty line to terminate header: */
    1083          14 :         wpabuf_put_str(buf, "\r\n");
    1084             : 
    1085          14 :         os_free(callback_urls);
    1086          14 :         http_request_send_and_deinit(req, buf);
    1087          14 :         return;
    1088             : 
    1089             : error:
    1090             :         /* Per UPnP spec:
    1091             :         * Errors
    1092             :         * Incompatible headers
    1093             :         *   400 Bad Request. If SID header and one of NT or CALLBACK headers
    1094             :         *     are present, the publisher must respond with HTTP error
    1095             :         *     400 Bad Request.
    1096             :         * Missing or invalid CALLBACK
    1097             :         *   412 Precondition Failed. If CALLBACK header is missing or does not
    1098             :         *     contain a valid HTTP URL, the publisher must respond with HTTP
    1099             :         *     error 412 Precondition Failed.
    1100             :         * Invalid NT
    1101             :         *   412 Precondition Failed. If NT header does not equal upnp:event,
    1102             :         *     the publisher must respond with HTTP error 412 Precondition
    1103             :         *     Failed.
    1104             :         * [For resubscription, use 412 if unknown uuid].
    1105             :         * Unable to accept subscription
    1106             :         *   5xx. If a publisher is not able to accept a subscription (such as
    1107             :         *     due to insufficient resources), it must respond with a
    1108             :         *     HTTP 500-series error code.
    1109             :         *   599 Too many subscriptions (not a standard HTTP error)
    1110             :         */
    1111           8 :         wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
    1112           8 :         http_put_empty(buf, ret);
    1113           8 :         http_request_send_and_deinit(req, buf);
    1114           8 :         os_free(callback_urls);
    1115             : }
    1116             : 
    1117             : 
    1118             : /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
    1119             :  *
    1120             :  * Format of UNSUBSCRIBE (case-insensitive):
    1121             :  *
    1122             :  * First line must be:
    1123             :  *      UNSUBSCRIBE /wps_event HTTP/1.1
    1124             :  *
    1125             :  * Our response (if no error) which includes only required lines is:
    1126             :  * HTTP/1.1 200 OK
    1127             :  * Content-Length: 0
    1128             :  *
    1129             :  * Header lines must end with \r\n
    1130             :  * Per RFC 2616, content-length: is not required but connection:close
    1131             :  * would appear to be required (given that we will be closing it!).
    1132             :  */
    1133          18 : static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
    1134             :                                              struct http_request *req,
    1135             :                                              const char *filename)
    1136             : {
    1137             :         struct wpabuf *buf;
    1138          18 :         char *hdr = http_request_get_hdr(req);
    1139             :         char *h;
    1140             :         char *match;
    1141             :         int match_len;
    1142             :         char *end;
    1143             :         u8 uuid[UUID_LEN];
    1144          18 :         int got_uuid = 0;
    1145          18 :         struct subscription *s = NULL;
    1146          18 :         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
    1147             : 
    1148             :         /* Parse/validate headers */
    1149          18 :         h = hdr;
    1150             :         /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
    1151             :          * has already been parsed.
    1152             :          */
    1153          18 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
    1154           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1155           1 :                 goto send_msg;
    1156             :         }
    1157          17 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
    1158          17 :         end = os_strchr(h, '\n');
    1159             : 
    1160          91 :         while (end) {
    1161             :                 /* Option line by option line */
    1162          74 :                 h = end + 1;
    1163          74 :                 end = os_strchr(h, '\n');
    1164          74 :                 if (end == NULL)
    1165          13 :                         break; /* no unterminated lines allowed */
    1166             : 
    1167             :                 /* HOST should refer to us */
    1168             : #if 0
    1169             :                 match = "HOST:";
    1170             :                 match_len = os_strlen(match);
    1171             :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1172             :                         h += match_len;
    1173             :                         while (*h == ' ' || *h == '\t')
    1174             :                                 h++;
    1175             :                         .....
    1176             :                 }
    1177             : #endif
    1178          61 :                 match = "SID:";
    1179          61 :                 match_len = os_strlen(match);
    1180          61 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1181          14 :                         h += match_len;
    1182          46 :                         while (*h == ' ' || *h == '\t')
    1183          18 :                                 h++;
    1184          14 :                         match = "uuid:";
    1185          14 :                         match_len = os_strlen(match);
    1186          14 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1187           1 :                                 ret = HTTP_BAD_REQUEST;
    1188           1 :                                 goto send_msg;
    1189             :                         }
    1190          13 :                         h += match_len;
    1191          29 :                         while (*h == ' ' || *h == '\t')
    1192           3 :                                 h++;
    1193          13 :                         if (uuid_str2bin(h, uuid)) {
    1194           1 :                                 ret = HTTP_BAD_REQUEST;
    1195           1 :                                 goto send_msg;
    1196             :                         }
    1197          12 :                         got_uuid = 1;
    1198          12 :                         continue;
    1199             :                 }
    1200             : 
    1201          47 :                 match = "NT:";
    1202          47 :                 match_len = os_strlen(match);
    1203          47 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1204           1 :                         ret = HTTP_BAD_REQUEST;
    1205           1 :                         goto send_msg;
    1206             :                 }
    1207             : 
    1208          46 :                 match = "CALLBACK:";
    1209          46 :                 match_len = os_strlen(match);
    1210          46 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1211           1 :                         ret = HTTP_BAD_REQUEST;
    1212           1 :                         goto send_msg;
    1213             :                 }
    1214             :         }
    1215             : 
    1216          13 :         if (got_uuid) {
    1217          12 :                 s = subscription_find(sm, uuid);
    1218          12 :                 if (s) {
    1219             :                         struct subscr_addr *sa;
    1220          11 :                         sa = dl_list_first(&s->addr_list, struct subscr_addr,
    1221             :                                            list);
    1222          22 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
    1223          11 :                                    s, (sa && sa->domain_and_port) ?
    1224             :                                    sa->domain_and_port : "-null-");
    1225          11 :                         dl_list_del(&s->list);
    1226          11 :                         subscription_destroy(s);
    1227             :                 } else {
    1228           1 :                         wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
    1229           1 :                         ret = HTTP_PRECONDITION_FAILED;
    1230           1 :                         goto send_msg;
    1231             :                 }
    1232             :         } else {
    1233           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
    1234             :                            "found)");
    1235           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1236           1 :                 goto send_msg;
    1237             :         }
    1238             : 
    1239          11 :         ret = HTTP_OK;
    1240             : 
    1241             : send_msg:
    1242          18 :         buf = wpabuf_alloc(200);
    1243          18 :         if (buf == NULL) {
    1244           0 :                 http_request_deinit(req);
    1245          18 :                 return;
    1246             :         }
    1247          18 :         http_put_empty(buf, ret);
    1248          18 :         http_request_send_and_deinit(req, buf);
    1249             : }
    1250             : 
    1251             : 
    1252             : /* Send error in response to unknown requests */
    1253           1 : static void web_connection_unimplemented(struct http_request *req)
    1254             : {
    1255             :         struct wpabuf *buf;
    1256           1 :         buf = wpabuf_alloc(200);
    1257           1 :         if (buf == NULL) {
    1258           0 :                 http_request_deinit(req);
    1259           1 :                 return;
    1260             :         }
    1261           1 :         http_put_empty(buf, HTTP_UNIMPLEMENTED);
    1262           1 :         http_request_send_and_deinit(req, buf);
    1263             : }
    1264             : 
    1265             : 
    1266             : 
    1267             : /* Called when we have gotten an apparently valid http request.
    1268             :  */
    1269         128 : static void web_connection_check_data(void *ctx, struct http_request *req)
    1270             : {
    1271         128 :         struct upnp_wps_device_sm *sm = ctx;
    1272         128 :         enum httpread_hdr_type htype = http_request_get_type(req);
    1273         128 :         char *filename = http_request_get_uri(req);
    1274         128 :         struct sockaddr_in *cli = http_request_get_cli_addr(req);
    1275             : 
    1276         128 :         if (!filename) {
    1277           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
    1278           0 :                 http_request_deinit(req);
    1279         128 :                 return;
    1280             :         }
    1281             :         /* Trim leading slashes from filename */
    1282         381 :         while (*filename == '/')
    1283         125 :                 filename++;
    1284             : 
    1285         128 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
    1286         128 :                    htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
    1287             : 
    1288         128 :         switch (htype) {
    1289             :         case HTTPREAD_HDR_TYPE_GET:
    1290          14 :                 web_connection_parse_get(sm, req, filename);
    1291          14 :                 break;
    1292             :         case HTTPREAD_HDR_TYPE_POST:
    1293          73 :                 web_connection_parse_post(sm, cli, req, filename);
    1294          73 :                 break;
    1295             :         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
    1296          22 :                 web_connection_parse_subscribe(sm, req, filename);
    1297          22 :                 break;
    1298             :         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
    1299          18 :                 web_connection_parse_unsubscribe(sm, req, filename);
    1300          18 :                 break;
    1301             : 
    1302             :                 /* We are not required to support M-POST; just plain
    1303             :                  * POST is supposed to work, so we only support that.
    1304             :                  * If for some reason we need to support M-POST, it is
    1305             :                  * mostly the same as POST, with small differences.
    1306             :                  */
    1307             :         default:
    1308             :                 /* Send 501 for anything else */
    1309           1 :                 web_connection_unimplemented(req);
    1310           1 :                 break;
    1311             :         }
    1312             : }
    1313             : 
    1314             : 
    1315             : /*
    1316             :  * Listening for web connections
    1317             :  * We have a single TCP listening port, and hand off connections as we get
    1318             :  * them.
    1319             :  */
    1320             : 
    1321          16 : void web_listener_stop(struct upnp_wps_device_sm *sm)
    1322             : {
    1323          16 :         http_server_deinit(sm->web_srv);
    1324          16 :         sm->web_srv = NULL;
    1325          16 : }
    1326             : 
    1327             : 
    1328          16 : int web_listener_start(struct upnp_wps_device_sm *sm)
    1329             : {
    1330             :         struct in_addr addr;
    1331          16 :         addr.s_addr = sm->ip_addr;
    1332          16 :         sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
    1333             :                                        sm);
    1334          16 :         if (sm->web_srv == NULL) {
    1335           0 :                 web_listener_stop(sm);
    1336           0 :                 return -1;
    1337             :         }
    1338          16 :         sm->web_port = http_server_get_port(sm->web_srv);
    1339             : 
    1340          16 :         return 0;
    1341             : }

Generated by: LCOV version 1.10