Add --with-qt and --without-qk options.
[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 static int
50 wpa_open(const char *ifname, char **path)
51 {
52         static int counter;
53         int fd;
54         socklen_t len;
55         struct sockaddr_un sun;
56
57         if ((fd = socket(AF_UNIX,
58             SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1)
59                 return -1;
60         memset(&sun, 0, sizeof(sun));
61         sun.sun_family = AF_UNIX;
62         snprintf(sun.sun_path, sizeof(sun.sun_path),
63             "/tmp/libdhcpcd-wpa-%d.%d", getpid(), counter++);
64         *path = strdup(sun.sun_path);
65         len = (socklen_t)SUN_LEN(&sun);
66         if (bind(fd, (struct sockaddr *)&sun, len) == -1) {
67                 close(fd);
68                 unlink(*path);
69                 free(*path);
70                 *path = NULL;
71                 return -1;
72         }
73         snprintf(sun.sun_path, sizeof(sun.sun_path),
74             WPA_CTRL_DIR "/%s", ifname);
75         len = (socklen_t)SUN_LEN(&sun);
76         if (connect(fd, (struct sockaddr *)&sun, len) == -1) {
77                 close(fd);
78                 unlink(*path);
79                 free(*path);
80                 *path = NULL;
81                 return -1;
82         }
83
84         return fd;
85 }
86
87 static ssize_t
88 wpa_cmd(int fd, const char *cmd, char *buffer, size_t len)
89 {
90         int retval;
91         ssize_t bytes;
92         struct pollfd pfd;
93
94         if (buffer)
95                 *buffer = '\0';
96         bytes = write(fd, cmd, strlen(cmd));
97         if (bytes == -1 || bytes == 0)
98                 return -1;
99         if (buffer == NULL || len == 0)
100                 return 0;
101         pfd.fd = fd;
102         pfd.events = POLLIN | POLLHUP;
103         pfd.revents = 0;
104         retval = poll(&pfd, 1, 2000);
105         if (retval == -1)
106                 return -1;
107         if (retval == 0 || !(pfd.revents & (POLLIN | POLLHUP)))
108                 return -1;
109
110         bytes = read(fd, buffer, len == 1 ? 1 : len - 1);
111         if (bytes != -1)
112                 buffer[bytes] = '\0';
113         return bytes;
114 }
115
116 bool
117 dhcpcd_wpa_command(DHCPCD_WPA *wpa, const char *cmd)
118 {
119         char buf[10];
120         ssize_t bytes;
121
122         bytes = wpa_cmd(wpa->command_fd, cmd, buf, sizeof(buf));
123         return (bytes == -1 || bytes == 0 ||
124             strcmp(buf, "OK\n")) ? false : true;
125 }
126
127 bool
128 dhcpcd_wpa_command_arg(DHCPCD_WPA *wpa, const char *cmd, const char *arg)
129 {
130         size_t cmdlen, nlen;
131
132         cmdlen = strlen(cmd);
133         nlen = cmdlen + strlen(arg) + 2;
134         if (!dhcpcd_realloc(wpa->con, nlen))
135                 return -1;
136         strlcpy(wpa->con->buf, cmd, wpa->con->buflen);
137         wpa->con->buf[cmdlen] = ' ';
138         strlcpy(wpa->con->buf + cmdlen + 1, arg, wpa->con->buflen - 1 - cmdlen);
139         return dhcpcd_wpa_command(wpa, wpa->con->buf);
140 }
141
142 static bool
143 dhcpcd_attach_detach(DHCPCD_WPA *wpa, int attach)
144 {
145         char buf[10];
146         ssize_t bytes;
147
148         if (wpa->attached == attach)
149                 return true;
150
151         bytes = wpa_cmd(wpa->listen_fd, attach > 0 ? "ATTACH" : "DETACH",
152             buf, sizeof(buf));
153         if (bytes == -1 || bytes == 0 || strcmp(buf, "OK\n"))
154                 return false;
155
156         wpa->attached = attach;
157         return true;
158 }
159
160 bool
161 dhcpcd_wpa_scan(DHCPCD_WPA *wpa)
162 {
163
164         return dhcpcd_wpa_command(wpa, "SCAN");
165 }
166
167 void
168 dhcpcd_wi_scans_free(DHCPCD_WI_SCAN *wis)
169 {
170         DHCPCD_WI_SCAN *n;
171
172         while (wis) {
173                 n = wis->next;
174                 free(wis);
175                 wis = n;
176         }
177 }
178
179 static void
180 dhcpcd_strtoi(int *val, const char *s)
181 {
182         long l;
183
184         l = strtol(s, NULL, 0);
185         if (l >= INT_MIN && l <= INT_MAX)
186                 *val = (int)l;
187         else
188                 errno = ERANGE;
189 }
190
191 static DHCPCD_WI_SCAN *
192 dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
193 {
194         size_t i;
195         ssize_t bytes;
196         DHCPCD_WI_SCAN *wis, *w, *l;
197         char *s, *p, buf[32];
198
199         if (!dhcpcd_realloc(wpa->con, 2048))
200                 return NULL;
201         wis = NULL;
202         for (i = 0; i < 1000; i++) {
203                 snprintf(buf, sizeof(buf), "BSS %zu", i);
204                 bytes = wpa_cmd(wpa->command_fd, buf,
205                     wpa->con->buf, wpa->con->buflen);
206                 if (bytes == 0 || bytes == -1 ||
207                     strncmp(wpa->con->buf, "FAIL", 4) == 0)
208                         break;
209                 p = wpa->con->buf;
210                 w = calloc(1, sizeof(*w));
211                 if (w == NULL)
212                         break;
213                 if (wis == NULL)
214                         wis = w;
215                 else
216                         l->next = w;
217                 l = w;
218                 while ((s = strsep(&p, "\n"))) {
219                         if (*s == '\0')
220                                 continue;
221                         if (strncmp(s, "bssid=", 6) == 0)
222                                 strlcpy(w->bssid, s + 6, sizeof(w->bssid));
223                         else if (strncmp(s, "freq=", 5) == 0)
224                                 dhcpcd_strtoi(&w->frequency, s + 5);
225 //                      else if (strncmp(s, "beacon_int=", 11) == 0)
226 //                              ;
227                         else if (strncmp(s, "qual=", 5) == 0)
228                                 dhcpcd_strtoi(&w->quality.value, s + 5);
229                         else if (strncmp(s, "noise=", 6) == 0)
230                                 dhcpcd_strtoi(&w->noise.value, s + 6);
231                         else if (strncmp(s, "level=", 6) == 0)
232                                 dhcpcd_strtoi(&w->level.value, s + 6);
233                         else if (strncmp(s, "flags=", 6) == 0)
234                                 strlcpy(w->flags, s + 6, sizeof(w->flags));
235                         else if (strncmp(s, "ssid=", 5) == 0)
236                                 strlcpy(w->ssid, s + 5, sizeof(w->ssid));
237                 }
238         }
239         return wis;
240 }
241
242 DHCPCD_WI_SCAN *
243 dhcpcd_wi_scans(DHCPCD_IF *i)
244 {
245         DHCPCD_WPA *wpa;
246         DHCPCD_WI_SCAN *wis, *w;
247         int nh;
248         DHCPCD_WI_HIST *h, *hl;
249
250         wpa = dhcpcd_wpa_find(i->con, i->ifname);
251         if (wpa == NULL)
252                 return NULL;
253         wis = dhcpcd_wpa_scans_read(wpa);
254         for (w = wis; w; w = w->next) {
255                 nh = 1;
256                 hl = NULL;
257                 w->quality.average = w->quality.value;
258                 w->noise.average = w->noise.value;
259                 w->level.average = w->level.value;
260
261                 for (h = wpa->con->wi_history; h; h = h->next) {
262                         if (strcmp(h->ifname, i->ifname) == 0 &&
263                             strcmp(h->bssid, wis->bssid) == 0)
264                         {
265                                 w->quality.average += h->quality;
266                                 w->noise.average += h->noise;
267                                 w->level.average += h->level;
268                                 if (++nh == DHCPCD_WI_HIST_MAX) {
269                                         hl->next = h->next;
270                                         free(h);
271                                         break;
272                                 }
273                         }
274                         hl = h;
275                 }
276
277                 if (nh != 1) {
278                         w->quality.average /= nh;
279                         w->noise.average /= nh;
280                         w->level.average /= nh;
281                 }
282                 h = malloc(sizeof(*h));
283                 if (h) {
284                         strlcpy(h->ifname, i->ifname, sizeof(h->ifname));
285                         strlcpy(h->bssid, w->bssid, sizeof(h->bssid));
286                         h->quality = w->quality.value;
287                         h->noise = w->noise.value;
288                         h->level = w->level.value;
289                         h->next = wpa->con->wi_history;
290                         wpa->con->wi_history = h;
291                 }
292         }
293
294         return wis;
295 }
296
297 bool
298 dhcpcd_wpa_reassociate(DHCPCD_WPA *wpa)
299 {
300
301         return dhcpcd_wpa_command(wpa, "REASSOCIATE");
302 }
303
304 bool
305 dhcpcd_wpa_disconnect(DHCPCD_WPA *wpa)
306 {
307
308         return dhcpcd_wpa_command(wpa, "DISCONNECT");
309 }
310
311 bool
312 dhcpcd_wpa_config_write(DHCPCD_WPA *wpa)
313 {
314
315         return dhcpcd_wpa_command(wpa, "SAVE_CONFIG");
316 }
317
318 static bool
319 dhcpcd_wpa_network(DHCPCD_WPA *wpa, const char *cmd, int id)
320 {
321         size_t len;
322
323         len = strlen(cmd) + 32;
324         if (!dhcpcd_realloc(wpa->con, len))
325                 return false;
326         snprintf(wpa->con->buf, wpa->con->buflen, "%s %d", cmd, id);
327         return dhcpcd_wpa_command(wpa, wpa->con->buf);
328 }
329
330 bool
331 dhcpcd_wpa_network_disable(DHCPCD_WPA *wpa, int id)
332 {
333
334         return dhcpcd_wpa_network(wpa, "DISABLE_NETWORK", id);
335 }
336
337 bool
338 dhcpcd_wpa_network_enable(DHCPCD_WPA *wpa, int id)
339 {
340
341         return dhcpcd_wpa_network(wpa, "ENABLE_NETWORK", id);
342 }
343
344 bool
345 dhcpcd_wpa_network_remove(DHCPCD_WPA *wpa, int id)
346 {
347
348         return dhcpcd_wpa_network(wpa, "REMOVE_NETWORK", id);
349 }
350
351 char *
352 dhcpcd_wpa_network_get(DHCPCD_WPA *wpa, int id, const char *param)
353 {
354         ssize_t bytes;
355
356         if (!dhcpcd_realloc(wpa->con, 2048))
357                 return NULL;
358         snprintf(wpa->con->buf, wpa->con->buflen, "GET_NETWORK %d %s",
359             id, param);
360         bytes = wpa_cmd(wpa->command_fd, wpa->con->buf,
361             wpa->con->buf, wpa->con->buflen);
362         if (bytes == 0 || bytes == -1)
363                 return NULL;
364         if (strcmp(wpa->con->buf, "FAIL\n") == 0) {
365                 errno = EINVAL;
366                 return NULL;
367         }
368         return wpa->con->buf;
369 }
370
371 bool
372 dhcpcd_wpa_network_set(DHCPCD_WPA *wpa, int id,
373     const char *param, const char *value)
374 {
375         size_t len;
376
377         len = strlen("SET_NETWORK") + 32 + strlen(param) + strlen(value) + 3;
378         if (!dhcpcd_realloc(wpa->con, len))
379                 return false;
380         snprintf(wpa->con->buf, wpa->con->buflen, "SET_NETWORK %d %s %s",
381             id, param, value);
382         return dhcpcd_wpa_command(wpa, wpa->con->buf);
383 }
384
385 static int
386 dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
387 {
388         ssize_t bytes;
389         char *s, *t, *ssid, *bssid, *flags;
390         long l;
391
392         dhcpcd_realloc(wpa->con, 2048);
393         bytes = wpa_cmd(wpa->command_fd, "LIST_NETWORKS",
394             wpa->con->buf, wpa->con->buflen);
395         if (bytes == 0 || bytes == -1)
396                 return -1;
397
398         s = strchr(wpa->con->buf, '\n');
399         if (s == NULL)
400                 return -1;
401         while ((t = strsep(&s, "\b"))) {
402                 if (*t == '\0')
403                         continue;
404                 ssid = strchr(t, '\t');
405                 if (ssid == NULL)
406                         break;
407                 *ssid++ = '\0';
408                 bssid = strchr(ssid, '\t');
409                 if (bssid == NULL)
410                         break;
411                 *bssid++ = '\0';
412                 flags = strchr(bssid, '\t');
413                 if (flags == NULL)
414                         break;
415                 *flags++ = '\0';
416                 l = strtol(t, NULL, 0);
417                 if (l < 0 || l > INT_MAX) {
418                         errno = ERANGE;
419                         break;
420                 }
421                 if (strcmp(ssid, fssid) == 0)
422                         return (int)l;
423         }
424         errno = ENOENT;
425         return -1;
426 }
427
428 static int
429 dhcpcd_wpa_network_new(DHCPCD_WPA *wpa)
430 {
431         ssize_t bytes;
432         long l;
433
434         dhcpcd_realloc(wpa->con, 32);
435         bytes = wpa_cmd(wpa->command_fd, "ADD_NETWORK",
436             wpa->con->buf, sizeof(wpa->con->buf));
437         if (bytes == 0 || bytes == -1)
438                 return -1;
439         l = strtol(wpa->con->buf, NULL, 0);
440         if (l < 0 || l > INT_MAX) {
441                 errno = ERANGE;
442                 return -1;
443         }
444         return (int)l;
445 }
446
447 int
448 dhcpcd_wpa_network_find_new(DHCPCD_WPA *wpa, const char *ssid)
449 {
450         int id;
451
452         id = dhcpcd_wpa_network_find(wpa, ssid);
453         if (id == -1)
454                 id = dhcpcd_wpa_network_new(wpa);
455         return id;
456 }
457
458 void
459 dhcpcd_wpa_close(DHCPCD_WPA *wpa)
460 {
461
462         if (wpa->command_fd == -1)
463                 return;
464
465         dhcpcd_attach_detach(wpa, -1);
466         shutdown(wpa->command_fd, SHUT_RDWR);
467         wpa->command_fd = -1;
468         shutdown(wpa->listen_fd, SHUT_RDWR);
469         wpa->listen_fd = -1;
470         unlink(wpa->command_path);
471         free(wpa->command_path);
472         wpa->command_path = NULL;
473         unlink(wpa->listen_path);
474         free(wpa->listen_path);
475         wpa->listen_path = NULL;
476
477         if (wpa->con->wpa_status_cb)
478                 wpa->con->wpa_status_cb(wpa, "down",
479                     wpa->con->wpa_status_context);
480
481 }
482
483 DHCPCD_WPA *
484 dhcpcd_wpa_find(DHCPCD_CONNECTION *con, const char *ifname)
485 {
486         DHCPCD_WPA *wpa;
487
488         for (wpa = con->wpa; wpa; wpa = wpa->next) {
489                 if (strcmp(wpa->ifname, ifname) == 0)
490                         return wpa;
491         }
492         return NULL;
493 }
494
495 DHCPCD_WPA *
496 dhcpcd_wpa_new(DHCPCD_CONNECTION *con, const char *ifname)
497 {
498         DHCPCD_WPA *wpa;
499
500         wpa = dhcpcd_wpa_find(con, ifname);
501         if (wpa)
502                 return wpa;
503
504         wpa = malloc(sizeof(*wpa));
505         if (wpa == NULL)
506                 return NULL;
507
508         wpa->con = con;
509         strlcpy(wpa->ifname, ifname, sizeof(wpa->ifname));
510         wpa->command_fd = wpa->listen_fd = -1;
511         wpa->command_path = wpa->listen_path = NULL;
512         wpa->next = con->wpa;
513         con->wpa = wpa;
514         return wpa;
515 }
516
517 DHCPCD_CONNECTION *
518 dhcpcd_wpa_connection(DHCPCD_WPA *wpa)
519 {
520
521         return wpa->con;
522 }
523
524 DHCPCD_IF *
525 dhcpcd_wpa_if(DHCPCD_WPA *wpa)
526 {
527
528         return dhcpcd_get_if(wpa->con, wpa->ifname, "link");
529 }
530
531 int
532 dhcpcd_wpa_open(DHCPCD_WPA *wpa)
533 {
534         int cmd_fd, list_fd = -1;
535         char *cmd_path = NULL, *list_path = NULL;
536
537         if (wpa->listen_fd != -1)
538                 return wpa->listen_fd;
539
540         cmd_fd = wpa_open(wpa->ifname, &cmd_path);
541         if (cmd_fd == -1)
542                 goto fail;
543
544         list_fd = wpa_open(wpa->ifname, &list_path);
545         if (list_fd == -1)
546                 goto fail;
547
548         wpa->attached = 0;
549         wpa->command_fd = cmd_fd;
550         wpa->command_path = cmd_path;
551         wpa->listen_fd = list_fd;
552         wpa->listen_path = list_path;
553         if (!dhcpcd_attach_detach(wpa, 1)) {
554                 dhcpcd_wpa_close(wpa);
555                 return -1;
556         }
557
558         if (wpa->con->wi_scanresults_cb)
559                 wpa->con->wi_scanresults_cb(wpa,
560                     wpa->con->wi_scanresults_context);
561
562         return wpa->listen_fd;
563
564 fail:
565         if (cmd_fd != -1)
566                 close(cmd_fd);
567         if (list_fd != -1)
568                 close(list_fd);
569         if (cmd_path)
570                 unlink(cmd_path);
571         free(cmd_path);
572         if (list_path)
573                 free(list_path);
574         return -1;
575 }
576
577 int
578 dhcpcd_wpa_get_fd(DHCPCD_WPA *wpa)
579 {
580
581         return wpa->listen_fd;
582 }
583
584 void
585 dhcpcd_wpa_set_scan_callback(DHCPCD_CONNECTION *con,
586     void (*cb)(DHCPCD_WPA *, void *), void *context)
587 {
588
589         con->wi_scanresults_cb = cb;
590         con->wi_scanresults_context = context;
591 }
592
593 void
594 dhcpcd_wpa_dispatch(DHCPCD_WPA *wpa)
595 {
596         char buffer[256], *p;
597         size_t bytes;
598
599         bytes = (size_t)read(wpa->listen_fd, buffer, sizeof(buffer));
600         if ((ssize_t)bytes == -1 || bytes == 0) {
601                 dhcpcd_wpa_close(wpa);
602                 return;
603         }
604
605         buffer[bytes] = '\0';
606         bytes = strlen(buffer);
607         if (buffer[bytes - 1] == ' ')
608                 buffer[--bytes] = '\0';
609         for (p = buffer + 1; *p != '\0'; p++)
610                 if (*p == '>') {
611                         p++;
612                         break;
613                 }
614         if (strcmp(p, "CTRL-EVENT-SCAN-RESULTS") == 0 &&
615             wpa->con->wi_scanresults_cb)
616                 wpa->con->wi_scanresults_cb(wpa,
617                     wpa->con->wi_scanresults_context);
618         return;
619 }
620
621 void
622 dhcpcd_wpa_if_event(DHCPCD_IF *i)
623 {
624         DHCPCD_WPA *wpa;
625
626         assert(i);
627         if (i->wireless && strcmp(i->type, "link") == 0) {
628                 if (strcmp(i->reason, "STOPPED") == 0) {
629                         wpa = dhcpcd_wpa_find(i->con, i->ifname);
630                         if (wpa)
631                                 dhcpcd_wpa_close(wpa);
632                 } else if (i->up) {
633                         wpa = dhcpcd_wpa_new(i->con, i->ifname);
634                         if (wpa && wpa->listen_fd == -1)
635                                 dhcpcd_wpa_open(wpa);
636                 }
637         }
638 }