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