LCOV - code coverage report
Current view: top level - src/wps - wps_upnp_web.c (source / functions) Hit Total Coverage
Test: wpa_supplicant hwsim test run 1401872338 Lines: 0 552 0.0 %
Date: 2014-06-04 Functions: 0 20 0.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           0 : 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           0 :         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           0 :         s = iface->wps->friendly_name;
     196           0 :         s = ((s && *s) ? s : "WPS Access Point");
     197           0 :         xml_add_tagged_data(buf, "friendlyName", s);
     198             : 
     199           0 :         s = iface->wps->dev.manufacturer;
     200           0 :         s = ((s && *s) ? s : "");
     201           0 :         xml_add_tagged_data(buf, "manufacturer", s);
     202             : 
     203           0 :         if (iface->wps->manufacturer_url)
     204           0 :                 xml_add_tagged_data(buf, "manufacturerURL",
     205           0 :                                     iface->wps->manufacturer_url);
     206             : 
     207           0 :         if (iface->wps->model_description)
     208           0 :                 xml_add_tagged_data(buf, "modelDescription",
     209           0 :                                     iface->wps->model_description);
     210             : 
     211           0 :         s = iface->wps->dev.model_name;
     212           0 :         s = ((s && *s) ? s : "");
     213           0 :         xml_add_tagged_data(buf, "modelName", s);
     214             : 
     215           0 :         if (iface->wps->dev.model_number)
     216           0 :                 xml_add_tagged_data(buf, "modelNumber",
     217           0 :                                     iface->wps->dev.model_number);
     218             : 
     219           0 :         if (iface->wps->model_url)
     220           0 :                 xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
     221             : 
     222           0 :         if (iface->wps->dev.serial_number)
     223           0 :                 xml_add_tagged_data(buf, "serialNumber",
     224           0 :                                     iface->wps->dev.serial_number);
     225             : 
     226           0 :         uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
     227           0 :         s = uuid_string;
     228             :         /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
     229             :          * easily...
     230             :          */
     231           0 :         wpabuf_put_str(buf, "<UDN>uuid:");
     232           0 :         xml_data_encode(buf, s, os_strlen(s));
     233           0 :         wpabuf_put_str(buf, "</UDN>\n");
     234             : 
     235           0 :         if (iface->wps->upc)
     236           0 :                 xml_add_tagged_data(buf, "UPC", iface->wps->upc);
     237             : 
     238           0 :         wpabuf_put_str(buf, wps_device_xml_postfix);
     239           0 : }
     240             : 
     241             : 
     242           0 : static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
     243             : {
     244           0 :         wpabuf_put_str(buf, "HTTP/1.1 ");
     245           0 :         switch (code) {
     246             :         case HTTP_OK:
     247           0 :                 wpabuf_put_str(buf, "200 OK\r\n");
     248           0 :                 break;
     249             :         case HTTP_BAD_REQUEST:
     250           0 :                 wpabuf_put_str(buf, "400 Bad request\r\n");
     251           0 :                 break;
     252             :         case HTTP_PRECONDITION_FAILED:
     253           0 :                 wpabuf_put_str(buf, "412 Precondition failed\r\n");
     254           0 :                 break;
     255             :         case HTTP_UNIMPLEMENTED:
     256           0 :                 wpabuf_put_str(buf, "501 Unimplemented\r\n");
     257           0 :                 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           0 : }
     264             : 
     265             : 
     266           0 : static void http_put_date(struct wpabuf *buf)
     267             : {
     268           0 :         wpabuf_put_str(buf, "Date: ");
     269           0 :         format_date(buf);
     270           0 :         wpabuf_put_str(buf, "\r\n");
     271           0 : }
     272             : 
     273             : 
     274           0 : static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
     275             : {
     276           0 :         http_put_reply_code(buf, code);
     277           0 :         wpabuf_put_str(buf, http_server_hdr);
     278           0 :         wpabuf_put_str(buf, http_connection_close);
     279           0 :         wpabuf_put_str(buf, "Content-Length: 0\r\n"
     280             :                        "\r\n");
     281           0 : }
     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           0 : 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           0 :         size_t extra_len = 0;
     313             :         int body_length;
     314             :         char len_buf[10];
     315             :         struct upnp_wps_device_interface *iface;
     316             : 
     317           0 :         iface = dl_list_first(&sm->interfaces,
     318             :                               struct upnp_wps_device_interface, list);
     319           0 :         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           0 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
     329           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
     330           0 :                 req = GET_DEVICE_XML_FILE;
     331           0 :                 extra_len = 3000;
     332           0 :                 if (iface->wps->friendly_name)
     333           0 :                         extra_len += os_strlen(iface->wps->friendly_name);
     334           0 :                 if (iface->wps->manufacturer_url)
     335           0 :                         extra_len += os_strlen(iface->wps->manufacturer_url);
     336           0 :                 if (iface->wps->model_description)
     337           0 :                         extra_len += os_strlen(iface->wps->model_description);
     338           0 :                 if (iface->wps->model_url)
     339           0 :                         extra_len += os_strlen(iface->wps->model_url);
     340           0 :                 if (iface->wps->upc)
     341           0 :                         extra_len += os_strlen(iface->wps->upc);
     342           0 :         } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
     343           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
     344           0 :                 req = GET_SCPD_XML_FILE;
     345           0 :                 extra_len = os_strlen(wps_scpd_xml);
     346             :         } else {
     347             :                 /* File not found */
     348           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
     349             :                            filename);
     350           0 :                 buf = wpabuf_alloc(200);
     351           0 :                 if (buf == NULL) {
     352           0 :                         http_request_deinit(hreq);
     353           0 :                         return;
     354             :                 }
     355           0 :                 wpabuf_put_str(buf,
     356             :                                "HTTP/1.1 404 Not Found\r\n"
     357             :                                "Connection: close\r\n");
     358             : 
     359           0 :                 http_put_date(buf);
     360             : 
     361             :                 /* terminating empty line */
     362           0 :                 wpabuf_put_str(buf, "\r\n");
     363             : 
     364           0 :                 goto send_buf;
     365             :         }
     366             : 
     367           0 :         buf = wpabuf_alloc(1000 + extra_len);
     368           0 :         if (buf == NULL) {
     369           0 :                 http_request_deinit(hreq);
     370           0 :                 return;
     371             :         }
     372             : 
     373           0 :         wpabuf_put_str(buf,
     374             :                        "HTTP/1.1 200 OK\r\n"
     375             :                        "Content-Type: text/xml; charset=\"utf-8\"\r\n");
     376           0 :         wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
     377           0 :         wpabuf_put_str(buf, "Connection: close\r\n");
     378           0 :         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           0 :         put_length_here = wpabuf_put(buf, 0);
     384           0 :         wpabuf_put_str(buf, "        \r\n");
     385             : 
     386           0 :         http_put_date(buf);
     387             : 
     388             :         /* terminating empty line */
     389           0 :         wpabuf_put_str(buf, "\r\n");
     390             : 
     391           0 :         body_start = wpabuf_put(buf, 0);
     392             : 
     393           0 :         switch (req) {
     394             :         case GET_DEVICE_XML_FILE:
     395           0 :                 format_wps_device_xml(iface, sm, buf);
     396           0 :                 break;
     397             :         case GET_SCPD_XML_FILE:
     398           0 :                 wpabuf_put_str(buf, wps_scpd_xml);
     399           0 :                 break;
     400             :         }
     401             : 
     402             :         /* Now patch in the content length at the end */
     403           0 :         body_length = (char *) wpabuf_put(buf, 0) - body_start;
     404           0 :         os_snprintf(len_buf, 10, "%d", body_length);
     405           0 :         os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
     406             : 
     407             : send_buf:
     408           0 :         http_request_send_and_deinit(hreq, buf);
     409             : }
     410             : 
     411             : 
     412             : static enum http_reply_code
     413           0 : 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           0 :         iface = dl_list_first(&sm->interfaces,
     422             :                               struct upnp_wps_device_interface, list);
     423             : 
     424           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
     425             : 
     426           0 :         if (!iface || iface->ctx->ap_pin == NULL)
     427           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     428             : 
     429           0 :         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           0 :         if (peer->wps)
     440           0 :                 wps_deinit(peer->wps);
     441             : 
     442           0 :         os_memset(&cfg, 0, sizeof(cfg));
     443           0 :         cfg.wps = iface->wps;
     444           0 :         cfg.pin = (u8 *) iface->ctx->ap_pin;
     445           0 :         cfg.pin_len = os_strlen(iface->ctx->ap_pin);
     446           0 :         peer->wps = wps_init(&cfg);
     447           0 :         if (peer->wps) {
     448             :                 enum wsc_op_code op_code;
     449           0 :                 *reply = wps_get_msg(peer->wps, &op_code);
     450           0 :                 if (*reply == NULL) {
     451           0 :                         wps_deinit(peer->wps);
     452           0 :                         peer->wps = NULL;
     453             :                 }
     454             :         } else
     455           0 :                 *reply = NULL;
     456           0 :         if (*reply == NULL) {
     457           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
     458           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     459             :         }
     460           0 :         *replyname = name;
     461           0 :         return HTTP_OK;
     462             : }
     463             : 
     464             : 
     465             : static enum http_reply_code
     466           0 : 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           0 :         iface = dl_list_first(&sm->interfaces,
     477             :                               struct upnp_wps_device_interface, list);
     478           0 :         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           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
     487           0 :         msg = xml_get_base64_item(data, "NewInMessage", &ret);
     488           0 :         if (msg == NULL)
     489           0 :                 return ret;
     490           0 :         res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
     491           0 :         if (res == WPS_FAILURE)
     492           0 :                 *reply = NULL;
     493             :         else
     494           0 :                 *reply = wps_get_msg(iface->peer.wps, &op_code);
     495           0 :         wpabuf_free(msg);
     496           0 :         if (*reply == NULL)
     497           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     498           0 :         *replyname = name;
     499           0 :         return HTTP_OK;
     500             : }
     501             : 
     502             : 
     503             : static enum http_reply_code
     504           0 : 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           0 :         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           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
     522           0 :         msg = xml_get_base64_item(data, "NewMessage", &ret);
     523           0 :         if (msg == NULL) {
     524           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
     525             :                            "from PutWLANResponse");
     526           0 :                 return ret;
     527             :         }
     528           0 :         val = xml_get_first_item(data, "NewWLANEventType");
     529           0 :         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           0 :         ev_type = atol(val);
     536           0 :         os_free(val);
     537           0 :         val = xml_get_first_item(data, "NewWLANEventMAC");
     538           0 :         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           0 :         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           0 :         os_free(val);
     573           0 :         if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
     574             :                 struct wps_parse_attr attr;
     575           0 :                 if (wps_parse_msg(msg, &attr) < 0 ||
     576           0 :                     attr.msg_type == NULL)
     577           0 :                         type = -1;
     578             :                 else
     579           0 :                         type = *attr.msg_type;
     580           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
     581             :         } else
     582           0 :                 type = -1;
     583           0 :         dl_list_for_each(iface, &sm->interfaces,
     584             :                          struct upnp_wps_device_interface, list) {
     585           0 :                 if (iface->ctx->rx_req_put_wlan_response &&
     586           0 :                     iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
     587             :                                                          macaddr, msg, type)
     588             :                     == 0)
     589           0 :                         ok = 1;
     590             :         }
     591             : 
     592           0 :         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           0 :         wpabuf_free(msg);
     599           0 :         *replyname = NULL;
     600           0 :         *reply = NULL;
     601           0 :         return HTTP_OK;
     602             : }
     603             : 
     604             : 
     605           0 : static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
     606             : {
     607             :         struct subscr_addr *a;
     608             : 
     609           0 :         dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
     610           0 :                 if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
     611           0 :                         return 1;
     612             :         }
     613           0 :         return 0;
     614             : }
     615             : 
     616             : 
     617           0 : static struct subscription * find_er(struct upnp_wps_device_sm *sm,
     618             :                                      struct sockaddr_in *cli)
     619             : {
     620             :         struct subscription *s;
     621           0 :         dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
     622           0 :                 if (find_er_addr(s, cli))
     623           0 :                         return s;
     624           0 :         return NULL;
     625             : }
     626             : 
     627             : 
     628             : static enum http_reply_code
     629           0 : 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           0 :         int err = 0;
     639             : 
     640           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
     641           0 :         s = find_er(sm, cli);
     642           0 :         if (s == NULL) {
     643           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
     644             :                            "from unknown ER");
     645           0 :                 return UPNP_ACTION_FAILED;
     646             :         }
     647           0 :         msg = xml_get_base64_item(data, "NewMessage", &ret);
     648           0 :         if (msg == NULL)
     649           0 :                 return ret;
     650           0 :         dl_list_for_each(iface, &sm->interfaces,
     651             :                          struct upnp_wps_device_interface, list) {
     652           0 :                 if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
     653             :                                                    msg))
     654           0 :                         err = 1;
     655             :         }
     656           0 :         wpabuf_free(msg);
     657           0 :         if (err)
     658           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     659           0 :         *replyname = NULL;
     660           0 :         *reply = NULL;
     661           0 :         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           0 : 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           0 :         char *put_length_here = NULL;
     694           0 :         char *body_start = NULL;
     695             : 
     696           0 :         if (reply) {
     697             :                 size_t len;
     698           0 :                 replydata = (char *) base64_encode(wpabuf_head(reply),
     699             :                                                    wpabuf_len(reply), &len);
     700             :         } else
     701           0 :                 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           0 :         buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
     709           0 :                            (action_len > 0 ? action_len * 2 : 0));
     710           0 :         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           0 :                 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           0 :         if (ret == HTTP_OK) {
     724           0 :                 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           0 :                 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
     730             :         }
     731           0 :         wpabuf_put_str(buf, http_connection_close);
     732             : 
     733           0 :         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           0 :         put_length_here = wpabuf_put(buf, 0);
     739           0 :         wpabuf_put_str(buf, "        \r\n");
     740             : 
     741           0 :         http_put_date(buf);
     742             : 
     743             :         /* terminating empty line */
     744           0 :         wpabuf_put_str(buf, "\r\n");
     745             : 
     746           0 :         body_start = wpabuf_put(buf, 0);
     747             : 
     748           0 :         if (ret == HTTP_OK) {
     749           0 :                 wpabuf_put_str(buf, soap_prefix);
     750           0 :                 wpabuf_put_str(buf, "<u:");
     751           0 :                 wpabuf_put_data(buf, action, action_len);
     752           0 :                 wpabuf_put_str(buf, "Response xmlns:u=\"");
     753           0 :                 wpabuf_put_str(buf, urn_wfawlanconfig);
     754           0 :                 wpabuf_put_str(buf, "\">\n");
     755           0 :                 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           0 :                         wpabuf_printf(buf, "<%s>", replyname);
     762           0 :                         wpabuf_put_str(buf, replydata);
     763           0 :                         wpabuf_printf(buf, "</%s>\n", replyname);
     764             :                 }
     765           0 :                 wpabuf_put_str(buf, "</u:");
     766           0 :                 wpabuf_put_data(buf, action, action_len);
     767           0 :                 wpabuf_put_str(buf, "Response>\n");
     768           0 :                 wpabuf_put_str(buf, soap_postfix);
     769             :         } else {
     770             :                 /* Error case */
     771           0 :                 wpabuf_put_str(buf, soap_prefix);
     772           0 :                 wpabuf_put_str(buf, soap_error_prefix);
     773           0 :                 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
     774           0 :                 wpabuf_put_str(buf, soap_error_postfix);
     775           0 :                 wpabuf_put_str(buf, soap_postfix);
     776             :         }
     777           0 :         os_free(replydata);
     778             : 
     779             :         /* Now patch in the content length at the end */
     780           0 :         if (body_start && put_length_here) {
     781           0 :                 int body_length = (char *) wpabuf_put(buf, 0) - body_start;
     782             :                 char len_buf[10];
     783           0 :                 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
     784           0 :                 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
     785             :         }
     786             : 
     787           0 :         http_request_send_and_deinit(req, buf);
     788             : }
     789             : 
     790             : 
     791           0 : 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           0 :         *action_len = 0;
     800             :         /* The SOAPAction line of the header tells us what we want to do */
     801           0 :         b = http_request_get_hdr_line(req, "SOAPAction:");
     802           0 :         if (b == NULL)
     803           0 :                 return NULL;
     804           0 :         if (*b == '"')
     805           0 :                 b++;
     806             :         else
     807           0 :                 return NULL;
     808           0 :         match = urn_wfawlanconfig;
     809           0 :         match_len = os_strlen(urn_wfawlanconfig) - 1;
     810           0 :         if (os_strncasecmp(b, match, match_len))
     811           0 :                 return NULL;
     812           0 :         b += match_len;
     813             :         /* skip over version */
     814           0 :         while (isgraph(*b) && *b != '#')
     815           0 :                 b++;
     816           0 :         if (*b != '#')
     817           0 :                 return NULL;
     818           0 :         b++;
     819             :         /* Following the sharp(#) should be the action and a double quote */
     820           0 :         action = b;
     821           0 :         while (isgraph(*b) && *b != '"')
     822           0 :                 b++;
     823           0 :         if (*b != '"')
     824           0 :                 return NULL;
     825           0 :         *action_len = b - action;
     826           0 :         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           0 : 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           0 :         char *data = http_request_get_data(req); /* body of http msg */
     855           0 :         const char *action = NULL;
     856           0 :         size_t action_len = 0;
     857           0 :         const char *replyname = NULL; /* argument name for the reply */
     858           0 :         struct wpabuf *reply = NULL; /* data for the reply */
     859             : 
     860           0 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
     861           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
     862             :                            filename);
     863           0 :                 ret = HTTP_NOT_FOUND;
     864           0 :                 goto bad;
     865             :         }
     866             : 
     867           0 :         ret = UPNP_INVALID_ACTION;
     868           0 :         action = web_get_action(req, &action_len);
     869           0 :         if (action == NULL)
     870           0 :                 goto bad;
     871             : 
     872           0 :         if (!os_strncasecmp("GetDeviceInfo", action, action_len))
     873           0 :                 ret = web_process_get_device_info(sm, &reply, &replyname);
     874           0 :         else if (!os_strncasecmp("PutMessage", action, action_len))
     875           0 :                 ret = web_process_put_message(sm, data, &reply, &replyname);
     876           0 :         else if (!os_strncasecmp("PutWLANResponse", action, action_len))
     877           0 :                 ret = web_process_put_wlan_response(sm, data, &reply,
     878             :                                                     &replyname);
     879           0 :         else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
     880           0 :                 ret = web_process_set_selected_registrar(sm, cli, data, &reply,
     881             :                                                          &replyname);
     882             :         else
     883           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
     884             : 
     885             : bad:
     886           0 :         if (ret != HTTP_OK)
     887           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
     888           0 :         web_connection_send_reply(req, ret, action, action_len, reply,
     889             :                                   replyname);
     890           0 :         wpabuf_free(reply);
     891           0 : }
     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           0 : 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           0 :         char *hdr = http_request_get_hdr(req);
     920             :         char *h;
     921             :         char *match;
     922             :         int match_len;
     923             :         char *end;
     924             :         int len;
     925           0 :         int got_nt = 0;
     926             :         u8 uuid[UUID_LEN];
     927           0 :         int got_uuid = 0;
     928           0 :         char *callback_urls = NULL;
     929           0 :         struct subscription *s = NULL;
     930           0 :         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
     931             : 
     932           0 :         buf = wpabuf_alloc(1000);
     933           0 :         if (buf == NULL) {
     934           0 :                 http_request_deinit(req);
     935           0 :                 return;
     936             :         }
     937             : 
     938           0 :         wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
     939             :                           (u8 *) hdr, os_strlen(hdr));
     940             : 
     941             :         /* Parse/validate headers */
     942           0 :         h = hdr;
     943             :         /* First line: SUBSCRIBE /wps_event HTTP/1.1
     944             :          * has already been parsed.
     945             :          */
     946           0 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
     947           0 :                 ret = HTTP_PRECONDITION_FAILED;
     948           0 :                 goto error;
     949             :         }
     950           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
     951           0 :         end = os_strchr(h, '\n');
     952             : 
     953           0 :         while (end) {
     954             :                 /* Option line by option line */
     955           0 :                 h = end + 1;
     956           0 :                 end = os_strchr(h, '\n');
     957           0 :                 if (end == NULL)
     958           0 :                         break; /* no unterminated lines allowed */
     959             : 
     960             :                 /* NT assures that it is our type of subscription;
     961             :                  * not used for a renewal.
     962             :                  **/
     963           0 :                 match = "NT:";
     964           0 :                 match_len = os_strlen(match);
     965           0 :                 if (os_strncasecmp(h, match, match_len) == 0) {
     966           0 :                         h += match_len;
     967           0 :                         while (*h == ' ' || *h == '\t')
     968           0 :                                 h++;
     969           0 :                         match = "upnp:event";
     970           0 :                         match_len = os_strlen(match);
     971           0 :                         if (os_strncasecmp(h, match, match_len) != 0) {
     972           0 :                                 ret = HTTP_BAD_REQUEST;
     973           0 :                                 goto error;
     974             :                         }
     975           0 :                         got_nt = 1;
     976           0 :                         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           0 :                 match = "CALLBACK:";
     994           0 :                 match_len = os_strlen(match);
     995           0 :                 if (os_strncasecmp(h, match, match_len) == 0) {
     996           0 :                         h += match_len;
     997           0 :                         while (*h == ' ' || *h == '\t')
     998           0 :                                 h++;
     999           0 :                         len = end - h;
    1000           0 :                         os_free(callback_urls);
    1001           0 :                         callback_urls = dup_binstr(h, len);
    1002           0 :                         if (callback_urls == NULL) {
    1003           0 :                                 ret = HTTP_INTERNAL_SERVER_ERROR;
    1004           0 :                                 goto error;
    1005             :                         }
    1006           0 :                         continue;
    1007             :                 }
    1008             :                 /* SID is only for renewal */
    1009           0 :                 match = "SID:";
    1010           0 :                 match_len = os_strlen(match);
    1011           0 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1012           0 :                         h += match_len;
    1013           0 :                         while (*h == ' ' || *h == '\t')
    1014           0 :                                 h++;
    1015           0 :                         match = "uuid:";
    1016           0 :                         match_len = os_strlen(match);
    1017           0 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1018           0 :                                 ret = HTTP_BAD_REQUEST;
    1019           0 :                                 goto error;
    1020             :                         }
    1021           0 :                         h += match_len;
    1022           0 :                         while (*h == ' ' || *h == '\t')
    1023           0 :                                 h++;
    1024           0 :                         if (uuid_str2bin(h, uuid)) {
    1025           0 :                                 ret = HTTP_BAD_REQUEST;
    1026           0 :                                 goto error;
    1027             :                         }
    1028           0 :                         got_uuid = 1;
    1029           0 :                         continue;
    1030             :                 }
    1031             :                 /* TIMEOUT is requested timeout, but apparently we can
    1032             :                  * just ignore this.
    1033             :                  */
    1034             :         }
    1035             : 
    1036           0 :         if (got_uuid) {
    1037             :                 /* renewal */
    1038           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
    1039           0 :                 if (callback_urls) {
    1040           0 :                         ret = HTTP_BAD_REQUEST;
    1041           0 :                         goto error;
    1042             :                 }
    1043           0 :                 s = subscription_renew(sm, uuid);
    1044           0 :                 if (s == NULL) {
    1045             :                         char str[80];
    1046           0 :                         uuid_bin2str(uuid, str, sizeof(str));
    1047           0 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
    1048             :                                    "SID %s", str);
    1049           0 :                         ret = HTTP_PRECONDITION_FAILED;
    1050           0 :                         goto error;
    1051             :                 }
    1052           0 :         } else if (callback_urls) {
    1053           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
    1054           0 :                 if (!got_nt) {
    1055           0 :                         ret = HTTP_PRECONDITION_FAILED;
    1056           0 :                         goto error;
    1057             :                 }
    1058           0 :                 s = subscription_start(sm, callback_urls);
    1059           0 :                 if (s == NULL) {
    1060           0 :                         ret = HTTP_INTERNAL_SERVER_ERROR;
    1061           0 :                         goto error;
    1062             :                 }
    1063             :         } else {
    1064           0 :                 ret = HTTP_PRECONDITION_FAILED;
    1065           0 :                 goto error;
    1066             :         }
    1067             : 
    1068             :         /* success */
    1069           0 :         http_put_reply_code(buf, HTTP_OK);
    1070           0 :         wpabuf_put_str(buf, http_server_hdr);
    1071           0 :         wpabuf_put_str(buf, http_connection_close);
    1072           0 :         wpabuf_put_str(buf, "Content-Length: 0\r\n");
    1073           0 :         wpabuf_put_str(buf, "SID: uuid:");
    1074             :         /* subscription id */
    1075           0 :         b = wpabuf_put(buf, 0);
    1076           0 :         uuid_bin2str(s->uuid, b, 80);
    1077           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
    1078           0 :         wpabuf_put(buf, os_strlen(b));
    1079           0 :         wpabuf_put_str(buf, "\r\n");
    1080           0 :         wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
    1081           0 :         http_put_date(buf);
    1082             :         /* And empty line to terminate header: */
    1083           0 :         wpabuf_put_str(buf, "\r\n");
    1084             : 
    1085           0 :         os_free(callback_urls);
    1086           0 :         http_request_send_and_deinit(req, buf);
    1087           0 :         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           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
    1112           0 :         http_put_empty(buf, ret);
    1113           0 :         http_request_send_and_deinit(req, buf);
    1114           0 :         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           0 : 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           0 :         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           0 :         int got_uuid = 0;
    1145           0 :         struct subscription *s = NULL;
    1146           0 :         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
    1147             : 
    1148             :         /* Parse/validate headers */
    1149           0 :         h = hdr;
    1150             :         /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
    1151             :          * has already been parsed.
    1152             :          */
    1153           0 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
    1154           0 :                 ret = HTTP_PRECONDITION_FAILED;
    1155           0 :                 goto send_msg;
    1156             :         }
    1157           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
    1158           0 :         end = os_strchr(h, '\n');
    1159             : 
    1160           0 :         while (end) {
    1161             :                 /* Option line by option line */
    1162           0 :                 h = end + 1;
    1163           0 :                 end = os_strchr(h, '\n');
    1164           0 :                 if (end == NULL)
    1165           0 :                         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           0 :                 match = "SID:";
    1179           0 :                 match_len = os_strlen(match);
    1180           0 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1181           0 :                         h += match_len;
    1182           0 :                         while (*h == ' ' || *h == '\t')
    1183           0 :                                 h++;
    1184           0 :                         match = "uuid:";
    1185           0 :                         match_len = os_strlen(match);
    1186           0 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1187           0 :                                 ret = HTTP_BAD_REQUEST;
    1188           0 :                                 goto send_msg;
    1189             :                         }
    1190           0 :                         h += match_len;
    1191           0 :                         while (*h == ' ' || *h == '\t')
    1192           0 :                                 h++;
    1193           0 :                         if (uuid_str2bin(h, uuid)) {
    1194           0 :                                 ret = HTTP_BAD_REQUEST;
    1195           0 :                                 goto send_msg;
    1196             :                         }
    1197           0 :                         got_uuid = 1;
    1198           0 :                         continue;
    1199             :                 }
    1200             : 
    1201           0 :                 match = "NT:";
    1202           0 :                 match_len = os_strlen(match);
    1203           0 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1204           0 :                         ret = HTTP_BAD_REQUEST;
    1205           0 :                         goto send_msg;
    1206             :                 }
    1207             : 
    1208           0 :                 match = "CALLBACK:";
    1209           0 :                 match_len = os_strlen(match);
    1210           0 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1211           0 :                         ret = HTTP_BAD_REQUEST;
    1212           0 :                         goto send_msg;
    1213             :                 }
    1214             :         }
    1215             : 
    1216           0 :         if (got_uuid) {
    1217           0 :                 s = subscription_find(sm, uuid);
    1218           0 :                 if (s) {
    1219             :                         struct subscr_addr *sa;
    1220           0 :                         sa = dl_list_first(&s->addr_list, struct subscr_addr,
    1221             :                                            list);
    1222           0 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
    1223           0 :                                    s, (sa && sa->domain_and_port) ?
    1224             :                                    sa->domain_and_port : "-null-");
    1225           0 :                         dl_list_del(&s->list);
    1226           0 :                         subscription_destroy(s);
    1227             :                 } else {
    1228           0 :                         wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
    1229           0 :                         ret = HTTP_PRECONDITION_FAILED;
    1230           0 :                         goto send_msg;
    1231             :                 }
    1232             :         } else {
    1233           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
    1234             :                            "found)");
    1235           0 :                 ret = HTTP_PRECONDITION_FAILED;
    1236           0 :                 goto send_msg;
    1237             :         }
    1238             : 
    1239           0 :         ret = HTTP_OK;
    1240             : 
    1241             : send_msg:
    1242           0 :         buf = wpabuf_alloc(200);
    1243           0 :         if (buf == NULL) {
    1244           0 :                 http_request_deinit(req);
    1245           0 :                 return;
    1246             :         }
    1247           0 :         http_put_empty(buf, ret);
    1248           0 :         http_request_send_and_deinit(req, buf);
    1249             : }
    1250             : 
    1251             : 
    1252             : /* Send error in response to unknown requests */
    1253           0 : static void web_connection_unimplemented(struct http_request *req)
    1254             : {
    1255             :         struct wpabuf *buf;
    1256           0 :         buf = wpabuf_alloc(200);
    1257           0 :         if (buf == NULL) {
    1258           0 :                 http_request_deinit(req);
    1259           0 :                 return;
    1260             :         }
    1261           0 :         http_put_empty(buf, HTTP_UNIMPLEMENTED);
    1262           0 :         http_request_send_and_deinit(req, buf);
    1263             : }
    1264             : 
    1265             : 
    1266             : 
    1267             : /* Called when we have gotten an apparently valid http request.
    1268             :  */
    1269           0 : static void web_connection_check_data(void *ctx, struct http_request *req)
    1270             : {
    1271           0 :         struct upnp_wps_device_sm *sm = ctx;
    1272           0 :         enum httpread_hdr_type htype = http_request_get_type(req);
    1273           0 :         char *filename = http_request_get_uri(req);
    1274           0 :         struct sockaddr_in *cli = http_request_get_cli_addr(req);
    1275             : 
    1276           0 :         if (!filename) {
    1277           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
    1278           0 :                 http_request_deinit(req);
    1279           0 :                 return;
    1280             :         }
    1281             :         /* Trim leading slashes from filename */
    1282           0 :         while (*filename == '/')
    1283           0 :                 filename++;
    1284             : 
    1285           0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
    1286           0 :                    htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
    1287             : 
    1288           0 :         switch (htype) {
    1289             :         case HTTPREAD_HDR_TYPE_GET:
    1290           0 :                 web_connection_parse_get(sm, req, filename);
    1291           0 :                 break;
    1292             :         case HTTPREAD_HDR_TYPE_POST:
    1293           0 :                 web_connection_parse_post(sm, cli, req, filename);
    1294           0 :                 break;
    1295             :         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
    1296           0 :                 web_connection_parse_subscribe(sm, req, filename);
    1297           0 :                 break;
    1298             :         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
    1299           0 :                 web_connection_parse_unsubscribe(sm, req, filename);
    1300           0 :                 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           0 :                 web_connection_unimplemented(req);
    1310           0 :                 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           0 : void web_listener_stop(struct upnp_wps_device_sm *sm)
    1322             : {
    1323           0 :         http_server_deinit(sm->web_srv);
    1324           0 :         sm->web_srv = NULL;
    1325           0 : }
    1326             : 
    1327             : 
    1328           0 : int web_listener_start(struct upnp_wps_device_sm *sm)
    1329             : {
    1330             :         struct in_addr addr;
    1331           0 :         addr.s_addr = sm->ip_addr;
    1332           0 :         sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
    1333             :                                        sm);
    1334           0 :         if (sm->web_srv == NULL) {
    1335           0 :                 web_listener_stop(sm);
    1336           0 :                 return -1;
    1337             :         }
    1338           0 :         sm->web_port = http_server_get_port(sm->web_srv);
    1339             : 
    1340           0 :         return 0;
    1341             : }

Generated by: LCOV version 1.10