07b125835400c051e7d2c5f6a1e196676f07c7b6
[dhcpcd-ui] / src / libdhcpcd / wpa.c
1 /*
2  * libdhcpcd
3  * Copyright 2009-2014 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 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/un.h>
30
31 #include <assert.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <poll.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #define IN_LIBDHCPCD
42 #include "config.h"
43 #include "dhcpcd.h"
44
45 #ifndef SUN_LEN
46 #define SUN_LEN(su) \
47         (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
48 #endif
49
50 #define CLAMP(x, low, high) \
51         (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
52
53 static int
54 wpa_open(const char *ifname, char **path)
55 {
56         static int counter;
57         int fd;
58         socklen_t len;
59         struct sockaddr_un sun;
60
61         if ((fd = socket(AF_UNIX,
62             SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1)
63                 return -1;
64         memset(&sun, 0, sizeof(sun));
65         sun.sun_family = AF_UNIX;
66         snprintf(sun.sun_path, sizeof(sun.sun_path),
67             "/tmp/libdhcpcd-wpa-%d.%d", getpid(), counter++);
68         *path = strdup(sun.sun_path);
69         len = (socklen_t)SUN_LEN(&sun);
70         if (bind(fd, (struct sockaddr *)&sun, len) == -1) {
71                 close(fd);
72                 unlink(*path);
73                 free(*path);
74                 *path = NULL;
75                 return -1;
76         }
77         snprintf(sun.sun_path, sizeof(sun.sun_path),
78             WPA_CTRL_DIR "/%s", ifname);
79         len = (socklen_t)SUN_LEN(&sun);
80         if (connect(fd, (struct sockaddr *)&sun, len) == -1) {
81                 close(fd);
82                 unlink(*path);
83                 free(*path);
84                 *path = NULL;
85                 return -1;
86         }
87
88         return fd;
89 }
90
91 static ssize_t
92 wpa_cmd(int fd, const char *cmd, char *buffer, size_t len)
93 {
94         int retval;
95         ssize_t bytes;
96         struct pollfd pfd;
97
98         if (buffer)
99                 *buffer = '\0';
100         bytes = write(fd, cmd, strlen(cmd));
101         if (bytes == -1 || bytes == 0)
102                 return -1;
103         if (buffer == NULL || len == 0)
104                 return 0;
105         pfd.fd = fd;
106         pfd.events = POLLIN | POLLHUP;
107         pfd.revents = 0;
108         retval = poll(&pfd, 1, 2000);
109         if (retval == -1)
110                 return -1;
111         if (retval == 0 || !(pfd.revents & (POLLIN | POLLHUP)))
112                 return -1;
113
114         bytes = read(fd, buffer, len == 1 ? 1 : len - 1);
115         if (bytes != -1)
116                 buffer[bytes] = '\0';
117         return bytes;
118 }
119
120 bool
121 dhcpcd_wpa_command(DHCPCD_WPA *wpa, const char *cmd)
122 {
123         char buf[10];
124         ssize_t bytes;
125
126         bytes = wpa_cmd(wpa->command_fd, cmd, buf, sizeof(buf));
127         return (bytes == -1 || bytes == 0 ||
128             strcmp(buf, "OK\n")) ? false : true;
129 }
130
131 bool
132 dhcpcd_wpa_command_arg(DHCPCD_WPA *wpa, const char *cmd, const char *arg)
133 {
134         size_t cmdlen, nlen;
135
136         cmdlen = strlen(cmd);
137         nlen = cmdlen + strlen(arg) + 2;
138         if (!dhcpcd_realloc(wpa->con, nlen))
139                 return -1;
140         strlcpy(wpa->con->buf, cmd, wpa->con->buflen);
141         wpa->con->buf[cmdlen] = ' ';
142         strlcpy(wpa->con->buf + cmdlen + 1, arg, wpa->con->buflen - 1 - cmdlen);
143         return dhcpcd_wpa_command(wpa, wpa->con->buf);
144 }
145
146 static bool
147 dhcpcd_attach_detach(DHCPCD_WPA *wpa, bool attach)
148 {
149         char buf[10];
150         ssize_t bytes;
151
152         if (wpa->attached == attach)
153                 return true;
154
155         bytes = wpa_cmd(wpa->listen_fd, attach > 0 ? "ATTACH" : "DETACH",
156             buf, sizeof(buf));
157         if (bytes == -1 || bytes == 0 || strcmp(buf, "OK\n"))
158                 return false;
159
160         wpa->attached = attach;
161         return true;
162 }
163
164 bool
165 dhcpcd_wpa_scan(DHCPCD_WPA *wpa)
166 {
167
168         return dhcpcd_wpa_command(wpa, "SCAN");
169 }
170
171 void
172 dhcpcd_wi_scans_free(DHCPCD_WI_SCAN *wis)
173 {
174         DHCPCD_WI_SCAN *n;
175
176         while (wis) {
177                 n = wis->next;
178                 free(wis);
179                 wis = n;
180         }
181 }
182
183 static void
184 dhcpcd_strtoi(int *val, const char *s)
185 {
186         long l;
187
188         l = strtol(s, NULL, 0);
189         if (l >= INT_MIN && l <= INT_MAX)
190                 *val = (int)l;
191         else
192                 errno = ERANGE;
193 }
194
195 static int
196 dhcpcd_wpa_hex2num(char c)
197 {
198
199         if (c >= '0' && c <= '9')
200                 return c - '0';
201         if (c >= 'a' && c <= 'f')
202                 return c - 'a' + 10;
203         if (c >= 'A' && c <= 'F')
204                 return c - 'A' + 10;
205         return -1;
206 }
207
208 static int
209 dhcpcd_wpa_hex2byte(const char *src)
210 {
211         int h, l;
212
213         if ((h = dhcpcd_wpa_hex2num(*src++)) == -1 ||
214             (l = dhcpcd_wpa_hex2num(*src)) == -1)
215                 return -1;
216         return (h << 4) | l;
217 }
218
219 static ssize_t
220 dhcpcd_wpa_decode_ssid(char *dst, size_t dlen, const char *src)
221 {
222         const char *start;
223         char c, esc;
224         int xb;
225
226         start = dst;
227         for (;;) {
228                 if (*src == '\0')
229                         break;
230                 if (--dlen == 0) {
231                         errno = ENOSPC;
232                         return -1;
233                 }
234                 c = *src++;
235                 switch (c) {
236                 case '\\':
237                         if (*src == '\0') {
238                                 errno = EINVAL;
239                                 return -1;
240                         }
241                         esc = *src++;
242                         switch (esc) {
243                         case '\\':
244                         case '"': *dst++ = esc; break;
245                         case 'n': *dst++ = '\n'; break;
246                         case 'r': *dst++ = '\r'; break;
247                         case 't': *dst++ = '\t'; break;
248                         case 'e': *dst++ = '\033'; break;
249                         case 'x':
250                                 if (src[0] == '\0' || src[1] == '\0') {
251                                         errno = EINVAL;
252                                         return -1;
253                                 }
254                                 if ((xb = dhcpcd_wpa_hex2byte(src)) == -1)
255                                         return -1;
256                                 *dst++ = (char)xb;
257                                 src += 2;
258                                 break;
259                         default: errno = EINVAL; return -1;
260                         }
261                 default: *dst++ = c; break;
262                 }
263         }
264         if (dlen == 0) {
265                 errno = ENOSPC;
266                 return -1;
267         }
268         *dst = '\0';
269         return dst - start;
270 }
271
272 static DHCPCD_WI_SCAN *
273 dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
274 {
275         size_t i;
276         ssize_t bytes, dl;
277         DHCPCD_WI_SCAN *wis, *w, *l;
278         char *s, *p, buf[32];
279         char wssid[sizeof(w->ssid)];
280
281         if (!dhcpcd_realloc(wpa->con, 2048))
282                 return NULL;
283         wis = NULL;
284         for (i = 0; i < 1000; i++) {
285                 snprintf(buf, sizeof(buf), "BSS %zu", i);
286                 bytes = wpa_cmd(wpa->command_fd, buf,
287                     wpa->con->buf, wpa->con->buflen);
288                 if (bytes == 0 || bytes == -1 ||
289                     strncmp(wpa->con->buf, "FAIL", 4) == 0)
290                         break;
291                 p = wpa->con->buf;
292                 w = calloc(1, sizeof(*w));
293                 if (w == NULL)
294                         break;
295                 dl = 0;
296                 wssid[0] = '\0';
297                 while ((s = strsep(&p, "\n"))) {
298                         if (*s == '\0')
299                                 continue;
300                         if (strncmp(s, "bssid=", 6) == 0)
301                                 strlcpy(w->bssid, s + 6, sizeof(w->bssid));
302                         else if (strncmp(s, "freq=", 5) == 0)
303                                 dhcpcd_strtoi(&w->frequency, s + 5);
304 //                      else if (strncmp(s, "beacon_int=", 11) == 0)
305 //                              ;
306                         else if (strncmp(s, "qual=", 5) == 0)
307                                 dhcpcd_strtoi(&w->quality.value, s + 5);
308                         else if (strncmp(s, "noise=", 6) == 0)
309                                 dhcpcd_strtoi(&w->noise.value, s + 6);
310                         else if (strncmp(s, "level=", 6) == 0)
311                                 dhcpcd_strtoi(&w->level.value, s + 6);
312                         else if (strncmp(s, "flags=", 6) == 0)
313                                 strlcpy(w->flags, s + 6, sizeof(w->flags));
314                         else if (strncmp(s, "ssid=", 5) == 0) {
315                                 /* Decode it from \xNN to \NNN
316                                  * so we're consistent */
317                                 dl = dhcpcd_wpa_decode_ssid(wssid,
318                                     sizeof(wssid), s + 5);
319                                 if (dl == -1)
320                                         break;
321                                 dl = dhcpcd_encode_string_escape(w->ssid,
322                                     sizeof(w->ssid), wssid, (size_t)dl);
323                                 if (dl == -1)
324                                         break;
325                         }
326                 }
327                 if (dl == -1) {
328                         free(w);
329                         break;
330                 }
331
332                 if (wis == NULL)
333                         wis = w;
334                 else
335                         l->next = w;
336                 l = w;
337
338                 w->strength.value = w->level.value;
339                 if (w->strength.value > 110 && w->strength.value < 256)
340                         /* Convert WEXT level to dBm */
341                         w->strength.value -= 256;
342
343                 if (w->strength.value < 0) {
344                         /* Assume dBm */
345                         w->strength.value =
346                             abs(CLAMP(w->strength.value, -100, -40) + 40);
347                         w->strength.value =
348                             100 - ((100 * w->strength.value) / 60);
349                 } else {
350                         /* Assume quality percentage */
351                         w->strength.value = CLAMP(w->strength.value, 0, 100);
352                 }
353         }
354         return wis;
355 }
356
357 DHCPCD_WI_SCAN *
358 dhcpcd_wi_scans(DHCPCD_IF *i)
359 {
360         DHCPCD_WPA *wpa;
361         DHCPCD_WI_SCAN *wis, *w;
362         int nh;
363         DHCPCD_WI_HIST *h, *hl;
364
365         wpa = dhcpcd_wpa_find(i->con, i->ifname);
366         if (wpa == NULL)
367                 return NULL;
368         wis = dhcpcd_wpa_scans_read(wpa);
369         for (w = wis; w; w = w->next) {
370                 nh = 1;
371                 hl = NULL;
372                 w->quality.average = w->quality.value;
373                 w->noise.average = w->noise.value;
374                 w->level.average = w->level.value;
375                 w->strength.average = w->strength.value;
376
377                 for (h = wpa->con->wi_history; h; h = h->next) {
378                         if (strcmp(h->ifname, i->ifname) == 0 &&
379                             strcmp(h->bssid, wis->bssid) == 0)
380                         {
381                                 w->quality.average += h->quality;
382                                 w->noise.average += h->noise;
383                                 w->level.average += h->level;
384                                 w->strength.average += h->strength;
385                                 if (++nh == DHCPCD_WI_HIST_MAX) {
386                                         hl->next = h->next;
387                                         free(h);
388                                         break;
389                                 }
390                         }
391                         hl = h;
392                 }
393
394                 if (nh != 1) {
395                         w->quality.average /= nh;
396                         w->noise.average /= nh;
397                         w->level.average /= nh;
398                         w->strength.average /= nh;
399                 }
400                 h = malloc(sizeof(*h));
401                 if (h) {
402                         strlcpy(h->ifname, i->ifname, sizeof(h->ifname));
403                         strlcpy(h->bssid, w->bssid, sizeof(h->bssid));
404                         h->quality = w->quality.value;
405                         h->noise = w->noise.value;
406                         h->level = w->level.value;
407                         h->strength = w->strength.value;
408                         h->next = wpa->con->wi_history;
409                         wpa->con->wi_history = h;
410                 }
411         }
412
413         return wis;
414 }
415
416 bool
417 dhcpcd_wpa_reassociate(DHCPCD_WPA *wpa)
418 {
419
420         return dhcpcd_wpa_command(wpa, "REASSOCIATE");
421 }
422
423 bool
424 dhcpcd_wpa_disconnect(DHCPCD_WPA *wpa)
425 {
426
427         return dhcpcd_wpa_command(wpa, "DISCONNECT");
428 }
429
430 bool
431 dhcpcd_wpa_config_write(DHCPCD_WPA *wpa)
432 {
433
434         return dhcpcd_wpa_command(wpa, "SAVE_CONFIG");
435 }
436
437 static bool
438 dhcpcd_wpa_network(DHCPCD_WPA *wpa, const char *cmd, int id)
439 {
440         size_t len;
441
442         len = strlen(cmd) + 32;
443         if (!dhcpcd_realloc(wpa->con, len))
444                 return false;
445         snprintf(wpa->con->buf, wpa->con->buflen, "%s %d", cmd, id);
446         return dhcpcd_wpa_command(wpa, wpa->con->buf);
447 }
448
449 bool
450 dhcpcd_wpa_network_disable(DHCPCD_WPA *wpa, int id)
451 {
452
453         return dhcpcd_wpa_network(wpa, "DISABLE_NETWORK", id);
454 }
455
456 bool
457 dhcpcd_wpa_network_enable(DHCPCD_WPA *wpa, int id)
458 {
459
460         return dhcpcd_wpa_network(wpa, "ENABLE_NETWORK", id);
461 }
462
463 bool
464 dhcpcd_wpa_network_remove(DHCPCD_WPA *wpa, int id)
465 {
466
467         return dhcpcd_wpa_network(wpa, "REMOVE_NETWORK", id);
468 }
469
470 char *
471 dhcpcd_wpa_network_get(DHCPCD_WPA *wpa, int id, const char *param)
472 {
473         ssize_t bytes;
474
475         if (!dhcpcd_realloc(wpa->con, 2048))
476                 return NULL;
477         snprintf(wpa->con->buf, wpa->con->buflen, "GET_NETWORK %d %s",
478             id, param);
479         bytes = wpa_cmd(wpa->command_fd, wpa->con->buf,
480             wpa->con->buf, wpa->con->buflen);
481         if (bytes == 0 || bytes == -1)
482                 return NULL;
483         if (strcmp(wpa->con->buf, "FAIL\n") == 0) {
484                 errno = EINVAL;
485                 return NULL;
486         }
487         return wpa->con->buf;
488 }
489
490 bool
491 dhcpcd_wpa_network_set(DHCPCD_WPA *wpa, int id,
492     const char *param, const char *value)
493 {
494         size_t len;
495
496         len = strlen("SET_NETWORK") + 32 + strlen(param) + strlen(value) + 3;
497         if (!dhcpcd_realloc(wpa->con, len))
498                 return false;
499         snprintf(wpa->con->buf, wpa->con->buflen, "SET_NETWORK %d %s %s",
500             id, param, value);
501         return dhcpcd_wpa_command(wpa, wpa->con->buf);
502 }
503
504 static int
505 dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
506 {
507         ssize_t bytes, dl, tl;
508         size_t fl;
509         char *s, *t, *ssid, *bssid, *flags;
510         char dssid[IF_SSIDSIZE], tssid[IF_SSIDSIZE];
511         long l;
512
513         dhcpcd_realloc(wpa->con, 2048);
514         bytes = wpa_cmd(wpa->command_fd, "LIST_NETWORKS",
515             wpa->con->buf, wpa->con->buflen);
516         if (bytes == 0 || bytes == -1)
517                 return -1;
518
519         fl = strlen(fssid);
520
521         s = strchr(wpa->con->buf, '\n');
522         if (s == NULL)
523                 return -1;
524         while ((t = strsep(&s, "\b"))) {
525                 if (*t == '\0')
526                         continue;
527                 ssid = strchr(t, '\t');
528                 if (ssid == NULL)
529                         break;
530                 *ssid++ = '\0';
531                 bssid = strchr(ssid, '\t');
532                 if (bssid == NULL)
533                         break;
534                 *bssid++ = '\0';
535                 flags = strchr(bssid, '\t');
536                 if (flags == NULL)
537                         break;
538                 *flags++ = '\0';
539                 l = strtol(t, NULL, 0);
540                 if (l < 0 || l > INT_MAX) {
541                         errno = ERANGE;
542                         break;
543                 }
544         
545                 /* Decode the wpa_supplicant SSID into raw chars and
546                  * then encode into our octal escaped string to
547                  * compare. */
548                 dl = dhcpcd_wpa_decode_ssid(dssid, sizeof(dssid), ssid);
549                 if (dl == -1)
550                         return -1;
551                 tl = dhcpcd_encode_string_escape(tssid,
552                     sizeof(tssid), dssid, (size_t)dl);
553                 if (tl == -1)
554                         return -1;
555                 if ((size_t)tl == fl && memcmp(tssid, fssid, (size_t)tl) == 0)
556                         return (int)l;
557         }
558         errno = ENOENT;
559         return -1;
560 }
561
562 static int
563 dhcpcd_wpa_network_new(DHCPCD_WPA *wpa)
564 {
565         ssize_t bytes;
566         long l;
567
568         dhcpcd_realloc(wpa->con, 32);
569         bytes = wpa_cmd(wpa->command_fd, "ADD_NETWORK",
570             wpa->con->buf, sizeof(wpa->con->buf));
571         if (bytes == 0 || bytes == -1)
572                 return -1;
573         l = strtol(wpa->con->buf, NULL, 0);
574         if (l < 0 || l > INT_MAX) {
575                 errno = ERANGE;
576                 return -1;
577         }
578         return (int)l;
579 }
580
581 static const char hexstr[] = "0123456789abcdef";
582 int
583 dhcpcd_wpa_network_find_new(DHCPCD_WPA *wpa, const char *ssid)
584 {
585         int id;
586         char dssid[IF_SSIDSIZE], essid[IF_SSIDSIZE], *ep;
587         ssize_t dl, i;
588
589         id = dhcpcd_wpa_network_find(wpa, ssid);
590         if (id != -1)
591                 return id;
592
593         dl = dhcpcd_decode_string_escape(dssid, sizeof(dssid), ssid);
594         if (dl == -1)
595                 return -1;
596
597         for (i = 0; i < dl; i++) {
598                 if (!isascii(dssid[i]) && !isprint(dssid[i]))
599                         break;
600         }
601         ep = essid;
602         if (i < dl) {
603                 /* Non standard characters found! Encode as hex string */
604                 char *dp;
605                 unsigned char c;
606
607                 dp = dssid;
608                 for (; dl; dl--) {
609                         c = (unsigned char)*dp++;
610                         *ep++ = hexstr[c >> 4];
611                         *ep++ = hexstr[c & 0xf];
612                 }
613         } else {
614                 *ep++ = '\"';
615                 ep = stpcpy(ep, dssid);
616                 *ep++ = '\"';
617         }
618         *ep = '\0';
619
620         id = dhcpcd_wpa_network_new(wpa);
621         if (id != -1)
622                 dhcpcd_wpa_network_set(wpa, id, "ssid", essid);
623         return id;
624 }
625
626 void
627 dhcpcd_wpa_close(DHCPCD_WPA *wpa)
628 {
629
630         assert(wpa);
631
632         if (wpa->command_fd == -1 || !wpa->open)
633                 return;
634
635         wpa->open = false;
636         dhcpcd_attach_detach(wpa, false);
637         shutdown(wpa->command_fd, SHUT_RDWR);
638         shutdown(wpa->listen_fd, SHUT_RDWR);
639
640         if (wpa->con->wpa_status_cb)
641                 wpa->con->wpa_status_cb(wpa, "down",
642                     wpa->con->wpa_status_context);
643
644         close(wpa->command_fd);
645         wpa->command_fd = -1;
646         close(wpa->listen_fd);
647         wpa->listen_fd = -1;
648         unlink(wpa->command_path);
649         free(wpa->command_path);
650         wpa->command_path = NULL;
651         unlink(wpa->listen_path);
652         free(wpa->listen_path);
653         wpa->listen_path = NULL;
654 }
655
656 DHCPCD_WPA *
657 dhcpcd_wpa_find(DHCPCD_CONNECTION *con, const char *ifname)
658 {
659         DHCPCD_WPA *wpa;
660
661         for (wpa = con->wpa; wpa; wpa = wpa->next) {
662                 if (strcmp(wpa->ifname, ifname) == 0)
663                         return wpa;
664         }
665         errno = ENOENT;
666         return NULL;
667 }
668
669 DHCPCD_WPA *
670 dhcpcd_wpa_new(DHCPCD_CONNECTION *con, const char *ifname)
671 {
672         DHCPCD_WPA *wpa;
673
674         wpa = dhcpcd_wpa_find(con, ifname);
675         if (wpa)
676                 return wpa;
677
678         wpa = malloc(sizeof(*wpa));
679         if (wpa == NULL)
680                 return NULL;
681
682         wpa->con = con;
683         strlcpy(wpa->ifname, ifname, sizeof(wpa->ifname));
684         wpa->command_fd = wpa->listen_fd = -1;
685         wpa->command_path = wpa->listen_path = NULL;
686         wpa->next = con->wpa;
687         con->wpa = wpa;
688         return wpa;
689 }
690
691 DHCPCD_CONNECTION *
692 dhcpcd_wpa_connection(DHCPCD_WPA *wpa)
693 {
694
695         assert(wpa);
696         return wpa->con;
697 }
698
699 DHCPCD_IF *
700 dhcpcd_wpa_if(DHCPCD_WPA *wpa)
701 {
702
703         return dhcpcd_get_if(wpa->con, wpa->ifname, "link");
704 }
705
706 int
707 dhcpcd_wpa_open(DHCPCD_WPA *wpa)
708 {
709         int cmd_fd, list_fd = -1;
710         char *cmd_path = NULL, *list_path = NULL;
711
712         if (wpa->listen_fd != -1) {
713                 if (!wpa->open) {
714                         errno = EISCONN;
715                         return -1;
716                 }
717                 return wpa->listen_fd;
718         }
719
720         cmd_fd = wpa_open(wpa->ifname, &cmd_path);
721         if (cmd_fd == -1)
722                 goto fail;
723
724         list_fd = wpa_open(wpa->ifname, &list_path);
725         if (list_fd == -1)
726                 goto fail;
727
728         wpa->open = true;
729         wpa->attached = false;
730         wpa->command_fd = cmd_fd;
731         wpa->command_path = cmd_path;
732         wpa->listen_fd = list_fd;
733         wpa->listen_path = list_path;
734         if (!dhcpcd_attach_detach(wpa, true)) {
735                 dhcpcd_wpa_close(wpa);
736                 return -1;
737         }
738
739         if (wpa->con->wi_scanresults_cb)
740                 wpa->con->wi_scanresults_cb(wpa,
741                     wpa->con->wi_scanresults_context);
742
743         return wpa->listen_fd;
744
745 fail:
746         if (cmd_fd != -1)
747                 close(cmd_fd);
748         if (list_fd != -1)
749                 close(list_fd);
750         if (cmd_path)
751                 unlink(cmd_path);
752         free(cmd_path);
753         if (list_path)
754                 free(list_path);
755         return -1;
756 }
757
758 int
759 dhcpcd_wpa_get_fd(DHCPCD_WPA *wpa)
760 {
761
762         assert(wpa);
763         return wpa->open ? wpa->listen_fd : -1;
764 }
765
766 void
767 dhcpcd_wpa_set_scan_callback(DHCPCD_CONNECTION *con,
768     void (*cb)(DHCPCD_WPA *, void *), void *context)
769 {
770
771         assert(con);
772         con->wi_scanresults_cb = cb;
773         con->wi_scanresults_context = context;
774 }
775
776
777 void dhcpcd_wpa_set_status_callback(DHCPCD_CONNECTION * con,
778     void (*cb)(DHCPCD_WPA *, const char *, void *), void *context)
779 {
780
781         assert(con);
782         con->wpa_status_cb = cb;
783         con->wpa_status_context = context;
784 }
785
786 void
787 dhcpcd_wpa_dispatch(DHCPCD_WPA *wpa)
788 {
789         char buffer[256], *p;
790         size_t bytes;
791
792         assert(wpa);
793         bytes = (size_t)read(wpa->listen_fd, buffer, sizeof(buffer));
794         if ((ssize_t)bytes == -1 || bytes == 0) {
795                 dhcpcd_wpa_close(wpa);
796                 return;
797         }
798
799         buffer[bytes] = '\0';
800         bytes = strlen(buffer);
801         if (buffer[bytes - 1] == ' ')
802                 buffer[--bytes] = '\0';
803         for (p = buffer + 1; *p != '\0'; p++)
804                 if (*p == '>') {
805                         p++;
806                         break;
807                 }
808         if (strcmp(p, "CTRL-EVENT-SCAN-RESULTS") == 0 &&
809             wpa->con->wi_scanresults_cb)
810                 wpa->con->wi_scanresults_cb(wpa,
811                     wpa->con->wi_scanresults_context);
812         return;
813 }
814
815 void
816 dhcpcd_wpa_if_event(DHCPCD_IF *i)
817 {
818         DHCPCD_WPA *wpa;
819
820         assert(i);
821         if (strcmp(i->type, "link") == 0) {
822                 if (strcmp(i->reason, "STOPPED") == 0 ||
823                     strcmp(i->reason, "DEPARTED") == 0)
824                 {
825                         wpa = dhcpcd_wpa_find(i->con, i->ifname);
826                         if (wpa)
827                                 dhcpcd_wpa_close(wpa);
828                 } else if (i->wireless && i->con->wpa_started) {
829                         wpa = dhcpcd_wpa_new(i->con, i->ifname);
830                         if (wpa && wpa->listen_fd == -1)
831                                 dhcpcd_wpa_open(wpa);
832                 }
833         }
834 }
835
836 void
837 dhcpcd_wpa_start(DHCPCD_CONNECTION *con)
838 {
839         DHCPCD_IF *i;
840
841         assert(con);
842         con->wpa_started = true;
843
844         for (i = con->interfaces; i; i = i->next)
845                 dhcpcd_wpa_if_event(i);
846 }
847
848 int
849 dhcpcd_wpa_configure_psk(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s, const char *psk)
850 {
851         const char *mgmt, *var;
852         int id;
853         char *npsk;
854         size_t psk_len;
855         bool r;
856
857         assert(wpa);
858         assert(s);
859
860         id = dhcpcd_wpa_network_find_new(wpa, s->ssid);
861         if (id == -1)
862                 return DHCPCD_WPA_ERR;
863
864         if (strcmp(s->flags, "[WEP]") == 0) {
865                 mgmt = "NONE";
866                 var = "wep_key0";
867         } else {
868                 mgmt = "WPA-PSK";
869                 var = "psk";
870         }
871
872         if (!dhcpcd_wpa_network_set(wpa, id, "key_mgmt", mgmt))
873                 return DHCPCD_WPA_ERR_SET;
874
875         if (psk)
876                 psk_len = strlen(psk);
877         else
878                 psk_len = 0;
879         npsk = malloc(psk_len + 3);
880         if (npsk == NULL)
881                 return DHCPCD_WPA_ERR;
882         npsk[0] = '"';
883         if (psk_len)
884                 memcpy(npsk + 1, psk, psk_len);
885         npsk[psk_len + 1] = '"';
886         npsk[psk_len + 2] = '\0';
887         r = dhcpcd_wpa_network_set(wpa, id, var, npsk);
888         free(npsk);
889         if (!r)
890                 return DHCPCD_WPA_ERR_SET_PSK;
891
892         if (!dhcpcd_wpa_network_enable(wpa, id))
893                 return DHCPCD_WPA_ERR_ENABLE;
894         if (!dhcpcd_wpa_reassociate(wpa))
895                 return DHCPCD_WPA_ERR_ASSOC;
896         if (!dhcpcd_wpa_config_write(wpa))
897                 return DHCPCD_WPA_ERR_WRITE;
898         return DHCPCD_WPA_SUCCESS;
899 }