1 /*
2  * libdhcpcd
3  * Copyright 2009-2015 Roy Marples <roy@marples.name>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #define _GNU_SOURCE /* for asprintf */
28 
29 #include <sys/socket.h>
30 #include <sys/stat.h>
31 #include <sys/un.h>
32 
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <poll.h>
38 #include <pwd.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #define IN_LIBDHCPCD
45 #include "config.h"
46 #include "dhcpcd.h"
47 
48 #ifndef SUN_LEN
49 #define SUN_LEN(su) \
50 	(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
51 #endif
52 
53 #define CLAMP(x, low, high) \
54 	(((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
55 
56 static int
wpa_open(const char * ifname,char ** path)57 wpa_open(const char *ifname, char **path)
58 {
59 	static int counter;
60 	size_t pwdbufsize;
61 	char *pwdbuf;
62 	struct passwd pwd, *tpwd;
63 	int fd, r;
64 	socklen_t len;
65 	struct sockaddr_un sun;
66 	char *tmpdir;
67 
68 	tmpdir = NULL;
69 	fd = r = -1;
70 	*path = NULL;
71 
72 	pwdbufsize = (size_t)sysconf(_SC_GETPW_R_SIZE_MAX);
73 	pwdbuf = malloc(pwdbufsize);
74 	if (pwdbuf == NULL)
75 		goto out;
76 	if (getpwuid_r(geteuid(), &pwd, pwdbuf, pwdbufsize, &tpwd) != 0)
77 		goto out;
78 	if (asprintf(&tmpdir, "%s-%s", DHCPCD_TMP_DIR, pwd.pw_name) == -1)
79 		goto out;
80 
81 	if (mkdir(tmpdir, DHCPCD_TMP_DIR_PERM) == -1 && errno != EEXIST)
82 		goto out;
83 
84 	if ((fd = socket(AF_UNIX,
85 	    SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1)
86 		goto out;
87 	memset(&sun, 0, sizeof(sun));
88 	sun.sun_family = AF_UNIX;
89 	snprintf(sun.sun_path, sizeof(sun.sun_path),
90 	    "%s/libdhcpcd-wpa-%d.%d", tmpdir, getpid(), counter++);
91 	*path = strdup(sun.sun_path);
92 	len = (socklen_t)SUN_LEN(&sun);
93 	if (bind(fd, (struct sockaddr *)&sun, len) == -1)
94 		goto out;
95 	snprintf(sun.sun_path, sizeof(sun.sun_path),
96 	    WPA_CTRL_DIR "/%s", ifname);
97 	len = (socklen_t)SUN_LEN(&sun);
98 	if (connect(fd, (struct sockaddr *)&sun, len) == -1)
99 		goto out;
100 
101 	/* Success! */
102 	r = 0;
103 
104 out:
105 	free(pwdbuf);
106 	free(tmpdir);
107 	if (r == 0)
108 		return fd;
109 	if (fd != -1)
110 		close(fd);
111 	if (*path != NULL) {
112 		unlink(*path);
113 		free(*path);
114 		*path = NULL;
115 	}
116 	return r;
117 }
118 
119 static ssize_t
wpa_cmd(int fd,const char * cmd,char * buffer,size_t len)120 wpa_cmd(int fd, const char *cmd, char *buffer, size_t len)
121 {
122 	int retval;
123 	ssize_t bytes;
124 	struct pollfd pfd;
125 
126 	if (buffer)
127 		*buffer = '\0';
128 	bytes = write(fd, cmd, strlen(cmd));
129 	if (bytes == -1)
130 		return -1;
131 	if (buffer == NULL || len == 0)
132 		return 0;
133 	pfd.fd = fd;
134 	pfd.events = POLLIN | POLLHUP;
135 	pfd.revents = 0;
136 	retval = poll(&pfd, 1, 2000);
137 	if (retval == -1)
138 		return -1;
139 	if (retval == 0 || !(pfd.revents & (POLLIN | POLLHUP)))
140 		return -1;
141 
142 	bytes = read(fd, buffer, len == 1 ? 1 : len - 1);
143 	if (bytes != -1)
144 		buffer[bytes] = '\0';
145 	return bytes;
146 }
147 
148 bool
dhcpcd_wpa_command(DHCPCD_WPA * wpa,const char * cmd)149 dhcpcd_wpa_command(DHCPCD_WPA *wpa, const char *cmd)
150 {
151 	char buf[10];
152 	ssize_t bytes;
153 
154 	bytes = wpa_cmd(wpa->command_fd, cmd, buf, sizeof(buf));
155 	return (bytes == -1 || bytes == 0 ||
156 	    strcmp(buf, "OK\n")) ? false : true;
157 }
158 
159 static void
dhcpcd_wpa_update_status(DHCPCD_WPA * wpa,unsigned int status)160 dhcpcd_wpa_update_status(DHCPCD_WPA *wpa, unsigned int status)
161 {
162 
163 	if (wpa->status != status) {
164 		wpa->status = status;
165 		if (wpa->con->wpa_status_cb)
166 			wpa->con->wpa_status_cb(wpa,
167 			    wpa->status, dhcpcd_cstates[wpa->status],
168 			    wpa->con->wpa_status_context);
169 	}
170 }
171 
172 bool
dhcpcd_wpa_ping(DHCPCD_WPA * wpa)173 dhcpcd_wpa_ping(DHCPCD_WPA *wpa)
174 {
175 	char buf[10];
176 	ssize_t bytes;
177 
178 	bytes = wpa_cmd(wpa->command_fd, "PING", buf, sizeof(buf));
179 	return (bytes == -1 || bytes == 0 ||
180 	    strcmp(buf, "PONG\n")) ? false : true;
181 }
182 
183 bool
dhcpcd_wpa_command_arg(DHCPCD_WPA * wpa,const char * cmd,const char * arg)184 dhcpcd_wpa_command_arg(DHCPCD_WPA *wpa, const char *cmd, const char *arg)
185 {
186 	size_t cmdlen, nlen;
187 
188 	cmdlen = strlen(cmd);
189 	nlen = cmdlen + strlen(arg) + 2;
190 	if (!dhcpcd_realloc(wpa->con, nlen))
191 		return -1;
192 	strlcpy(wpa->con->buf, cmd, wpa->con->buflen);
193 	wpa->con->buf[cmdlen] = ' ';
194 	strlcpy(wpa->con->buf + cmdlen + 1, arg, wpa->con->buflen - 1 - cmdlen);
195 	return dhcpcd_wpa_command(wpa, wpa->con->buf);
196 }
197 
198 static bool
dhcpcd_attach_detach(DHCPCD_WPA * wpa,bool attach)199 dhcpcd_attach_detach(DHCPCD_WPA *wpa, bool attach)
200 {
201 	char buf[10];
202 	ssize_t bytes;
203 
204 	if (wpa->attached == attach)
205 		return true;
206 
207 	bytes = wpa_cmd(wpa->listen_fd, attach > 0 ? "ATTACH" : "DETACH",
208 	    buf, sizeof(buf));
209 	if (bytes == -1 || bytes == 0 || strcmp(buf, "OK\n"))
210 		return false;
211 
212 	wpa->attached = attach;
213 	return true;
214 }
215 
216 #define UNUSED(x)	(void)(x)
217 bool
dhcpcd_wpa_can_background_scan(DHCPCD_WPA * wpa)218 dhcpcd_wpa_can_background_scan(DHCPCD_WPA *wpa)
219 {
220 
221 	UNUSED(wpa); /* BSD will use this moving forwards */
222 #ifdef __linux__
223 	return true;
224 #else
225 	return false;
226 #endif
227 }
228 
229 bool
dhcpcd_wpa_scan(DHCPCD_WPA * wpa)230 dhcpcd_wpa_scan(DHCPCD_WPA *wpa)
231 {
232 
233 	return dhcpcd_wpa_command(wpa, "SCAN");
234 }
235 
236 bool
dhcpcd_wi_associated(DHCPCD_IF * i,DHCPCD_WI_SCAN * scan)237 dhcpcd_wi_associated(DHCPCD_IF *i, DHCPCD_WI_SCAN *scan)
238 {
239 
240 	assert(i);
241 	assert(scan);
242 
243 	return (i->up && i->ssid && strcmp(i->ssid, scan->ssid) == 0);
244 }
245 
246 void
dhcpcd_wi_scans_free(DHCPCD_WI_SCAN * wis)247 dhcpcd_wi_scans_free(DHCPCD_WI_SCAN *wis)
248 {
249 	DHCPCD_WI_SCAN *n;
250 
251 	while (wis) {
252 		n = wis->next;
253 		free(wis);
254 		wis = n;
255 	}
256 }
257 
258 static void
dhcpcd_strtoi(int * val,const char * s)259 dhcpcd_strtoi(int *val, const char *s)
260 {
261 	long l;
262 
263 	l = strtol(s, NULL, 0);
264 	if (l >= INT_MIN && l <= INT_MAX)
265 		*val = (int)l;
266 	else
267 		errno = ERANGE;
268 }
269 
270 static int
dhcpcd_wpa_hex2num(char c)271 dhcpcd_wpa_hex2num(char c)
272 {
273 
274 	if (c >= '0' && c <= '9')
275 		return c - '0';
276 	if (c >= 'a' && c <= 'f')
277 		return c - 'a' + 10;
278 	if (c >= 'A' && c <= 'F')
279 		return c - 'A' + 10;
280 	return -1;
281 }
282 
283 static int
dhcpcd_wpa_hex2byte(const char * src)284 dhcpcd_wpa_hex2byte(const char *src)
285 {
286 	int h, l;
287 
288 	if ((h = dhcpcd_wpa_hex2num(*src++)) == -1 ||
289 	    (l = dhcpcd_wpa_hex2num(*src)) == -1)
290 		return -1;
291 	return (h << 4) | l;
292 }
293 
294 static ssize_t
dhcpcd_wpa_decode_ssid(char * dst,size_t dlen,const char * src)295 dhcpcd_wpa_decode_ssid(char *dst, size_t dlen, const char *src)
296 {
297 	const char *start;
298 	char c, esc;
299 	int xb;
300 
301 	start = dst;
302 	for (;;) {
303 		if (*src == '\0')
304 			break;
305 		if (--dlen == 0) {
306 			errno = ENOSPC;
307 			return -1;
308 		}
309 		c = *src++;
310 		switch (c) {
311 		case '\\':
312 			if (*src == '\0') {
313 				errno = EINVAL;
314 				return -1;
315 			}
316 			esc = *src++;
317 			switch (esc) {
318 			case '\\':
319 			case '"': *dst++ = esc; break;
320 			case 'n': *dst++ = '\n'; break;
321 			case 'r': *dst++ = '\r'; break;
322 			case 't': *dst++ = '\t'; break;
323 			case 'e': *dst++ = '\033'; break;
324 			case 'x':
325 				if (src[0] == '\0' || src[1] == '\0') {
326 					errno = EINVAL;
327 					return -1;
328 				}
329 				if ((xb = dhcpcd_wpa_hex2byte(src)) == -1)
330 					return -1;
331 				if (xb != 0)
332 					*dst++ = (char)xb;
333 				src += 2;
334 				break;
335 			default: errno = EINVAL; return -1;
336 			}
337 			break;
338 		default: *dst++ = c; break;
339 		}
340 	}
341 	if (dlen == 0) {
342 		errno = ENOSPC;
343 		return -1;
344 	}
345 	*dst = '\0';
346 	return dst - start;
347 }
348 
349 static DHCPCD_WI_SCAN *
dhcpcd_wpa_scans_read(DHCPCD_WPA * wpa)350 dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
351 {
352 	size_t i;
353 	ssize_t bytes, dl;
354 	DHCPCD_WI_SCAN *wis, *w, *l;
355 	char *s, *p, buf[32];
356 	char wssid[sizeof(w->ssid)];
357 	const char *proto;
358 
359 	if (!dhcpcd_realloc(wpa->con, 2048))
360 		return NULL;
361 	wis = NULL;
362 	for (i = 0; i < 1000; i++) {
363 		snprintf(buf, sizeof(buf), "BSS %zu", i);
364 		bytes = wpa_cmd(wpa->command_fd, buf,
365 		    wpa->con->buf, wpa->con->buflen);
366 		if (bytes == 0 || bytes == -1 ||
367 		    strncmp(wpa->con->buf, "FAIL", 4) == 0)
368 			break;
369 		p = wpa->con->buf;
370 		w = calloc(1, sizeof(*w));
371 		if (w == NULL)
372 			break;
373 		dl = 0;
374 		wssid[0] = '\0';
375 		while ((s = strsep(&p, "\n"))) {
376 			if (*s == '\0')
377 				continue;
378 			if (strncmp(s, "bssid=", 6) == 0)
379 				strlcpy(w->bssid, s + 6, sizeof(w->bssid));
380 			else if (strncmp(s, "freq=", 5) == 0)
381 				dhcpcd_strtoi(&w->frequency, s + 5);
382 //			else if (strncmp(s, "beacon_int=", 11) == 0)
383 //				;
384 			else if (strncmp(s, "qual=", 5) == 0)
385 				dhcpcd_strtoi(&w->quality.value, s + 5);
386 			else if (strncmp(s, "noise=", 6) == 0)
387 				dhcpcd_strtoi(&w->noise.value, s + 6);
388 			else if (strncmp(s, "level=", 6) == 0)
389 				dhcpcd_strtoi(&w->level.value, s + 6);
390 			else if (strncmp(s, "flags=", 6) == 0)
391 				strlcpy(w->wpa_flags, s + 6,
392 				    sizeof(w->wpa_flags));
393 			else if (strncmp(s, "ssid=", 5) == 0) {
394 				/* Decode it from \xNN to \NNN
395 				 * so we're consistent */
396 				dl = dhcpcd_wpa_decode_ssid(wssid,
397 				    sizeof(wssid), s + 5);
398 				if (dl == -1)
399 					break;
400 				dl = dhcpcd_encode_string_escape(w->ssid,
401 				    sizeof(w->ssid), wssid, (size_t)dl);
402 				if (dl == -1)
403 					break;
404 			}
405 		}
406 		if (dl == -1) {
407 			free(w);
408 			break;
409 		}
410 
411 		if (wis == NULL)
412 			wis = w;
413 		else
414 			l->next = w;
415 		l = w;
416 
417 		if ((proto = strstr(w->wpa_flags, "[WPA-")) ||
418 		    (proto = strstr(w->wpa_flags, "[WPA2-")) ||
419 		    (proto = strstr(w->wpa_flags, "[RSN-")))
420 		{
421 			const char *endp, *psk;
422 
423 			w->flags = WSF_WPA | WSF_SECURE;
424 			endp = strchr(proto, ']');
425 			if ((psk = strstr(proto, "-PSK]")) ||
426 			    (psk = strstr(proto, "-PSK-")) ||
427 			    (psk = strstr(proto, "-PSK+")))
428 			{
429 				if (psk < endp)
430 					w->flags |= WSF_PSK;
431 			}
432 		}
433 		if (strstr(w->wpa_flags, "[WEP]"))
434 			w->flags = WSF_WEP | WSF_PSK | WSF_SECURE;
435 
436 		w->strength.value = w->level.value;
437 #ifdef __linux__
438 		if (w->strength.value > 110 && w->strength.value < 256)
439 			/* Convert WEXT level to dBm */
440 			w->strength.value -= 256;
441 #endif
442 
443 		if (w->strength.value < 0) {
444 			/* Assume dBm */
445 			w->strength.value =
446 			    abs(CLAMP(w->strength.value, -100, -40) + 40);
447 			w->strength.value =
448 			    100 - ((100 * w->strength.value) / 60);
449 		} else {
450 			/* Assume quality percentage */
451 			w->strength.value = CLAMP(w->strength.value, 0, 100);
452 		}
453 	}
454 	return wis;
455 }
456 
457 int
dhcpcd_wi_scan_compare(DHCPCD_WI_SCAN * a,DHCPCD_WI_SCAN * b)458 dhcpcd_wi_scan_compare(DHCPCD_WI_SCAN *a, DHCPCD_WI_SCAN *b)
459 {
460 	int cmp;
461 
462 	/* Fist sort non case sensitive, then case sensitive */
463 	cmp = strcasecmp(a->ssid, b->ssid);
464 	if (cmp == 0)
465 		cmp = strcmp(a->ssid, b->ssid);
466 
467 	/* If still the same, return strongest first */
468 	if (cmp == 0)
469 		cmp = b->strength.value - a->strength.value;
470 
471 	return cmp;
472 }
473 
474 /*
475  * This function is copyright 2001 Simon Tatham.
476  *
477  * Permission is hereby granted, free of charge, to any person
478  * obtaining a copy of this software and associated documentation
479  * files (the "Software"), to deal in the Software without
480  * restriction, including without limitation the rights to use,
481  * copy, modify, merge, publish, distribute, sublicense, and/or
482  * sell copies of the Software, and to permit persons to whom the
483  * Software is furnished to do so, subject to the following
484  * conditions:
485  *
486  * The above copyright notice and this permission notice shall be
487  * included in all copies or substantial portions of the Software.
488  *
489  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
490  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
491  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
492  * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
493  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
494  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
495  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
496  * SOFTWARE.
497  */
498 static DHCPCD_WI_SCAN *
dhcpcd_wi_scans_sort(DHCPCD_WI_SCAN * list)499 dhcpcd_wi_scans_sort(DHCPCD_WI_SCAN *list)
500 {
501 	DHCPCD_WI_SCAN *p, *q, *e, *tail;
502 	size_t insize, nmerges, psize, qsize, i;
503 
504 	/* Silly special case: if `list' was passed in as NULL, return
505 	 * NULL immediately. */
506 	if (!list)
507 		return NULL;
508 
509 	insize = 1;
510 
511 	for (;;) {
512 		p = list;
513 		list = tail = NULL;
514 		nmerges = 0;  /* count number of merges we do in this pass */
515 
516 		while (p) {
517 			nmerges++;  /* there exists a merge to be done */
518 			/* step `insize' places along from p */
519 			q = p;
520 			psize = 0;
521 			for (i = 0; i < insize; i++) {
522 				psize++;
523 				q = q->next;
524 				if (!q)
525 					break;
526 			}
527 
528 			/* if q hasn't fallen off end,
529 			 * we have two lists to merge */
530 			qsize = insize;
531 
532 			/* now we have two lists; merge them */
533 			while (psize > 0 || (qsize > 0 && q)) {
534 				/* decide whether next element of merge comes
535 				 * from p or q */
536 				if (psize == 0) {
537 					/* p is empty; e must come from q. */
538 					e = q; q = q->next; qsize--;
539 				} else if (qsize == 0 || !q) {
540 					/* q is empty; e must come from p. */
541 					e = p; p = p->next; psize--;
542 				} else if (dhcpcd_wi_scan_compare(p, q) <= 0) {
543 					/* First element of p is lower
544 					 * (or same); e must come from p. */
545 					e = p; p = p->next; psize--;
546 				} else {
547 					/* First element of q is lower;
548 					 * e must come from q. */
549 					e = q; q = q->next; qsize--;
550 				}
551 
552 				/* add the next element to the merged list */
553 				if (tail)
554 					tail->next = e;
555 				else
556 					list = e;
557 				tail = e;
558 			}
559 
560 			/* now p has stepped `insize' places along,
561 			 * and q has too */
562 			p = q;
563 		}
564 		tail->next = NULL;
565 
566 		/* If we have done only one merge, we're finished. */
567 		if (nmerges <= 1)   /* allow for nmerges==0, the empty list */
568 			return list;
569 
570 		/* Otherwise repeat, merging lists twice the size */
571 		insize *= 2;
572 	}
573 }
574 
575 static unsigned int
dhcpcd_wi_freqflags(DHCPCD_WI_SCAN * w)576 dhcpcd_wi_freqflags(DHCPCD_WI_SCAN *w)
577 {
578 
579 	if (WPA_FREQ_IS_2G(w->frequency))
580 		return WSF_2G;
581 	if (WPA_FREQ_IS_5G(w->frequency))
582 		return WSF_5G;
583 	/* Unknown frequency */
584 	return 0;
585 }
586 
587 DHCPCD_WI_SCAN *
dhcpcd_wi_scans(DHCPCD_IF * i)588 dhcpcd_wi_scans(DHCPCD_IF *i)
589 {
590 	DHCPCD_WPA *wpa;
591 	DHCPCD_WI_SCAN *wis, *w, *n, *p;
592 	int nh;
593 	DHCPCD_WI_HIST *h, *hl;
594 
595 	wpa = dhcpcd_wpa_find(i->con, i->ifname);
596 	if (wpa == NULL)
597 		return NULL;
598 	wis = dhcpcd_wpa_scans_read(wpa);
599 
600 	/* Sort the resultant list alphabetically and then by strength */
601 	wis = dhcpcd_wi_scans_sort(wis);
602 
603 	p = NULL;
604 	for (w = wis; w && (n = w->next, 1); w = n) {
605 		/* Currently we don't support non SSID broadcasting APs */
606 		if (*w->ssid == '\0') {
607 			if (p == NULL)
608 				wis = n;
609 			else
610 				p->next = n;
611 			free(w);
612 			continue;
613 		}
614 		/* Strip duplicated SSIDs, only show the strongest */
615 		if (p && strcmp(p->ssid, w->ssid) == 0) {
616 			/* Set frequency flag from the duplicate */
617 			p->flags |= dhcpcd_wi_freqflags(w);
618 			p->next = n;
619 			free(w);
620 			continue;
621 		}
622 		/* Remember this as the previous next time */
623 		p = w;
624 
625 		/* Set frequency flags */
626 		p->flags |= dhcpcd_wi_freqflags(w);
627 
628 		nh = 1;
629 		hl = NULL;
630 		w->quality.average = w->quality.value;
631 		w->noise.average = w->noise.value;
632 		w->level.average = w->level.value;
633 		w->strength.average = w->strength.value;
634 
635 		for (h = wpa->con->wi_history; h; h = h->next) {
636 			if (strcmp(h->ifname, i->ifname) == 0 &&
637 			    strcmp(h->bssid, wis->bssid) == 0)
638 			{
639 				w->quality.average += h->quality;
640 				w->noise.average += h->noise;
641 				w->level.average += h->level;
642 				w->strength.average += h->strength;
643 				if (++nh == DHCPCD_WI_HIST_MAX) {
644 					hl->next = h->next;
645 					free(h);
646 					break;
647 				}
648 			}
649 			hl = h;
650 		}
651 
652 		if (nh != 1) {
653 			w->quality.average /= nh;
654 			w->noise.average /= nh;
655 			w->level.average /= nh;
656 			w->strength.average /= nh;
657 		}
658 		h = malloc(sizeof(*h));
659 		if (h) {
660 			strlcpy(h->ifname, i->ifname, sizeof(h->ifname));
661 			strlcpy(h->bssid, w->bssid, sizeof(h->bssid));
662 			h->quality = w->quality.value;
663 			h->noise = w->noise.value;
664 			h->level = w->level.value;
665 			h->strength = w->strength.value;
666 			h->next = wpa->con->wi_history;
667 			wpa->con->wi_history = h;
668 		}
669 	}
670 
671 	return wis;
672 }
673 
674 bool
dhcpcd_wpa_reconfigure(DHCPCD_WPA * wpa)675 dhcpcd_wpa_reconfigure(DHCPCD_WPA *wpa)
676 {
677 
678 	return dhcpcd_wpa_command(wpa, "RECONFIGURE");
679 }
680 
681 bool
dhcpcd_wpa_reassociate(DHCPCD_WPA * wpa)682 dhcpcd_wpa_reassociate(DHCPCD_WPA *wpa)
683 {
684 
685 	return dhcpcd_wpa_command(wpa, "REASSOCIATE");
686 }
687 
688 bool
dhcpcd_wpa_disconnect(DHCPCD_WPA * wpa)689 dhcpcd_wpa_disconnect(DHCPCD_WPA *wpa)
690 {
691 
692 	return dhcpcd_wpa_command(wpa, "DISCONNECT");
693 }
694 
695 bool
dhcpcd_wpa_config_write(DHCPCD_WPA * wpa)696 dhcpcd_wpa_config_write(DHCPCD_WPA *wpa)
697 {
698 
699 	return dhcpcd_wpa_command(wpa, "SAVE_CONFIG");
700 }
701 
702 static bool
dhcpcd_wpa_network(DHCPCD_WPA * wpa,const char * cmd,int id)703 dhcpcd_wpa_network(DHCPCD_WPA *wpa, const char *cmd, int id)
704 {
705 	size_t len;
706 
707 	len = strlen(cmd) + 32;
708 	if (!dhcpcd_realloc(wpa->con, len))
709 		return false;
710 	snprintf(wpa->con->buf, wpa->con->buflen, "%s %d", cmd, id);
711 	return dhcpcd_wpa_command(wpa, wpa->con->buf);
712 }
713 
714 bool
dhcpcd_wpa_network_disable(DHCPCD_WPA * wpa,int id)715 dhcpcd_wpa_network_disable(DHCPCD_WPA *wpa, int id)
716 {
717 
718 	return dhcpcd_wpa_network(wpa, "DISABLE_NETWORK", id);
719 }
720 
721 bool
dhcpcd_wpa_network_enable(DHCPCD_WPA * wpa,int id)722 dhcpcd_wpa_network_enable(DHCPCD_WPA *wpa, int id)
723 {
724 
725 	return dhcpcd_wpa_network(wpa, "ENABLE_NETWORK", id);
726 }
727 
728 bool
dhcpcd_wpa_network_select(DHCPCD_WPA * wpa,int id)729 dhcpcd_wpa_network_select(DHCPCD_WPA *wpa, int id)
730 {
731 
732 	return dhcpcd_wpa_network(wpa, "SELECT_NETWORK", id);
733 }
734 
735 bool
dhcpcd_wpa_network_remove(DHCPCD_WPA * wpa,int id)736 dhcpcd_wpa_network_remove(DHCPCD_WPA *wpa, int id)
737 {
738 
739 	return dhcpcd_wpa_network(wpa, "REMOVE_NETWORK", id);
740 }
741 
742 char *
dhcpcd_wpa_network_get(DHCPCD_WPA * wpa,int id,const char * param)743 dhcpcd_wpa_network_get(DHCPCD_WPA *wpa, int id, const char *param)
744 {
745 	ssize_t bytes;
746 
747 	if (!dhcpcd_realloc(wpa->con, 2048))
748 		return NULL;
749 	snprintf(wpa->con->buf, wpa->con->buflen, "GET_NETWORK %d %s",
750 	    id, param);
751 	bytes = wpa_cmd(wpa->command_fd, wpa->con->buf,
752 	    wpa->con->buf, wpa->con->buflen);
753 	if (bytes == 0 || bytes == -1)
754 		return NULL;
755 	if (strcmp(wpa->con->buf, "FAIL\n") == 0) {
756 		errno = EINVAL;
757 		return NULL;
758 	}
759 	return wpa->con->buf;
760 }
761 
762 bool
dhcpcd_wpa_network_set(DHCPCD_WPA * wpa,int id,const char * param,const char * value)763 dhcpcd_wpa_network_set(DHCPCD_WPA *wpa, int id,
764     const char *param, const char *value)
765 {
766 	size_t len;
767 
768 	len = strlen("SET_NETWORK") + 32 + strlen(param) + strlen(value) + 3;
769 	if (!dhcpcd_realloc(wpa->con, len))
770 		return false;
771 	snprintf(wpa->con->buf, wpa->con->buflen, "SET_NETWORK %d %s %s",
772 	    id, param, value);
773 	return dhcpcd_wpa_command(wpa, wpa->con->buf);
774 }
775 
776 static int
dhcpcd_wpa_network_find(DHCPCD_WPA * wpa,const char * fssid)777 dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
778 {
779 	ssize_t bytes, dl, tl;
780 	size_t fl;
781 	char *s, *t, *ssid, *bssid, *flags;
782 	char dssid[IF_SSIDSIZE], tssid[IF_SSIDSIZE];
783 	long l;
784 
785 	dhcpcd_realloc(wpa->con, 2048);
786 	bytes = wpa_cmd(wpa->command_fd, "LIST_NETWORKS",
787 	    wpa->con->buf, wpa->con->buflen);
788 	if (bytes == 0 || bytes == -1)
789 		return -1;
790 
791 	fl = strlen(fssid);
792 
793 	s = strchr(wpa->con->buf, '\n');
794 	if (s == NULL)
795 		return -1;
796 	while ((t = strsep(&s, "\b\n"))) {
797 		if (*t == '\0')
798 			continue;
799 		ssid = strchr(t, '\t');
800 		if (ssid == NULL)
801 			break;
802 		*ssid++ = '\0';
803 		bssid = strchr(ssid, '\t');
804 		if (bssid == NULL)
805 			break;
806 		*bssid++ = '\0';
807 		flags = strchr(bssid, '\t');
808 		if (flags == NULL)
809 			break;
810 		*flags++ = '\0';
811 		l = strtol(t, NULL, 0);
812 		if (l < 0 || l > INT_MAX) {
813 			errno = ERANGE;
814 			break;
815 		}
816 
817 		/* Decode the wpa_supplicant SSID into raw chars and
818 		 * then encode into our octal escaped string to
819 		 * compare. */
820 		dl = dhcpcd_wpa_decode_ssid(dssid, sizeof(dssid), ssid);
821 		if (dl == -1)
822 			return -1;
823 		tl = dhcpcd_encode_string_escape(tssid,
824 		    sizeof(tssid), dssid, (size_t)dl);
825 		if (tl == -1)
826 			return -1;
827 		if ((size_t)tl == fl && memcmp(tssid, fssid, (size_t)tl) == 0)
828 			return (int)l;
829 	}
830 	errno = ENOENT;
831 	return -1;
832 }
833 
834 static int
dhcpcd_wpa_network_new(DHCPCD_WPA * wpa)835 dhcpcd_wpa_network_new(DHCPCD_WPA *wpa)
836 {
837 	ssize_t bytes;
838 	long l;
839 
840 	dhcpcd_realloc(wpa->con, 32);
841 	bytes = wpa_cmd(wpa->command_fd, "ADD_NETWORK",
842 	    wpa->con->buf, sizeof(wpa->con->buf));
843 	if (bytes == 0 || bytes == -1)
844 		return -1;
845 	l = strtol(wpa->con->buf, NULL, 0);
846 	if (l < 0 || l > INT_MAX) {
847 		errno = ERANGE;
848 		return -1;
849 	}
850 	return (int)l;
851 }
852 
853 static const char hexchrs[] = "0123456789abcdef";
854 int
dhcpcd_wpa_network_find_new(DHCPCD_WPA * wpa,const char * ssid)855 dhcpcd_wpa_network_find_new(DHCPCD_WPA *wpa, const char *ssid)
856 {
857 	int id;
858 	char dssid[IF_SSIDSIZE], essid[IF_SSIDSIZE], *ep;
859 	ssize_t dl, i;
860 	char *dp;
861 
862 	id = dhcpcd_wpa_network_find(wpa, ssid);
863 	if (id != -1)
864 		return id;
865 
866 	dl = dhcpcd_decode_string_escape(dssid, sizeof(dssid), ssid);
867 	if (dl == -1)
868 		return -1;
869 
870 	for (i = 0; i < dl; i++) {
871 		if (!isascii((int)dssid[i]) && !isprint((int)dssid[i]))
872 			break;
873 	}
874 	dp = dssid;
875 	ep = essid;
876 	if (i < dl) {
877 		/* Non standard characters found! Encode as hex string */
878 		unsigned char c;
879 
880 		for (; dl; dl--) {
881 			c = (unsigned char)*dp++;
882 			*ep++ = hexchrs[(c & 0xf0) >> 4];
883 			*ep++ = hexchrs[(c & 0x0f)];
884 		}
885 	} else {
886 		*ep++ = '\"';
887 		do
888 			*ep++ = *dp;
889 		while (*++dp != '\0');
890 		*ep++ = '\"';
891 	}
892 	*ep = '\0';
893 
894 	id = dhcpcd_wpa_network_new(wpa);
895 	if (id != -1)
896 		dhcpcd_wpa_network_set(wpa, id, "ssid", essid);
897 	return id;
898 }
899 
900 void
dhcpcd_wpa_close(DHCPCD_WPA * wpa)901 dhcpcd_wpa_close(DHCPCD_WPA *wpa)
902 {
903 
904 	assert(wpa);
905 
906 	if (wpa->command_fd == -1)
907 		return;
908 
909 	dhcpcd_attach_detach(wpa, false);
910 
911 	if (wpa->status != DHC_DOWN) {
912 		shutdown(wpa->command_fd, SHUT_RDWR);
913 		shutdown(wpa->listen_fd, SHUT_RDWR);
914 		dhcpcd_wpa_update_status(wpa, DHC_DOWN);
915 	}
916 
917 	close(wpa->command_fd);
918 	wpa->command_fd = -1;
919 	close(wpa->listen_fd);
920 	wpa->listen_fd = -1;
921 	unlink(wpa->command_path);
922 	free(wpa->command_path);
923 	wpa->command_path = NULL;
924 	unlink(wpa->listen_path);
925 	free(wpa->listen_path);
926 	wpa->listen_path = NULL;
927 }
928 
929 DHCPCD_WPA *
dhcpcd_wpa_find(DHCPCD_CONNECTION * con,const char * ifname)930 dhcpcd_wpa_find(DHCPCD_CONNECTION *con, const char *ifname)
931 {
932 	DHCPCD_WPA *wpa;
933 
934 	for (wpa = con->wpa; wpa; wpa = wpa->next) {
935 		if (strcmp(wpa->ifname, ifname) == 0)
936 			return wpa;
937 	}
938 	errno = ENOENT;
939 	return NULL;
940 }
941 
942 DHCPCD_WPA *
dhcpcd_wpa_new(DHCPCD_CONNECTION * con,const char * ifname)943 dhcpcd_wpa_new(DHCPCD_CONNECTION *con, const char *ifname)
944 {
945 	DHCPCD_WPA *wpa;
946 
947 	wpa = dhcpcd_wpa_find(con, ifname);
948 	if (wpa)
949 		return wpa;
950 
951 	wpa = calloc(1, sizeof(*wpa));
952 	if (wpa == NULL)
953 		return NULL;
954 
955 	wpa->con = con;
956 	strlcpy(wpa->ifname, ifname, sizeof(wpa->ifname));
957 	wpa->status = DHC_DOWN;
958 	wpa->command_fd = wpa->listen_fd = -1;
959 	wpa->command_path = wpa->listen_path = NULL;
960 	wpa->next = con->wpa;
961 	con->wpa = wpa;
962 	return wpa;
963 }
964 
965 DHCPCD_CONNECTION *
dhcpcd_wpa_connection(DHCPCD_WPA * wpa)966 dhcpcd_wpa_connection(DHCPCD_WPA *wpa)
967 {
968 
969 	assert(wpa);
970 	return wpa->con;
971 }
972 
973 DHCPCD_IF *
dhcpcd_wpa_if(DHCPCD_WPA * wpa)974 dhcpcd_wpa_if(DHCPCD_WPA *wpa)
975 {
976 
977 	assert(wpa);
978 	return dhcpcd_get_if(wpa->con, wpa->ifname, DHT_LINK);
979 }
980 
981 static void
dhcpcd_wpa_if_freq(DHCPCD_WPA * wpa)982 dhcpcd_wpa_if_freq(DHCPCD_WPA *wpa)
983 {
984 	DHCPCD_IF *i;
985 
986 	i = dhcpcd_wpa_if(wpa);
987 	if (i != NULL)
988 		i->freq = dhcpcd_wpa_freq(wpa);
989 }
990 
991 static void
dhcpcd_wpa_if_freq_zero(DHCPCD_WPA * wpa)992 dhcpcd_wpa_if_freq_zero(DHCPCD_WPA *wpa)
993 {
994 	DHCPCD_IF *i;
995 
996 	i = dhcpcd_wpa_if(wpa);
997 	if (i != NULL)
998 		i->freq = 0;
999 }
1000 
1001 int
dhcpcd_wpa_open(DHCPCD_WPA * wpa)1002 dhcpcd_wpa_open(DHCPCD_WPA *wpa)
1003 {
1004 	int cmd_fd, list_fd = -1;
1005 	char *cmd_path = NULL, *list_path = NULL;
1006 
1007 	if (wpa->listen_fd != -1) {
1008 		if (wpa->status == DHC_CONNECTED)
1009 			return wpa->listen_fd;
1010 		errno = EISCONN;
1011 		return -1;
1012 	}
1013 
1014 	cmd_fd = wpa_open(wpa->ifname, &cmd_path);
1015 	if (cmd_fd == -1)
1016 		goto fail;
1017 
1018 	list_fd = wpa_open(wpa->ifname, &list_path);
1019 	if (list_fd == -1)
1020 		goto fail;
1021 
1022 	wpa->status = DHC_CONNECTING;
1023 	wpa->attached = false;
1024 	wpa->command_fd = cmd_fd;
1025 	wpa->command_path = cmd_path;
1026 	wpa->listen_fd = list_fd;
1027 	wpa->listen_path = list_path;
1028 	if (!dhcpcd_attach_detach(wpa, true)) {
1029 		dhcpcd_wpa_close(wpa);
1030 		return -1;
1031 	}
1032 
1033 	dhcpcd_wpa_if_freq(wpa);
1034 
1035 	dhcpcd_wpa_update_status(wpa, DHC_CONNECTED);
1036 	if (wpa->con->wi_scanresults_cb)
1037 		wpa->con->wi_scanresults_cb(wpa,
1038 		    wpa->con->wi_scanresults_context);
1039 
1040 	return wpa->listen_fd;
1041 
1042 fail:
1043 	if (cmd_fd != -1)
1044 		close(cmd_fd);
1045 	if (list_fd != -1)
1046 		close(list_fd);
1047 	if (cmd_path)
1048 		unlink(cmd_path);
1049 	free(cmd_path);
1050 	if (list_path)
1051 		free(list_path);
1052 	return -1;
1053 }
1054 
1055 unsigned int
dhcpcd_wpa_status(DHCPCD_WPA * wpa,const char ** status_msg)1056 dhcpcd_wpa_status(DHCPCD_WPA *wpa, const char **status_msg)
1057 {
1058 
1059 	assert(wpa);
1060 	if (status_msg)
1061 		*status_msg = dhcpcd_cstates[wpa->status];
1062 	return wpa->status;
1063 }
1064 
1065 int
dhcpcd_wpa_get_fd(DHCPCD_WPA * wpa)1066 dhcpcd_wpa_get_fd(DHCPCD_WPA *wpa)
1067 {
1068 
1069 	assert(wpa);
1070 	return wpa->listen_fd;
1071 }
1072 
1073 void
dhcpcd_wpa_set_scan_callback(DHCPCD_CONNECTION * con,void (* cb)(DHCPCD_WPA *,void *),void * context)1074 dhcpcd_wpa_set_scan_callback(DHCPCD_CONNECTION *con,
1075     void (*cb)(DHCPCD_WPA *, void *), void *context)
1076 {
1077 
1078 	assert(con);
1079 	con->wi_scanresults_cb = cb;
1080 	con->wi_scanresults_context = context;
1081 }
1082 
1083 
dhcpcd_wpa_set_status_callback(DHCPCD_CONNECTION * con,void (* cb)(DHCPCD_WPA *,unsigned int,const char *,void *),void * context)1084 void dhcpcd_wpa_set_status_callback(DHCPCD_CONNECTION * con,
1085     void (*cb)(DHCPCD_WPA *, unsigned int, const char *, void *),
1086     void *context)
1087 {
1088 
1089 	assert(con);
1090 	con->wpa_status_cb = cb;
1091 	con->wpa_status_context = context;
1092 }
1093 
1094 void
dhcpcd_wpa_dispatch(DHCPCD_WPA * wpa)1095 dhcpcd_wpa_dispatch(DHCPCD_WPA *wpa)
1096 {
1097 	char buffer[256], *p;
1098 	size_t bytes;
1099 
1100 	assert(wpa);
1101 	bytes = (size_t)read(wpa->listen_fd, buffer, sizeof(buffer));
1102 	if ((ssize_t)bytes == -1) {
1103 		dhcpcd_wpa_close(wpa);
1104 		return;
1105 	}
1106 
1107 	buffer[bytes] = '\0';
1108 	bytes = strlen(buffer);
1109 	if (buffer[bytes - 1] == ' ')
1110 		buffer[--bytes] = '\0';
1111 	for (p = buffer + 1; *p != '\0'; p++) {
1112 		if (*p == '>') {
1113 			p++;
1114 			break;
1115 		}
1116 	}
1117 
1118 #define	CE_SCAN_RESULTS		"CTRL-EVENT-SCAN-RESULTS"
1119 #define	CE_CONNECTED		"CTRL-EVENT-CONNECTED"
1120 #define	CE_DISCONNECTED		"CTRL-EVENT-DISCONNECTED"
1121 #define	CE_TERMINATING		"CTRL-EVENT-TERMINATING"
1122 	if (strncmp(p, CE_SCAN_RESULTS, strlen(CE_SCAN_RESULTS)) == 0 &&
1123 	    wpa->con->wi_scanresults_cb)
1124 		wpa->con->wi_scanresults_cb(wpa,
1125 		    wpa->con->wi_scanresults_context);
1126 	else if (strncmp(p, CE_CONNECTED, strlen(CE_CONNECTED)) == 0)
1127 		dhcpcd_wpa_if_freq(wpa);
1128 	else if (strncmp(p, CE_DISCONNECTED, strlen(CE_DISCONNECTED)) == 0)
1129 		dhcpcd_wpa_if_freq_zero(wpa);
1130 	else if (strncmp(p, CE_TERMINATING, strlen(CE_TERMINATING)) == 0)
1131 		dhcpcd_wpa_close(wpa);
1132 }
1133 
1134 void
dhcpcd_wpa_if_event(DHCPCD_IF * i)1135 dhcpcd_wpa_if_event(DHCPCD_IF *i)
1136 {
1137 	DHCPCD_WPA *wpa;
1138 
1139 	assert(i);
1140 	if (i->type == DHT_LINK) {
1141 		if (i->state == DHS_STOPPED || i->state == DHS_DEPARTED) {
1142 			wpa = dhcpcd_wpa_find(i->con, i->ifname);
1143 			if (wpa)
1144 				dhcpcd_wpa_close(wpa);
1145 		} else if (i->wireless && i->con->wpa_started) {
1146 			wpa = dhcpcd_wpa_new(i->con, i->ifname);
1147 			if (wpa) {
1148 				if (wpa->listen_fd == -1)
1149 					dhcpcd_wpa_open(wpa);
1150 				i->freq = dhcpcd_wpa_freq(wpa);
1151 			}
1152 		}
1153 	}
1154 }
1155 
1156 void
dhcpcd_wpa_start(DHCPCD_CONNECTION * con)1157 dhcpcd_wpa_start(DHCPCD_CONNECTION *con)
1158 {
1159 	DHCPCD_IF *i;
1160 
1161 	assert(con);
1162 	con->wpa_started = true;
1163 
1164 	for (i = con->interfaces; i; i = i->next)
1165 		dhcpcd_wpa_if_event(i);
1166 }
1167 
1168 static const char *
dhcpcd_wpa_var_psk(DHCPCD_WI_SCAN * s)1169 dhcpcd_wpa_var_psk(DHCPCD_WI_SCAN *s)
1170 {
1171 
1172 	if (s->flags & WSF_WEP)
1173 		return "wep_key0";
1174 	else if ((s->flags & (WSF_WPA | WSF_PSK)) == (WSF_WPA | WSF_PSK))
1175 		return "psk";
1176 	return NULL;
1177 }
1178 
1179 static const char *
dhcpcd_wpa_var_mgmt(DHCPCD_WI_SCAN * s)1180 dhcpcd_wpa_var_mgmt(DHCPCD_WI_SCAN *s)
1181 {
1182 
1183 	if (s->flags & WSF_WPA) {
1184 		if (s->flags & WSF_PSK)
1185 			return "WPA-PSK";
1186 	}
1187 	return "NONE";
1188 }
1189 
1190 static int
dhcpcd_wpa_configure1(DHCPCD_WPA * wpa,DHCPCD_WI_SCAN * s,const char * psk)1191 dhcpcd_wpa_configure1(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s, const char *psk)
1192 {
1193 	const char *mgmt, *var;
1194 	int id, retval;
1195 	char *npsk;
1196 	size_t psk_len;
1197 	bool r;
1198 
1199 	if (!dhcpcd_wpa_disconnect(wpa))
1200 		return DHCPCD_WPA_ERR_DISCONN;
1201 
1202 	/* reload the configuration so that when we don't save
1203 	 * the disabled networks to the config file. */
1204 	if (!dhcpcd_wpa_reconfigure(wpa))
1205 		return DHCPCD_WPA_ERR_RECONF;
1206 
1207 	id = dhcpcd_wpa_network_find_new(wpa, s->ssid);
1208 	if (id == -1)
1209 		return DHCPCD_WPA_ERR;
1210 
1211 	mgmt = dhcpcd_wpa_var_mgmt(s);
1212 	if (mgmt && !dhcpcd_wpa_network_set(wpa, id, "key_mgmt", mgmt))
1213 		return DHCPCD_WPA_ERR_SET;
1214 
1215 	var = dhcpcd_wpa_var_psk(s);
1216 	if (var) {
1217 
1218 		if (psk)
1219 			psk_len = strlen(psk);
1220 		else
1221 			psk_len = 0;
1222 		npsk = malloc(psk_len + 3);
1223 		if (npsk == NULL)
1224 			return DHCPCD_WPA_ERR;
1225 		npsk[0] = '"';
1226 		if (psk_len)
1227 			memcpy(npsk + 1, psk, psk_len);
1228 		npsk[psk_len + 1] = '"';
1229 		npsk[psk_len + 2] = '\0';
1230 		r = dhcpcd_wpa_network_set(wpa, id, var, npsk);
1231 		free(npsk);
1232 		if (!r)
1233 			return DHCPCD_WPA_ERR_SET_PSK;
1234 	}
1235 
1236 	if (!dhcpcd_wpa_network_enable(wpa, id))
1237 		return DHCPCD_WPA_ERR_ENABLE;
1238 	if (dhcpcd_wpa_config_write(wpa))
1239 		retval = DHCPCD_WPA_SUCCESS;
1240 	else
1241 		retval = DHCPCD_WPA_ERR_WRITE;
1242 	/* Selecting a network disables the others.
1243 	 * This should not be saved. */
1244 	if (!dhcpcd_wpa_network_select(wpa, id) && retval == DHCPCD_WPA_SUCCESS)
1245 		return DHCPCD_WPA_ERR_SELECT;
1246 	return retval;
1247 }
1248 
1249 int
dhcpcd_wpa_configure(DHCPCD_WPA * wpa,DHCPCD_WI_SCAN * s,const char * psk)1250 dhcpcd_wpa_configure(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s, const char *psk)
1251 {
1252 	int retval;
1253 
1254 	retval = dhcpcd_wpa_configure1(wpa, s, psk);
1255 	/* Always reassociate */
1256 	if (!dhcpcd_wpa_reassociate(wpa)) {
1257 		if (retval == DHCPCD_WPA_SUCCESS)
1258 			retval = DHCPCD_WPA_ERR_ASSOC;
1259 	}
1260 	return retval;
1261 }
1262 
1263 int
dhcpcd_wpa_select(DHCPCD_WPA * wpa,DHCPCD_WI_SCAN * s)1264 dhcpcd_wpa_select(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s)
1265 {
1266 	int id, retval;
1267 
1268 	assert(wpa);
1269 	assert(s);
1270 
1271 	id = dhcpcd_wpa_network_find(wpa, s->ssid);
1272 	if (id == -1)
1273 		return DHCPCD_WPA_ERR;
1274 
1275 	if (!dhcpcd_wpa_disconnect(wpa))
1276 		retval = DHCPCD_WPA_ERR_DISCONN;
1277 	else if (!dhcpcd_wpa_network_select(wpa, id))
1278 		retval = DHCPCD_WPA_ERR_SELECT;
1279 	else
1280 		retval = DHCPCD_WPA_SUCCESS;
1281 
1282 	/* Always reassociate */
1283 	if (!dhcpcd_wpa_reassociate(wpa)) {
1284 		if (retval == DHCPCD_WPA_SUCCESS)
1285 			retval = DHCPCD_WPA_ERR_ASSOC;
1286 	}
1287 	return retval;
1288 }
1289 
1290 int
dhcpcd_wpa_freq(DHCPCD_WPA * wpa)1291 dhcpcd_wpa_freq(DHCPCD_WPA *wpa)
1292 {
1293 	char buf[256], *p, *s;
1294 	ssize_t bytes;
1295 	int freq;
1296 
1297 	bytes = wpa_cmd(wpa->command_fd, "STATUS", buf, sizeof(buf));
1298 	if (bytes == 0 || bytes == -1)
1299 		return false;
1300 
1301 	p = buf;
1302 	while ((s = strsep(&p, "\n"))) {
1303 		if (*s == '\0')
1304 			continue;
1305 		if (strncmp(s, "freq=", 5) == 0) {
1306 			dhcpcd_strtoi(&freq, s + 5);
1307 			return freq;
1308 		}
1309 	}
1310 
1311 	errno = ENOENT;
1312 	return 0;
1313 }
1314 
1315 int
dhcpcd_wi_print_tooltip(char * buf,size_t buflen,DHCPCD_WI_SCAN * s,unsigned int options)1316 dhcpcd_wi_print_tooltip(char *buf, size_t buflen, DHCPCD_WI_SCAN *s,
1317     unsigned int options)
1318 {
1319 	int r, printed = 0;
1320 
1321 	/* Provide a default */
1322 	if (options == 0)
1323 		options = WST_BSSID | WST_FREQ;
1324 
1325 #define	TOOLTIP(fmt, ...) do {	\
1326 	r = snprintf(buf, buflen, fmt, __VA_ARGS__);	\
1327 	if (r == -1 || (size_t)r > buflen)		\
1328 		return printed + r;			\
1329 	buf += r;					\
1330 	buflen -= (size_t)r;				\
1331 	printed += r;					\
1332 	} while (0 /* CONSTCOND */)
1333 
1334 	if (options & WST_BSSID)
1335 		TOOLTIP("%s", s->bssid);
1336 	if (options & WST_FLAGS && s->wpa_flags[0] != '\0')
1337 		TOOLTIP(" %s", s->wpa_flags);
1338 	if (options & WST_FREQ) {
1339 		if (s->flags & WSF_2G)
1340 			TOOLTIP(" %s", "2G");
1341 		if (s->flags & WSF_5G)
1342 			TOOLTIP(" %s", "5G");
1343 	}
1344 
1345 	return printed;
1346 }
1347