1214501Srpaulo/*
2214501Srpaulo * Wi-Fi Protected Setup - External Registrar (SSDP)
3214501Srpaulo * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12214501Srpaulo#include "uuid.h"
13214501Srpaulo#include "eloop.h"
14214501Srpaulo#include "wps_i.h"
15214501Srpaulo#include "wps_upnp.h"
16214501Srpaulo#include "wps_upnp_i.h"
17214501Srpaulo#include "wps_er.h"
18214501Srpaulo
19214501Srpaulo
20214501Srpaulostatic void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
21214501Srpaulo{
22214501Srpaulo	struct wps_er *er = eloop_ctx;
23214501Srpaulo	struct sockaddr_in addr; /* client address */
24214501Srpaulo	socklen_t addr_len;
25214501Srpaulo	int nread;
26214501Srpaulo	char buf[MULTICAST_MAX_READ], *pos, *pos2, *start;
27214501Srpaulo	int wfa = 0, byebye = 0;
28214501Srpaulo	int max_age = -1;
29214501Srpaulo	char *location = NULL;
30214501Srpaulo	u8 uuid[WPS_UUID_LEN];
31214501Srpaulo
32214501Srpaulo	addr_len = sizeof(addr);
33214501Srpaulo	nread = recvfrom(sd, buf, sizeof(buf) - 1, 0,
34214501Srpaulo			 (struct sockaddr *) &addr, &addr_len);
35214501Srpaulo	if (nread <= 0)
36214501Srpaulo		return;
37214501Srpaulo	buf[nread] = '\0';
38252726Srpaulo	if (er->filter_addr.s_addr &&
39252726Srpaulo	    er->filter_addr.s_addr != addr.sin_addr.s_addr)
40252726Srpaulo		return;
41214501Srpaulo
42214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
43214501Srpaulo		   inet_ntoa(addr.sin_addr));
44214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents",
45214501Srpaulo			  (u8 *) buf, nread);
46214501Srpaulo
47214501Srpaulo	if (sd == er->multicast_sd) {
48214501Srpaulo		/* Reply to M-SEARCH */
49214501Srpaulo		if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0)
50214501Srpaulo			return; /* unexpected response header */
51214501Srpaulo	} else {
52214501Srpaulo		/* Unsolicited message (likely NOTIFY or M-SEARCH) */
53214501Srpaulo		if (os_strncmp(buf, "NOTIFY ", 7) != 0)
54214501Srpaulo			return; /* only process notifications */
55214501Srpaulo	}
56214501Srpaulo
57214501Srpaulo	os_memset(uuid, 0, sizeof(uuid));
58214501Srpaulo
59214501Srpaulo	for (start = buf; start && *start; start = pos) {
60214501Srpaulo		pos = os_strchr(start, '\n');
61214501Srpaulo		if (pos) {
62214501Srpaulo			if (pos[-1] == '\r')
63214501Srpaulo				pos[-1] = '\0';
64214501Srpaulo			*pos++ = '\0';
65214501Srpaulo		}
66214501Srpaulo		if (os_strstr(start, "schemas-wifialliance-org:device:"
67214501Srpaulo			      "WFADevice:1"))
68214501Srpaulo			wfa = 1;
69214501Srpaulo		if (os_strstr(start, "schemas-wifialliance-org:service:"
70214501Srpaulo			      "WFAWLANConfig:1"))
71214501Srpaulo			wfa = 1;
72214501Srpaulo		if (os_strncasecmp(start, "LOCATION:", 9) == 0) {
73214501Srpaulo			start += 9;
74214501Srpaulo			while (*start == ' ')
75214501Srpaulo				start++;
76214501Srpaulo			location = start;
77214501Srpaulo		} else if (os_strncasecmp(start, "NTS:", 4) == 0) {
78214501Srpaulo			if (os_strstr(start, "ssdp:byebye"))
79214501Srpaulo				byebye = 1;
80214501Srpaulo		} else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
81289549Srpaulo			start += 14;
82214501Srpaulo			pos2 = os_strstr(start, "max-age=");
83214501Srpaulo			if (pos2 == NULL)
84214501Srpaulo				continue;
85214501Srpaulo			pos2 += 8;
86214501Srpaulo			max_age = atoi(pos2);
87214501Srpaulo		} else if (os_strncasecmp(start, "USN:", 4) == 0) {
88214501Srpaulo			start += 4;
89214501Srpaulo			pos2 = os_strstr(start, "uuid:");
90214501Srpaulo			if (pos2) {
91214501Srpaulo				pos2 += 5;
92214501Srpaulo				while (*pos2 == ' ')
93214501Srpaulo					pos2++;
94214501Srpaulo				if (uuid_str2bin(pos2, uuid) < 0) {
95214501Srpaulo					wpa_printf(MSG_DEBUG, "WPS ER: "
96214501Srpaulo						   "Invalid UUID in USN: %s",
97214501Srpaulo						   pos2);
98214501Srpaulo					return;
99214501Srpaulo				}
100214501Srpaulo			}
101214501Srpaulo		}
102214501Srpaulo	}
103214501Srpaulo
104214501Srpaulo	if (!wfa)
105214501Srpaulo		return; /* Not WPS advertisement/reply */
106214501Srpaulo
107214501Srpaulo	if (byebye) {
108252726Srpaulo		wps_er_ap_cache_settings(er, &addr.sin_addr);
109214501Srpaulo		wps_er_ap_remove(er, &addr.sin_addr);
110214501Srpaulo		return;
111214501Srpaulo	}
112214501Srpaulo
113214501Srpaulo	if (!location)
114214501Srpaulo		return; /* Unknown location */
115214501Srpaulo
116214501Srpaulo	if (max_age < 1)
117214501Srpaulo		return; /* No max-age reported */
118214501Srpaulo
119214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s "
120214501Srpaulo		   "(packet source: %s  max-age: %d)",
121214501Srpaulo		   location, inet_ntoa(addr.sin_addr), max_age);
122214501Srpaulo
123214501Srpaulo	wps_er_ap_add(er, uuid, &addr.sin_addr, location, max_age);
124214501Srpaulo}
125214501Srpaulo
126214501Srpaulo
127214501Srpaulovoid wps_er_send_ssdp_msearch(struct wps_er *er)
128214501Srpaulo{
129214501Srpaulo	struct wpabuf *msg;
130214501Srpaulo	struct sockaddr_in dest;
131214501Srpaulo
132214501Srpaulo	msg = wpabuf_alloc(500);
133214501Srpaulo	if (msg == NULL)
134214501Srpaulo		return;
135214501Srpaulo
136214501Srpaulo	wpabuf_put_str(msg,
137214501Srpaulo		       "M-SEARCH * HTTP/1.1\r\n"
138214501Srpaulo		       "HOST: 239.255.255.250:1900\r\n"
139214501Srpaulo		       "MAN: \"ssdp:discover\"\r\n"
140214501Srpaulo		       "MX: 3\r\n"
141214501Srpaulo		       "ST: urn:schemas-wifialliance-org:device:WFADevice:1"
142214501Srpaulo		       "\r\n"
143214501Srpaulo		       "\r\n");
144214501Srpaulo
145214501Srpaulo	os_memset(&dest, 0, sizeof(dest));
146214501Srpaulo	dest.sin_family = AF_INET;
147214501Srpaulo	dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
148214501Srpaulo	dest.sin_port = htons(UPNP_MULTICAST_PORT);
149214501Srpaulo
150214501Srpaulo	if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
151214501Srpaulo		   (struct sockaddr *) &dest, sizeof(dest)) < 0)
152214501Srpaulo		wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: "
153214501Srpaulo			   "%d (%s)", errno, strerror(errno));
154214501Srpaulo
155214501Srpaulo	wpabuf_free(msg);
156214501Srpaulo}
157214501Srpaulo
158214501Srpaulo
159214501Srpauloint wps_er_ssdp_init(struct wps_er *er)
160214501Srpaulo{
161252726Srpaulo	if (add_ssdp_network(er->ifname)) {
162252726Srpaulo		wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for "
163252726Srpaulo			   "SSDP");
164214501Srpaulo		return -1;
165252726Srpaulo	}
166214501Srpaulo
167281806Srpaulo	er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr,
168281806Srpaulo						    er->forced_ifname ?
169281806Srpaulo						    er->ifname : NULL);
170252726Srpaulo	if (er->multicast_sd < 0) {
171252726Srpaulo		wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket "
172252726Srpaulo			   "for SSDP");
173214501Srpaulo		return -1;
174252726Srpaulo	}
175214501Srpaulo
176214501Srpaulo	er->ssdp_sd = ssdp_listener_open();
177252726Srpaulo	if (er->ssdp_sd < 0) {
178252726Srpaulo		wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener "
179252726Srpaulo			   "socket");
180214501Srpaulo		return -1;
181252726Srpaulo	}
182214501Srpaulo
183214501Srpaulo	if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
184214501Srpaulo				wps_er_ssdp_rx, er, NULL) ||
185214501Srpaulo	    eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ,
186214501Srpaulo				wps_er_ssdp_rx, er, NULL))
187214501Srpaulo		return -1;
188214501Srpaulo
189214501Srpaulo	wps_er_send_ssdp_msearch(er);
190214501Srpaulo
191214501Srpaulo	return 0;
192214501Srpaulo}
193214501Srpaulo
194214501Srpaulo
195214501Srpaulovoid wps_er_ssdp_deinit(struct wps_er *er)
196214501Srpaulo{
197214501Srpaulo	if (er->multicast_sd >= 0) {
198214501Srpaulo		eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);
199214501Srpaulo		close(er->multicast_sd);
200214501Srpaulo	}
201214501Srpaulo	if (er->ssdp_sd >= 0) {
202214501Srpaulo		eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
203214501Srpaulo		close(er->ssdp_sd);
204214501Srpaulo	}
205214501Srpaulo}
206