1189251Ssam/*
2189251Ssam * UPnP WPS Device
3189251Ssam * Copyright (c) 2000-2003 Intel Corporation
4189251Ssam * Copyright (c) 2006-2007 Sony Corporation
5189251Ssam * Copyright (c) 2008-2009 Atheros Communications
6189251Ssam * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
7189251Ssam *
8189251Ssam * See below for more details on licensing and code history.
9189251Ssam */
10189251Ssam
11189251Ssam/*
12189251Ssam * This has been greatly stripped down from the original file
13189251Ssam * (upnp_wps_device.c) by Ted Merrill, Atheros Communications
14189251Ssam * in order to eliminate use of the bulky libupnp library etc.
15189251Ssam *
16189251Ssam * History:
17189251Ssam * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
18189251Ssam * the libupnp library.
19189251Ssam * The layering (by Sony) was well done; only a very minor modification
20189251Ssam * to API of upnp_wps_device.c was required.
21189251Ssam * libupnp was found to be undesirable because:
22189251Ssam * -- It consumed too much code and data space
23189251Ssam * -- It uses multiple threads, making debugging more difficult
24189251Ssam *      and possibly reducing reliability.
25189251Ssam * -- It uses static variables and only supports one instance.
26189251Ssam * The shim and libupnp are here replaced by special code written
27189251Ssam * specifically for the needs of hostapd.
28189251Ssam * Various shortcuts can and are taken to keep the code size small.
29189251Ssam * Generally, execution time is not as crucial.
30189251Ssam *
31189251Ssam * BUGS:
32189251Ssam * -- UPnP requires that we be able to resolve domain names.
33189251Ssam * While uncommon, if we have to do it then it will stall the entire
34189251Ssam * hostapd program, which is bad.
35189251Ssam * This is because we use the standard linux getaddrinfo() function
36189251Ssam * which is syncronous.
37189251Ssam * An asyncronous solution would be to use the free "ares" library.
38189251Ssam * -- Does not have a robust output buffering scheme.  Uses a single
39189251Ssam * fixed size output buffer per TCP/HTTP connection, with possible (although
40189251Ssam * unlikely) possibility of overflow and likely excessive use of RAM.
41189251Ssam * A better solution would be to write the HTTP output as a buffered stream,
42189251Ssam * using chunking: (handle header specially, then) generate data with
43189251Ssam * a printf-like function into a buffer, catching buffer full condition,
44189251Ssam * then send it out surrounded by http chunking.
45189251Ssam * -- There is some code that could be separated out into the common
46189251Ssam * library to be shared with wpa_supplicant.
47189251Ssam * -- Needs renaming with module prefix to avoid polluting the debugger
48189251Ssam * namespace and causing possible collisions with other static fncs
49189251Ssam * and structure declarations when using the debugger.
50189251Ssam * -- The http error code generation is pretty bogus, hopefully noone cares.
51189251Ssam *
52189251Ssam * Author: Ted Merrill, Atheros Communications, based upon earlier work
53189251Ssam * as explained above and below.
54189251Ssam *
55189251Ssam * Copyright:
56189251Ssam * Copyright 2008 Atheros Communications.
57189251Ssam *
58189251Ssam * The original header (of upnp_wps_device.c) reads:
59189251Ssam *
60189251Ssam *  Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
61189251Ssam *
62189251Ssam *  File Name: upnp_wps_device.c
63189251Ssam *  Description: EAP-WPS UPnP device source
64189251Ssam *
65189251Ssam *   Redistribution and use in source and binary forms, with or without
66189251Ssam *   modification, are permitted provided that the following conditions
67189251Ssam *   are met:
68189251Ssam *
69189251Ssam *     * Redistributions of source code must retain the above copyright
70189251Ssam *       notice, this list of conditions and the following disclaimer.
71189251Ssam *     * Redistributions in binary form must reproduce the above copyright
72189251Ssam *       notice, this list of conditions and the following disclaimer in
73189251Ssam *       the documentation and/or other materials provided with the
74189251Ssam *       distribution.
75189251Ssam *     * Neither the name of Sony Corporation nor the names of its
76189251Ssam *       contributors may be used to endorse or promote products derived
77189251Ssam *       from this software without specific prior written permission.
78189251Ssam *
79189251Ssam *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
80189251Ssam *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
81189251Ssam *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
82189251Ssam *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
83189251Ssam *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
84189251Ssam *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
85189251Ssam *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
86189251Ssam *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
87189251Ssam *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
88189251Ssam *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
89189251Ssam *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
90189251Ssam *
91189251Ssam * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
92189251Ssam * typical header:
93189251Ssam *
94189251Ssam * Copyright (c) 2000-2003 Intel Corporation
95189251Ssam * All rights reserved.
96189251Ssam *
97189251Ssam * Redistribution and use in source and binary forms, with or without
98189251Ssam * modification, are permitted provided that the following conditions are met:
99189251Ssam *
100189251Ssam * * Redistributions of source code must retain the above copyright notice,
101189251Ssam * this list of conditions and the following disclaimer.
102189251Ssam * * Redistributions in binary form must reproduce the above copyright notice,
103189251Ssam * this list of conditions and the following disclaimer in the documentation
104189251Ssam * and/or other materials provided with the distribution.
105189251Ssam * * Neither name of Intel Corporation nor the names of its contributors
106189251Ssam * may be used to endorse or promote products derived from this software
107189251Ssam * without specific prior written permission.
108189251Ssam *
109189251Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
110189251Ssam * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
111189251Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
112189251Ssam * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
113189251Ssam * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
114189251Ssam * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
115189251Ssam * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
116189251Ssam * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
117189251Ssam * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
118189251Ssam * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
119189251Ssam * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
120189251Ssam*/
121189251Ssam
122189251Ssam/*
123189251Ssam * Overview of WPS over UPnP:
124189251Ssam *
125189251Ssam * UPnP is a protocol that allows devices to discover each other and control
126189251Ssam * each other. In UPnP terminology, a device is either a "device" (a server
127189251Ssam * that provides information about itself and allows itself to be controlled)
128189251Ssam * or a "control point" (a client that controls "devices") or possibly both.
129189251Ssam * This file implements a UPnP "device".
130189251Ssam *
131189251Ssam * For us, we use mostly basic UPnP discovery, but the control part of interest
132189251Ssam * is WPS carried via UPnP messages. There is quite a bit of basic UPnP
133189251Ssam * discovery to do before we can get to WPS, however.
134189251Ssam *
135189251Ssam * UPnP discovery begins with "devices" send out multicast UDP packets to a
136189251Ssam * certain fixed multicast IP address and port, and "control points" sending
137189251Ssam * out other such UDP packets.
138189251Ssam *
139189251Ssam * The packets sent by devices are NOTIFY packets (not to be confused with TCP
140189251Ssam * NOTIFY packets that are used later) and those sent by control points are
141189251Ssam * M-SEARCH packets. These packets contain a simple HTTP style header. The
142189251Ssam * packets are sent redundantly to get around packet loss. Devices respond to
143189251Ssam * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
144189251Ssam * messages, which give similar information as the UDP NOTIFY packets.
145189251Ssam *
146189251Ssam * The above UDP packets advertise the (arbitrary) TCP ports that the
147189251Ssam * respective parties will listen to. The control point can then do a HTTP
148189251Ssam * SUBSCRIBE (something like an HTTP PUT) after which the device can do a
149189251Ssam * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
150189251Ssam *
151189251Ssam * The control point will also do HTTP GET of the "device file" listed in the
152189251Ssam * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
153189251Ssam * data), and based on this will do additional GETs... HTTP POSTs are done to
154189251Ssam * cause an action.
155189251Ssam *
156189251Ssam * Beyond some basic information in HTTP headers, additional information is in
157189251Ssam * the HTTP bodies, in a format set by the SOAP and XML standards, a markup
158189251Ssam * language related to HTML used for web pages. This language is intended to
159189251Ssam * provide the ultimate in self-documentation by providing a universal
160189251Ssam * namespace based on pseudo-URLs called URIs. Note that although a URI looks
161189251Ssam * like a URL (a web address), they are never accessed as such but are used
162189251Ssam * only as identifiers.
163189251Ssam *
164189251Ssam * The POST of a GetDeviceInfo gets information similar to what might be
165189251Ssam * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
166189251Ssam * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
167189251Ssam * to a bin64 ascii representation for encapsulation. When proxying messages,
168189251Ssam * WLANEvent and PutWLANResponse are used.
169189251Ssam *
170189251Ssam * This of course glosses over a lot of details.
171189251Ssam */
172189251Ssam
173189251Ssam#include "includes.h"
174189251Ssam
175189251Ssam#include <assert.h>
176189251Ssam#include <net/if.h>
177189251Ssam#include <netdb.h>
178189251Ssam#include <sys/ioctl.h>
179189251Ssam
180189251Ssam#include "common.h"
181189251Ssam#include "uuid.h"
182189251Ssam#include "base64.h"
183189251Ssam#include "wps.h"
184189251Ssam#include "wps_i.h"
185189251Ssam#include "wps_upnp.h"
186189251Ssam#include "wps_upnp_i.h"
187189251Ssam
188189251Ssam
189189251Ssam/*
190189251Ssam * UPnP allows a client ("control point") to send a server like us ("device")
191189251Ssam * a domain name for registration, and we are supposed to resolve it. This is
192189251Ssam * bad because, using the standard Linux library, we will stall the entire
193189251Ssam * hostapd waiting for resolution.
194189251Ssam *
195189251Ssam * The "correct" solution would be to use an event driven library for domain
196189251Ssam * name resolution such as "ares". However, this would increase code size
197189251Ssam * further. Since it is unlikely that we'll actually see such domain names, we
198189251Ssam * can just refuse to accept them.
199189251Ssam */
200189251Ssam#define NO_DOMAIN_NAME_RESOLUTION 1  /* 1 to allow only dotted ip addresses */
201189251Ssam
202189251Ssam
203189251Ssam/*
204189251Ssam * UPnP does not scale well. If we were in a room with thousands of control
205189251Ssam * points then potentially we could be expected to handle subscriptions for
206189251Ssam * each of them, which would exhaust our memory. So we must set a limit. In
207189251Ssam * practice we are unlikely to see more than one or two.
208189251Ssam */
209189251Ssam#define MAX_SUBSCRIPTIONS 4    /* how many subscribing clients we handle */
210189251Ssam#define MAX_ADDR_PER_SUBSCRIPTION 8
211189251Ssam
212189251Ssam
213189251Ssam/* Write the current date/time per RFC */
214189251Ssamvoid format_date(struct wpabuf *buf)
215189251Ssam{
216189251Ssam	const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
217189251Ssam	const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
218189251Ssam		"Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
219189251Ssam	struct tm *date;
220189251Ssam	time_t t;
221189251Ssam
222189251Ssam	t = time(NULL);
223189251Ssam	date = gmtime(&t);
224189251Ssam	wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
225189251Ssam		      &weekday_str[date->tm_wday * 4], date->tm_mday,
226189251Ssam		      &month_str[date->tm_mon * 4], date->tm_year + 1900,
227189251Ssam		      date->tm_hour, date->tm_min, date->tm_sec);
228189251Ssam}
229189251Ssam
230189251Ssam
231189251Ssam/***************************************************************************
232189251Ssam * UUIDs (unique identifiers)
233189251Ssam *
234189251Ssam * These are supposed to be unique in all the world.
235189251Ssam * Sometimes permanent ones are used, sometimes temporary ones
236189251Ssam * based on random numbers... there are different rules for valid content
237189251Ssam * of different types.
238189251Ssam * Each uuid is 16 bytes long.
239189251Ssam **************************************************************************/
240189251Ssam
241189251Ssam/* uuid_make -- construct a random UUID
242189251Ssam * The UPnP documents don't seem to offer any guidelines as to which method to
243189251Ssam * use for constructing UUIDs for subscriptions. Presumably any method from
244189251Ssam * rfc4122 is good enough; I've chosen random number method.
245189251Ssam */
246189251Ssamstatic void uuid_make(u8 uuid[UUID_LEN])
247189251Ssam{
248189251Ssam	os_get_random(uuid, UUID_LEN);
249189251Ssam
250189251Ssam	/* Replace certain bits as specified in rfc4122 or X.667 */
251189251Ssam	uuid[6] &= 0x0f; uuid[6] |= (4 << 4);   /* version 4 == random gen */
252189251Ssam	uuid[8] &= 0x3f; uuid[8] |= 0x80;
253189251Ssam}
254189251Ssam
255189251Ssam
256189251Ssam/*
257189251Ssam * Subscriber address handling.
258189251Ssam * Since a subscriber may have an arbitrary number of addresses, we have to
259189251Ssam * add a bunch of code to handle them.
260189251Ssam *
261189251Ssam * Addresses are passed in text, and MAY be domain names instead of the (usual
262189251Ssam * and expected) dotted IP addresses. Resolving domain names consumes a lot of
263189251Ssam * resources. Worse, we are currently using the standard Linux getaddrinfo()
264189251Ssam * which will block the entire program until complete or timeout! The proper
265189251Ssam * solution would be to use the "ares" library or similar with more state
266189251Ssam * machine steps etc. or just disable domain name resolution by setting
267189251Ssam * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
268189251Ssam */
269189251Ssam
270189251Ssam/* subscr_addr_delete -- delete single unlinked subscriber address
271189251Ssam * (be sure to unlink first if need be)
272189251Ssam */
273189251Ssamstatic void subscr_addr_delete(struct subscr_addr *a)
274189251Ssam{
275189251Ssam	/*
276189251Ssam	 * Note: do NOT free domain_and_port or path because they point to
277189251Ssam	 * memory within the allocation of "a".
278189251Ssam	 */
279189251Ssam	os_free(a);
280189251Ssam}
281189251Ssam
282189251Ssam
283189251Ssam/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
284189251Ssamstatic void subscr_addr_free_all(struct subscription *s)
285189251Ssam{
286214734Srpaulo	struct subscr_addr *a, *tmp;
287214734Srpaulo	dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
288214734Srpaulo	{
289214734Srpaulo		dl_list_del(&a->list);
290189251Ssam		subscr_addr_delete(a);
291189251Ssam	}
292189251Ssam}
293189251Ssam
294189251Ssam
295189251Ssam/* subscr_addr_add_url -- add address(es) for one url to subscription */
296189251Ssamstatic void subscr_addr_add_url(struct subscription *s, const char *url)
297189251Ssam{
298189251Ssam	int alloc_len;
299189251Ssam	char *scratch_mem = NULL;
300189251Ssam	char *mem;
301189251Ssam	char *domain_and_port;
302189251Ssam	char *delim;
303189251Ssam	char *path;
304189251Ssam	char *domain;
305189251Ssam	int port = 80;  /* port to send to (default is port 80) */
306189251Ssam	struct addrinfo hints;
307189251Ssam	struct addrinfo *result = NULL;
308189251Ssam	struct addrinfo *rp;
309189251Ssam	int rerr;
310189251Ssam	struct subscr_addr *a = NULL;
311189251Ssam
312189251Ssam	/* url MUST begin with http: */
313189251Ssam	if (os_strncasecmp(url, "http://", 7))
314189251Ssam		goto fail;
315189251Ssam	url += 7;
316189251Ssam
317189251Ssam	/* allocate memory for the extra stuff we need */
318189251Ssam	alloc_len = (2 * (os_strlen(url) + 1));
319189251Ssam	scratch_mem = os_zalloc(alloc_len);
320189251Ssam	if (scratch_mem == NULL)
321189251Ssam		goto fail;
322189251Ssam	mem = scratch_mem;
323189251Ssam	strcpy(mem, url);
324189251Ssam	domain_and_port = mem;
325189251Ssam	mem += 1 + os_strlen(mem);
326189251Ssam	delim = os_strchr(domain_and_port, '/');
327189251Ssam	if (delim) {
328189251Ssam		*delim++ = 0;   /* null terminate domain and port */
329189251Ssam		path = delim;
330189251Ssam	} else {
331189251Ssam		path = domain_and_port + os_strlen(domain_and_port);
332189251Ssam	}
333189251Ssam	domain = mem;
334189251Ssam	strcpy(domain, domain_and_port);
335189251Ssam	delim = strchr(domain, ':');
336189251Ssam	if (delim) {
337189251Ssam		*delim++ = 0;   /* null terminate domain */
338189251Ssam		if (isdigit(*delim))
339189251Ssam			port = atol(delim);
340189251Ssam	}
341189251Ssam
342189251Ssam	/*
343189251Ssam	 * getaddrinfo does the right thing with dotted decimal notations, or
344189251Ssam	 * will resolve domain names. Resolving domain names will unfortunately
345189251Ssam	 * hang the entire program until it is resolved or it times out
346189251Ssam	 * internal to getaddrinfo; fortunately we think that the use of actual
347189251Ssam	 * domain names (vs. dotted decimal notations) should be uncommon.
348189251Ssam	 */
349189251Ssam	os_memset(&hints, 0, sizeof(struct addrinfo));
350189251Ssam	hints.ai_family = AF_INET;      /* IPv4 */
351189251Ssam	hints.ai_socktype = SOCK_STREAM;
352189251Ssam#if NO_DOMAIN_NAME_RESOLUTION
353189251Ssam	/* Suppress domain name resolutions that would halt
354189251Ssam	 * the program for periods of time
355189251Ssam	 */
356189251Ssam	hints.ai_flags = AI_NUMERICHOST;
357189251Ssam#else
358189251Ssam	/* Allow domain name resolution. */
359189251Ssam	hints.ai_flags = 0;
360189251Ssam#endif
361189251Ssam	hints.ai_protocol = 0;          /* Any protocol? */
362189251Ssam	rerr = getaddrinfo(domain, NULL /* fill in port ourselves */,
363189251Ssam			   &hints, &result);
364189251Ssam	if (rerr) {
365189251Ssam		wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
366189251Ssam			   rerr, gai_strerror(rerr), domain);
367189251Ssam		goto fail;
368189251Ssam	}
369189251Ssam	for (rp = result; rp; rp = rp->ai_next) {
370189251Ssam		/* Limit no. of address to avoid denial of service attack */
371214734Srpaulo		if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
372189251Ssam			wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
373189251Ssam				   "Ignoring excessive addresses");
374189251Ssam			break;
375189251Ssam		}
376189251Ssam
377189251Ssam		a = os_zalloc(sizeof(*a) + alloc_len);
378189251Ssam		if (a == NULL)
379189251Ssam			continue;
380189251Ssam		mem = (void *) (a + 1);
381189251Ssam		a->domain_and_port = mem;
382189251Ssam		strcpy(mem, domain_and_port);
383189251Ssam		mem += 1 + strlen(mem);
384189251Ssam		a->path = mem;
385189251Ssam		if (path[0] != '/')
386189251Ssam			*mem++ = '/';
387189251Ssam		strcpy(mem, path);
388189251Ssam		mem += 1 + strlen(mem);
389189251Ssam		os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
390189251Ssam		a->saddr.sin_port = htons(port);
391189251Ssam
392214734Srpaulo		dl_list_add(&s->addr_list, &a->list);
393189251Ssam		a = NULL;       /* don't free it below */
394189251Ssam	}
395189251Ssam
396189251Ssamfail:
397189251Ssam	if (result)
398189251Ssam		freeaddrinfo(result);
399189251Ssam	os_free(scratch_mem);
400189251Ssam	os_free(a);
401189251Ssam}
402189251Ssam
403189251Ssam
404189251Ssam/* subscr_addr_list_create -- create list from urls in string.
405189251Ssam *      Each url is enclosed by angle brackets.
406189251Ssam */
407189251Ssamstatic void subscr_addr_list_create(struct subscription *s,
408189251Ssam				    const char *url_list)
409189251Ssam{
410189251Ssam	char *end;
411189251Ssam	for (;;) {
412189251Ssam		while (*url_list == ' ' || *url_list == '\t')
413189251Ssam			url_list++;
414189251Ssam		if (*url_list != '<')
415189251Ssam			break;
416189251Ssam		url_list++;
417189251Ssam		end = os_strchr(url_list, '>');
418189251Ssam		if (end == NULL)
419189251Ssam			break;
420189251Ssam		*end++ = 0;
421189251Ssam		subscr_addr_add_url(s, url_list);
422189251Ssam		url_list = end;
423189251Ssam	}
424189251Ssam}
425189251Ssam
426189251Ssam
427189251Ssamint send_wpabuf(int fd, struct wpabuf *buf)
428189251Ssam{
429189251Ssam	wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message",
430189251Ssam		   (unsigned long) wpabuf_len(buf));
431189251Ssam	errno = 0;
432189251Ssam	if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) !=
433189251Ssam	    (int) wpabuf_len(buf)) {
434189251Ssam		wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: "
435189251Ssam			   "errno=%d (%s)",
436189251Ssam			   errno, strerror(errno));
437189251Ssam		return -1;
438189251Ssam	}
439189251Ssam
440189251Ssam	return 0;
441189251Ssam}
442189251Ssam
443189251Ssam
444189251Ssamstatic void wpabuf_put_property(struct wpabuf *buf, const char *name,
445189251Ssam				const char *value)
446189251Ssam{
447189251Ssam	wpabuf_put_str(buf, "<e:property>");
448189251Ssam	wpabuf_printf(buf, "<%s>", name);
449189251Ssam	if (value)
450189251Ssam		wpabuf_put_str(buf, value);
451189251Ssam	wpabuf_printf(buf, "</%s>", name);
452189251Ssam	wpabuf_put_str(buf, "</e:property>\n");
453189251Ssam}
454189251Ssam
455189251Ssam
456189251Ssam/**
457189251Ssam * upnp_wps_device_send_event - Queue event messages for subscribers
458189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init()
459189251Ssam *
460189251Ssam * This function queues the last WLANEvent to be sent for all currently
461189251Ssam * subscribed UPnP control points. sm->wlanevent must have been set with the
462189251Ssam * encoded data before calling this function.
463189251Ssam */
464189251Ssamstatic void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
465189251Ssam{
466189251Ssam	/* Enqueue event message for all subscribers */
467189251Ssam	struct wpabuf *buf; /* holds event message */
468189251Ssam	int buf_size = 0;
469214734Srpaulo	struct subscription *s, *tmp;
470189251Ssam	/* Actually, utf-8 is the default, but it doesn't hurt to specify it */
471189251Ssam	const char *format_head =
472189251Ssam		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
473189251Ssam		"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
474189251Ssam	const char *format_tail = "</e:propertyset>\n";
475189251Ssam
476214734Srpaulo	if (dl_list_empty(&sm->subscriptions)) {
477189251Ssam		/* optimize */
478189251Ssam		return;
479189251Ssam	}
480189251Ssam
481189251Ssam	/* Determine buffer size needed first */
482189251Ssam	buf_size += os_strlen(format_head);
483189251Ssam	buf_size += 50 + 2 * os_strlen("WLANEvent");
484189251Ssam	if (sm->wlanevent)
485189251Ssam		buf_size += os_strlen(sm->wlanevent);
486189251Ssam	buf_size += os_strlen(format_tail);
487189251Ssam
488189251Ssam	buf = wpabuf_alloc(buf_size);
489189251Ssam	if (buf == NULL)
490189251Ssam		return;
491189251Ssam	wpabuf_put_str(buf, format_head);
492189251Ssam	wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
493189251Ssam	wpabuf_put_str(buf, format_tail);
494189251Ssam
495189251Ssam	wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
496189251Ssam		   (char *) wpabuf_head(buf));
497189251Ssam
498214734Srpaulo	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
499214734Srpaulo			      list) {
500189251Ssam		if (event_add(s, buf)) {
501189251Ssam			wpa_printf(MSG_INFO, "WPS UPnP: Dropping "
502189251Ssam				   "subscriber due to event backlog");
503214734Srpaulo			dl_list_del(&s->list);
504214734Srpaulo			subscription_destroy(s);
505189251Ssam		}
506214734Srpaulo	}
507189251Ssam
508189251Ssam	wpabuf_free(buf);
509189251Ssam}
510189251Ssam
511189251Ssam
512189251Ssam/*
513189251Ssam * Event subscription (subscriber machines register with us to receive event
514189251Ssam * messages).
515189251Ssam * This is the result of an incoming HTTP over TCP SUBSCRIBE request.
516189251Ssam */
517189251Ssam
518189251Ssam/* subscription_destroy -- destroy an unlinked subscription
519189251Ssam * Be sure to unlink first if necessary.
520189251Ssam */
521189251Ssamvoid subscription_destroy(struct subscription *s)
522189251Ssam{
523189251Ssam	wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
524214734Srpaulo	subscr_addr_free_all(s);
525189251Ssam	event_delete_all(s);
526214734Srpaulo	upnp_er_remove_notification(s);
527189251Ssam	os_free(s);
528189251Ssam}
529189251Ssam
530189251Ssam
531189251Ssam/* subscription_list_age -- remove expired subscriptions */
532189251Ssamstatic void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
533189251Ssam{
534214734Srpaulo	struct subscription *s, *tmp;
535214734Srpaulo	dl_list_for_each_safe(s, tmp, &sm->subscriptions,
536214734Srpaulo			      struct subscription, list) {
537214734Srpaulo		if (s->timeout_time > now)
538214734Srpaulo			break;
539189251Ssam		wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
540214734Srpaulo		dl_list_del(&s->list);
541189251Ssam		subscription_destroy(s);
542189251Ssam	}
543189251Ssam}
544189251Ssam
545189251Ssam
546189251Ssam/* subscription_find -- return existing subscription matching uuid, if any
547189251Ssam * returns NULL if not found
548189251Ssam */
549189251Ssamstruct subscription * subscription_find(struct upnp_wps_device_sm *sm,
550189251Ssam					const u8 uuid[UUID_LEN])
551189251Ssam{
552214734Srpaulo	struct subscription *s;
553214734Srpaulo	dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
554189251Ssam		if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
555189251Ssam			return s; /* Found match */
556214734Srpaulo	}
557189251Ssam	return NULL;
558189251Ssam}
559189251Ssam
560189251Ssam
561209158Srpaulostatic struct wpabuf * build_fake_wsc_ack(void)
562209158Srpaulo{
563209158Srpaulo	struct wpabuf *msg = wpabuf_alloc(100);
564209158Srpaulo	if (msg == NULL)
565209158Srpaulo		return NULL;
566209158Srpaulo	wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
567209158Srpaulo	wpabuf_put_str(msg, "00:00:00:00:00:00");
568214734Srpaulo	if (wps_build_version(msg) ||
569214734Srpaulo	    wps_build_msg_type(msg, WPS_WSC_ACK)) {
570214734Srpaulo		wpabuf_free(msg);
571214734Srpaulo		return NULL;
572214734Srpaulo	}
573209158Srpaulo	/* Enrollee Nonce */
574209158Srpaulo	wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
575209158Srpaulo	wpabuf_put_be16(msg, WPS_NONCE_LEN);
576209158Srpaulo	wpabuf_put(msg, WPS_NONCE_LEN);
577209158Srpaulo	/* Registrar Nonce */
578209158Srpaulo	wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
579209158Srpaulo	wpabuf_put_be16(msg, WPS_NONCE_LEN);
580209158Srpaulo	wpabuf_put(msg, WPS_NONCE_LEN);
581209158Srpaulo	return msg;
582209158Srpaulo}
583209158Srpaulo
584209158Srpaulo
585189251Ssam/* subscription_first_event -- send format/queue event that is automatically
586189251Ssam * sent on a new subscription.
587189251Ssam */
588189251Ssamstatic int subscription_first_event(struct subscription *s)
589189251Ssam{
590189251Ssam	/*
591189251Ssam	 * Actually, utf-8 is the default, but it doesn't hurt to specify it.
592189251Ssam	 *
593189251Ssam	 * APStatus is apparently a bit set,
594189251Ssam	 * 0x1 = configuration change (but is always set?)
595189251Ssam	 * 0x10 = ap is locked
596189251Ssam	 *
597189251Ssam	 * Per UPnP spec, we send out the last value of each variable, even
598189251Ssam	 * for WLANEvent, whatever it was.
599189251Ssam	 */
600189251Ssam	char *wlan_event;
601189251Ssam	struct wpabuf *buf;
602189251Ssam	int ap_status = 1;      /* TODO: add 0x10 if access point is locked */
603189251Ssam	const char *head =
604189251Ssam		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
605189251Ssam		"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
606189251Ssam	const char *tail = "</e:propertyset>\n";
607189251Ssam	char txt[10];
608189251Ssam
609209158Srpaulo	if (s->sm->wlanevent == NULL) {
610209158Srpaulo		/*
611209158Srpaulo		 * There has been no events before the subscription. However,
612209158Srpaulo		 * UPnP device architecture specification requires all the
613209158Srpaulo		 * evented variables to be included, so generate a dummy event
614209158Srpaulo		 * for this particular case using a WSC_ACK and all-zeros
615209158Srpaulo		 * nonces. The ER (UPnP control point) will ignore this, but at
616209158Srpaulo		 * least it will learn that WLANEvent variable will be used in
617209158Srpaulo		 * event notifications in the future.
618209158Srpaulo		 */
619209158Srpaulo		struct wpabuf *msg;
620209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
621209158Srpaulo			   "initial WLANEvent");
622209158Srpaulo		msg = build_fake_wsc_ack();
623209158Srpaulo		if (msg) {
624209158Srpaulo			s->sm->wlanevent = (char *)
625209158Srpaulo				base64_encode(wpabuf_head(msg),
626209158Srpaulo					      wpabuf_len(msg), NULL);
627209158Srpaulo			wpabuf_free(msg);
628209158Srpaulo		}
629209158Srpaulo	}
630209158Srpaulo
631189251Ssam	wlan_event = s->sm->wlanevent;
632189251Ssam	if (wlan_event == NULL || *wlan_event == '\0') {
633189251Ssam		wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
634189251Ssam			   "initial event message");
635189251Ssam		wlan_event = "";
636189251Ssam	}
637189251Ssam	buf = wpabuf_alloc(500 + os_strlen(wlan_event));
638189251Ssam	if (buf == NULL)
639189251Ssam		return 1;
640189251Ssam
641189251Ssam	wpabuf_put_str(buf, head);
642189251Ssam	wpabuf_put_property(buf, "STAStatus", "1");
643189251Ssam	os_snprintf(txt, sizeof(txt), "%d", ap_status);
644189251Ssam	wpabuf_put_property(buf, "APStatus", txt);
645189251Ssam	if (*wlan_event)
646189251Ssam		wpabuf_put_property(buf, "WLANEvent", wlan_event);
647189251Ssam	wpabuf_put_str(buf, tail);
648189251Ssam
649189251Ssam	if (event_add(s, buf)) {
650189251Ssam		wpabuf_free(buf);
651189251Ssam		return 1;
652189251Ssam	}
653189251Ssam	wpabuf_free(buf);
654189251Ssam
655189251Ssam	return 0;
656189251Ssam}
657189251Ssam
658189251Ssam
659189251Ssam/**
660209158Srpaulo * subscription_start - Remember a UPnP control point to send events to.
661189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init()
662209158Srpaulo * @callback_urls: Callback URLs
663189251Ssam * Returns: %NULL on error, or pointer to new subscription structure.
664189251Ssam */
665189251Ssamstruct subscription * subscription_start(struct upnp_wps_device_sm *sm,
666209158Srpaulo					 const char *callback_urls)
667189251Ssam{
668189251Ssam	struct subscription *s;
669189251Ssam	time_t now = time(NULL);
670189251Ssam	time_t expire = now + UPNP_SUBSCRIBE_SEC;
671189251Ssam
672189251Ssam	/* Get rid of expired subscriptions so we have room */
673189251Ssam	subscription_list_age(sm, now);
674189251Ssam
675189251Ssam	/* If too many subscriptions, remove oldest */
676214734Srpaulo	if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
677214734Srpaulo		s = dl_list_first(&sm->subscriptions, struct subscription,
678214734Srpaulo				  list);
679189251Ssam		wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, "
680189251Ssam			   "trashing oldest");
681214734Srpaulo		dl_list_del(&s->list);
682189251Ssam		subscription_destroy(s);
683189251Ssam	}
684189251Ssam
685189251Ssam	s = os_zalloc(sizeof(*s));
686189251Ssam	if (s == NULL)
687189251Ssam		return NULL;
688214734Srpaulo	dl_list_init(&s->addr_list);
689214734Srpaulo	dl_list_init(&s->event_queue);
690189251Ssam
691189251Ssam	s->sm = sm;
692189251Ssam	s->timeout_time = expire;
693189251Ssam	uuid_make(s->uuid);
694189251Ssam	subscr_addr_list_create(s, callback_urls);
695189251Ssam	/* Add to end of list, since it has the highest expiration time */
696214734Srpaulo	dl_list_add_tail(&sm->subscriptions, &s->list);
697189251Ssam	/* Queue up immediate event message (our last event)
698189251Ssam	 * as required by UPnP spec.
699189251Ssam	 */
700189251Ssam	if (subscription_first_event(s)) {
701189251Ssam		wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
702189251Ssam			   "event backlog");
703214734Srpaulo		dl_list_del(&s->list);
704189251Ssam		subscription_destroy(s);
705189251Ssam		return NULL;
706189251Ssam	}
707189251Ssam	wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
708189251Ssam		   s, callback_urls);
709189251Ssam	/* Schedule sending this */
710189251Ssam	event_send_all_later(sm);
711189251Ssam	return s;
712189251Ssam}
713189251Ssam
714189251Ssam
715189251Ssam/* subscription_renew -- find subscription and reset timeout */
716189251Ssamstruct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
717189251Ssam					 const u8 uuid[UUID_LEN])
718189251Ssam{
719189251Ssam	time_t now = time(NULL);
720189251Ssam	time_t expire = now + UPNP_SUBSCRIBE_SEC;
721189251Ssam	struct subscription *s = subscription_find(sm, uuid);
722189251Ssam	if (s == NULL)
723189251Ssam		return NULL;
724189251Ssam	wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
725214734Srpaulo	dl_list_del(&s->list);
726189251Ssam	s->timeout_time = expire;
727189251Ssam	/* add back to end of list, since it now has highest expiry */
728214734Srpaulo	dl_list_add_tail(&sm->subscriptions, &s->list);
729189251Ssam	return s;
730189251Ssam}
731189251Ssam
732189251Ssam
733189251Ssam/**
734189251Ssam * upnp_wps_device_send_wlan_event - Event notification
735189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init()
736189251Ssam * @from_mac_addr: Source (Enrollee) MAC address for the event
737189251Ssam * @ev_type: Event type
738189251Ssam * @msg: Event data
739189251Ssam * Returns: 0 on success, -1 on failure
740189251Ssam *
741189251Ssam * Tell external Registrars (UPnP control points) that something happened. In
742189251Ssam * particular, events include WPS messages from clients that are proxied to
743189251Ssam * external Registrars.
744189251Ssam */
745189251Ssamint upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
746189251Ssam				    const u8 from_mac_addr[ETH_ALEN],
747189251Ssam				    enum upnp_wps_wlanevent_type ev_type,
748189251Ssam				    const struct wpabuf *msg)
749189251Ssam{
750189251Ssam	int ret = -1;
751189251Ssam	char type[2];
752189251Ssam	const u8 *mac = from_mac_addr;
753189251Ssam	char mac_text[18];
754189251Ssam	u8 *raw = NULL;
755189251Ssam	size_t raw_len;
756189251Ssam	char *val;
757189251Ssam	size_t val_len;
758189251Ssam	int pos = 0;
759189251Ssam
760189251Ssam	if (!sm)
761189251Ssam		goto fail;
762189251Ssam
763189251Ssam	os_snprintf(type, sizeof(type), "%1u", ev_type);
764189251Ssam
765189251Ssam	raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
766189251Ssam	raw = os_zalloc(raw_len);
767189251Ssam	if (!raw)
768189251Ssam		goto fail;
769189251Ssam
770189251Ssam	*(raw + pos) = (u8) ev_type;
771189251Ssam	pos += 1;
772189251Ssam	os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
773189251Ssam	wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
774189251Ssam		   mac_text);
775189251Ssam	os_memcpy(raw + pos, mac_text, 17);
776189251Ssam	pos += 17;
777189251Ssam	if (msg) {
778189251Ssam		os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
779189251Ssam		pos += wpabuf_len(msg);
780189251Ssam	}
781189251Ssam	raw_len = pos;
782189251Ssam
783189251Ssam	val = (char *) base64_encode(raw, raw_len, &val_len);
784189251Ssam	if (val == NULL)
785189251Ssam		goto fail;
786189251Ssam
787189251Ssam	os_free(sm->wlanevent);
788189251Ssam	sm->wlanevent = val;
789189251Ssam	upnp_wps_device_send_event(sm);
790189251Ssam
791189251Ssam	ret = 0;
792189251Ssam
793189251Ssamfail:
794189251Ssam	os_free(raw);
795189251Ssam
796189251Ssam	return ret;
797189251Ssam}
798189251Ssam
799189251Ssam
800214734Srpaulo#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
801209158Srpaulo#include <sys/sysctl.h>
802209158Srpaulo#include <net/route.h>
803209158Srpaulo#include <net/if_dl.h>
804209158Srpaulo
805209158Srpaulostatic int eth_get(const char *device, u8 ea[ETH_ALEN])
806209158Srpaulo{
807209158Srpaulo	struct if_msghdr *ifm;
808209158Srpaulo	struct sockaddr_dl *sdl;
809209158Srpaulo	u_char *p, *buf;
810209158Srpaulo	size_t len;
811209158Srpaulo	int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
812209158Srpaulo
813209158Srpaulo	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
814209158Srpaulo		return -1;
815209158Srpaulo	if ((buf = os_malloc(len)) == NULL)
816209158Srpaulo		return -1;
817209158Srpaulo	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
818209158Srpaulo		os_free(buf);
819209158Srpaulo		return -1;
820209158Srpaulo	}
821209158Srpaulo	for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
822209158Srpaulo		ifm = (struct if_msghdr *)p;
823209158Srpaulo		sdl = (struct sockaddr_dl *)(ifm + 1);
824209158Srpaulo		if (ifm->ifm_type != RTM_IFINFO ||
825209158Srpaulo		    (ifm->ifm_addrs & RTA_IFP) == 0)
826209158Srpaulo			continue;
827209158Srpaulo		if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
828209158Srpaulo		    os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
829209158Srpaulo			continue;
830209158Srpaulo		os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
831209158Srpaulo		break;
832209158Srpaulo	}
833209158Srpaulo	os_free(buf);
834209158Srpaulo
835209158Srpaulo	if (p >= buf + len) {
836209158Srpaulo		errno = ESRCH;
837209158Srpaulo		return -1;
838209158Srpaulo	}
839209158Srpaulo	return 0;
840209158Srpaulo}
841209158Srpaulo#endif /* __FreeBSD__ */
842209158Srpaulo
843209158Srpaulo
844189251Ssam/**
845189251Ssam * get_netif_info - Get hw and IP addresses for network device
846189251Ssam * @net_if: Selected network interface name
847189251Ssam * @ip_addr: Buffer for returning IP address in network byte order
848189251Ssam * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
849189251Ssam * @mac: Buffer for returning MAC address
850189251Ssam * Returns: 0 on success, -1 on failure
851189251Ssam */
852214734Srpauloint get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
853214734Srpaulo		   u8 mac[ETH_ALEN])
854189251Ssam{
855189251Ssam	struct ifreq req;
856189251Ssam	int sock = -1;
857189251Ssam	struct sockaddr_in *addr;
858189251Ssam	struct in_addr in_addr;
859189251Ssam
860189251Ssam	*ip_addr_text = os_zalloc(16);
861214734Srpaulo	if (*ip_addr_text == NULL)
862189251Ssam		goto fail;
863189251Ssam
864189251Ssam	sock = socket(AF_INET, SOCK_DGRAM, 0);
865189251Ssam	if (sock < 0)
866189251Ssam		goto fail;
867189251Ssam
868189251Ssam	os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
869189251Ssam	if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
870189251Ssam		wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
871189251Ssam			   errno, strerror(errno));
872189251Ssam		goto fail;
873189251Ssam	}
874189251Ssam	addr = (void *) &req.ifr_addr;
875189251Ssam	*ip_addr = addr->sin_addr.s_addr;
876189251Ssam	in_addr.s_addr = *ip_addr;
877189251Ssam	os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
878189251Ssam
879209158Srpaulo#ifdef __linux__
880189251Ssam	os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
881189251Ssam	if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
882189251Ssam		wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
883189251Ssam			   "%d (%s)", errno, strerror(errno));
884189251Ssam		goto fail;
885189251Ssam	}
886189251Ssam	os_memcpy(mac, req.ifr_addr.sa_data, 6);
887214734Srpaulo#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
888209158Srpaulo	if (eth_get(net_if, mac) < 0) {
889209158Srpaulo		wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
890209158Srpaulo		goto fail;
891209158Srpaulo	}
892209158Srpaulo#else
893209158Srpaulo#error MAC address fetch not implemented
894209158Srpaulo#endif
895189251Ssam
896189251Ssam	close(sock);
897189251Ssam	return 0;
898189251Ssam
899189251Ssamfail:
900189251Ssam	if (sock >= 0)
901189251Ssam		close(sock);
902189251Ssam	os_free(*ip_addr_text);
903189251Ssam	*ip_addr_text = NULL;
904189251Ssam	return -1;
905189251Ssam}
906189251Ssam
907189251Ssam
908214734Srpaulostatic void upnp_wps_free_msearchreply(struct dl_list *head)
909214734Srpaulo{
910214734Srpaulo	struct advertisement_state_machine *a, *tmp;
911214734Srpaulo	dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
912214734Srpaulo			      list)
913214734Srpaulo		msearchreply_state_machine_stop(a);
914214734Srpaulo}
915214734Srpaulo
916214734Srpaulo
917214734Srpaulostatic void upnp_wps_free_subscriptions(struct dl_list *head)
918214734Srpaulo{
919214734Srpaulo	struct subscription *s, *tmp;
920214734Srpaulo	dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
921214734Srpaulo		dl_list_del(&s->list);
922214734Srpaulo		subscription_destroy(s);
923214734Srpaulo	}
924214734Srpaulo}
925214734Srpaulo
926214734Srpaulo
927189251Ssam/**
928189251Ssam * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
929189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init()
930189251Ssam */
931189251Ssamvoid upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
932189251Ssam{
933189251Ssam	if (!sm || !sm->started)
934189251Ssam		return;
935189251Ssam
936189251Ssam	wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
937189251Ssam	web_listener_stop(sm);
938214734Srpaulo	upnp_wps_free_msearchreply(&sm->msearch_replies);
939214734Srpaulo	upnp_wps_free_subscriptions(&sm->subscriptions);
940189251Ssam
941209158Srpaulo	advertisement_state_machine_stop(sm, 1);
942189251Ssam
943189251Ssam	event_send_stop_all(sm);
944189251Ssam	os_free(sm->wlanevent);
945189251Ssam	sm->wlanevent = NULL;
946189251Ssam	os_free(sm->ip_addr_text);
947189251Ssam	sm->ip_addr_text = NULL;
948189251Ssam	if (sm->multicast_sd >= 0)
949189251Ssam		close(sm->multicast_sd);
950189251Ssam	sm->multicast_sd = -1;
951189251Ssam	ssdp_listener_stop(sm);
952189251Ssam
953189251Ssam	sm->started = 0;
954189251Ssam}
955189251Ssam
956189251Ssam
957189251Ssam/**
958189251Ssam * upnp_wps_device_start - Start WPS UPnP operations on an interface
959189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init()
960189251Ssam * @net_if: Selected network interface name
961189251Ssam * Returns: 0 on success, -1 on failure
962189251Ssam */
963189251Ssamint upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
964189251Ssam{
965189251Ssam	if (!sm || !net_if)
966189251Ssam		return -1;
967189251Ssam
968189251Ssam	if (sm->started)
969189251Ssam		upnp_wps_device_stop(sm);
970189251Ssam
971189251Ssam	sm->multicast_sd = -1;
972189251Ssam	sm->ssdp_sd = -1;
973189251Ssam	sm->started = 1;
974189251Ssam	sm->advertise_count = 0;
975189251Ssam
976189251Ssam	/* Fix up linux multicast handling */
977189251Ssam	if (add_ssdp_network(net_if))
978189251Ssam		goto fail;
979189251Ssam
980189251Ssam	/* Determine which IP and mac address we're using */
981214734Srpaulo	if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
982214734Srpaulo			   sm->mac_addr)) {
983189251Ssam		wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
984189251Ssam			   "for %s. Does it have IP address?", net_if);
985189251Ssam		goto fail;
986189251Ssam	}
987189251Ssam
988189251Ssam	/* Listen for incoming TCP connections so that others
989189251Ssam	 * can fetch our "xml files" from us.
990189251Ssam	 */
991189251Ssam	if (web_listener_start(sm))
992189251Ssam		goto fail;
993189251Ssam
994189251Ssam	/* Set up for receiving discovery (UDP) packets */
995189251Ssam	if (ssdp_listener_start(sm))
996189251Ssam		goto fail;
997189251Ssam
998189251Ssam	/* Set up for sending multicast */
999189251Ssam	if (ssdp_open_multicast(sm) < 0)
1000189251Ssam		goto fail;
1001189251Ssam
1002189251Ssam	/*
1003189251Ssam	 * Broadcast NOTIFY messages to let the world know we exist.
1004189251Ssam	 * This is done via a state machine since the messages should not be
1005189251Ssam	 * all sent out at once.
1006189251Ssam	 */
1007189251Ssam	if (advertisement_state_machine_start(sm))
1008189251Ssam		goto fail;
1009189251Ssam
1010189251Ssam	return 0;
1011189251Ssam
1012189251Ssamfail:
1013189251Ssam	upnp_wps_device_stop(sm);
1014189251Ssam	return -1;
1015189251Ssam}
1016189251Ssam
1017189251Ssam
1018189251Ssam/**
1019189251Ssam * upnp_wps_device_deinit - Deinitialize WPS UPnP
1020189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init()
1021189251Ssam */
1022189251Ssamvoid upnp_wps_device_deinit(struct upnp_wps_device_sm *sm)
1023189251Ssam{
1024189251Ssam	if (!sm)
1025189251Ssam		return;
1026189251Ssam
1027189251Ssam	upnp_wps_device_stop(sm);
1028189251Ssam
1029189251Ssam	if (sm->peer.wps)
1030189251Ssam		wps_deinit(sm->peer.wps);
1031189251Ssam	os_free(sm->root_dir);
1032189251Ssam	os_free(sm->desc_url);
1033214734Srpaulo	os_free(sm->ctx->ap_pin);
1034189251Ssam	os_free(sm->ctx);
1035189251Ssam	os_free(sm);
1036189251Ssam}
1037189251Ssam
1038189251Ssam
1039189251Ssam/**
1040189251Ssam * upnp_wps_device_init - Initialize WPS UPnP
1041189251Ssam * @ctx: callback table; we must eventually free it
1042189251Ssam * @wps: Pointer to longterm WPS context
1043189251Ssam * @priv: External context data that will be used in callbacks
1044189251Ssam * Returns: WPS UPnP state or %NULL on failure
1045189251Ssam */
1046189251Ssamstruct upnp_wps_device_sm *
1047189251Ssamupnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
1048189251Ssam		     void *priv)
1049189251Ssam{
1050189251Ssam	struct upnp_wps_device_sm *sm;
1051189251Ssam
1052189251Ssam	sm = os_zalloc(sizeof(*sm));
1053189251Ssam	if (!sm) {
1054189251Ssam		wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed");
1055189251Ssam		return NULL;
1056189251Ssam	}
1057189251Ssam
1058189251Ssam	sm->ctx = ctx;
1059189251Ssam	sm->wps = wps;
1060189251Ssam	sm->priv = priv;
1061214734Srpaulo	dl_list_init(&sm->msearch_replies);
1062214734Srpaulo	dl_list_init(&sm->subscriptions);
1063189251Ssam
1064189251Ssam	return sm;
1065189251Ssam}
1066189251Ssam
1067189251Ssam
1068189251Ssam/**
1069189251Ssam * upnp_wps_subscribers - Check whether there are any event subscribers
1070189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init()
1071189251Ssam * Returns: 0 if no subscribers, 1 if subscribers
1072189251Ssam */
1073189251Ssamint upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
1074189251Ssam{
1075214734Srpaulo	return !dl_list_empty(&sm->subscriptions);
1076189251Ssam}
1077214734Srpaulo
1078214734Srpaulo
1079214734Srpauloint upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
1080214734Srpaulo{
1081214734Srpaulo	if (sm == NULL)
1082214734Srpaulo		return 0;
1083214734Srpaulo
1084214734Srpaulo	os_free(sm->ctx->ap_pin);
1085214734Srpaulo	if (ap_pin) {
1086214734Srpaulo		sm->ctx->ap_pin = os_strdup(ap_pin);
1087214734Srpaulo		if (sm->ctx->ap_pin == NULL)
1088214734Srpaulo			return -1;
1089214734Srpaulo	} else
1090214734Srpaulo		sm->ctx->ap_pin = NULL;
1091214734Srpaulo
1092214734Srpaulo	return 0;
1093214734Srpaulo}
1094