07a004c68134db7e78dae07d537fbbe7ede1ade9
[libeap.git] / src / wps / wps_upnp_web.c
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>GetAPSettings</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>NewAPSettings</name>\n"
85 "<direction>out</direction>\n"
86 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
87 "</argument>\n"
88 "</argumentList>\n"
89 "</action>\n"
90 "<action>\n"
91 "<name>SetAPSettings</name>\n"
92 "<argumentList>\n"
93 "<argument>\n"
94 "<name>APSettings</name>\n"
95 "<direction>in</direction>\n"
96 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
97 "</argument>\n"
98 "</argumentList>\n"
99 "</action>\n"
100 "<action>\n"
101 "<name>DelAPSettings</name>\n"
102 "<argumentList>\n"
103 "<argument>\n"
104 "<name>NewAPSettings</name>\n"
105 "<direction>in</direction>\n"
106 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
107 "</argument>\n"
108 "</argumentList>\n"
109 "</action>\n"
110 "<action>\n"
111 "<name>GetSTASettings</name>\n"
112 "<argumentList>\n"
113 "<argument>\n"
114 "<name>NewMessage</name>\n"
115 "<direction>in</direction>\n"
116 "<relatedStateVariable>Message</relatedStateVariable>\n"
117 "</argument>\n"
118 "<argument>\n"
119 "<name>NewSTASettings</name>\n"
120 "<direction>out</direction>\n"
121 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
122 "</argument>\n"
123 "</argumentList>\n"
124 "</action>\n"
125 "<action>\n"
126 "<name>SetSTASettings</name>\n"
127 "<argumentList>\n"
128 "<argument>\n"
129 "<name>NewSTASettings</name>\n"
130 "<direction>out</direction>\n"
131 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
132 "</argument>\n"
133 "</argumentList>\n"
134 "</action>\n"
135 "<action>\n"
136 "<name>DelSTASettings</name>\n"
137 "<argumentList>\n"
138 "<argument>\n"
139 "<name>NewSTASettings</name>\n"
140 "<direction>in</direction>\n"
141 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
142 "</argument>\n"
143 "</argumentList>\n"
144 "</action>\n"
145 "<action>\n"
146 "<name>PutWLANResponse</name>\n"
147 "<argumentList>\n"
148 "<argument>\n"
149 "<name>NewMessage</name>\n"
150 "<direction>in</direction>\n"
151 "<relatedStateVariable>Message</relatedStateVariable>\n"
152 "</argument>\n"
153 "<argument>\n"
154 "<name>NewWLANEventType</name>\n"
155 "<direction>in</direction>\n"
156 "<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
157 "</argument>\n"
158 "<argument>\n"
159 "<name>NewWLANEventMAC</name>\n"
160 "<direction>in</direction>\n"
161 "<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
162 "</argument>\n"
163 "</argumentList>\n"
164 "</action>\n"
165 "<action>\n"
166 "<name>SetSelectedRegistrar</name>\n"
167 "<argumentList>\n"
168 "<argument>\n"
169 "<name>NewMessage</name>\n"
170 "<direction>in</direction>\n"
171 "<relatedStateVariable>Message</relatedStateVariable>\n"
172 "</argument>\n"
173 "</argumentList>\n"
174 "</action>\n"
175 "<action>\n"
176 "<name>RebootAP</name>\n"
177 "<argumentList>\n"
178 "<argument>\n"
179 "<name>NewAPSettings</name>\n"
180 "<direction>in</direction>\n"
181 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
182 "</argument>\n"
183 "</argumentList>\n"
184 "</action>\n"
185 "<action>\n"
186 "<name>ResetAP</name>\n"
187 "<argumentList>\n"
188 "<argument>\n"
189 "<name>NewMessage</name>\n"
190 "<direction>in</direction>\n"
191 "<relatedStateVariable>Message</relatedStateVariable>\n"
192 "</argument>\n"
193 "</argumentList>\n"
194 "</action>\n"
195 "<action>\n"
196 "<name>RebootSTA</name>\n"
197 "<argumentList>\n"
198 "<argument>\n"
199 "<name>NewSTASettings</name>\n"
200 "<direction>in</direction>\n"
201 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
202 "</argument>\n"
203 "</argumentList>\n"
204 "</action>\n"
205 "<action>\n"
206 "<name>ResetSTA</name>\n"
207 "<argumentList>\n"
208 "<argument>\n"
209 "<name>NewMessage</name>\n"
210 "<direction>in</direction>\n"
211 "<relatedStateVariable>Message</relatedStateVariable>\n"
212 "</argument>\n"
213 "</argumentList>\n"
214 "</action>\n"
215 "</actionList>\n"
216 "<serviceStateTable>\n"
217 "<stateVariable sendEvents=\"no\">\n"
218 "<name>Message</name>\n"
219 "<dataType>bin.base64</dataType>\n"
220 "</stateVariable>\n"
221 "<stateVariable sendEvents=\"no\">\n"
222 "<name>InMessage</name>\n"
223 "<dataType>bin.base64</dataType>\n"
224 "</stateVariable>\n"
225 "<stateVariable sendEvents=\"no\">\n"
226 "<name>OutMessage</name>\n"
227 "<dataType>bin.base64</dataType>\n"
228 "</stateVariable>\n"
229 "<stateVariable sendEvents=\"no\">\n"
230 "<name>DeviceInfo</name>\n"
231 "<dataType>bin.base64</dataType>\n"
232 "</stateVariable>\n"
233 "<stateVariable sendEvents=\"no\">\n"
234 "<name>APSettings</name>\n"
235 "<dataType>bin.base64</dataType>\n"
236 "</stateVariable>\n"
237 "<stateVariable sendEvents=\"yes\">\n"
238 "<name>APStatus</name>\n"
239 "<dataType>ui1</dataType>\n"
240 "</stateVariable>\n"
241 "<stateVariable sendEvents=\"no\">\n"
242 "<name>STASettings</name>\n"
243 "<dataType>bin.base64</dataType>\n"
244 "</stateVariable>\n"
245 "<stateVariable sendEvents=\"yes\">\n"
246 "<name>STAStatus</name>\n"
247 "<dataType>ui1</dataType>\n"
248 "</stateVariable>\n"
249 "<stateVariable sendEvents=\"yes\">\n"
250 "<name>WLANEvent</name>\n"
251 "<dataType>bin.base64</dataType>\n"
252 "</stateVariable>\n"
253 "<stateVariable sendEvents=\"no\">\n"
254 "<name>WLANEventType</name>\n"
255 "<dataType>ui1</dataType>\n"
256 "</stateVariable>\n"
257 "<stateVariable sendEvents=\"no\">\n"
258 "<name>WLANEventMAC</name>\n"
259 "<dataType>string</dataType>\n"
260 "</stateVariable>\n"
261 "<stateVariable sendEvents=\"no\">\n"
262 "<name>WLANResponse</name>\n"
263 "<dataType>bin.base64</dataType>\n"
264 "</stateVariable>\n"
265 "</serviceStateTable>\n"
266 "</scpd>\n"
267 ;
268
269
270 static const char *wps_device_xml_prefix =
271         "<?xml version=\"1.0\"?>\n"
272         "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
273         "<specVersion>\n"
274         "<major>1</major>\n"
275         "<minor>0</minor>\n"
276         "</specVersion>\n"
277         "<device>\n"
278         "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
279         "</deviceType>\n";
280
281 static const char *wps_device_xml_postfix =
282         "<serviceList>\n"
283         "<service>\n"
284         "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
285         "</serviceType>\n"
286         "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
287         "\n"
288         "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
289         "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
290         "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
291         "</service>\n"
292         "</serviceList>\n"
293         "</device>\n"
294         "</root>\n";
295
296
297 /* format_wps_device_xml -- produce content of "file" wps_device.xml
298  * (UPNP_WPS_DEVICE_XML_FILE)
299  */
300 static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
301                                   struct wpabuf *buf)
302 {
303         const char *s;
304         char uuid_string[80];
305
306         wpabuf_put_str(buf, wps_device_xml_prefix);
307
308         /*
309          * Add required fields with default values if not configured. Add
310          * optional and recommended fields only if configured.
311          */
312         s = sm->wps->friendly_name;
313         s = ((s && *s) ? s : "WPS Access Point");
314         xml_add_tagged_data(buf, "friendlyName", s);
315
316         s = sm->wps->dev.manufacturer;
317         s = ((s && *s) ? s : "");
318         xml_add_tagged_data(buf, "manufacturer", s);
319
320         if (sm->wps->manufacturer_url)
321                 xml_add_tagged_data(buf, "manufacturerURL",
322                                     sm->wps->manufacturer_url);
323
324         if (sm->wps->model_description)
325                 xml_add_tagged_data(buf, "modelDescription",
326                                     sm->wps->model_description);
327
328         s = sm->wps->dev.model_name;
329         s = ((s && *s) ? s : "");
330         xml_add_tagged_data(buf, "modelName", s);
331
332         if (sm->wps->dev.model_number)
333                 xml_add_tagged_data(buf, "modelNumber",
334                                     sm->wps->dev.model_number);
335
336         if (sm->wps->model_url)
337                 xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
338
339         if (sm->wps->dev.serial_number)
340                 xml_add_tagged_data(buf, "serialNumber",
341                                     sm->wps->dev.serial_number);
342
343         uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
344         s = uuid_string;
345         /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
346          * easily...
347          */
348         wpabuf_put_str(buf, "<UDN>uuid:");
349         xml_data_encode(buf, s, os_strlen(s));
350         wpabuf_put_str(buf, "</UDN>\n");
351
352         if (sm->wps->upc)
353                 xml_add_tagged_data(buf, "UPC", sm->wps->upc);
354
355         wpabuf_put_str(buf, wps_device_xml_postfix);
356 }
357
358
359 static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
360 {
361         wpabuf_put_str(buf, "HTTP/1.1 ");
362         switch (code) {
363         case HTTP_OK:
364                 wpabuf_put_str(buf, "200 OK\r\n");
365                 break;
366         case HTTP_BAD_REQUEST:
367                 wpabuf_put_str(buf, "400 Bad request\r\n");
368                 break;
369         case HTTP_PRECONDITION_FAILED:
370                 wpabuf_put_str(buf, "412 Precondition failed\r\n");
371                 break;
372         case HTTP_UNIMPLEMENTED:
373                 wpabuf_put_str(buf, "501 Unimplemented\r\n");
374                 break;
375         case HTTP_INTERNAL_SERVER_ERROR:
376         default:
377                 wpabuf_put_str(buf, "500 Internal server error\r\n");
378                 break;
379         }
380 }
381
382
383 static void http_put_date(struct wpabuf *buf)
384 {
385         wpabuf_put_str(buf, "Date: ");
386         format_date(buf);
387         wpabuf_put_str(buf, "\r\n");
388 }
389
390
391 static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
392 {
393         http_put_reply_code(buf, code);
394         wpabuf_put_str(buf, http_server_hdr);
395         wpabuf_put_str(buf, http_connection_close);
396         wpabuf_put_str(buf, "Content-Length: 0\r\n"
397                        "\r\n");
398 }
399
400
401 /* Given that we have received a header w/ GET, act upon it
402  *
403  * Format of GET (case-insensitive):
404  *
405  * First line must be:
406  *      GET /<file> HTTP/1.1
407  * Since we don't do anything fancy we just ignore other lines.
408  *
409  * Our response (if no error) which includes only required lines is:
410  * HTTP/1.1 200 OK
411  * Connection: close
412  * Content-Type: text/xml
413  * Date: <rfc1123-date>
414  *
415  * Header lines must end with \r\n
416  * Per RFC 2616, content-length: is not required but connection:close
417  * would appear to be required (given that we will be closing it!).
418  */
419 static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
420                                      struct http_request *hreq, char *filename)
421 {
422         struct wpabuf *buf; /* output buffer, allocated */
423         char *put_length_here;
424         char *body_start;
425         enum {
426                 GET_DEVICE_XML_FILE,
427                 GET_SCPD_XML_FILE
428         } req;
429         size_t extra_len = 0;
430         int body_length;
431         char len_buf[10];
432
433         /*
434          * It is not required that filenames be case insensitive but it is
435          * allowed and cannot hurt here.
436          */
437         if (filename == NULL)
438                 filename = "(null)"; /* just in case */
439         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
440                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
441                 req = GET_DEVICE_XML_FILE;
442                 extra_len = 3000;
443                 if (sm->wps->friendly_name)
444                         extra_len += os_strlen(sm->wps->friendly_name);
445                 if (sm->wps->manufacturer_url)
446                         extra_len += os_strlen(sm->wps->manufacturer_url);
447                 if (sm->wps->model_description)
448                         extra_len += os_strlen(sm->wps->model_description);
449                 if (sm->wps->model_url)
450                         extra_len += os_strlen(sm->wps->model_url);
451                 if (sm->wps->upc)
452                         extra_len += os_strlen(sm->wps->upc);
453         } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
454                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
455                 req = GET_SCPD_XML_FILE;
456                 extra_len = os_strlen(wps_scpd_xml);
457         } else {
458                 /* File not found */
459                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
460                            filename);
461                 buf = wpabuf_alloc(200);
462                 if (buf == NULL) {
463                         http_request_deinit(hreq);
464                         return;
465                 }
466                 wpabuf_put_str(buf,
467                                "HTTP/1.1 404 Not Found\r\n"
468                                "Connection: close\r\n");
469
470                 http_put_date(buf);
471
472                 /* terminating empty line */
473                 wpabuf_put_str(buf, "\r\n");
474
475                 goto send_buf;
476         }
477
478         buf = wpabuf_alloc(1000 + extra_len);
479         if (buf == NULL) {
480                 http_request_deinit(hreq);
481                 return;
482         }
483
484         wpabuf_put_str(buf,
485                        "HTTP/1.1 200 OK\r\n"
486                        "Content-Type: text/xml; charset=\"utf-8\"\r\n");
487         wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
488         wpabuf_put_str(buf, "Connection: close\r\n");
489         wpabuf_put_str(buf, "Content-Length: ");
490         /*
491          * We will paste the length in later, leaving some extra whitespace.
492          * HTTP code is supposed to be tolerant of extra whitespace.
493          */
494         put_length_here = wpabuf_put(buf, 0);
495         wpabuf_put_str(buf, "        \r\n");
496
497         http_put_date(buf);
498
499         /* terminating empty line */
500         wpabuf_put_str(buf, "\r\n");
501
502         body_start = wpabuf_put(buf, 0);
503
504         switch (req) {
505         case GET_DEVICE_XML_FILE:
506                 format_wps_device_xml(sm, buf);
507                 break;
508         case GET_SCPD_XML_FILE:
509                 wpabuf_put_str(buf, wps_scpd_xml);
510                 break;
511         }
512
513         /* Now patch in the content length at the end */
514         body_length = (char *) wpabuf_put(buf, 0) - body_start;
515         os_snprintf(len_buf, 10, "%d", body_length);
516         os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
517
518 send_buf:
519         http_request_send_and_deinit(hreq, buf);
520 }
521
522
523 static enum http_reply_code
524 web_process_get_device_info(struct upnp_wps_device_sm *sm,
525                             struct wpabuf **reply, const char **replyname)
526 {
527         static const char *name = "NewDeviceInfo";
528
529         wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
530         if (sm->ctx->rx_req_get_device_info == NULL)
531                 return HTTP_INTERNAL_SERVER_ERROR;
532         *reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer);
533         if (*reply == NULL) {
534                 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
535                 return HTTP_INTERNAL_SERVER_ERROR;
536         }
537         *replyname = name;
538         return HTTP_OK;
539 }
540
541
542 static enum http_reply_code
543 web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
544                         struct wpabuf **reply, const char **replyname)
545 {
546         struct wpabuf *msg;
547         static const char *name = "NewOutMessage";
548         enum http_reply_code ret;
549
550         /*
551          * PutMessage is used by external UPnP-based Registrar to perform WPS
552          * operation with the access point itself; as compared with
553          * PutWLANResponse which is for proxying.
554          */
555         wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
556         if (sm->ctx->rx_req_put_message == NULL)
557                 return HTTP_INTERNAL_SERVER_ERROR;
558         msg = xml_get_base64_item(data, "NewInMessage", &ret);
559         if (msg == NULL)
560                 return ret;
561         *reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg);
562         wpabuf_free(msg);
563         if (*reply == NULL)
564                 return HTTP_INTERNAL_SERVER_ERROR;
565         *replyname = name;
566         return HTTP_OK;
567 }
568
569
570 static enum http_reply_code
571 web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data,
572                             struct wpabuf **reply, const char **replyname)
573 {
574         struct wpabuf *msg;
575         static const char *name = "NewAPSettings";
576         enum http_reply_code ret;
577
578         wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings");
579         if (sm->ctx->rx_req_get_ap_settings == NULL)
580                 return HTTP_INTERNAL_SERVER_ERROR;
581         msg = xml_get_base64_item(data, "NewMessage", &ret);
582         if (msg == NULL)
583                 return ret;
584         *reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg);
585         wpabuf_free(msg);
586         if (*reply == NULL)
587                 return HTTP_INTERNAL_SERVER_ERROR;
588         *replyname = name;
589         return HTTP_OK;
590 }
591
592
593 static enum http_reply_code
594 web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data,
595                             struct wpabuf **reply, const char **replyname)
596 {
597         struct wpabuf *msg;
598         enum http_reply_code ret;
599
600         wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings");
601         msg = xml_get_base64_item(data, "NewAPSettings", &ret);
602         if (msg == NULL)
603                 return ret;
604         if (!sm->ctx->rx_req_set_ap_settings ||
605             sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) {
606                 wpabuf_free(msg);
607                 return HTTP_INTERNAL_SERVER_ERROR;
608         }
609         wpabuf_free(msg);
610         *replyname = NULL;
611         *reply = NULL;
612         return HTTP_OK;
613 }
614
615
616 static enum http_reply_code
617 web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data,
618                             struct wpabuf **reply, const char **replyname)
619 {
620         struct wpabuf *msg;
621         enum http_reply_code ret;
622
623         wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings");
624         msg = xml_get_base64_item(data, "NewAPSettings", &ret);
625         if (msg == NULL)
626                 return ret;
627         if (!sm->ctx->rx_req_del_ap_settings ||
628             sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) {
629                 wpabuf_free(msg);
630                 return HTTP_INTERNAL_SERVER_ERROR;
631         }
632         wpabuf_free(msg);
633         *replyname = NULL;
634         *reply = NULL;
635         return HTTP_OK;
636 }
637
638
639 static enum http_reply_code
640 web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data,
641                              struct wpabuf **reply, const char **replyname)
642 {
643         struct wpabuf *msg;
644         static const char *name = "NewSTASettings";
645         enum http_reply_code ret;
646
647         wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings");
648         if (sm->ctx->rx_req_get_sta_settings == NULL)
649                 return HTTP_INTERNAL_SERVER_ERROR;
650         msg = xml_get_base64_item(data, "NewMessage", &ret);
651         if (msg == NULL)
652                 return ret;
653         *reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg);
654         wpabuf_free(msg);
655         if (*reply == NULL)
656                 return HTTP_INTERNAL_SERVER_ERROR;
657         *replyname = name;
658         return HTTP_OK;
659 }
660
661
662 static enum http_reply_code
663 web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data,
664                              struct wpabuf **reply, const char **replyname)
665 {
666         struct wpabuf *msg;
667         enum http_reply_code ret;
668
669         wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings");
670         msg = xml_get_base64_item(data, "NewSTASettings", &ret);
671         if (msg == NULL)
672                 return ret;
673         if (!sm->ctx->rx_req_set_sta_settings ||
674             sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) {
675                 wpabuf_free(msg);
676                 return HTTP_INTERNAL_SERVER_ERROR;
677         }
678         wpabuf_free(msg);
679         *replyname = NULL;
680         *reply = NULL;
681         return HTTP_OK;
682 }
683
684
685 static enum http_reply_code
686 web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data,
687                              struct wpabuf **reply, const char **replyname)
688 {
689         struct wpabuf *msg;
690         enum http_reply_code ret;
691
692         wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings");
693         msg = xml_get_base64_item(data, "NewSTASettings", &ret);
694         if (msg == NULL)
695                 return ret;
696         if (!sm->ctx->rx_req_del_sta_settings ||
697             sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) {
698                 wpabuf_free(msg);
699                 return HTTP_INTERNAL_SERVER_ERROR;
700         }
701         wpabuf_free(msg);
702         *replyname = NULL;
703         *reply = NULL;
704         return HTTP_OK;
705 }
706
707
708 static enum http_reply_code
709 web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
710                               struct wpabuf **reply, const char **replyname)
711 {
712         struct wpabuf *msg;
713         enum http_reply_code ret;
714         u8 macaddr[ETH_ALEN];
715         int ev_type;
716         int type;
717         char *val;
718
719         /*
720          * External UPnP-based Registrar is passing us a message to be proxied
721          * over to a Wi-Fi -based client of ours.
722          */
723
724         wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
725         msg = xml_get_base64_item(data, "NewMessage", &ret);
726         if (msg == NULL)
727                 return ret;
728         val = xml_get_first_item(data, "NewWLANEventType");
729         if (val == NULL) {
730                 wpabuf_free(msg);
731                 return UPNP_ARG_VALUE_INVALID;
732         }
733         ev_type = atol(val);
734         os_free(val);
735         val = xml_get_first_item(data, "NewWLANEventMAC");
736         if (val == NULL || hwaddr_aton(val, macaddr)) {
737                 wpabuf_free(msg);
738                 os_free(val);
739                 return UPNP_ARG_VALUE_INVALID;
740         }
741         os_free(val);
742         if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
743                 struct wps_parse_attr attr;
744                 if (wps_parse_msg(msg, &attr) < 0 ||
745                     attr.msg_type == NULL)
746                         type = -1;
747                 else
748                         type = *attr.msg_type;
749                 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
750         } else
751                 type = -1;
752         if (!sm->ctx->rx_req_put_wlan_response ||
753             sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
754                                               type)) {
755                 wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
756                            "rx_req_put_wlan_response");
757                 wpabuf_free(msg);
758                 return HTTP_INTERNAL_SERVER_ERROR;
759         }
760         wpabuf_free(msg);
761         *replyname = NULL;
762         *reply = NULL;
763         return HTTP_OK;
764 }
765
766
767 static enum http_reply_code
768 web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data,
769                                    struct wpabuf **reply,
770                                    const char **replyname)
771 {
772         struct wpabuf *msg;
773         enum http_reply_code ret;
774
775         wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
776         msg = xml_get_base64_item(data, "NewMessage", &ret);
777         if (msg == NULL)
778                 return ret;
779         if (!sm->ctx->rx_req_set_selected_registrar ||
780             sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) {
781                 wpabuf_free(msg);
782                 return HTTP_INTERNAL_SERVER_ERROR;
783         }
784         wpabuf_free(msg);
785         *replyname = NULL;
786         *reply = NULL;
787         return HTTP_OK;
788 }
789
790
791 static enum http_reply_code
792 web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data,
793                       struct wpabuf **reply, const char **replyname)
794 {
795         struct wpabuf *msg;
796         enum http_reply_code ret;
797
798         wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP");
799         msg = xml_get_base64_item(data, "NewAPSettings", &ret);
800         if (msg == NULL)
801                 return ret;
802         if (!sm->ctx->rx_req_reboot_ap ||
803             sm->ctx->rx_req_reboot_ap(sm->priv, msg)) {
804                 wpabuf_free(msg);
805                 return HTTP_INTERNAL_SERVER_ERROR;
806         }
807         wpabuf_free(msg);
808         *replyname = NULL;
809         *reply = NULL;
810         return HTTP_OK;
811 }
812
813
814 static enum http_reply_code
815 web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data,
816                      struct wpabuf **reply, const char **replyname)
817 {
818         struct wpabuf *msg;
819         enum http_reply_code ret;
820
821         wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP");
822         msg = xml_get_base64_item(data, "NewMessage", &ret);
823         if (msg == NULL)
824                 return ret;
825         if (!sm->ctx->rx_req_reset_ap ||
826             sm->ctx->rx_req_reset_ap(sm->priv, msg)) {
827                 wpabuf_free(msg);
828                 return HTTP_INTERNAL_SERVER_ERROR;
829         }
830         wpabuf_free(msg);
831         *replyname = NULL;
832         *reply = NULL;
833         return HTTP_OK;
834 }
835
836
837 static enum http_reply_code
838 web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data,
839                        struct wpabuf **reply, const char **replyname)
840 {
841         struct wpabuf *msg;
842         enum http_reply_code ret;
843
844         wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA");
845         msg = xml_get_base64_item(data, "NewSTASettings", &ret);
846         if (msg == NULL)
847                 return ret;
848         if (!sm->ctx->rx_req_reboot_sta ||
849             sm->ctx->rx_req_reboot_sta(sm->priv, msg)) {
850                 wpabuf_free(msg);
851                 return HTTP_INTERNAL_SERVER_ERROR;
852         }
853         wpabuf_free(msg);
854         *replyname = NULL;
855         *reply = NULL;
856         return HTTP_OK;
857 }
858
859
860 static enum http_reply_code
861 web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data,
862                       struct wpabuf **reply, const char **replyname)
863 {
864         struct wpabuf *msg;
865         enum http_reply_code ret;
866
867         wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA");
868         msg = xml_get_base64_item(data, "NewMessage", &ret);
869         if (msg == NULL)
870                 return ret;
871         if (!sm->ctx->rx_req_reset_sta ||
872             sm->ctx->rx_req_reset_sta(sm->priv, msg)) {
873                 wpabuf_free(msg);
874                 return HTTP_INTERNAL_SERVER_ERROR;
875         }
876         wpabuf_free(msg);
877         *replyname = NULL;
878         *reply = NULL;
879         return HTTP_OK;
880 }
881
882
883 static const char *soap_prefix =
884         "<?xml version=\"1.0\"?>\n"
885         "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
886         "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
887         "<s:Body>\n";
888 static const char *soap_postfix =
889         "</s:Body>\n</s:Envelope>\n";
890
891 static const char *soap_error_prefix =
892         "<s:Fault>\n"
893         "<faultcode>s:Client</faultcode>\n"
894         "<faultstring>UPnPError</faultstring>\n"
895         "<detail>\n"
896         "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
897 static const char *soap_error_postfix =
898         "<errorDescription>Error</errorDescription>\n"
899         "</UPnPError>\n"
900         "</detail>\n"
901         "</s:Fault>\n";
902
903 static void web_connection_send_reply(struct http_request *req,
904                                       enum http_reply_code ret,
905                                       const char *action, int action_len,
906                                       const struct wpabuf *reply,
907                                       const char *replyname)
908 {
909         struct wpabuf *buf;
910         char *replydata;
911         char *put_length_here = NULL;
912         char *body_start = NULL;
913
914         if (reply) {
915                 size_t len;
916                 replydata = (char *) base64_encode(wpabuf_head(reply),
917                                                    wpabuf_len(reply), &len);
918         } else
919                 replydata = NULL;
920
921         /* Parameters of the response:
922          *      action(action_len) -- action we are responding to
923          *      replyname -- a name we need for the reply
924          *      replydata -- NULL or null-terminated string
925          */
926         buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
927                            (action_len > 0 ? action_len * 2 : 0));
928         if (buf == NULL) {
929                 wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
930                            "POST");
931                 os_free(replydata);
932                 http_request_deinit(req);
933                 return;
934         }
935
936         /*
937          * Assuming we will be successful, put in the output header first.
938          * Note: we do not keep connections alive (and httpread does
939          * not support it)... therefore we must have Connection: close.
940          */
941         if (ret == HTTP_OK) {
942                 wpabuf_put_str(buf,
943                                "HTTP/1.1 200 OK\r\n"
944                                "Content-Type: text/xml; "
945                                "charset=\"utf-8\"\r\n");
946         } else {
947                 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
948         }
949         wpabuf_put_str(buf, http_connection_close);
950
951         wpabuf_put_str(buf, "Content-Length: ");
952         /*
953          * We will paste the length in later, leaving some extra whitespace.
954          * HTTP code is supposed to be tolerant of extra whitespace.
955          */
956         put_length_here = wpabuf_put(buf, 0);
957         wpabuf_put_str(buf, "        \r\n");
958
959         http_put_date(buf);
960
961         /* terminating empty line */
962         wpabuf_put_str(buf, "\r\n");
963
964         body_start = wpabuf_put(buf, 0);
965
966         if (ret == HTTP_OK) {
967                 wpabuf_put_str(buf, soap_prefix);
968                 wpabuf_put_str(buf, "<u:");
969                 wpabuf_put_data(buf, action, action_len);
970                 wpabuf_put_str(buf, "Response xmlns:u=\"");
971                 wpabuf_put_str(buf, urn_wfawlanconfig);
972                 wpabuf_put_str(buf, "\">\n");
973                 if (replydata && replyname) {
974                         /* TODO: might possibly need to escape part of reply
975                          * data? ...
976                          * probably not, unlikely to have ampersand(&) or left
977                          * angle bracket (<) in it...
978                          */
979                         wpabuf_printf(buf, "<%s>", replyname);
980                         wpabuf_put_str(buf, replydata);
981                         wpabuf_printf(buf, "</%s>\n", replyname);
982                 }
983                 wpabuf_put_str(buf, "</u:");
984                 wpabuf_put_data(buf, action, action_len);
985                 wpabuf_put_str(buf, "Response>\n");
986                 wpabuf_put_str(buf, soap_postfix);
987         } else {
988                 /* Error case */
989                 wpabuf_put_str(buf, soap_prefix);
990                 wpabuf_put_str(buf, soap_error_prefix);
991                 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
992                 wpabuf_put_str(buf, soap_error_postfix);
993                 wpabuf_put_str(buf, soap_postfix);
994         }
995         os_free(replydata);
996
997         /* Now patch in the content length at the end */
998         if (body_start && put_length_here) {
999                 int body_length = (char *) wpabuf_put(buf, 0) - body_start;
1000                 char len_buf[10];
1001                 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
1002                 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
1003         }
1004
1005         http_request_send_and_deinit(req, buf);
1006 }
1007
1008
1009 static const char * web_get_action(struct http_request *req,
1010                                    const char *filename, size_t *action_len)
1011 {
1012         const char *match;
1013         int match_len;
1014         char *b;
1015         char *action;
1016
1017         *action_len = 0;
1018         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
1019                 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
1020                            filename);
1021                 return NULL;
1022         }
1023         /* The SOAPAction line of the header tells us what we want to do */
1024         b = http_request_get_hdr_line(req, "SOAPAction:");
1025         if (b == NULL)
1026                 return NULL;
1027         if (*b == '"')
1028                 b++;
1029         else
1030                 return NULL;
1031         match = urn_wfawlanconfig;
1032         match_len = os_strlen(urn_wfawlanconfig) - 1;
1033         if (os_strncasecmp(b, match, match_len))
1034                 return NULL;
1035         b += match_len;
1036         /* skip over version */
1037         while (isgraph(*b) && *b != '#')
1038                 b++;
1039         if (*b != '#')
1040                 return NULL;
1041         b++;
1042         /* Following the sharp(#) should be the action and a double quote */
1043         action = b;
1044         while (isgraph(*b) && *b != '"')
1045                 b++;
1046         if (*b != '"')
1047                 return NULL;
1048         *action_len = b - action;
1049         return action;
1050 }
1051
1052
1053 /* Given that we have received a header w/ POST, act upon it
1054  *
1055  * Format of POST (case-insensitive):
1056  *
1057  * First line must be:
1058  *      POST /<file> HTTP/1.1
1059  * Since we don't do anything fancy we just ignore other lines.
1060  *
1061  * Our response (if no error) which includes only required lines is:
1062  * HTTP/1.1 200 OK
1063  * Connection: close
1064  * Content-Type: text/xml
1065  * Date: <rfc1123-date>
1066  *
1067  * Header lines must end with \r\n
1068  * Per RFC 2616, content-length: is not required but connection:close
1069  * would appear to be required (given that we will be closing it!).
1070  */
1071 static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
1072                                       struct http_request *req,
1073                                       const char *filename)
1074 {
1075         enum http_reply_code ret;
1076         char *data = http_request_get_data(req); /* body of http msg */
1077         const char *action;
1078         size_t action_len;
1079         const char *replyname = NULL; /* argument name for the reply */
1080         struct wpabuf *reply = NULL; /* data for the reply */
1081
1082         ret = UPNP_INVALID_ACTION;
1083         action = web_get_action(req, filename, &action_len);
1084         if (action == NULL)
1085                 goto bad;
1086
1087         /*
1088          * There are quite a few possible actions. Although we appear to
1089          * support them all here, not all of them are necessarily supported by
1090          * callbacks at higher levels.
1091          */
1092         if (!os_strncasecmp("GetDeviceInfo", action, action_len))
1093                 ret = web_process_get_device_info(sm, &reply, &replyname);
1094         else if (!os_strncasecmp("PutMessage", action, action_len))
1095                 ret = web_process_put_message(sm, data, &reply, &replyname);
1096         else if (!os_strncasecmp("GetAPSettings", action, action_len))
1097                 ret = web_process_get_ap_settings(sm, data, &reply,
1098                                                   &replyname);
1099         else if (!os_strncasecmp("SetAPSettings", action, action_len))
1100                 ret = web_process_set_ap_settings(sm, data, &reply,
1101                                                   &replyname);
1102         else if (!os_strncasecmp("DelAPSettings", action, action_len))
1103                 ret = web_process_del_ap_settings(sm, data, &reply,
1104                                                   &replyname);
1105         else if (!os_strncasecmp("GetSTASettings", action, action_len))
1106                 ret = web_process_get_sta_settings(sm, data, &reply,
1107                                                    &replyname);
1108         else if (!os_strncasecmp("SetSTASettings", action, action_len))
1109                 ret = web_process_set_sta_settings(sm, data, &reply,
1110                                                    &replyname);
1111         else if (!os_strncasecmp("DelSTASettings", action, action_len))
1112                 ret = web_process_del_sta_settings(sm, data, &reply,
1113                                                   &replyname);
1114         else if (!os_strncasecmp("PutWLANResponse", action, action_len))
1115                 ret = web_process_put_wlan_response(sm, data, &reply,
1116                                                     &replyname);
1117         else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
1118                 ret = web_process_set_selected_registrar(sm, data, &reply,
1119                                                          &replyname);
1120         else if (!os_strncasecmp("RebootAP", action, action_len))
1121                 ret = web_process_reboot_ap(sm, data, &reply, &replyname);
1122         else if (!os_strncasecmp("ResetAP", action, action_len))
1123                 ret = web_process_reset_ap(sm, data, &reply, &replyname);
1124         else if (!os_strncasecmp("RebootSTA", action, action_len))
1125                 ret = web_process_reboot_sta(sm, data, &reply, &replyname);
1126         else if (!os_strncasecmp("ResetSTA", action, action_len))
1127                 ret = web_process_reset_sta(sm, data, &reply, &replyname);
1128         else
1129                 wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
1130
1131 bad:
1132         if (ret != HTTP_OK)
1133                 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
1134         web_connection_send_reply(req, ret, action, action_len, reply,
1135                                   replyname);
1136         wpabuf_free(reply);
1137 }
1138
1139
1140 /* Given that we have received a header w/ SUBSCRIBE, act upon it
1141  *
1142  * Format of SUBSCRIBE (case-insensitive):
1143  *
1144  * First line must be:
1145  *      SUBSCRIBE /wps_event HTTP/1.1
1146  *
1147  * Our response (if no error) which includes only required lines is:
1148  * HTTP/1.1 200 OK
1149  * Server: xx, UPnP/1.0, xx
1150  * SID: uuid:xxxxxxxxx
1151  * Timeout: Second-<n>
1152  * Content-Length: 0
1153  * Date: xxxx
1154  *
1155  * Header lines must end with \r\n
1156  * Per RFC 2616, content-length: is not required but connection:close
1157  * would appear to be required (given that we will be closing it!).
1158  */
1159 static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
1160                                            struct http_request *req,
1161                                            const char *filename)
1162 {
1163         struct wpabuf *buf;
1164         char *b;
1165         char *hdr = http_request_get_hdr(req);
1166         char *h;
1167         char *match;
1168         int match_len;
1169         char *end;
1170         int len;
1171         int got_nt = 0;
1172         u8 uuid[UUID_LEN];
1173         int got_uuid = 0;
1174         char *callback_urls = NULL;
1175         struct subscription *s = NULL;
1176         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1177
1178         buf = wpabuf_alloc(1000);
1179         if (buf == NULL) {
1180                 http_request_deinit(req);
1181                 return;
1182         }
1183
1184         /* Parse/validate headers */
1185         h = hdr;
1186         /* First line: SUBSCRIBE /wps_event HTTP/1.1
1187          * has already been parsed.
1188          */
1189         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1190                 ret = HTTP_PRECONDITION_FAILED;
1191                 goto error;
1192         }
1193         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
1194         end = os_strchr(h, '\n');
1195
1196         for (; end != NULL; h = end + 1) {
1197                 /* Option line by option line */
1198                 h = end + 1;
1199                 end = os_strchr(h, '\n');
1200                 if (end == NULL)
1201                         break; /* no unterminated lines allowed */
1202
1203                 /* NT assures that it is our type of subscription;
1204                  * not used for a renewl.
1205                  **/
1206                 match = "NT:";
1207                 match_len = os_strlen(match);
1208                 if (os_strncasecmp(h, match, match_len) == 0) {
1209                         h += match_len;
1210                         while (*h == ' ' || *h == '\t')
1211                                 h++;
1212                         match = "upnp:event";
1213                         match_len = os_strlen(match);
1214                         if (os_strncasecmp(h, match, match_len) != 0) {
1215                                 ret = HTTP_BAD_REQUEST;
1216                                 goto error;
1217                         }
1218                         got_nt = 1;
1219                         continue;
1220                 }
1221                 /* HOST should refer to us */
1222 #if 0
1223                 match = "HOST:";
1224                 match_len = os_strlen(match);
1225                 if (os_strncasecmp(h, match, match_len) == 0) {
1226                         h += match_len;
1227                         while (*h == ' ' || *h == '\t')
1228                                 h++;
1229                         .....
1230                 }
1231 #endif
1232                 /* CALLBACK gives one or more URLs for NOTIFYs
1233                  * to be sent as a result of the subscription.
1234                  * Each URL is enclosed in angle brackets.
1235                  */
1236                 match = "CALLBACK:";
1237                 match_len = os_strlen(match);
1238                 if (os_strncasecmp(h, match, match_len) == 0) {
1239                         h += match_len;
1240                         while (*h == ' ' || *h == '\t')
1241                                 h++;
1242                         len = end - h;
1243                         os_free(callback_urls);
1244                         callback_urls = os_malloc(len + 1);
1245                         if (callback_urls == NULL) {
1246                                 ret = HTTP_INTERNAL_SERVER_ERROR;
1247                                 goto error;
1248                         }
1249                         os_memcpy(callback_urls, h, len);
1250                         callback_urls[len] = 0;
1251                         continue;
1252                 }
1253                 /* SID is only for renewal */
1254                 match = "SID:";
1255                 match_len = os_strlen(match);
1256                 if (os_strncasecmp(h, match, match_len) == 0) {
1257                         h += match_len;
1258                         while (*h == ' ' || *h == '\t')
1259                                 h++;
1260                         match = "uuid:";
1261                         match_len = os_strlen(match);
1262                         if (os_strncasecmp(h, match, match_len) != 0) {
1263                                 ret = HTTP_BAD_REQUEST;
1264                                 goto error;
1265                         }
1266                         h += match_len;
1267                         while (*h == ' ' || *h == '\t')
1268                                 h++;
1269                         if (uuid_str2bin(h, uuid)) {
1270                                 ret = HTTP_BAD_REQUEST;
1271                                 goto error;
1272                         }
1273                         got_uuid = 1;
1274                         continue;
1275                 }
1276                 /* TIMEOUT is requested timeout, but apparently we can
1277                  * just ignore this.
1278                  */
1279         }
1280
1281         if (got_uuid) {
1282                 /* renewal */
1283                 if (callback_urls) {
1284                         ret = HTTP_BAD_REQUEST;
1285                         goto error;
1286                 }
1287                 s = subscription_renew(sm, uuid);
1288                 if (s == NULL) {
1289                         ret = HTTP_PRECONDITION_FAILED;
1290                         goto error;
1291                 }
1292         } else if (callback_urls) {
1293                 if (!got_nt) {
1294                         ret = HTTP_PRECONDITION_FAILED;
1295                         goto error;
1296                 }
1297                 s = subscription_start(sm, callback_urls);
1298                 if (s == NULL) {
1299                         ret = HTTP_INTERNAL_SERVER_ERROR;
1300                         goto error;
1301                 }
1302                 callback_urls = NULL;   /* is now owned by subscription */
1303         } else {
1304                 ret = HTTP_PRECONDITION_FAILED;
1305                 goto error;
1306         }
1307
1308         /* success */
1309         http_put_reply_code(buf, HTTP_OK);
1310         wpabuf_put_str(buf, http_server_hdr);
1311         wpabuf_put_str(buf, http_connection_close);
1312         wpabuf_put_str(buf, "Content-Length: 0\r\n");
1313         wpabuf_put_str(buf, "SID: uuid:");
1314         /* subscription id */
1315         b = wpabuf_put(buf, 0);
1316         uuid_bin2str(s->uuid, b, 80);
1317         wpabuf_put(buf, os_strlen(b));
1318         wpabuf_put_str(buf, "\r\n");
1319         wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
1320         http_put_date(buf);
1321         /* And empty line to terminate header: */
1322         wpabuf_put_str(buf, "\r\n");
1323
1324         os_free(callback_urls);
1325         http_request_send_and_deinit(req, buf);
1326         return;
1327
1328 error:
1329         /* Per UPnP spec:
1330         * Errors
1331         * Incompatible headers
1332         *   400 Bad Request. If SID header and one of NT or CALLBACK headers
1333         *     are present, the publisher must respond with HTTP error
1334         *     400 Bad Request.
1335         * Missing or invalid CALLBACK
1336         *   412 Precondition Failed. If CALLBACK header is missing or does not
1337         *     contain a valid HTTP URL, the publisher must respond with HTTP
1338         *     error 412 Precondition Failed.
1339         * Invalid NT
1340         *   412 Precondition Failed. If NT header does not equal upnp:event,
1341         *     the publisher must respond with HTTP error 412 Precondition
1342         *     Failed.
1343         * [For resubscription, use 412 if unknown uuid].
1344         * Unable to accept subscription
1345         *   5xx. If a publisher is not able to accept a subscription (such as
1346         *     due to insufficient resources), it must respond with a
1347         *     HTTP 500-series error code.
1348         *   599 Too many subscriptions (not a standard HTTP error)
1349         */
1350         http_put_empty(buf, ret);
1351         http_request_send_and_deinit(req, buf);
1352 }
1353
1354
1355 /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
1356  *
1357  * Format of UNSUBSCRIBE (case-insensitive):
1358  *
1359  * First line must be:
1360  *      UNSUBSCRIBE /wps_event HTTP/1.1
1361  *
1362  * Our response (if no error) which includes only required lines is:
1363  * HTTP/1.1 200 OK
1364  * Content-Length: 0
1365  *
1366  * Header lines must end with \r\n
1367  * Per RFC 2616, content-length: is not required but connection:close
1368  * would appear to be required (given that we will be closing it!).
1369  */
1370 static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
1371                                              struct http_request *req,
1372                                              const char *filename)
1373 {
1374         struct wpabuf *buf;
1375         char *hdr = http_request_get_hdr(req);
1376         char *h;
1377         char *match;
1378         int match_len;
1379         char *end;
1380         u8 uuid[UUID_LEN];
1381         int got_uuid = 0;
1382         struct subscription *s = NULL;
1383         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1384
1385         /* Parse/validate headers */
1386         h = hdr;
1387         /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
1388          * has already been parsed.
1389          */
1390         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1391                 ret = HTTP_PRECONDITION_FAILED;
1392                 goto send_msg;
1393         }
1394         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
1395         end = os_strchr(h, '\n');
1396
1397         for (; end != NULL; h = end + 1) {
1398                 /* Option line by option line */
1399                 h = end + 1;
1400                 end = os_strchr(h, '\n');
1401                 if (end == NULL)
1402                         break; /* no unterminated lines allowed */
1403
1404                 /* HOST should refer to us */
1405 #if 0
1406                 match = "HOST:";
1407                 match_len = os_strlen(match);
1408                 if (os_strncasecmp(h, match, match_len) == 0) {
1409                         h += match_len;
1410                         while (*h == ' ' || *h == '\t')
1411                                 h++;
1412                         .....
1413                 }
1414 #endif
1415                 /* SID is only for renewal */
1416                 match = "SID:";
1417                 match_len = os_strlen(match);
1418                 if (os_strncasecmp(h, match, match_len) == 0) {
1419                         h += match_len;
1420                         while (*h == ' ' || *h == '\t')
1421                                 h++;
1422                         match = "uuid:";
1423                         match_len = os_strlen(match);
1424                         if (os_strncasecmp(h, match, match_len) != 0) {
1425                                 ret = HTTP_BAD_REQUEST;
1426                                 goto send_msg;
1427                         }
1428                         h += match_len;
1429                         while (*h == ' ' || *h == '\t')
1430                                 h++;
1431                         if (uuid_str2bin(h, uuid)) {
1432                                 ret = HTTP_BAD_REQUEST;
1433                                 goto send_msg;
1434                         }
1435                         got_uuid = 1;
1436                         continue;
1437                 }
1438         }
1439
1440         if (got_uuid) {
1441                 s = subscription_find(sm, uuid);
1442                 if (s) {
1443                         wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
1444                                    s,
1445                                    (s && s->addr_list &&
1446                                     s->addr_list->domain_and_port) ?
1447                                    s->addr_list->domain_and_port : "-null-");
1448                         subscription_unlink(s);
1449                         subscription_destroy(s);
1450                 }
1451         } else {
1452                 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
1453                            "found)");
1454                 ret = HTTP_PRECONDITION_FAILED;
1455                 goto send_msg;
1456         }
1457
1458         ret = HTTP_OK;
1459
1460 send_msg:
1461         buf = wpabuf_alloc(200);
1462         if (buf == NULL) {
1463                 http_request_deinit(req);
1464                 return;
1465         }
1466         http_put_empty(buf, ret);
1467         http_request_send_and_deinit(req, buf);
1468 }
1469
1470
1471 /* Send error in response to unknown requests */
1472 static void web_connection_unimplemented(struct http_request *req)
1473 {
1474         struct wpabuf *buf;
1475         buf = wpabuf_alloc(200);
1476         if (buf == NULL) {
1477                 http_request_deinit(req);
1478                 return;
1479         }
1480         http_put_empty(buf, HTTP_UNIMPLEMENTED);
1481         http_request_send_and_deinit(req, buf);
1482 }
1483
1484
1485
1486 /* Called when we have gotten an apparently valid http request.
1487  */
1488 static void web_connection_check_data(void *ctx, struct http_request *req)
1489 {
1490         struct upnp_wps_device_sm *sm = ctx;
1491         enum httpread_hdr_type htype = http_request_get_type(req);
1492         char *filename = http_request_get_uri(req);
1493         struct sockaddr_in *cli = http_request_get_cli_addr(req);
1494
1495         if (!filename) {
1496                 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
1497                 http_request_deinit(req);
1498                 return;
1499         }
1500         /* Trim leading slashes from filename */
1501         while (*filename == '/')
1502                 filename++;
1503
1504         wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
1505                    htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
1506
1507         switch (htype) {
1508         case HTTPREAD_HDR_TYPE_GET:
1509                 web_connection_parse_get(sm, req, filename);
1510                 break;
1511         case HTTPREAD_HDR_TYPE_POST:
1512                 web_connection_parse_post(sm, req, filename);
1513                 break;
1514         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
1515                 web_connection_parse_subscribe(sm, req, filename);
1516                 break;
1517         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
1518                 web_connection_parse_unsubscribe(sm, req, filename);
1519                 break;
1520
1521                 /* We are not required to support M-POST; just plain
1522                  * POST is supposed to work, so we only support that.
1523                  * If for some reason we need to support M-POST, it is
1524                  * mostly the same as POST, with small differences.
1525                  */
1526         default:
1527                 /* Send 501 for anything else */
1528                 web_connection_unimplemented(req);
1529                 break;
1530         }
1531 }
1532
1533
1534 /*
1535  * Listening for web connections
1536  * We have a single TCP listening port, and hand off connections as we get
1537  * them.
1538  */
1539
1540 void web_listener_stop(struct upnp_wps_device_sm *sm)
1541 {
1542         http_server_deinit(sm->web_srv);
1543         sm->web_srv = NULL;
1544 }
1545
1546
1547 int web_listener_start(struct upnp_wps_device_sm *sm)
1548 {
1549         struct in_addr addr;
1550         addr.s_addr = sm->ip_addr;
1551         sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
1552                                        sm);
1553         if (sm->web_srv == NULL) {
1554                 web_listener_stop(sm);
1555                 return -1;
1556         }
1557         sm->web_port = http_server_get_port(sm->web_srv);
1558
1559         return 0;
1560 }