Line data Source code
1 : /*
2 : * UPnP WPS Device
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-2010, Jouni Malinen <j@w1.fi>
7 : *
8 : * See below for more details on licensing and code history.
9 : */
10 :
11 : /*
12 : * This has been greatly stripped down from the original file
13 : * (upnp_wps_device.c) by Ted Merrill, Atheros Communications
14 : * in order to eliminate use of the bulky libupnp library etc.
15 : *
16 : * History:
17 : * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
18 : * the libupnp library.
19 : * The layering (by Sony) was well done; only a very minor modification
20 : * to API of upnp_wps_device.c was required.
21 : * libupnp was found to be undesirable because:
22 : * -- It consumed too much code and data space
23 : * -- It uses multiple threads, making debugging more difficult
24 : * and possibly reducing reliability.
25 : * -- It uses static variables and only supports one instance.
26 : * The shim and libupnp are here replaced by special code written
27 : * specifically for the needs of hostapd.
28 : * Various shortcuts can and are taken to keep the code size small.
29 : * Generally, execution time is not as crucial.
30 : *
31 : * BUGS:
32 : * -- UPnP requires that we be able to resolve domain names.
33 : * While uncommon, if we have to do it then it will stall the entire
34 : * hostapd program, which is bad.
35 : * This is because we use the standard linux getaddrinfo() function
36 : * which is syncronous.
37 : * An asyncronous solution would be to use the free "ares" library.
38 : * -- Does not have a robust output buffering scheme. Uses a single
39 : * fixed size output buffer per TCP/HTTP connection, with possible (although
40 : * unlikely) possibility of overflow and likely excessive use of RAM.
41 : * A better solution would be to write the HTTP output as a buffered stream,
42 : * using chunking: (handle header specially, then) generate data with
43 : * a printf-like function into a buffer, catching buffer full condition,
44 : * then send it out surrounded by http chunking.
45 : * -- There is some code that could be separated out into the common
46 : * library to be shared with wpa_supplicant.
47 : * -- Needs renaming with module prefix to avoid polluting the debugger
48 : * namespace and causing possible collisions with other static fncs
49 : * and structure declarations when using the debugger.
50 : * -- The http error code generation is pretty bogus, hopefully no one cares.
51 : *
52 : * Author: Ted Merrill, Atheros Communications, based upon earlier work
53 : * as explained above and below.
54 : *
55 : * Copyright:
56 : * Copyright 2008 Atheros Communications.
57 : *
58 : * The original header (of upnp_wps_device.c) reads:
59 : *
60 : * Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
61 : *
62 : * File Name: upnp_wps_device.c
63 : * Description: EAP-WPS UPnP device source
64 : *
65 : * Redistribution and use in source and binary forms, with or without
66 : * modification, are permitted provided that the following conditions
67 : * are met:
68 : *
69 : * * Redistributions of source code must retain the above copyright
70 : * notice, this list of conditions and the following disclaimer.
71 : * * Redistributions in binary form must reproduce the above copyright
72 : * notice, this list of conditions and the following disclaimer in
73 : * the documentation and/or other materials provided with the
74 : * distribution.
75 : * * Neither the name of Sony Corporation nor the names of its
76 : * contributors may be used to endorse or promote products derived
77 : * from this software without specific prior written permission.
78 : *
79 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
80 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
81 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
82 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
83 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
84 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
85 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
86 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
87 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
88 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
89 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
90 : *
91 : * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
92 : * typical header:
93 : *
94 : * Copyright (c) 2000-2003 Intel Corporation
95 : * All rights reserved.
96 : *
97 : * Redistribution and use in source and binary forms, with or without
98 : * modification, are permitted provided that the following conditions are met:
99 : *
100 : * * Redistributions of source code must retain the above copyright notice,
101 : * this list of conditions and the following disclaimer.
102 : * * Redistributions in binary form must reproduce the above copyright notice,
103 : * this list of conditions and the following disclaimer in the documentation
104 : * and/or other materials provided with the distribution.
105 : * * Neither name of Intel Corporation nor the names of its contributors
106 : * may be used to endorse or promote products derived from this software
107 : * without specific prior written permission.
108 : *
109 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
110 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
111 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
112 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
113 : * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
114 : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
115 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
116 : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
117 : * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
118 : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
119 : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
120 : */
121 :
122 : /*
123 : * Overview of WPS over UPnP:
124 : *
125 : * UPnP is a protocol that allows devices to discover each other and control
126 : * each other. In UPnP terminology, a device is either a "device" (a server
127 : * that provides information about itself and allows itself to be controlled)
128 : * or a "control point" (a client that controls "devices") or possibly both.
129 : * This file implements a UPnP "device".
130 : *
131 : * For us, we use mostly basic UPnP discovery, but the control part of interest
132 : * is WPS carried via UPnP messages. There is quite a bit of basic UPnP
133 : * discovery to do before we can get to WPS, however.
134 : *
135 : * UPnP discovery begins with "devices" send out multicast UDP packets to a
136 : * certain fixed multicast IP address and port, and "control points" sending
137 : * out other such UDP packets.
138 : *
139 : * The packets sent by devices are NOTIFY packets (not to be confused with TCP
140 : * NOTIFY packets that are used later) and those sent by control points are
141 : * M-SEARCH packets. These packets contain a simple HTTP style header. The
142 : * packets are sent redundantly to get around packet loss. Devices respond to
143 : * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
144 : * messages, which give similar information as the UDP NOTIFY packets.
145 : *
146 : * The above UDP packets advertise the (arbitrary) TCP ports that the
147 : * respective parties will listen to. The control point can then do a HTTP
148 : * SUBSCRIBE (something like an HTTP PUT) after which the device can do a
149 : * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
150 : *
151 : * The control point will also do HTTP GET of the "device file" listed in the
152 : * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
153 : * data), and based on this will do additional GETs... HTTP POSTs are done to
154 : * cause an action.
155 : *
156 : * Beyond some basic information in HTTP headers, additional information is in
157 : * the HTTP bodies, in a format set by the SOAP and XML standards, a markup
158 : * language related to HTML used for web pages. This language is intended to
159 : * provide the ultimate in self-documentation by providing a universal
160 : * namespace based on pseudo-URLs called URIs. Note that although a URI looks
161 : * like a URL (a web address), they are never accessed as such but are used
162 : * only as identifiers.
163 : *
164 : * The POST of a GetDeviceInfo gets information similar to what might be
165 : * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
166 : * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
167 : * to a bin64 ascii representation for encapsulation. When proxying messages,
168 : * WLANEvent and PutWLANResponse are used.
169 : *
170 : * This of course glosses over a lot of details.
171 : */
172 :
173 : #include "includes.h"
174 :
175 : #include <time.h>
176 : #include <net/if.h>
177 : #include <netdb.h>
178 : #include <sys/ioctl.h>
179 :
180 : #include "common.h"
181 : #include "uuid.h"
182 : #include "base64.h"
183 : #include "wps.h"
184 : #include "wps_i.h"
185 : #include "wps_upnp.h"
186 : #include "wps_upnp_i.h"
187 :
188 :
189 : /*
190 : * UPnP allows a client ("control point") to send a server like us ("device")
191 : * a domain name for registration, and we are supposed to resolve it. This is
192 : * bad because, using the standard Linux library, we will stall the entire
193 : * hostapd waiting for resolution.
194 : *
195 : * The "correct" solution would be to use an event driven library for domain
196 : * name resolution such as "ares". However, this would increase code size
197 : * further. Since it is unlikely that we'll actually see such domain names, we
198 : * can just refuse to accept them.
199 : */
200 : #define NO_DOMAIN_NAME_RESOLUTION 1 /* 1 to allow only dotted ip addresses */
201 :
202 :
203 : /*
204 : * UPnP does not scale well. If we were in a room with thousands of control
205 : * points then potentially we could be expected to handle subscriptions for
206 : * each of them, which would exhaust our memory. So we must set a limit. In
207 : * practice we are unlikely to see more than one or two.
208 : */
209 : #define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */
210 : #define MAX_ADDR_PER_SUBSCRIPTION 8
211 :
212 : /* Maximum number of Probe Request events per second */
213 : #define MAX_EVENTS_PER_SEC 5
214 :
215 :
216 : static struct upnp_wps_device_sm *shared_upnp_device = NULL;
217 :
218 :
219 : /* Write the current date/time per RFC */
220 564 : void format_date(struct wpabuf *buf)
221 : {
222 564 : const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
223 564 : const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
224 : "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
225 : struct tm *date;
226 : time_t t;
227 :
228 564 : t = time(NULL);
229 564 : date = gmtime(&t);
230 2256 : wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
231 564 : &weekday_str[date->tm_wday * 4], date->tm_mday,
232 1128 : &month_str[date->tm_mon * 4], date->tm_year + 1900,
233 : date->tm_hour, date->tm_min, date->tm_sec);
234 564 : }
235 :
236 :
237 : /***************************************************************************
238 : * UUIDs (unique identifiers)
239 : *
240 : * These are supposed to be unique in all the world.
241 : * Sometimes permanent ones are used, sometimes temporary ones
242 : * based on random numbers... there are different rules for valid content
243 : * of different types.
244 : * Each uuid is 16 bytes long.
245 : **************************************************************************/
246 :
247 : /* uuid_make -- construct a random UUID
248 : * The UPnP documents don't seem to offer any guidelines as to which method to
249 : * use for constructing UUIDs for subscriptions. Presumably any method from
250 : * rfc4122 is good enough; I've chosen random number method.
251 : */
252 11 : static void uuid_make(u8 uuid[UUID_LEN])
253 : {
254 11 : os_get_random(uuid, UUID_LEN);
255 :
256 : /* Replace certain bits as specified in rfc4122 or X.667 */
257 11 : uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */
258 11 : uuid[8] &= 0x3f; uuid[8] |= 0x80;
259 11 : }
260 :
261 :
262 : /*
263 : * Subscriber address handling.
264 : * Since a subscriber may have an arbitrary number of addresses, we have to
265 : * add a bunch of code to handle them.
266 : *
267 : * Addresses are passed in text, and MAY be domain names instead of the (usual
268 : * and expected) dotted IP addresses. Resolving domain names consumes a lot of
269 : * resources. Worse, we are currently using the standard Linux getaddrinfo()
270 : * which will block the entire program until complete or timeout! The proper
271 : * solution would be to use the "ares" library or similar with more state
272 : * machine steps etc. or just disable domain name resolution by setting
273 : * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
274 : */
275 :
276 : /* subscr_addr_delete -- delete single unlinked subscriber address
277 : * (be sure to unlink first if need be)
278 : */
279 18 : void subscr_addr_delete(struct subscr_addr *a)
280 : {
281 : /*
282 : * Note: do NOT free domain_and_port or path because they point to
283 : * memory within the allocation of "a".
284 : */
285 18 : os_free(a);
286 18 : }
287 :
288 :
289 : /* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
290 11 : static void subscr_addr_free_all(struct subscription *s)
291 : {
292 : struct subscr_addr *a, *tmp;
293 29 : dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
294 : {
295 18 : dl_list_del(&a->list);
296 18 : subscr_addr_delete(a);
297 : }
298 11 : }
299 :
300 :
301 : /* subscr_addr_add_url -- add address(es) for one url to subscription */
302 34 : static void subscr_addr_add_url(struct subscription *s, const char *url,
303 : size_t url_len)
304 : {
305 : int alloc_len;
306 34 : char *scratch_mem = NULL;
307 : char *mem;
308 : char *host;
309 : char *delim;
310 : char *path;
311 34 : int port = 80; /* port to send to (default is port 80) */
312 : struct addrinfo hints;
313 34 : struct addrinfo *result = NULL;
314 : struct addrinfo *rp;
315 : int rerr;
316 : size_t host_len, path_len;
317 :
318 : /* url MUST begin with http: */
319 34 : if (url_len < 7 || os_strncasecmp(url, "http://", 7))
320 : goto fail;
321 34 : url += 7;
322 34 : url_len -= 7;
323 :
324 : /* Make a copy of the string to allow modification during parsing */
325 34 : scratch_mem = dup_binstr(url, url_len);
326 34 : if (scratch_mem == NULL)
327 0 : goto fail;
328 34 : wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
329 34 : host = scratch_mem;
330 34 : path = os_strchr(host, '/');
331 34 : if (path)
332 34 : *path++ = '\0'; /* null terminate host */
333 :
334 : /* Process and remove optional port component */
335 34 : delim = os_strchr(host, ':');
336 34 : if (delim) {
337 34 : *delim = '\0'; /* null terminate host name for now */
338 34 : if (isdigit(delim[1]))
339 34 : port = atol(delim + 1);
340 : }
341 :
342 : /*
343 : * getaddrinfo does the right thing with dotted decimal notations, or
344 : * will resolve domain names. Resolving domain names will unfortunately
345 : * hang the entire program until it is resolved or it times out
346 : * internal to getaddrinfo; fortunately we think that the use of actual
347 : * domain names (vs. dotted decimal notations) should be uncommon.
348 : */
349 34 : os_memset(&hints, 0, sizeof(struct addrinfo));
350 34 : hints.ai_family = AF_INET; /* IPv4 */
351 34 : hints.ai_socktype = SOCK_STREAM;
352 : #if NO_DOMAIN_NAME_RESOLUTION
353 : /* Suppress domain name resolutions that would halt
354 : * the program for periods of time
355 : */
356 34 : hints.ai_flags = AI_NUMERICHOST;
357 : #else
358 : /* Allow domain name resolution. */
359 : hints.ai_flags = 0;
360 : #endif
361 34 : hints.ai_protocol = 0; /* Any protocol? */
362 34 : rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
363 : &hints, &result);
364 34 : if (rerr) {
365 0 : wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
366 : rerr, gai_strerror(rerr), host);
367 0 : goto fail;
368 : }
369 :
370 34 : if (delim)
371 34 : *delim = ':'; /* Restore port */
372 :
373 34 : host_len = os_strlen(host);
374 34 : path_len = path ? os_strlen(path) : 0;
375 34 : alloc_len = host_len + 1 + 1 + path_len + 1;
376 :
377 52 : for (rp = result; rp; rp = rp->ai_next) {
378 : struct subscr_addr *a;
379 :
380 : /* Limit no. of address to avoid denial of service attack */
381 34 : if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
382 16 : wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
383 : "Ignoring excessive addresses");
384 16 : break;
385 : }
386 :
387 18 : a = os_zalloc(sizeof(*a) + alloc_len);
388 18 : if (a == NULL)
389 0 : break;
390 18 : mem = (char *) (a + 1);
391 18 : a->domain_and_port = mem;
392 18 : os_memcpy(mem, host, host_len);
393 18 : mem += host_len + 1;
394 18 : a->path = mem;
395 18 : if (path == NULL || path[0] != '/')
396 18 : *mem++ = '/';
397 18 : if (path)
398 18 : os_memcpy(mem, path, path_len);
399 18 : os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
400 18 : a->saddr.sin_port = htons(port);
401 :
402 18 : dl_list_add(&s->addr_list, &a->list);
403 : }
404 :
405 : fail:
406 34 : if (result)
407 34 : freeaddrinfo(result);
408 34 : os_free(scratch_mem);
409 34 : }
410 :
411 :
412 : /* subscr_addr_list_create -- create list from urls in string.
413 : * Each url is enclosed by angle brackets.
414 : */
415 11 : static void subscr_addr_list_create(struct subscription *s,
416 : const char *url_list)
417 : {
418 : const char *end;
419 11 : wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
420 : for (;;) {
421 92 : while (*url_list == ' ' || *url_list == '\t')
422 2 : url_list++;
423 45 : if (*url_list != '<')
424 11 : break;
425 34 : url_list++;
426 34 : end = os_strchr(url_list, '>');
427 34 : if (end == NULL)
428 0 : break;
429 34 : subscr_addr_add_url(s, url_list, end - url_list);
430 34 : url_list = end + 1;
431 34 : }
432 11 : }
433 :
434 :
435 78 : static void wpabuf_put_property(struct wpabuf *buf, const char *name,
436 : const char *value)
437 : {
438 78 : wpabuf_put_str(buf, "<e:property>");
439 78 : wpabuf_printf(buf, "<%s>", name);
440 78 : if (value)
441 78 : wpabuf_put_str(buf, value);
442 78 : wpabuf_printf(buf, "</%s>", name);
443 78 : wpabuf_put_str(buf, "</e:property>\n");
444 78 : }
445 :
446 :
447 : /**
448 : * upnp_wps_device_send_event - Queue event messages for subscribers
449 : * @sm: WPS UPnP state machine from upnp_wps_device_init()
450 : *
451 : * This function queues the last WLANEvent to be sent for all currently
452 : * subscribed UPnP control points. sm->wlanevent must have been set with the
453 : * encoded data before calling this function.
454 : */
455 64 : static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
456 : {
457 : /* Enqueue event message for all subscribers */
458 : struct wpabuf *buf; /* holds event message */
459 64 : int buf_size = 0;
460 : struct subscription *s, *tmp;
461 : /* Actually, utf-8 is the default, but it doesn't hurt to specify it */
462 64 : const char *format_head =
463 : "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
464 : "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
465 64 : const char *format_tail = "</e:propertyset>\n";
466 : struct os_reltime now;
467 :
468 64 : if (dl_list_empty(&sm->subscriptions)) {
469 : /* optimize */
470 36 : return;
471 : }
472 :
473 47 : if (os_get_reltime(&now) == 0) {
474 47 : if (now.sec != sm->last_event_sec) {
475 25 : sm->last_event_sec = now.sec;
476 25 : sm->num_events_in_sec = 1;
477 : } else {
478 22 : sm->num_events_in_sec++;
479 : /*
480 : * In theory, this should apply to all WLANEvent
481 : * notifications, but EAP messages are of much higher
482 : * priority and Probe Request notifications should not
483 : * be allowed to drop EAP messages, so only throttle
484 : * Probe Request notifications.
485 : */
486 24 : if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
487 2 : sm->wlanevent_type ==
488 : UPNP_WPS_WLANEVENT_TYPE_PROBE) {
489 2 : wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
490 : "event notifications (%u seen "
491 : "during one second)",
492 : sm->num_events_in_sec);
493 2 : return;
494 : }
495 : }
496 : }
497 :
498 : /* Determine buffer size needed first */
499 45 : buf_size += os_strlen(format_head);
500 45 : buf_size += 50 + 2 * os_strlen("WLANEvent");
501 45 : if (sm->wlanevent)
502 45 : buf_size += os_strlen(sm->wlanevent);
503 45 : buf_size += os_strlen(format_tail);
504 :
505 45 : buf = wpabuf_alloc(buf_size);
506 45 : if (buf == NULL)
507 0 : return;
508 45 : wpabuf_put_str(buf, format_head);
509 45 : wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
510 45 : wpabuf_put_str(buf, format_tail);
511 :
512 45 : wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
513 45 : (char *) wpabuf_head(buf));
514 :
515 90 : dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
516 : list) {
517 45 : event_add(s, buf,
518 45 : sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
519 : }
520 :
521 45 : wpabuf_free(buf);
522 : }
523 :
524 :
525 : /*
526 : * Event subscription (subscriber machines register with us to receive event
527 : * messages).
528 : * This is the result of an incoming HTTP over TCP SUBSCRIBE request.
529 : */
530 :
531 : /* subscription_destroy -- destroy an unlinked subscription
532 : * Be sure to unlink first if necessary.
533 : */
534 11 : void subscription_destroy(struct subscription *s)
535 : {
536 : struct upnp_wps_device_interface *iface;
537 11 : wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
538 11 : subscr_addr_free_all(s);
539 11 : event_delete_all(s);
540 22 : dl_list_for_each(iface, &s->sm->interfaces,
541 : struct upnp_wps_device_interface, list)
542 11 : upnp_er_remove_notification(iface->wps->registrar, s);
543 11 : os_free(s);
544 11 : }
545 :
546 :
547 : /* subscription_list_age -- remove expired subscriptions */
548 11 : static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
549 : {
550 : struct subscription *s, *tmp;
551 11 : dl_list_for_each_safe(s, tmp, &sm->subscriptions,
552 : struct subscription, list) {
553 0 : if (s->timeout_time > now)
554 0 : break;
555 0 : wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
556 0 : dl_list_del(&s->list);
557 0 : subscription_destroy(s);
558 : }
559 11 : }
560 :
561 :
562 : /* subscription_find -- return existing subscription matching uuid, if any
563 : * returns NULL if not found
564 : */
565 14 : struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
566 : const u8 uuid[UUID_LEN])
567 : {
568 : struct subscription *s;
569 15 : dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
570 13 : if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
571 12 : return s; /* Found match */
572 : }
573 2 : return NULL;
574 : }
575 :
576 :
577 1 : static struct wpabuf * build_fake_wsc_ack(void)
578 : {
579 1 : struct wpabuf *msg = wpabuf_alloc(100);
580 1 : if (msg == NULL)
581 0 : return NULL;
582 1 : wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
583 1 : wpabuf_put_str(msg, "00:00:00:00:00:00");
584 2 : if (wps_build_version(msg) ||
585 1 : wps_build_msg_type(msg, WPS_WSC_ACK)) {
586 0 : wpabuf_free(msg);
587 0 : return NULL;
588 : }
589 : /* Enrollee Nonce */
590 1 : wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
591 1 : wpabuf_put_be16(msg, WPS_NONCE_LEN);
592 1 : wpabuf_put(msg, WPS_NONCE_LEN);
593 : /* Registrar Nonce */
594 1 : wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
595 1 : wpabuf_put_be16(msg, WPS_NONCE_LEN);
596 1 : wpabuf_put(msg, WPS_NONCE_LEN);
597 1 : wps_build_wfa_ext(msg, 0, NULL, 0);
598 1 : return msg;
599 : }
600 :
601 :
602 : /* subscription_first_event -- send format/queue event that is automatically
603 : * sent on a new subscription.
604 : */
605 11 : static int subscription_first_event(struct subscription *s)
606 : {
607 : /*
608 : * Actually, utf-8 is the default, but it doesn't hurt to specify it.
609 : *
610 : * APStatus is apparently a bit set,
611 : * 0x1 = configuration change (but is always set?)
612 : * 0x10 = ap is locked
613 : *
614 : * Per UPnP spec, we send out the last value of each variable, even
615 : * for WLANEvent, whatever it was.
616 : */
617 : char *wlan_event;
618 : struct wpabuf *buf;
619 11 : int ap_status = 1; /* TODO: add 0x10 if access point is locked */
620 11 : const char *head =
621 : "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
622 : "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
623 11 : const char *tail = "</e:propertyset>\n";
624 : char txt[10];
625 : int ret;
626 :
627 11 : if (s->sm->wlanevent == NULL) {
628 : /*
629 : * There has been no events before the subscription. However,
630 : * UPnP device architecture specification requires all the
631 : * evented variables to be included, so generate a dummy event
632 : * for this particular case using a WSC_ACK and all-zeros
633 : * nonces. The ER (UPnP control point) will ignore this, but at
634 : * least it will learn that WLANEvent variable will be used in
635 : * event notifications in the future.
636 : */
637 : struct wpabuf *msg;
638 1 : wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
639 : "initial WLANEvent");
640 1 : msg = build_fake_wsc_ack();
641 1 : if (msg) {
642 2 : s->sm->wlanevent = (char *)
643 1 : base64_encode(wpabuf_head(msg),
644 : wpabuf_len(msg), NULL);
645 1 : wpabuf_free(msg);
646 : }
647 : }
648 :
649 11 : wlan_event = s->sm->wlanevent;
650 11 : if (wlan_event == NULL || *wlan_event == '\0') {
651 0 : wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
652 : "initial event message");
653 0 : wlan_event = "";
654 : }
655 11 : buf = wpabuf_alloc(500 + os_strlen(wlan_event));
656 11 : if (buf == NULL)
657 0 : return -1;
658 :
659 11 : wpabuf_put_str(buf, head);
660 11 : wpabuf_put_property(buf, "STAStatus", "1");
661 11 : os_snprintf(txt, sizeof(txt), "%d", ap_status);
662 11 : wpabuf_put_property(buf, "APStatus", txt);
663 11 : if (*wlan_event)
664 11 : wpabuf_put_property(buf, "WLANEvent", wlan_event);
665 11 : wpabuf_put_str(buf, tail);
666 :
667 11 : ret = event_add(s, buf, 0);
668 11 : if (ret) {
669 0 : wpabuf_free(buf);
670 0 : return ret;
671 : }
672 11 : wpabuf_free(buf);
673 :
674 11 : return 0;
675 : }
676 :
677 :
678 : /**
679 : * subscription_start - Remember a UPnP control point to send events to.
680 : * @sm: WPS UPnP state machine from upnp_wps_device_init()
681 : * @callback_urls: Callback URLs
682 : * Returns: %NULL on error, or pointer to new subscription structure.
683 : */
684 11 : struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
685 : const char *callback_urls)
686 : {
687 : struct subscription *s;
688 11 : time_t now = time(NULL);
689 11 : time_t expire = now + UPNP_SUBSCRIBE_SEC;
690 :
691 : /* Get rid of expired subscriptions so we have room */
692 11 : subscription_list_age(sm, now);
693 :
694 : /* If too many subscriptions, remove oldest */
695 11 : if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
696 0 : s = dl_list_first(&sm->subscriptions, struct subscription,
697 : list);
698 0 : wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, "
699 : "trashing oldest");
700 0 : dl_list_del(&s->list);
701 0 : subscription_destroy(s);
702 : }
703 :
704 11 : s = os_zalloc(sizeof(*s));
705 11 : if (s == NULL)
706 0 : return NULL;
707 11 : dl_list_init(&s->addr_list);
708 11 : dl_list_init(&s->event_queue);
709 :
710 11 : s->sm = sm;
711 11 : s->timeout_time = expire;
712 11 : uuid_make(s->uuid);
713 11 : subscr_addr_list_create(s, callback_urls);
714 11 : if (dl_list_empty(&s->addr_list)) {
715 0 : wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
716 : "'%s' - drop subscription", callback_urls);
717 0 : subscription_destroy(s);
718 0 : return NULL;
719 : }
720 :
721 : /* Add to end of list, since it has the highest expiration time */
722 11 : dl_list_add_tail(&sm->subscriptions, &s->list);
723 : /* Queue up immediate event message (our last event)
724 : * as required by UPnP spec.
725 : */
726 11 : if (subscription_first_event(s)) {
727 0 : wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
728 : "event backlog");
729 0 : dl_list_del(&s->list);
730 0 : subscription_destroy(s);
731 0 : return NULL;
732 : }
733 11 : wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
734 : s, callback_urls);
735 : /* Schedule sending this */
736 11 : event_send_all_later(sm);
737 11 : return s;
738 : }
739 :
740 :
741 : /* subscription_renew -- find subscription and reset timeout */
742 3 : struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
743 : const u8 uuid[UUID_LEN])
744 : {
745 3 : time_t now = time(NULL);
746 3 : time_t expire = now + UPNP_SUBSCRIBE_SEC;
747 3 : struct subscription *s = subscription_find(sm, uuid);
748 3 : if (s == NULL)
749 1 : return NULL;
750 2 : wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
751 2 : dl_list_del(&s->list);
752 2 : s->timeout_time = expire;
753 : /* add back to end of list, since it now has highest expiry */
754 2 : dl_list_add_tail(&sm->subscriptions, &s->list);
755 2 : return s;
756 : }
757 :
758 :
759 : /**
760 : * upnp_wps_device_send_wlan_event - Event notification
761 : * @sm: WPS UPnP state machine from upnp_wps_device_init()
762 : * @from_mac_addr: Source (Enrollee) MAC address for the event
763 : * @ev_type: Event type
764 : * @msg: Event data
765 : * Returns: 0 on success, -1 on failure
766 : *
767 : * Tell external Registrars (UPnP control points) that something happened. In
768 : * particular, events include WPS messages from clients that are proxied to
769 : * external Registrars.
770 : */
771 384 : int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
772 : const u8 from_mac_addr[ETH_ALEN],
773 : enum upnp_wps_wlanevent_type ev_type,
774 : const struct wpabuf *msg)
775 : {
776 384 : int ret = -1;
777 : char type[2];
778 384 : const u8 *mac = from_mac_addr;
779 : char mac_text[18];
780 384 : u8 *raw = NULL;
781 : size_t raw_len;
782 : char *val;
783 : size_t val_len;
784 384 : int pos = 0;
785 :
786 384 : if (!sm)
787 320 : goto fail;
788 :
789 64 : os_snprintf(type, sizeof(type), "%1u", ev_type);
790 :
791 64 : raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
792 64 : raw = os_zalloc(raw_len);
793 64 : if (!raw)
794 0 : goto fail;
795 :
796 64 : *(raw + pos) = (u8) ev_type;
797 64 : pos += 1;
798 64 : os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
799 64 : wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
800 : mac_text);
801 64 : os_memcpy(raw + pos, mac_text, 17);
802 64 : pos += 17;
803 64 : if (msg) {
804 64 : os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
805 64 : pos += wpabuf_len(msg);
806 : }
807 64 : raw_len = pos;
808 :
809 64 : val = (char *) base64_encode(raw, raw_len, &val_len);
810 64 : if (val == NULL)
811 0 : goto fail;
812 :
813 64 : os_free(sm->wlanevent);
814 64 : sm->wlanevent = val;
815 64 : sm->wlanevent_type = ev_type;
816 64 : upnp_wps_device_send_event(sm);
817 :
818 64 : ret = 0;
819 :
820 : fail:
821 384 : os_free(raw);
822 :
823 384 : return ret;
824 : }
825 :
826 :
827 : #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
828 : #include <sys/sysctl.h>
829 : #include <net/route.h>
830 : #include <net/if_dl.h>
831 :
832 : static int eth_get(const char *device, u8 ea[ETH_ALEN])
833 : {
834 : struct if_msghdr *ifm;
835 : struct sockaddr_dl *sdl;
836 : u_char *p, *buf;
837 : size_t len;
838 : int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
839 :
840 : if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
841 : return -1;
842 : if ((buf = os_malloc(len)) == NULL)
843 : return -1;
844 : if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
845 : os_free(buf);
846 : return -1;
847 : }
848 : for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
849 : ifm = (struct if_msghdr *)p;
850 : sdl = (struct sockaddr_dl *)(ifm + 1);
851 : if (ifm->ifm_type != RTM_IFINFO ||
852 : (ifm->ifm_addrs & RTA_IFP) == 0)
853 : continue;
854 : if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
855 : os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
856 : continue;
857 : os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
858 : break;
859 : }
860 : os_free(buf);
861 :
862 : if (p >= buf + len) {
863 : errno = ESRCH;
864 : return -1;
865 : }
866 : return 0;
867 : }
868 : #endif /* __FreeBSD__ */
869 :
870 :
871 : /**
872 : * get_netif_info - Get hw and IP addresses for network device
873 : * @net_if: Selected network interface name
874 : * @ip_addr: Buffer for returning IP address in network byte order
875 : * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
876 : * @mac: Buffer for returning MAC address
877 : * Returns: 0 on success, -1 on failure
878 : */
879 24 : int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
880 : u8 mac[ETH_ALEN])
881 : {
882 : struct ifreq req;
883 24 : int sock = -1;
884 : struct sockaddr_in *addr;
885 : struct in_addr in_addr;
886 :
887 24 : *ip_addr_text = os_zalloc(16);
888 24 : if (*ip_addr_text == NULL)
889 0 : goto fail;
890 :
891 24 : sock = socket(AF_INET, SOCK_DGRAM, 0);
892 24 : if (sock < 0)
893 0 : goto fail;
894 :
895 24 : os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
896 24 : if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
897 0 : wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
898 0 : errno, strerror(errno));
899 0 : goto fail;
900 : }
901 24 : addr = (void *) &req.ifr_addr;
902 24 : *ip_addr = addr->sin_addr.s_addr;
903 24 : in_addr.s_addr = *ip_addr;
904 24 : os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
905 :
906 : #ifdef __linux__
907 24 : os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
908 24 : if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
909 0 : wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
910 0 : "%d (%s)", errno, strerror(errno));
911 0 : goto fail;
912 : }
913 24 : os_memcpy(mac, req.ifr_addr.sa_data, 6);
914 : #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
915 : if (eth_get(net_if, mac) < 0) {
916 : wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
917 : goto fail;
918 : }
919 : #else
920 : #error MAC address fetch not implemented
921 : #endif
922 :
923 24 : close(sock);
924 24 : return 0;
925 :
926 : fail:
927 0 : if (sock >= 0)
928 0 : close(sock);
929 0 : os_free(*ip_addr_text);
930 0 : *ip_addr_text = NULL;
931 0 : return -1;
932 : }
933 :
934 :
935 15 : static void upnp_wps_free_msearchreply(struct dl_list *head)
936 : {
937 : struct advertisement_state_machine *a, *tmp;
938 35 : dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
939 : list)
940 20 : msearchreply_state_machine_stop(a);
941 15 : }
942 :
943 :
944 15 : static void upnp_wps_free_subscriptions(struct dl_list *head,
945 : struct wps_registrar *reg)
946 : {
947 : struct subscription *s, *tmp;
948 16 : dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
949 1 : if (reg && s->reg != reg)
950 0 : continue;
951 1 : dl_list_del(&s->list);
952 1 : subscription_destroy(s);
953 : }
954 15 : }
955 :
956 :
957 : /**
958 : * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
959 : * @sm: WPS UPnP state machine from upnp_wps_device_init()
960 : */
961 15 : static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
962 : {
963 15 : if (!sm || !sm->started)
964 15 : return;
965 :
966 15 : wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
967 15 : web_listener_stop(sm);
968 15 : ssdp_listener_stop(sm);
969 15 : upnp_wps_free_msearchreply(&sm->msearch_replies);
970 15 : upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
971 :
972 15 : advertisement_state_machine_stop(sm, 1);
973 :
974 15 : event_send_stop_all(sm);
975 15 : os_free(sm->wlanevent);
976 15 : sm->wlanevent = NULL;
977 15 : os_free(sm->ip_addr_text);
978 15 : sm->ip_addr_text = NULL;
979 15 : if (sm->multicast_sd >= 0)
980 15 : close(sm->multicast_sd);
981 15 : sm->multicast_sd = -1;
982 :
983 15 : sm->started = 0;
984 : }
985 :
986 :
987 : /**
988 : * upnp_wps_device_start - Start WPS UPnP operations on an interface
989 : * @sm: WPS UPnP state machine from upnp_wps_device_init()
990 : * @net_if: Selected network interface name
991 : * Returns: 0 on success, -1 on failure
992 : */
993 15 : static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
994 : {
995 15 : if (!sm || !net_if)
996 0 : return -1;
997 :
998 15 : if (sm->started)
999 0 : upnp_wps_device_stop(sm);
1000 :
1001 15 : sm->multicast_sd = -1;
1002 15 : sm->ssdp_sd = -1;
1003 15 : sm->started = 1;
1004 15 : sm->advertise_count = 0;
1005 :
1006 : /* Fix up linux multicast handling */
1007 15 : if (add_ssdp_network(net_if))
1008 0 : goto fail;
1009 :
1010 : /* Determine which IP and mac address we're using */
1011 15 : if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
1012 15 : sm->mac_addr)) {
1013 0 : wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
1014 : "for %s. Does it have IP address?", net_if);
1015 0 : goto fail;
1016 : }
1017 :
1018 : /* Listen for incoming TCP connections so that others
1019 : * can fetch our "xml files" from us.
1020 : */
1021 15 : if (web_listener_start(sm))
1022 0 : goto fail;
1023 :
1024 : /* Set up for receiving discovery (UDP) packets */
1025 15 : if (ssdp_listener_start(sm))
1026 0 : goto fail;
1027 :
1028 : /* Set up for sending multicast */
1029 15 : if (ssdp_open_multicast(sm) < 0)
1030 0 : goto fail;
1031 :
1032 : /*
1033 : * Broadcast NOTIFY messages to let the world know we exist.
1034 : * This is done via a state machine since the messages should not be
1035 : * all sent out at once.
1036 : */
1037 15 : if (advertisement_state_machine_start(sm))
1038 0 : goto fail;
1039 :
1040 15 : return 0;
1041 :
1042 : fail:
1043 0 : upnp_wps_device_stop(sm);
1044 0 : return -1;
1045 : }
1046 :
1047 :
1048 : static struct upnp_wps_device_interface *
1049 15 : upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
1050 : {
1051 : struct upnp_wps_device_interface *iface;
1052 15 : dl_list_for_each(iface, &sm->interfaces,
1053 : struct upnp_wps_device_interface, list) {
1054 15 : if (iface->priv == priv)
1055 15 : return iface;
1056 : }
1057 0 : return NULL;
1058 : }
1059 :
1060 :
1061 : /**
1062 : * upnp_wps_device_deinit - Deinitialize WPS UPnP
1063 : * @sm: WPS UPnP state machine from upnp_wps_device_init()
1064 : * @priv: External context data that was used in upnp_wps_device_init() call
1065 : */
1066 193 : void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
1067 : {
1068 : struct upnp_wps_device_interface *iface;
1069 :
1070 193 : if (!sm)
1071 178 : return;
1072 :
1073 15 : iface = upnp_wps_get_iface(sm, priv);
1074 15 : if (iface == NULL) {
1075 0 : wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
1076 : "instance to deinit");
1077 0 : return;
1078 : }
1079 15 : wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
1080 15 : if (dl_list_len(&sm->interfaces) == 1) {
1081 15 : wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
1082 : "- free global device instance");
1083 15 : upnp_wps_device_stop(sm);
1084 : } else
1085 0 : upnp_wps_free_subscriptions(&sm->subscriptions,
1086 0 : iface->wps->registrar);
1087 15 : dl_list_del(&iface->list);
1088 :
1089 15 : if (iface->peer.wps)
1090 10 : wps_deinit(iface->peer.wps);
1091 15 : os_free(iface->ctx->ap_pin);
1092 15 : os_free(iface->ctx);
1093 15 : os_free(iface);
1094 :
1095 15 : if (dl_list_empty(&sm->interfaces)) {
1096 15 : os_free(sm->root_dir);
1097 15 : os_free(sm->desc_url);
1098 15 : os_free(sm);
1099 15 : shared_upnp_device = NULL;
1100 : }
1101 : }
1102 :
1103 :
1104 : /**
1105 : * upnp_wps_device_init - Initialize WPS UPnP
1106 : * @ctx: callback table; we must eventually free it
1107 : * @wps: Pointer to longterm WPS context
1108 : * @priv: External context data that will be used in callbacks
1109 : * @net_if: Selected network interface name
1110 : * Returns: WPS UPnP state or %NULL on failure
1111 : */
1112 : struct upnp_wps_device_sm *
1113 15 : upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
1114 : void *priv, char *net_if)
1115 : {
1116 : struct upnp_wps_device_sm *sm;
1117 : struct upnp_wps_device_interface *iface;
1118 15 : int start = 0;
1119 :
1120 15 : iface = os_zalloc(sizeof(*iface));
1121 15 : if (iface == NULL) {
1122 0 : os_free(ctx->ap_pin);
1123 0 : os_free(ctx);
1124 0 : return NULL;
1125 : }
1126 15 : wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
1127 :
1128 15 : iface->ctx = ctx;
1129 15 : iface->wps = wps;
1130 15 : iface->priv = priv;
1131 :
1132 15 : if (shared_upnp_device) {
1133 0 : wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
1134 : "context");
1135 0 : sm = shared_upnp_device;
1136 : } else {
1137 15 : wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
1138 15 : sm = os_zalloc(sizeof(*sm));
1139 15 : if (!sm) {
1140 0 : wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
1141 : "failed");
1142 0 : os_free(iface);
1143 0 : os_free(ctx->ap_pin);
1144 0 : os_free(ctx);
1145 0 : return NULL;
1146 : }
1147 15 : shared_upnp_device = sm;
1148 :
1149 15 : dl_list_init(&sm->msearch_replies);
1150 15 : dl_list_init(&sm->subscriptions);
1151 15 : dl_list_init(&sm->interfaces);
1152 15 : start = 1;
1153 : }
1154 :
1155 15 : dl_list_add(&sm->interfaces, &iface->list);
1156 :
1157 15 : if (start && upnp_wps_device_start(sm, net_if)) {
1158 0 : upnp_wps_device_deinit(sm, priv);
1159 0 : return NULL;
1160 : }
1161 :
1162 :
1163 15 : return sm;
1164 : }
1165 :
1166 :
1167 : /**
1168 : * upnp_wps_subscribers - Check whether there are any event subscribers
1169 : * @sm: WPS UPnP state machine from upnp_wps_device_init()
1170 : * Returns: 0 if no subscribers, 1 if subscribers
1171 : */
1172 17 : int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
1173 : {
1174 17 : return !dl_list_empty(&sm->subscriptions);
1175 : }
1176 :
1177 :
1178 10 : int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
1179 : {
1180 : struct upnp_wps_device_interface *iface;
1181 10 : if (sm == NULL)
1182 1 : return 0;
1183 :
1184 18 : dl_list_for_each(iface, &sm->interfaces,
1185 : struct upnp_wps_device_interface, list) {
1186 9 : os_free(iface->ctx->ap_pin);
1187 9 : if (ap_pin) {
1188 4 : iface->ctx->ap_pin = os_strdup(ap_pin);
1189 4 : if (iface->ctx->ap_pin == NULL)
1190 0 : return -1;
1191 : } else
1192 5 : iface->ctx->ap_pin = NULL;
1193 : }
1194 :
1195 9 : return 0;
1196 : }
|