Merge branch 'vendor/MPFR'
[dragonfly.git] / contrib / hostapd / 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 #include <fcntl.h>
13
14 #include "common.h"
15 #include "base64.h"
16 #include "eloop.h"
17 #include "uuid.h"
18 #include "httpread.h"
19 #include "wps_i.h"
20 #include "wps_upnp.h"
21 #include "wps_upnp_i.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  * Incoming web connections are recorded in this struct.
42  * A web connection is a TCP connection to us, the server;
43  * it is called a "web connection" because we use http and serve
44  * data that looks like web pages.
45  * State information is need to track the connection until we figure
46  * out what they want and what we want to do about it.
47  */
48 struct web_connection {
49         /* double linked list */
50         struct web_connection *next;
51         struct web_connection *prev;
52         struct upnp_wps_device_sm *sm; /* parent */
53         int sd; /* socket to read from */
54         struct sockaddr_in cli_addr;
55         int sd_registered; /* nonzero if we must cancel registration */
56         struct httpread *hread; /* state machine for reading socket */
57         int n_rcvd_data; /* how much data read so far */
58         int done; /* internal flag, set when we've finished */
59 };
60
61
62 /*
63  * XML parsing and formatting
64  *
65  * XML is a markup language based on unicode; usually (and in our case,
66  * always!) based on utf-8. utf-8 uses a variable number of bytes per
67  * character. utf-8 has the advantage that all non-ASCII unicode characters are
68  * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
69  * characters are single ascii bytes, thus we can use typical text processing.
70  *
71  * (One other interesting thing about utf-8 is that it is possible to look at
72  * any random byte and determine if it is the first byte of a character as
73  * versus a continuation byte).
74  *
75  * The base syntax of XML uses a few ASCII punctionation characters; any
76  * characters that would appear in the payload data are rewritten using
77  * sequences, e.g., &amp; for ampersand(&) and &lt for left angle bracket (<).
78  * Five such escapes total (more can be defined but that does not apply to our
79  * case). Thus we can safely parse for angle brackets etc.
80  *
81  * XML describes tree structures of tagged data, with each element beginning
82  * with an opening tag <label> and ending with a closing tag </label> with
83  * matching label. (There is also a self-closing tag <label/> which is supposed
84  * to be equivalent to <label></label>, i.e., no payload, but we are unlikely
85  * to see it for our purpose).
86  *
87  * Actually the opening tags are a little more complicated because they can
88  * contain "attributes" after the label (delimited by ascii space or tab chars)
89  * of the form attribute_label="value" or attribute_label='value'; as it turns
90  * out we do not have to read any of these attributes, just ignore them.
91  *
92  * Labels are any sequence of chars other than space, tab, right angle bracket
93  * (and ?), but may have an inner structure of <namespace><colon><plain_label>.
94  * As it turns out, we can ignore the namespaces, in fact we can ignore the
95  * entire tree hierarchy, because the plain labels we are looking for will be
96  * unique (not in general, but for this application). We do however have to be
97  * careful to skip over the namespaces.
98  *
99  * In generating XML we have to be more careful, but that is easy because
100  * everything we do is pretty canned. The only real care to take is to escape
101  * any special chars in our payload.
102  */
103
104 /**
105  * xml_next_tag - Advance to next tag
106  * @in: Input
107  * @out: OUT: start of tag just after '<'
108  * @out_tagname: OUT: start of name of tag, skipping namespace
109  * @end: OUT: one after tag
110  * Returns: 0 on success, 1 on failure
111  *
112  * A tag has form:
113  *     <left angle bracket><...><right angle bracket>
114  * Within the angle brackets, there is an optional leading forward slash (which
115  * makes the tag an ending tag), then an optional leading label (followed by
116  * colon) and then the tag name itself.
117  *
118  * Note that angle brackets present in the original data must have been encoded
119  * as &lt; and &gt; so they will not trouble us.
120  */
121 static int xml_next_tag(char *in, char **out, char **out_tagname,
122                         char **end)
123 {
124         while (*in && *in != '<')
125                 in++;
126         if (*in != '<')
127                 return 1;
128         *out = ++in;
129         if (*in == '/')
130                 in++;
131         *out_tagname = in; /* maybe */
132         while (isalnum(*in) || *in == '-')
133                 in++;
134         if (*in == ':')
135                 *out_tagname = ++in;
136         while (*in && *in != '>')
137                 in++;
138         if (*in != '>')
139                 return 1;
140         *end = ++in;
141         return 0;
142 }
143
144
145 /* xml_data_encode -- format data for xml file, escaping special characters.
146  *
147  * Note that we assume we are using utf8 both as input and as output!
148  * In utf8, characters may be classed as follows:
149  *     0xxxxxxx(2) -- 1 byte ascii char
150  *     11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
151  *         110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
152  *         1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
153  *         11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
154  *      10xxxxxx(2) -- extension byte (6 payload bits per byte)
155  *      Some values implied by the above are however illegal because they
156  *      do not represent unicode chars or are not the shortest encoding.
157  * Actually, we can almost entirely ignore the above and just do
158  * text processing same as for ascii text.
159  *
160  * XML is written with arbitrary unicode characters, except that five
161  * characters have special meaning and so must be escaped where they
162  * appear in payload data... which we do here.
163  */
164 static void xml_data_encode(struct wpabuf *buf, const char *data, int len)
165 {
166         int i;
167         for (i = 0; i < len; i++) {
168                 u8 c = ((u8 *) data)[i];
169                 if (c == '<') {
170                         wpabuf_put_str(buf, "&lt;");
171                         continue;
172                 }
173                 if (c == '>') {
174                         wpabuf_put_str(buf, "&gt;");
175                         continue;
176                 }
177                 if (c == '&') {
178                         wpabuf_put_str(buf, "&amp;");
179                         continue;
180                 }
181                 if (c == '\'') {
182                         wpabuf_put_str(buf, "&apos;");
183                         continue;
184                 }
185                 if (c == '"') {
186                         wpabuf_put_str(buf, "&quot;");
187                         continue;
188                 }
189                 /*
190                  * We could try to represent control characters using the
191                  * sequence: &#x; where x is replaced by a hex numeral, but not
192                  * clear why we would do this.
193                  */
194                 wpabuf_put_u8(buf, c);
195         }
196 }
197
198
199 /* xml_add_tagged_data -- format tagged data as a new xml line.
200  *
201  * tag must not have any special chars.
202  * data may have special chars, which are escaped.
203  */
204 static void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
205                                 const char *data)
206 {
207         wpabuf_printf(buf, "<%s>", tag);
208         xml_data_encode(buf, data, os_strlen(data));
209         wpabuf_printf(buf, "</%s>\n", tag);
210 }
211
212
213 /* A POST body looks something like (per upnp spec):
214  * <?xml version="1.0"?>
215  * <s:Envelope
216  *     xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
217  *     s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
218  *   <s:Body>
219  *     <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
220  *       <argumentName>in arg value</argumentName>
221  *       other in args and their values go here, if any
222  *     </u:actionName>
223  *   </s:Body>
224  * </s:Envelope>
225  *
226  * where :
227  *      s: might be some other namespace name followed by colon
228  *      u: might be some other namespace name followed by colon
229  *      actionName will be replaced according to action requested
230  *      schema following actionName will be WFA scheme instead
231  *      argumentName will be actual argument name
232  *      (in arg value) will be actual argument value
233  */
234 static int
235 upnp_get_first_document_item(char *doc, const char *item, char **value)
236 {
237         const char *match = item;
238         int match_len = os_strlen(item);
239         char *tag;
240         char *tagname;
241         char *end;
242
243         *value = NULL;          /* default, bad */
244
245         /*
246          * This is crude: ignore any possible tag name conflicts and go right
247          * to the first tag of this name. This should be ok for the limited
248          * domain of UPnP messages.
249          */
250         for (;;) {
251                 if (xml_next_tag(doc, &tag, &tagname, &end))
252                         return 1;
253                 doc = end;
254                 if (!os_strncasecmp(tagname, match, match_len) &&
255                     *tag != '/' &&
256                     (tagname[match_len] == '>' ||
257                      !isgraph(tagname[match_len]))) {
258                         break;
259                 }
260         }
261         end = doc;
262         while (*end && *end != '<')
263                 end++;
264         *value = os_zalloc(1 + (end - doc));
265         if (*value == NULL)
266                 return 1;
267         os_memcpy(*value, doc, end - doc);
268         return 0;
269 }
270
271
272 /*
273  * "Files" that we serve via HTTP. The format of these files is given by
274  * WFA WPS specifications. Extra white space has been removed to save space.
275  */
276
277 static const char wps_scpd_xml[] =
278 "<?xml version=\"1.0\"?>\n"
279 "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
280 "<specVersion><major>1</major><minor>0</minor></specVersion>\n"
281 "<actionList>\n"
282 "<action>\n"
283 "<name>GetDeviceInfo</name>\n"
284 "<argumentList>\n"
285 "<argument>\n"
286 "<name>NewDeviceInfo</name>\n"
287 "<direction>out</direction>\n"
288 "<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
289 "</argument>\n"
290 "</argumentList>\n"
291 "</action>\n"
292 "<action>\n"
293 "<name>PutMessage</name>\n"
294 "<argumentList>\n"
295 "<argument>\n"
296 "<name>NewInMessage</name>\n"
297 "<direction>in</direction>\n"
298 "<relatedStateVariable>InMessage</relatedStateVariable>\n"
299 "</argument>\n"
300 "<argument>\n"
301 "<name>NewOutMessage</name>\n"
302 "<direction>out</direction>\n"
303 "<relatedStateVariable>OutMessage</relatedStateVariable>\n"
304 "</argument>\n"
305 "</argumentList>\n"
306 "</action>\n"
307 "<action>\n"
308 "<name>GetAPSettings</name>\n"
309 "<argumentList>\n"
310 "<argument>\n"
311 "<name>NewMessage</name>\n"
312 "<direction>in</direction>\n"
313 "<relatedStateVariable>Message</relatedStateVariable>\n"
314 "</argument>\n"
315 "<argument>\n"
316 "<name>NewAPSettings</name>\n"
317 "<direction>out</direction>\n"
318 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
319 "</argument>\n"
320 "</argumentList>\n"
321 "</action>\n"
322 "<action>\n"
323 "<name>SetAPSettings</name>\n"
324 "<argumentList>\n"
325 "<argument>\n"
326 "<name>APSettings</name>\n"
327 "<direction>in</direction>\n"
328 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
329 "</argument>\n"
330 "</argumentList>\n"
331 "</action>\n"
332 "<action>\n"
333 "<name>DelAPSettings</name>\n"
334 "<argumentList>\n"
335 "<argument>\n"
336 "<name>NewAPSettings</name>\n"
337 "<direction>in</direction>\n"
338 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
339 "</argument>\n"
340 "</argumentList>\n"
341 "</action>\n"
342 "<action>\n"
343 "<name>GetSTASettings</name>\n"
344 "<argumentList>\n"
345 "<argument>\n"
346 "<name>NewMessage</name>\n"
347 "<direction>in</direction>\n"
348 "<relatedStateVariable>Message</relatedStateVariable>\n"
349 "</argument>\n"
350 "<argument>\n"
351 "<name>NewSTASettings</name>\n"
352 "<direction>out</direction>\n"
353 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
354 "</argument>\n"
355 "</argumentList>\n"
356 "</action>\n"
357 "<action>\n"
358 "<name>SetSTASettings</name>\n"
359 "<argumentList>\n"
360 "<argument>\n"
361 "<name>NewSTASettings</name>\n"
362 "<direction>out</direction>\n"
363 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
364 "</argument>\n"
365 "</argumentList>\n"
366 "</action>\n"
367 "<action>\n"
368 "<name>DelSTASettings</name>\n"
369 "<argumentList>\n"
370 "<argument>\n"
371 "<name>NewSTASettings</name>\n"
372 "<direction>in</direction>\n"
373 "<relatedStateVariable>STASettings</relatedStateVariable>\n"
374 "</argument>\n"
375 "</argumentList>\n"
376 "</action>\n"
377 "<action>\n"
378 "<name>PutWLANResponse</name>\n"
379 "<argumentList>\n"
380 "<argument>\n"
381 "<name>NewMessage</name>\n"
382 "<direction>in</direction>\n"
383 "<relatedStateVariable>Message</relatedStateVariable>\n"
384 "</argument>\n"
385 "<argument>\n"
386 "<name>NewWLANEventType</name>\n"
387 "<direction>in</direction>\n"
388 "<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
389 "</argument>\n"
390 "<argument>\n"
391 "<name>NewWLANEventMAC</name>\n"
392 "<direction>in</direction>\n"
393 "<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
394 "</argument>\n"
395 "</argumentList>\n"
396 "</action>\n"
397 "<action>\n"
398 "<name>SetSelectedRegistrar</name>\n"
399 "<argumentList>\n"
400 "<argument>\n"
401 "<name>NewMessage</name>\n"
402 "<direction>in</direction>\n"
403 "<relatedStateVariable>Message</relatedStateVariable>\n"
404 "</argument>\n"
405 "</argumentList>\n"
406 "</action>\n"
407 "<action>\n"
408 "<name>RebootAP</name>\n"
409 "<argumentList>\n"
410 "<argument>\n"
411 "<name>NewAPSettings</name>\n"
412 "<direction>in</direction>\n"
413 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
414 "</argument>\n"
415 "</argumentList>\n"
416 "</action>\n"
417 "<action>\n"
418 "<name>ResetAP</name>\n"
419 "<argumentList>\n"
420 "<argument>\n"
421 "<name>NewMessage</name>\n"
422 "<direction>in</direction>\n"
423 "<relatedStateVariable>Message</relatedStateVariable>\n"
424 "</argument>\n"
425 "</argumentList>\n"
426 "</action>\n"
427 "<action>\n"
428 "<name>RebootSTA</name>\n"
429 "<argumentList>\n"
430 "<argument>\n"
431 "<name>NewSTASettings</name>\n"
432 "<direction>in</direction>\n"
433 "<relatedStateVariable>APSettings</relatedStateVariable>\n"
434 "</argument>\n"
435 "</argumentList>\n"
436 "</action>\n"
437 "<action>\n"
438 "<name>ResetSTA</name>\n"
439 "<argumentList>\n"
440 "<argument>\n"
441 "<name>NewMessage</name>\n"
442 "<direction>in</direction>\n"
443 "<relatedStateVariable>Message</relatedStateVariable>\n"
444 "</argument>\n"
445 "</argumentList>\n"
446 "</action>\n"
447 "</actionList>\n"
448 "<serviceStateTable>\n"
449 "<stateVariable sendEvents=\"no\">\n"
450 "<name>Message</name>\n"
451 "<dataType>bin.base64</dataType>\n"
452 "</stateVariable>\n"
453 "<stateVariable sendEvents=\"no\">\n"
454 "<name>InMessage</name>\n"
455 "<dataType>bin.base64</dataType>\n"
456 "</stateVariable>\n"
457 "<stateVariable sendEvents=\"no\">\n"
458 "<name>OutMessage</name>\n"
459 "<dataType>bin.base64</dataType>\n"
460 "</stateVariable>\n"
461 "<stateVariable sendEvents=\"no\">\n"
462 "<name>DeviceInfo</name>\n"
463 "<dataType>bin.base64</dataType>\n"
464 "</stateVariable>\n"
465 "<stateVariable sendEvents=\"no\">\n"
466 "<name>APSettings</name>\n"
467 "<dataType>bin.base64</dataType>\n"
468 "</stateVariable>\n"
469 "<stateVariable sendEvents=\"yes\">\n"
470 "<name>APStatus</name>\n"
471 "<dataType>ui1</dataType>\n"
472 "</stateVariable>\n"
473 "<stateVariable sendEvents=\"no\">\n"
474 "<name>STASettings</name>\n"
475 "<dataType>bin.base64</dataType>\n"
476 "</stateVariable>\n"
477 "<stateVariable sendEvents=\"yes\">\n"
478 "<name>STAStatus</name>\n"
479 "<dataType>ui1</dataType>\n"
480 "</stateVariable>\n"
481 "<stateVariable sendEvents=\"yes\">\n"
482 "<name>WLANEvent</name>\n"
483 "<dataType>bin.base64</dataType>\n"
484 "</stateVariable>\n"
485 "<stateVariable sendEvents=\"no\">\n"
486 "<name>WLANEventType</name>\n"
487 "<dataType>ui1</dataType>\n"
488 "</stateVariable>\n"
489 "<stateVariable sendEvents=\"no\">\n"
490 "<name>WLANEventMAC</name>\n"
491 "<dataType>string</dataType>\n"
492 "</stateVariable>\n"
493 "<stateVariable sendEvents=\"no\">\n"
494 "<name>WLANResponse</name>\n"
495 "<dataType>bin.base64</dataType>\n"
496 "</stateVariable>\n"
497 "</serviceStateTable>\n"
498 "</scpd>\n"
499 ;
500
501
502 static const char *wps_device_xml_prefix =
503         "<?xml version=\"1.0\"?>\n"
504         "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
505         "<specVersion>\n"
506         "<major>1</major>\n"
507         "<minor>0</minor>\n"
508         "</specVersion>\n"
509         "<device>\n"
510         "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
511         "</deviceType>\n";
512
513 static const char *wps_device_xml_postfix =
514         "<serviceList>\n"
515         "<service>\n"
516         "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
517         "</serviceType>\n"
518         "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
519         "\n"
520         "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
521         "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
522         "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
523         "</service>\n"
524         "</serviceList>\n"
525         "</device>\n"
526         "</root>\n";
527
528
529 /* format_wps_device_xml -- produce content of "file" wps_device.xml
530  * (UPNP_WPS_DEVICE_XML_FILE)
531  */
532 static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
533                                   struct wpabuf *buf)
534 {
535         const char *s;
536         char uuid_string[80];
537
538         wpabuf_put_str(buf, wps_device_xml_prefix);
539
540         /*
541          * Add required fields with default values if not configured. Add
542          * optional and recommended fields only if configured.
543          */
544         s = sm->wps->friendly_name;
545         s = ((s && *s) ? s : "WPS Access Point");
546         xml_add_tagged_data(buf, "friendlyName", s);
547
548         s = sm->wps->dev.manufacturer;
549         s = ((s && *s) ? s : "");
550         xml_add_tagged_data(buf, "manufacturer", s);
551
552         if (sm->wps->manufacturer_url)
553                 xml_add_tagged_data(buf, "manufacturerURL",
554                                     sm->wps->manufacturer_url);
555
556         if (sm->wps->model_description)
557                 xml_add_tagged_data(buf, "modelDescription",
558                                     sm->wps->model_description);
559
560         s = sm->wps->dev.model_name;
561         s = ((s && *s) ? s : "");
562         xml_add_tagged_data(buf, "modelName", s);
563
564         if (sm->wps->dev.model_number)
565                 xml_add_tagged_data(buf, "modelNumber",
566                                     sm->wps->dev.model_number);
567
568         if (sm->wps->model_url)
569                 xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
570
571         if (sm->wps->dev.serial_number)
572                 xml_add_tagged_data(buf, "serialNumber",
573                                     sm->wps->dev.serial_number);
574
575         uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
576         s = uuid_string;
577         /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
578          * easily...
579          */
580         wpabuf_put_str(buf, "<UDN>uuid:");
581         xml_data_encode(buf, s, os_strlen(s));
582         wpabuf_put_str(buf, "</UDN>\n");
583
584         if (sm->wps->upc)
585                 xml_add_tagged_data(buf, "UPC", sm->wps->upc);
586
587         wpabuf_put_str(buf, wps_device_xml_postfix);
588 }
589
590
591 void web_connection_stop(struct web_connection *c)
592 {
593         struct upnp_wps_device_sm *sm = c->sm;
594
595         httpread_destroy(c->hread);
596         c->hread = NULL;
597         close(c->sd);
598         c->sd = -1;
599         if (c->next == c) {
600                 sm->web_connections = NULL;
601         } else {
602                 if (sm->web_connections == c)
603                         sm->web_connections = c->next;
604                 c->next->prev = c->prev;
605                 c->prev->next = c->next;
606         }
607         os_free(c);
608         sm->n_web_connections--;
609 }
610
611
612 static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
613 {
614         wpabuf_put_str(buf, "HTTP/1.1 ");
615         switch (code) {
616         case HTTP_OK:
617                 wpabuf_put_str(buf, "200 OK\r\n");
618                 break;
619         case HTTP_BAD_REQUEST:
620                 wpabuf_put_str(buf, "400 Bad request\r\n");
621                 break;
622         case HTTP_PRECONDITION_FAILED:
623                 wpabuf_put_str(buf, "412 Precondition failed\r\n");
624                 break;
625         case HTTP_UNIMPLEMENTED:
626                 wpabuf_put_str(buf, "501 Unimplemented\r\n");
627                 break;
628         case HTTP_INTERNAL_SERVER_ERROR:
629         default:
630                 wpabuf_put_str(buf, "500 Internal server error\r\n");
631                 break;
632         }
633 }
634
635
636 static void http_put_date(struct wpabuf *buf)
637 {
638         wpabuf_put_str(buf, "Date: ");
639         format_date(buf);
640         wpabuf_put_str(buf, "\r\n");
641 }
642
643
644 static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
645 {
646         http_put_reply_code(buf, code);
647         wpabuf_put_str(buf, http_server_hdr);
648         wpabuf_put_str(buf, http_connection_close);
649         wpabuf_put_str(buf, "Content-Length: 0\r\n"
650                        "\r\n");
651 }
652
653
654 /* Given that we have received a header w/ GET, act upon it
655  *
656  * Format of GET (case-insensitive):
657  *
658  * First line must be:
659  *      GET /<file> HTTP/1.1
660  * Since we don't do anything fancy we just ignore other lines.
661  *
662  * Our response (if no error) which includes only required lines is:
663  * HTTP/1.1 200 OK
664  * Connection: close
665  * Content-Type: text/xml
666  * Date: <rfc1123-date>
667  *
668  * Header lines must end with \r\n
669  * Per RFC 2616, content-length: is not required but connection:close
670  * would appear to be required (given that we will be closing it!).
671  */
672 static void web_connection_parse_get(struct web_connection *c, char *filename)
673 {
674         struct upnp_wps_device_sm *sm = c->sm;
675         struct wpabuf *buf; /* output buffer, allocated */
676         char *put_length_here;
677         char *body_start;
678         enum {
679                 GET_DEVICE_XML_FILE,
680                 GET_SCPD_XML_FILE
681         } req;
682         size_t extra_len = 0;
683         int body_length;
684         char len_buf[10];
685
686         /*
687          * It is not required that filenames be case insensitive but it is
688          * allowed and cannot hurt here.
689          */
690         if (filename == NULL)
691                 filename = "(null)"; /* just in case */
692         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
693                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
694                 req = GET_DEVICE_XML_FILE;
695                 extra_len = 3000;
696                 if (sm->wps->friendly_name)
697                         extra_len += os_strlen(sm->wps->friendly_name);
698                 if (sm->wps->manufacturer_url)
699                         extra_len += os_strlen(sm->wps->manufacturer_url);
700                 if (sm->wps->model_description)
701                         extra_len += os_strlen(sm->wps->model_description);
702                 if (sm->wps->model_url)
703                         extra_len += os_strlen(sm->wps->model_url);
704                 if (sm->wps->upc)
705                         extra_len += os_strlen(sm->wps->upc);
706         } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
707                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
708                 req = GET_SCPD_XML_FILE;
709                 extra_len = os_strlen(wps_scpd_xml);
710         } else {
711                 /* File not found */
712                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
713                            filename);
714                 buf = wpabuf_alloc(200);
715                 if (buf == NULL)
716                         return;
717                 wpabuf_put_str(buf,
718                                "HTTP/1.1 404 Not Found\r\n"
719                                "Connection: close\r\n");
720
721                 http_put_date(buf);
722
723                 /* terminating empty line */
724                 wpabuf_put_str(buf, "\r\n");
725
726                 goto send_buf;
727         }
728
729         buf = wpabuf_alloc(1000 + extra_len);
730         if (buf == NULL)
731                 return;
732
733         wpabuf_put_str(buf,
734                        "HTTP/1.1 200 OK\r\n"
735                        "Content-Type: text/xml; charset=\"utf-8\"\r\n");
736         wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
737         wpabuf_put_str(buf, "Connection: close\r\n");
738         wpabuf_put_str(buf, "Content-Length: ");
739         /*
740          * We will paste the length in later, leaving some extra whitespace.
741          * HTTP code is supposed to be tolerant of extra whitespace.
742          */
743         put_length_here = wpabuf_put(buf, 0);
744         wpabuf_put_str(buf, "        \r\n");
745
746         http_put_date(buf);
747
748         /* terminating empty line */
749         wpabuf_put_str(buf, "\r\n");
750
751         body_start = wpabuf_put(buf, 0);
752
753         switch (req) {
754         case GET_DEVICE_XML_FILE:
755                 format_wps_device_xml(sm, buf);
756                 break;
757         case GET_SCPD_XML_FILE:
758                 wpabuf_put_str(buf, wps_scpd_xml);
759                 break;
760         }
761
762         /* Now patch in the content length at the end */
763         body_length = (char *) wpabuf_put(buf, 0) - body_start;
764         os_snprintf(len_buf, 10, "%d", body_length);
765         os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
766
767 send_buf:
768         send_wpabuf(c->sd, buf);
769         wpabuf_free(buf);
770 }
771
772
773 static struct wpabuf * web_get_item(char *data, const char *name,
774                                     enum http_reply_code *ret)
775 {
776         char *msg;
777         struct wpabuf *buf;
778         unsigned char *decoded;
779         size_t len;
780
781         if (upnp_get_first_document_item(data, name, &msg)) {
782                 *ret = UPNP_ARG_VALUE_INVALID;
783                 return NULL;
784         }
785
786         decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
787         os_free(msg);
788         if (decoded == NULL) {
789                 *ret = UPNP_OUT_OF_MEMORY;
790                 return NULL;
791         }
792
793         buf = wpabuf_alloc_ext_data(decoded, len);
794         if (buf == NULL) {
795                 os_free(decoded);
796                 *ret = UPNP_OUT_OF_MEMORY;
797                 return NULL;
798         }
799         return buf;
800 }
801
802
803 static enum http_reply_code
804 web_process_get_device_info(struct upnp_wps_device_sm *sm,
805                             struct wpabuf **reply, const char **replyname)
806 {
807         static const char *name = "NewDeviceInfo";
808
809         wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
810         if (sm->ctx->rx_req_get_device_info == NULL)
811                 return HTTP_INTERNAL_SERVER_ERROR;
812         *reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer);
813         if (*reply == NULL) {
814                 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
815                 return HTTP_INTERNAL_SERVER_ERROR;
816         }
817         *replyname = name;
818         return HTTP_OK;
819 }
820
821
822 static enum http_reply_code
823 web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
824                         struct wpabuf **reply, const char **replyname)
825 {
826         struct wpabuf *msg;
827         static const char *name = "NewOutMessage";
828         enum http_reply_code ret;
829
830         /*
831          * PutMessage is used by external UPnP-based Registrar to perform WPS
832          * operation with the access point itself; as compared with
833          * PutWLANResponse which is for proxying.
834          */
835         wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
836         if (sm->ctx->rx_req_put_message == NULL)
837                 return HTTP_INTERNAL_SERVER_ERROR;
838         msg = web_get_item(data, "NewInMessage", &ret);
839         if (msg == NULL)
840                 return ret;
841         *reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg);
842         wpabuf_free(msg);
843         if (*reply == NULL)
844                 return HTTP_INTERNAL_SERVER_ERROR;
845         *replyname = name;
846         return HTTP_OK;
847 }
848
849
850 static enum http_reply_code
851 web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data,
852                             struct wpabuf **reply, const char **replyname)
853 {
854         struct wpabuf *msg;
855         static const char *name = "NewAPSettings";
856         enum http_reply_code ret;
857
858         wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings");
859         if (sm->ctx->rx_req_get_ap_settings == NULL)
860                 return HTTP_INTERNAL_SERVER_ERROR;
861         msg = web_get_item(data, "NewMessage", &ret);
862         if (msg == NULL)
863                 return ret;
864         *reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg);
865         wpabuf_free(msg);
866         if (*reply == NULL)
867                 return HTTP_INTERNAL_SERVER_ERROR;
868         *replyname = name;
869         return HTTP_OK;
870 }
871
872
873 static enum http_reply_code
874 web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data,
875                             struct wpabuf **reply, const char **replyname)
876 {
877         struct wpabuf *msg;
878         enum http_reply_code ret;
879
880         wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings");
881         msg = web_get_item(data, "NewAPSettings", &ret);
882         if (msg == NULL)
883                 return ret;
884         if (!sm->ctx->rx_req_set_ap_settings ||
885             sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) {
886                 wpabuf_free(msg);
887                 return HTTP_INTERNAL_SERVER_ERROR;
888         }
889         wpabuf_free(msg);
890         *replyname = NULL;
891         *reply = NULL;
892         return HTTP_OK;
893 }
894
895
896 static enum http_reply_code
897 web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data,
898                             struct wpabuf **reply, const char **replyname)
899 {
900         struct wpabuf *msg;
901         enum http_reply_code ret;
902
903         wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings");
904         msg = web_get_item(data, "NewAPSettings", &ret);
905         if (msg == NULL)
906                 return ret;
907         if (!sm->ctx->rx_req_del_ap_settings ||
908             sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) {
909                 wpabuf_free(msg);
910                 return HTTP_INTERNAL_SERVER_ERROR;
911         }
912         wpabuf_free(msg);
913         *replyname = NULL;
914         *reply = NULL;
915         return HTTP_OK;
916 }
917
918
919 static enum http_reply_code
920 web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data,
921                              struct wpabuf **reply, const char **replyname)
922 {
923         struct wpabuf *msg;
924         static const char *name = "NewSTASettings";
925         enum http_reply_code ret;
926
927         wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings");
928         if (sm->ctx->rx_req_get_sta_settings == NULL)
929                 return HTTP_INTERNAL_SERVER_ERROR;
930         msg = web_get_item(data, "NewMessage", &ret);
931         if (msg == NULL)
932                 return ret;
933         *reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg);
934         wpabuf_free(msg);
935         if (*reply == NULL)
936                 return HTTP_INTERNAL_SERVER_ERROR;
937         *replyname = name;
938         return HTTP_OK;
939 }
940
941
942 static enum http_reply_code
943 web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data,
944                              struct wpabuf **reply, const char **replyname)
945 {
946         struct wpabuf *msg;
947         enum http_reply_code ret;
948
949         wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings");
950         msg = web_get_item(data, "NewSTASettings", &ret);
951         if (msg == NULL)
952                 return ret;
953         if (!sm->ctx->rx_req_set_sta_settings ||
954             sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) {
955                 wpabuf_free(msg);
956                 return HTTP_INTERNAL_SERVER_ERROR;
957         }
958         wpabuf_free(msg);
959         *replyname = NULL;
960         *reply = NULL;
961         return HTTP_OK;
962 }
963
964
965 static enum http_reply_code
966 web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data,
967                              struct wpabuf **reply, const char **replyname)
968 {
969         struct wpabuf *msg;
970         enum http_reply_code ret;
971
972         wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings");
973         msg = web_get_item(data, "NewSTASettings", &ret);
974         if (msg == NULL)
975                 return ret;
976         if (!sm->ctx->rx_req_del_sta_settings ||
977             sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) {
978                 wpabuf_free(msg);
979                 return HTTP_INTERNAL_SERVER_ERROR;
980         }
981         wpabuf_free(msg);
982         *replyname = NULL;
983         *reply = NULL;
984         return HTTP_OK;
985 }
986
987
988 static enum http_reply_code
989 web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
990                               struct wpabuf **reply, const char **replyname)
991 {
992         struct wpabuf *msg;
993         enum http_reply_code ret;
994         u8 macaddr[ETH_ALEN];
995         int ev_type;
996         int type;
997         char *val;
998
999         /*
1000          * External UPnP-based Registrar is passing us a message to be proxied
1001          * over to a Wi-Fi -based client of ours.
1002          */
1003
1004         wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
1005         msg = web_get_item(data, "NewMessage", &ret);
1006         if (msg == NULL)
1007                 return ret;
1008         if (upnp_get_first_document_item(data, "NewWLANEventType", &val)) {
1009                 wpabuf_free(msg);
1010                 return UPNP_ARG_VALUE_INVALID;
1011         }
1012         ev_type = atol(val);
1013         os_free(val);
1014         val = NULL;
1015         if (upnp_get_first_document_item(data, "NewWLANEventMAC", &val) ||
1016             hwaddr_aton(val, macaddr)) {
1017                 wpabuf_free(msg);
1018                 os_free(val);
1019                 return UPNP_ARG_VALUE_INVALID;
1020         }
1021         os_free(val);
1022         if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
1023                 struct wps_parse_attr attr;
1024                 if (wps_parse_msg(msg, &attr) < 0 ||
1025                     attr.msg_type == NULL)
1026                         type = -1;
1027                 else
1028                         type = *attr.msg_type;
1029                 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
1030         } else
1031                 type = -1;
1032         if (!sm->ctx->rx_req_put_wlan_response ||
1033             sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
1034                                               type)) {
1035                 wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
1036                            "rx_req_put_wlan_response");
1037                 wpabuf_free(msg);
1038                 return HTTP_INTERNAL_SERVER_ERROR;
1039         }
1040         wpabuf_free(msg);
1041         *replyname = NULL;
1042         *reply = NULL;
1043         return HTTP_OK;
1044 }
1045
1046
1047 static enum http_reply_code
1048 web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data,
1049                                    struct wpabuf **reply,
1050                                    const char **replyname)
1051 {
1052         struct wpabuf *msg;
1053         enum http_reply_code ret;
1054
1055         wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
1056         msg = web_get_item(data, "NewMessage", &ret);
1057         if (msg == NULL)
1058                 return ret;
1059         if (!sm->ctx->rx_req_set_selected_registrar ||
1060             sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) {
1061                 wpabuf_free(msg);
1062                 return HTTP_INTERNAL_SERVER_ERROR;
1063         }
1064         wpabuf_free(msg);
1065         *replyname = NULL;
1066         *reply = NULL;
1067         return HTTP_OK;
1068 }
1069
1070
1071 static enum http_reply_code
1072 web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data,
1073                       struct wpabuf **reply, const char **replyname)
1074 {
1075         struct wpabuf *msg;
1076         enum http_reply_code ret;
1077
1078         wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP");
1079         msg = web_get_item(data, "NewAPSettings", &ret);
1080         if (msg == NULL)
1081                 return ret;
1082         if (!sm->ctx->rx_req_reboot_ap ||
1083             sm->ctx->rx_req_reboot_ap(sm->priv, msg)) {
1084                 wpabuf_free(msg);
1085                 return HTTP_INTERNAL_SERVER_ERROR;
1086         }
1087         wpabuf_free(msg);
1088         *replyname = NULL;
1089         *reply = NULL;
1090         return HTTP_OK;
1091 }
1092
1093
1094 static enum http_reply_code
1095 web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data,
1096                      struct wpabuf **reply, const char **replyname)
1097 {
1098         struct wpabuf *msg;
1099         enum http_reply_code ret;
1100
1101         wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP");
1102         msg = web_get_item(data, "NewMessage", &ret);
1103         if (msg == NULL)
1104                 return ret;
1105         if (!sm->ctx->rx_req_reset_ap ||
1106             sm->ctx->rx_req_reset_ap(sm->priv, msg)) {
1107                 wpabuf_free(msg);
1108                 return HTTP_INTERNAL_SERVER_ERROR;
1109         }
1110         wpabuf_free(msg);
1111         *replyname = NULL;
1112         *reply = NULL;
1113         return HTTP_OK;
1114 }
1115
1116
1117 static enum http_reply_code
1118 web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data,
1119                        struct wpabuf **reply, const char **replyname)
1120 {
1121         struct wpabuf *msg;
1122         enum http_reply_code ret;
1123
1124         wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA");
1125         msg = web_get_item(data, "NewSTASettings", &ret);
1126         if (msg == NULL)
1127                 return ret;
1128         if (!sm->ctx->rx_req_reboot_sta ||
1129             sm->ctx->rx_req_reboot_sta(sm->priv, msg)) {
1130                 wpabuf_free(msg);
1131                 return HTTP_INTERNAL_SERVER_ERROR;
1132         }
1133         wpabuf_free(msg);
1134         *replyname = NULL;
1135         *reply = NULL;
1136         return HTTP_OK;
1137 }
1138
1139
1140 static enum http_reply_code
1141 web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data,
1142                       struct wpabuf **reply, const char **replyname)
1143 {
1144         struct wpabuf *msg;
1145         enum http_reply_code ret;
1146
1147         wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA");
1148         msg = web_get_item(data, "NewMessage", &ret);
1149         if (msg == NULL)
1150                 return ret;
1151         if (!sm->ctx->rx_req_reset_sta ||
1152             sm->ctx->rx_req_reset_sta(sm->priv, msg)) {
1153                 wpabuf_free(msg);
1154                 return HTTP_INTERNAL_SERVER_ERROR;
1155         }
1156         wpabuf_free(msg);
1157         *replyname = NULL;
1158         *reply = NULL;
1159         return HTTP_OK;
1160 }
1161
1162
1163 static const char *soap_prefix =
1164         "<?xml version=\"1.0\"?>\n"
1165         "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
1166         "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
1167         "<s:Body>\n";
1168 static const char *soap_postfix =
1169         "</s:Body>\n</s:Envelope>\n";
1170
1171 static const char *soap_error_prefix =
1172         "<s:Fault>\n"
1173         "<faultcode>s:Client</faultcode>\n"
1174         "<faultstring>UPnPError</faultstring>\n"
1175         "<detail>\n"
1176         "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
1177 static const char *soap_error_postfix =
1178         "<errorDescription>Error</errorDescription>\n"
1179         "</UPnPError>\n"
1180         "</detail>\n"
1181         "</s:Fault>\n";
1182
1183 static void web_connection_send_reply(struct web_connection *c,
1184                                       enum http_reply_code ret,
1185                                       const char *action, int action_len,
1186                                       const struct wpabuf *reply,
1187                                       const char *replyname)
1188 {
1189         struct wpabuf *buf;
1190         char *replydata;
1191         char *put_length_here = NULL;
1192         char *body_start = NULL;
1193
1194         if (reply) {
1195                 size_t len;
1196                 replydata = (char *) base64_encode(wpabuf_head(reply),
1197                                                    wpabuf_len(reply), &len);
1198         } else
1199                 replydata = NULL;
1200
1201         /* Parameters of the response:
1202          *      action(action_len) -- action we are responding to
1203          *      replyname -- a name we need for the reply
1204          *      replydata -- NULL or null-terminated string
1205          */
1206         buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
1207                            (action_len > 0 ? action_len * 2 : 0));
1208         if (buf == NULL) {
1209                 wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
1210                            "POST");
1211                 wpabuf_free(buf);
1212                 os_free(replydata);
1213                 return;
1214         }
1215
1216         /*
1217          * Assuming we will be successful, put in the output header first.
1218          * Note: we do not keep connections alive (and httpread does
1219          * not support it)... therefore we must have Connection: close.
1220          */
1221         if (ret == HTTP_OK) {
1222                 wpabuf_put_str(buf,
1223                                "HTTP/1.1 200 OK\r\n"
1224                                "Content-Type: text/xml; "
1225                                "charset=\"utf-8\"\r\n");
1226         } else {
1227                 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
1228         }
1229         wpabuf_put_str(buf, http_connection_close);
1230
1231         wpabuf_put_str(buf, "Content-Length: ");
1232         /*
1233          * We will paste the length in later, leaving some extra whitespace.
1234          * HTTP code is supposed to be tolerant of extra whitespace.
1235          */
1236         put_length_here = wpabuf_put(buf, 0);
1237         wpabuf_put_str(buf, "        \r\n");
1238
1239         http_put_date(buf);
1240
1241         /* terminating empty line */
1242         wpabuf_put_str(buf, "\r\n");
1243
1244         body_start = wpabuf_put(buf, 0);
1245
1246         if (ret == HTTP_OK) {
1247                 wpabuf_put_str(buf, soap_prefix);
1248                 wpabuf_put_str(buf, "<u:");
1249                 wpabuf_put_data(buf, action, action_len);
1250                 wpabuf_put_str(buf, "Response xmlns:u=\"");
1251                 wpabuf_put_str(buf, urn_wfawlanconfig);
1252                 wpabuf_put_str(buf, "\">\n");
1253                 if (replydata && replyname) {
1254                         /* TODO: might possibly need to escape part of reply
1255                          * data? ...
1256                          * probably not, unlikely to have ampersand(&) or left
1257                          * angle bracket (<) in it...
1258                          */
1259                         wpabuf_printf(buf, "<%s>", replyname);
1260                         wpabuf_put_str(buf, replydata);
1261                         wpabuf_printf(buf, "</%s>\n", replyname);
1262                 }
1263                 wpabuf_put_str(buf, "</u:");
1264                 wpabuf_put_data(buf, action, action_len);
1265                 wpabuf_put_str(buf, "Response>\n");
1266                 wpabuf_put_str(buf, soap_postfix);
1267         } else {
1268                 /* Error case */
1269                 wpabuf_put_str(buf, soap_prefix);
1270                 wpabuf_put_str(buf, soap_error_prefix);
1271                 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
1272                 wpabuf_put_str(buf, soap_error_postfix);
1273                 wpabuf_put_str(buf, soap_postfix);
1274         }
1275         os_free(replydata);
1276
1277         /* Now patch in the content length at the end */
1278         if (body_start && put_length_here) {
1279                 int body_length = (char *) wpabuf_put(buf, 0) - body_start;
1280                 char len_buf[10];
1281                 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
1282                 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
1283         }
1284
1285         send_wpabuf(c->sd, buf);
1286         wpabuf_free(buf);
1287 }
1288
1289
1290 static const char * web_get_action(struct web_connection *c,
1291                                    const char *filename, size_t *action_len)
1292 {
1293         const char *match;
1294         int match_len;
1295         char *b;
1296         char *action;
1297
1298         *action_len = 0;
1299         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
1300                 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
1301                            filename);
1302                 return NULL;
1303         }
1304         /* The SOAPAction line of the header tells us what we want to do */
1305         b = httpread_hdr_line_get(c->hread, "SOAPAction:");
1306         if (b == NULL)
1307                 return NULL;
1308         if (*b == '"')
1309                 b++;
1310         else
1311                 return NULL;
1312         match = urn_wfawlanconfig;
1313         match_len = os_strlen(urn_wfawlanconfig) - 1;
1314         if (os_strncasecmp(b, match, match_len))
1315                 return NULL;
1316         b += match_len;
1317         /* skip over version */
1318         while (isgraph(*b) && *b != '#')
1319                 b++;
1320         if (*b != '#')
1321                 return NULL;
1322         b++;
1323         /* Following the sharp(#) should be the action and a double quote */
1324         action = b;
1325         while (isgraph(*b) && *b != '"')
1326                 b++;
1327         if (*b != '"')
1328                 return NULL;
1329         *action_len = b - action;
1330         return action;
1331 }
1332
1333
1334 /* Given that we have received a header w/ POST, act upon it
1335  *
1336  * Format of POST (case-insensitive):
1337  *
1338  * First line must be:
1339  *      POST /<file> HTTP/1.1
1340  * Since we don't do anything fancy we just ignore other lines.
1341  *
1342  * Our response (if no error) which includes only required lines is:
1343  * HTTP/1.1 200 OK
1344  * Connection: close
1345  * Content-Type: text/xml
1346  * Date: <rfc1123-date>
1347  *
1348  * Header lines must end with \r\n
1349  * Per RFC 2616, content-length: is not required but connection:close
1350  * would appear to be required (given that we will be closing it!).
1351  */
1352 static void web_connection_parse_post(struct web_connection *c,
1353                                       const char *filename)
1354 {
1355         enum http_reply_code ret;
1356         struct upnp_wps_device_sm *sm = c->sm;
1357         char *data = httpread_data_get(c->hread); /* body of http msg */
1358         const char *action;
1359         size_t action_len;
1360         const char *replyname = NULL; /* argument name for the reply */
1361         struct wpabuf *reply = NULL; /* data for the reply */
1362
1363         ret = UPNP_INVALID_ACTION;
1364         action = web_get_action(c, filename, &action_len);
1365         if (action == NULL)
1366                 goto bad;
1367
1368         /*
1369          * There are quite a few possible actions. Although we appear to
1370          * support them all here, not all of them are necessarily supported by
1371          * callbacks at higher levels.
1372          */
1373         if (!os_strncasecmp("GetDeviceInfo", action, action_len))
1374                 ret = web_process_get_device_info(sm, &reply, &replyname);
1375         else if (!os_strncasecmp("PutMessage", action, action_len))
1376                 ret = web_process_put_message(sm, data, &reply, &replyname);
1377         else if (!os_strncasecmp("GetAPSettings", action, action_len))
1378                 ret = web_process_get_ap_settings(sm, data, &reply,
1379                                                   &replyname);
1380         else if (!os_strncasecmp("SetAPSettings", action, action_len))
1381                 ret = web_process_set_ap_settings(sm, data, &reply,
1382                                                   &replyname);
1383         else if (!os_strncasecmp("DelAPSettings", action, action_len))
1384                 ret = web_process_del_ap_settings(sm, data, &reply,
1385                                                   &replyname);
1386         else if (!os_strncasecmp("GetSTASettings", action, action_len))
1387                 ret = web_process_get_sta_settings(sm, data, &reply,
1388                                                    &replyname);
1389         else if (!os_strncasecmp("SetSTASettings", action, action_len))
1390                 ret = web_process_set_sta_settings(sm, data, &reply,
1391                                                    &replyname);
1392         else if (!os_strncasecmp("DelSTASettings", action, action_len))
1393                 ret = web_process_del_sta_settings(sm, data, &reply,
1394                                                   &replyname);
1395         else if (!os_strncasecmp("PutWLANResponse", action, action_len))
1396                 ret = web_process_put_wlan_response(sm, data, &reply,
1397                                                     &replyname);
1398         else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
1399                 ret = web_process_set_selected_registrar(sm, data, &reply,
1400                                                          &replyname);
1401         else if (!os_strncasecmp("RebootAP", action, action_len))
1402                 ret = web_process_reboot_ap(sm, data, &reply, &replyname);
1403         else if (!os_strncasecmp("ResetAP", action, action_len))
1404                 ret = web_process_reset_ap(sm, data, &reply, &replyname);
1405         else if (!os_strncasecmp("RebootSTA", action, action_len))
1406                 ret = web_process_reboot_sta(sm, data, &reply, &replyname);
1407         else if (!os_strncasecmp("ResetSTA", action, action_len))
1408                 ret = web_process_reset_sta(sm, data, &reply, &replyname);
1409         else
1410                 wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
1411
1412 bad:
1413         if (ret != HTTP_OK)
1414                 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
1415         web_connection_send_reply(c, ret, action, action_len, reply,
1416                                   replyname);
1417         wpabuf_free(reply);
1418 }
1419
1420
1421 /* Given that we have received a header w/ SUBSCRIBE, act upon it
1422  *
1423  * Format of SUBSCRIBE (case-insensitive):
1424  *
1425  * First line must be:
1426  *      SUBSCRIBE /wps_event HTTP/1.1
1427  *
1428  * Our response (if no error) which includes only required lines is:
1429  * HTTP/1.1 200 OK
1430  * Server: xx, UPnP/1.0, xx
1431  * SID: uuid:xxxxxxxxx
1432  * Timeout: Second-<n>
1433  * Content-Length: 0
1434  * Date: xxxx
1435  *
1436  * Header lines must end with \r\n
1437  * Per RFC 2616, content-length: is not required but connection:close
1438  * would appear to be required (given that we will be closing it!).
1439  */
1440 static void web_connection_parse_subscribe(struct web_connection *c,
1441                                            const char *filename)
1442 {
1443         struct upnp_wps_device_sm *sm = c->sm;
1444         struct wpabuf *buf;
1445         char *b;
1446         char *hdr = httpread_hdr_get(c->hread);
1447         char *h;
1448         char *match;
1449         int match_len;
1450         char *end;
1451         int len;
1452         int got_nt = 0;
1453         u8 uuid[UUID_LEN];
1454         int got_uuid = 0;
1455         char *callback_urls = NULL;
1456         struct subscription *s = NULL;
1457         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1458
1459         buf = wpabuf_alloc(1000);
1460         if (buf == NULL)
1461                 return;
1462
1463         /* Parse/validate headers */
1464         h = hdr;
1465         /* First line: SUBSCRIBE /wps_event HTTP/1.1
1466          * has already been parsed.
1467          */
1468         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1469                 ret = HTTP_PRECONDITION_FAILED;
1470                 goto error;
1471         }
1472         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
1473         end = os_strchr(h, '\n');
1474
1475         for (; end != NULL; h = end + 1) {
1476                 /* Option line by option line */
1477                 h = end + 1;
1478                 end = os_strchr(h, '\n');
1479                 if (end == NULL)
1480                         break; /* no unterminated lines allowed */
1481
1482                 /* NT assures that it is our type of subscription;
1483                  * not used for a renewl.
1484                  **/
1485                 match = "NT:";
1486                 match_len = os_strlen(match);
1487                 if (os_strncasecmp(h, match, match_len) == 0) {
1488                         h += match_len;
1489                         while (*h == ' ' || *h == '\t')
1490                                 h++;
1491                         match = "upnp:event";
1492                         match_len = os_strlen(match);
1493                         if (os_strncasecmp(h, match, match_len) != 0) {
1494                                 ret = HTTP_BAD_REQUEST;
1495                                 goto error;
1496                         }
1497                         got_nt = 1;
1498                         continue;
1499                 }
1500                 /* HOST should refer to us */
1501 #if 0
1502                 match = "HOST:";
1503                 match_len = os_strlen(match);
1504                 if (os_strncasecmp(h, match, match_len) == 0) {
1505                         h += match_len;
1506                         while (*h == ' ' || *h == '\t')
1507                                 h++;
1508                         .....
1509                 }
1510 #endif
1511                 /* CALLBACK gives one or more URLs for NOTIFYs
1512                  * to be sent as a result of the subscription.
1513                  * Each URL is enclosed in angle brackets.
1514                  */
1515                 match = "CALLBACK:";
1516                 match_len = os_strlen(match);
1517                 if (os_strncasecmp(h, match, match_len) == 0) {
1518                         h += match_len;
1519                         while (*h == ' ' || *h == '\t')
1520                                 h++;
1521                         len = end - h;
1522                         os_free(callback_urls);
1523                         callback_urls = os_malloc(len + 1);
1524                         if (callback_urls == NULL) {
1525                                 ret = HTTP_INTERNAL_SERVER_ERROR;
1526                                 goto error;
1527                         }
1528                         os_memcpy(callback_urls, h, len);
1529                         callback_urls[len] = 0;
1530                         continue;
1531                 }
1532                 /* SID is only for renewal */
1533                 match = "SID:";
1534                 match_len = os_strlen(match);
1535                 if (os_strncasecmp(h, match, match_len) == 0) {
1536                         h += match_len;
1537                         while (*h == ' ' || *h == '\t')
1538                                 h++;
1539                         match = "uuid:";
1540                         match_len = os_strlen(match);
1541                         if (os_strncasecmp(h, match, match_len) != 0) {
1542                                 ret = HTTP_BAD_REQUEST;
1543                                 goto error;
1544                         }
1545                         h += match_len;
1546                         while (*h == ' ' || *h == '\t')
1547                                 h++;
1548                         if (uuid_str2bin(h, uuid)) {
1549                                 ret = HTTP_BAD_REQUEST;
1550                                 goto error;
1551                         }
1552                         got_uuid = 1;
1553                         continue;
1554                 }
1555                 /* TIMEOUT is requested timeout, but apparently we can
1556                  * just ignore this.
1557                  */
1558         }
1559
1560         if (got_uuid) {
1561                 /* renewal */
1562                 if (callback_urls) {
1563                         ret = HTTP_BAD_REQUEST;
1564                         goto error;
1565                 }
1566                 s = subscription_renew(sm, uuid);
1567                 if (s == NULL) {
1568                         ret = HTTP_PRECONDITION_FAILED;
1569                         goto error;
1570                 }
1571         } else if (callback_urls) {
1572                 if (!got_nt) {
1573                         ret = HTTP_PRECONDITION_FAILED;
1574                         goto error;
1575                 }
1576                 s = subscription_start(sm, callback_urls);
1577                 if (s == NULL) {
1578                         ret = HTTP_INTERNAL_SERVER_ERROR;
1579                         goto error;
1580                 }
1581         } else {
1582                 ret = HTTP_PRECONDITION_FAILED;
1583                 goto error;
1584         }
1585
1586         /* success */
1587         http_put_reply_code(buf, HTTP_OK);
1588         wpabuf_put_str(buf, http_server_hdr);
1589         wpabuf_put_str(buf, http_connection_close);
1590         wpabuf_put_str(buf, "Content-Length: 0\r\n");
1591         wpabuf_put_str(buf, "SID: uuid:");
1592         /* subscription id */
1593         b = wpabuf_put(buf, 0);
1594         uuid_bin2str(s->uuid, b, 80);
1595         wpabuf_put(buf, os_strlen(b));
1596         wpabuf_put_str(buf, "\r\n");
1597         wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
1598         http_put_date(buf);
1599         /* And empty line to terminate header: */
1600         wpabuf_put_str(buf, "\r\n");
1601
1602         send_wpabuf(c->sd, buf);
1603         wpabuf_free(buf);
1604         os_free(callback_urls);
1605         return;
1606
1607 error:
1608         /* Per UPnP spec:
1609         * Errors
1610         * Incompatible headers
1611         *   400 Bad Request. If SID header and one of NT or CALLBACK headers
1612         *     are present, the publisher must respond with HTTP error
1613         *     400 Bad Request.
1614         * Missing or invalid CALLBACK
1615         *   412 Precondition Failed. If CALLBACK header is missing or does not
1616         *     contain a valid HTTP URL, the publisher must respond with HTTP
1617         *     error 412 Precondition Failed.
1618         * Invalid NT
1619         *   412 Precondition Failed. If NT header does not equal upnp:event,
1620         *     the publisher must respond with HTTP error 412 Precondition
1621         *     Failed.
1622         * [For resubscription, use 412 if unknown uuid].
1623         * Unable to accept subscription
1624         *   5xx. If a publisher is not able to accept a subscription (such as
1625         *     due to insufficient resources), it must respond with a
1626         *     HTTP 500-series error code.
1627         *   599 Too many subscriptions (not a standard HTTP error)
1628         */
1629         http_put_empty(buf, ret);
1630         send_wpabuf(c->sd, buf);
1631         wpabuf_free(buf);
1632         os_free(callback_urls);
1633 }
1634
1635
1636 /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
1637  *
1638  * Format of UNSUBSCRIBE (case-insensitive):
1639  *
1640  * First line must be:
1641  *      UNSUBSCRIBE /wps_event HTTP/1.1
1642  *
1643  * Our response (if no error) which includes only required lines is:
1644  * HTTP/1.1 200 OK
1645  * Content-Length: 0
1646  *
1647  * Header lines must end with \r\n
1648  * Per RFC 2616, content-length: is not required but connection:close
1649  * would appear to be required (given that we will be closing it!).
1650  */
1651 static void web_connection_parse_unsubscribe(struct web_connection *c,
1652                                              const char *filename)
1653 {
1654         struct upnp_wps_device_sm *sm = c->sm;
1655         struct wpabuf *buf;
1656         char *hdr = httpread_hdr_get(c->hread);
1657         char *h;
1658         char *match;
1659         int match_len;
1660         char *end;
1661         u8 uuid[UUID_LEN];
1662         int got_uuid = 0;
1663         struct subscription *s = NULL;
1664         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1665
1666         /* Parse/validate headers */
1667         h = hdr;
1668         /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
1669          * has already been parsed.
1670          */
1671         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1672                 ret = HTTP_PRECONDITION_FAILED;
1673                 goto send_msg;
1674         }
1675         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
1676         end = os_strchr(h, '\n');
1677
1678         for (; end != NULL; h = end + 1) {
1679                 /* Option line by option line */
1680                 h = end + 1;
1681                 end = os_strchr(h, '\n');
1682                 if (end == NULL)
1683                         break; /* no unterminated lines allowed */
1684
1685                 /* HOST should refer to us */
1686 #if 0
1687                 match = "HOST:";
1688                 match_len = os_strlen(match);
1689                 if (os_strncasecmp(h, match, match_len) == 0) {
1690                         h += match_len;
1691                         while (*h == ' ' || *h == '\t')
1692                                 h++;
1693                         .....
1694                 }
1695 #endif
1696                 /* SID is only for renewal */
1697                 match = "SID:";
1698                 match_len = os_strlen(match);
1699                 if (os_strncasecmp(h, match, match_len) == 0) {
1700                         h += match_len;
1701                         while (*h == ' ' || *h == '\t')
1702                                 h++;
1703                         match = "uuid:";
1704                         match_len = os_strlen(match);
1705                         if (os_strncasecmp(h, match, match_len) != 0) {
1706                                 ret = HTTP_BAD_REQUEST;
1707                                 goto send_msg;
1708                         }
1709                         h += match_len;
1710                         while (*h == ' ' || *h == '\t')
1711                                 h++;
1712                         if (uuid_str2bin(h, uuid)) {
1713                                 ret = HTTP_BAD_REQUEST;
1714                                 goto send_msg;
1715                         }
1716                         got_uuid = 1;
1717                         continue;
1718                 }
1719         }
1720
1721         if (got_uuid) {
1722                 s = subscription_find(sm, uuid);
1723                 if (s) {
1724                         wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
1725                                    s,
1726                                    (s && s->addr_list &&
1727                                     s->addr_list->domain_and_port) ?
1728                                    s->addr_list->domain_and_port : "-null-");
1729                         subscription_unlink(s);
1730                         subscription_destroy(s);
1731                 }
1732         } else {
1733                 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
1734                            "found)");
1735                 ret = HTTP_PRECONDITION_FAILED;
1736                 goto send_msg;
1737         }
1738
1739         ret = HTTP_OK;
1740
1741 send_msg:
1742         buf = wpabuf_alloc(200);
1743         if (buf == NULL)
1744                 return;
1745         http_put_empty(buf, ret);
1746         send_wpabuf(c->sd, buf);
1747         wpabuf_free(buf);
1748 }
1749
1750
1751 /* Send error in response to unknown requests */
1752 static void web_connection_unimplemented(struct web_connection *c)
1753 {
1754         struct wpabuf *buf;
1755         buf = wpabuf_alloc(200);
1756         if (buf == NULL)
1757                 return;
1758         http_put_empty(buf, HTTP_UNIMPLEMENTED);
1759         send_wpabuf(c->sd, buf);
1760         wpabuf_free(buf);
1761 }
1762
1763
1764
1765 /* Called when we have gotten an apparently valid http request.
1766  */
1767 static void web_connection_check_data(struct web_connection *c)
1768 {
1769         struct httpread *hread = c->hread;
1770         enum httpread_hdr_type htype = httpread_hdr_type_get(hread);
1771         /* char *data = httpread_data_get(hread); */
1772         char *filename = httpread_uri_get(hread);
1773
1774         c->done = 1;
1775         if (!filename) {
1776                 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
1777                 return;
1778         }
1779         /* Trim leading slashes from filename */
1780         while (*filename == '/')
1781                 filename++;
1782
1783         wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
1784                    htype, inet_ntoa(c->cli_addr.sin_addr),
1785                    htons(c->cli_addr.sin_port));
1786
1787         switch (htype) {
1788         case HTTPREAD_HDR_TYPE_GET:
1789                 web_connection_parse_get(c, filename);
1790                 break;
1791         case HTTPREAD_HDR_TYPE_POST:
1792                 web_connection_parse_post(c, filename);
1793                 break;
1794         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
1795                 web_connection_parse_subscribe(c, filename);
1796                 break;
1797         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
1798                 web_connection_parse_unsubscribe(c, filename);
1799                 break;
1800                 /* We are not required to support M-POST; just plain
1801                  * POST is supposed to work, so we only support that.
1802                  * If for some reason we need to support M-POST, it is
1803                  * mostly the same as POST, with small differences.
1804                  */
1805         default:
1806                 /* Send 501 for anything else */
1807                 web_connection_unimplemented(c);
1808                 break;
1809         }
1810 }
1811
1812
1813
1814 /* called back when we have gotten request */
1815 static void web_connection_got_file_handler(struct httpread *handle,
1816                                             void *cookie,
1817                                             enum httpread_event en)
1818 {
1819         struct web_connection *c = cookie;
1820
1821         if (en == HTTPREAD_EVENT_FILE_READY)
1822                 web_connection_check_data(c);
1823         web_connection_stop(c);
1824 }
1825
1826
1827 /* web_connection_start - Start web connection
1828  * @sm: WPS UPnP state machine from upnp_wps_device_init()
1829  * @sd: Socket descriptor
1830  * @addr: Client address
1831  *
1832  * The socket descriptor sd is handed over for ownership by the WPS UPnP
1833  * state machine.
1834  */
1835 static void web_connection_start(struct upnp_wps_device_sm *sm,
1836                                  int sd, struct sockaddr_in *addr)
1837 {
1838         struct web_connection *c = NULL;
1839
1840         /* if too many connections, bail */
1841         if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) {
1842                 close(sd);
1843                 return;
1844         }
1845
1846         c = os_zalloc(sizeof(*c));
1847         if (c == NULL)
1848                 return;
1849         os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr));
1850         c->sm = sm;
1851         c->sd = sd;
1852 #if 0
1853         /*
1854          * Setting non-blocking should not be necessary for read, and can mess
1855          * up sending where blocking might be better.
1856          */
1857         if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
1858                 break;
1859 #endif
1860         c->hread = httpread_create(c->sd, web_connection_got_file_handler,
1861                                    c /* cookie */,
1862                                    WEB_CONNECTION_MAX_READ,
1863                                    WEB_CONNECTION_TIMEOUT_SEC);
1864         if (c->hread == NULL)
1865                 goto fail;
1866         if (sm->web_connections) {
1867                 c->next = sm->web_connections;
1868                 c->prev = c->next->prev;
1869                 c->prev->next = c;
1870                 c->next->prev = c;
1871         } else {
1872                 sm->web_connections = c->next = c->prev = c;
1873         }
1874         sm->n_web_connections++;
1875         return;
1876
1877 fail:
1878         if (c)
1879                 web_connection_stop(c);
1880 }
1881
1882
1883 /*
1884  * Listening for web connections
1885  * We have a single TCP listening port, and hand off connections as we get
1886  * them.
1887  */
1888
1889 void web_listener_stop(struct upnp_wps_device_sm *sm)
1890 {
1891         if (sm->web_sd_registered) {
1892                 sm->web_sd_registered = 0;
1893                 eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ);
1894         }
1895         if (sm->web_sd >= 0)
1896                 close(sm->web_sd);
1897         sm->web_sd = -1;
1898 }
1899
1900
1901 static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
1902 {
1903         struct sockaddr_in addr;
1904         socklen_t addr_len = sizeof(addr);
1905         struct upnp_wps_device_sm *sm = sock_ctx;
1906         int new_sd;
1907
1908         /* Create state for new connection */
1909         /* Remember so we can cancel if need be */
1910         new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len);
1911         if (new_sd < 0) {
1912                 wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept "
1913                            "errno=%d (%s) web_sd=%d",
1914                            errno, strerror(errno), sm->web_sd);
1915                 return;
1916         }
1917         web_connection_start(sm, new_sd, &addr);
1918 }
1919
1920
1921 int web_listener_start(struct upnp_wps_device_sm *sm)
1922 {
1923         struct sockaddr_in addr;
1924         int port;
1925
1926         sm->web_sd = socket(AF_INET, SOCK_STREAM, 0);
1927         if (sm->web_sd < 0)
1928                 goto fail;
1929         if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0)
1930                 goto fail;
1931         port = 49152;  /* first non-reserved port */
1932         for (;;) {
1933                 os_memset(&addr, 0, sizeof(addr));
1934                 addr.sin_family = AF_INET;
1935                 addr.sin_addr.s_addr = sm->ip_addr;
1936                 addr.sin_port = htons(port);
1937                 if (bind(sm->web_sd, (struct sockaddr *) &addr,
1938                          sizeof(addr)) == 0)
1939                         break;
1940                 if (errno == EADDRINUSE) {
1941                         /* search for unused port */
1942                         if (++port == 65535)
1943                                 goto fail;
1944                         continue;
1945                 }
1946                 goto fail;
1947         }
1948         if (listen(sm->web_sd, 10 /* max backlog */) != 0)
1949                 goto fail;
1950         if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0)
1951                 goto fail;
1952         if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ,
1953                                 web_listener_handler, NULL, sm))
1954                 goto fail;
1955         sm->web_sd_registered = 1;
1956         sm->web_port = port;
1957
1958         return 0;
1959
1960 fail:
1961         /* Error */
1962         web_listener_stop(sm);
1963         return -1;
1964 }