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