Bring over configure imporvements from dhcpcd.
[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 <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #define IN_LIBDHCPCD
33 #include "libdhcpcd.h"
34 #include "config.h"
35
36 #define HIST_MAX 10 /* Max history per ssid/bssid */
37
38 void
39 dhcpcd_wi_history_clear(DHCPCD_CONNECTION *con)
40 {
41         DHCPCD_WI_HIST *h;
42
43         while (con->wi_history) {
44                 h = con->wi_history->next;
45                 free(con->wi_history);
46                 con->wi_history = h;
47         }
48 }
49
50 void
51 dhcpcd_wi_scans_free(DHCPCD_WI_SCAN *wis)
52 {
53         DHCPCD_WI_SCAN *n;
54
55         while (wis) {
56                 n = wis->next;
57                 free(wis);
58                 wis = n;
59         }
60 }
61
62 static DHCPCD_WI_SCAN *
63 dhcpcd_scanresult_new(DHCPCD_CONNECTION *con, DBusMessageIter *array)
64 {
65         DBusMessageIter dict, entry, var;
66         DHCPCD_WI_SCAN *wis;
67         char *s;
68         int32_t i32;
69         int errors;
70
71         wis = calloc(1, sizeof(*wis));
72         if (wis == NULL) {
73                 dhcpcd_error_set(con, NULL, errno);
74                 return NULL;
75         }
76         errors = con->errors;
77         dbus_message_iter_recurse(array, &dict);
78         for (;
79              dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY;
80              dbus_message_iter_next(&dict))
81         {
82                 dbus_message_iter_recurse(&dict, &entry);
83                 if (!dhcpcd_iter_get(con, &entry, DBUS_TYPE_STRING, &s))
84                     break;
85                 if (dbus_message_iter_get_arg_type(&entry) !=
86                     DBUS_TYPE_VARIANT)
87                         break;
88                 dbus_message_iter_recurse(&entry, &var);
89                 if (strcmp(s, "BSSID") == 0) {
90                         if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_STRING, &s))
91                                 break;
92                         strlcpy(wis->bssid, s, sizeof(wis->bssid));
93                 } else if (strcmp(s, "Frequency") == 0) {
94                         if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_INT32, &i32))
95                                 break;
96                         wis->frequency = i32;
97                 } else if (strcmp(s, "Quality") == 0) {
98                         if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_INT32, &i32))
99                                 break;
100                         wis->quality.value = i32;
101                 } else if (strcmp(s, "Noise") == 0) {
102                         if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_INT32, &i32))
103                                 break;
104                         wis->noise.value = i32;
105                 } else if (strcmp(s, "Level") == 0) {
106                         if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_INT32, &i32))
107                                 break;
108                         wis->level.value = i32;
109                 } else if (strcmp(s, "Flags") == 0) {
110                         if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_STRING, &s))
111                                 break;
112                         strlcpy(wis->flags, s, sizeof(wis->flags));
113                 } else if (strcmp(s, "SSID") == 0) {
114                         if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_STRING, &s))
115                                 break;
116                         strlcpy(wis->ssid, s, sizeof(wis->ssid));
117                 }
118         }
119         if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
120                 if (con->errors == errors)
121                         dhcpcd_error_set(con, NULL, EINVAL);
122                 free(wis);
123                 return NULL;
124         }
125         return wis;
126 }
127
128 DHCPCD_WI_SCAN *
129 dhcpcd_wi_scans(DHCPCD_CONNECTION *con, DHCPCD_IF *i)
130 {
131         DBusMessage *msg;
132         DBusMessageIter args, array;
133         DHCPCD_WI_SCAN *wis, *scans, *l;
134         DHCPCD_WI_HIST *h, *hl;
135         int errors, nh;
136
137         msg = dhcpcd_message_reply(con, "ScanResults", i->ifname);
138         if (msg == NULL)
139                 return NULL;
140         if (!dbus_message_iter_init(msg, &args) ||
141             dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
142         {
143                 dhcpcd_error_set(con, NULL, EINVAL);
144                 return NULL;
145         }
146
147         scans = l = NULL;
148         errors = con->errors;
149         dbus_message_iter_recurse(&args, &array);
150         for(;
151             dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_ARRAY;
152             dbus_message_iter_next(&array))
153         {
154                 wis = dhcpcd_scanresult_new(con, &array);
155                 if (wis == NULL)
156                         break;
157                 nh = 1;
158                 hl = NULL;
159                 wis->quality.average = wis->quality.value;
160                 wis->noise.average = wis->noise.value;
161                 wis->level.average = wis->level.value;
162                 for (h = con->wi_history; h; h = h->next) {
163                         if (strcmp(h->ifname, i->ifname) == 0 &&
164                             strcmp(h->bssid, wis->bssid) == 0)
165                         {
166                                 wis->quality.average += h->quality;
167                                 wis->noise.average += h->noise;
168                                 wis->level.average += h->level;
169                                 if (++nh == HIST_MAX) {
170                                         hl->next = h->next;
171                                         free(h);
172                                         break;
173                                 }
174                         }
175                         hl = h;
176                 }
177                 if (nh != 1) {
178                         wis->quality.average /= nh;
179                         wis->noise.average /= nh;
180                         wis->level.average /= nh;
181                 }
182                 h = malloc(sizeof(*h));
183                 if (h) {
184                         strlcpy(h->ifname, i->ifname, sizeof(h->ifname));
185                         strlcpy(h->bssid, wis->bssid, sizeof(h->bssid));
186                         h->quality = wis->quality.value;
187                         h->noise = wis->noise.value;
188                         h->level = wis->level.value;
189                         h->next = con->wi_history;
190                         con->wi_history = h;
191                 }
192                 if (l == NULL)
193                         scans = l = wis;
194                 else {
195                         l->next = wis;
196                         l = l->next;
197                 }
198         }
199         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
200                 if (con->errors == errors)
201                         dhcpcd_error_set(con, NULL, EINVAL);
202                 dhcpcd_wi_scans_free(scans);
203                 scans = NULL;
204         }
205         dbus_message_unref(msg);
206         return scans;
207 }
208
209 static int
210 dhcpcd_wpa_find_network(DHCPCD_CONNECTION *con, DHCPCD_IF *i, const char *ssid)
211 {
212         DBusMessage *msg;
213         DBusMessageIter args, array, entry;
214         int32_t id;
215         char *s;
216         int errors;
217
218         msg = dhcpcd_message_reply(con, "ListNetworks", i->ifname);
219         if (msg == NULL)
220                 return -1;
221         if (!dbus_message_iter_init(msg, &args)) {
222                 dhcpcd_error_set(con, NULL, EINVAL);
223                 return -1;
224         }
225
226         errors = con->errors;
227         for(;
228             dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_ARRAY;
229             dbus_message_iter_next(&args))
230         {
231                 dbus_message_iter_recurse(&args, &array);
232                 for(;
233                     dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT;
234                     dbus_message_iter_next(&array))
235                 {
236                         dbus_message_iter_recurse(&array, &entry);
237                         if (!dhcpcd_iter_get(con, &entry,
238                                 DBUS_TYPE_INT32, &id) ||
239                             !dhcpcd_iter_get(con, &entry,
240                                 DBUS_TYPE_STRING, &s))
241                                 break;
242                         if (strcmp(s, ssid) == 0) {
243                                 dbus_message_unref(msg);
244                                 return (int)id;
245                         }
246                 }
247         }
248         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_INVALID &&
249             con->errors == errors)
250                 dhcpcd_error_set(con, NULL, EINVAL);
251         dbus_message_unref(msg);
252         return -1;
253 }
254
255 static int
256 dhcpcd_wpa_add_network(DHCPCD_CONNECTION *con, DHCPCD_IF *i)
257 {
258         DBusMessage *msg;
259         DBusMessageIter args;
260         int32_t id;
261         int ret;
262
263         msg = dhcpcd_message_reply(con, "AddNetwork", i->ifname);
264         if (msg == NULL)
265                 return -1;
266         ret = -1;
267         if (dbus_message_iter_init(msg, &args)) {
268                 if (dhcpcd_iter_get(con, &args, DBUS_TYPE_INT32, &id))
269                         ret = id;
270         } else
271                 dhcpcd_error_set(con, NULL, EINVAL);
272         dbus_message_unref(msg);
273         return ret;
274 }
275
276 bool
277 dhcpcd_wpa_set_network(DHCPCD_CONNECTION *con, DHCPCD_IF *i, int id,
278     const char *opt, const char *val)
279 {
280         DBusMessage *msg, *reply;
281         DBusMessageIter args;
282         bool retval;
283         char *ifname;
284
285         msg = dbus_message_new_method_call(DHCPCD_SERVICE, DHCPCD_PATH,
286             DHCPCD_SERVICE, "SetNetwork");
287         if (msg == NULL) {
288                 dhcpcd_error_set(con, 0, errno);
289                 return false;
290         }
291         dbus_message_iter_init_append(msg, &args);
292         ifname = i->ifname;
293         dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &ifname);
294         dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &id);
295         dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &opt);
296         dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &val);
297         reply = dhcpcd_send_reply(con, msg);
298         dbus_message_unref(msg);
299         if (reply == NULL)
300                 retval = false;
301         else {
302                 dbus_message_unref(reply);
303                 retval = true;
304         }
305         return retval;
306 }
307
308 int
309 dhcpcd_wpa_find_network_new(DHCPCD_CONNECTION *con, DHCPCD_IF *i,
310     const char *ssid)
311 {
312         int errors, id;
313         char *q;
314         size_t len;
315         bool retval;
316
317         len = strlen(ssid) + 3;
318         q = malloc(len);
319         if (q == NULL) {
320                 dhcpcd_error_set(con, 0, errno);
321                 return -1;
322         }
323         errors = con->errors;
324         id = dhcpcd_wpa_find_network(con, i, ssid);
325         if (id != -1 || con->errors != errors) {
326                 free(q);
327                 return id;
328         }
329         id = dhcpcd_wpa_add_network(con, i);
330         if (id == -1) {
331                 free(q);
332                 return -1;
333         }
334         snprintf(q, len, "\"%s\"", ssid);
335         retval = dhcpcd_wpa_set_network(con, i, id, "ssid", q);
336         free(q);
337         return retval;
338 }
339
340 bool
341 dhcpcd_wpa_command(DHCPCD_CONNECTION *con, DHCPCD_IF *i,
342     const char *cmd, int id)
343 {
344         DBusMessage *msg, *reply;
345         DBusMessageIter args;
346         char *ifname;
347         bool retval;
348
349         msg = dbus_message_new_method_call(DHCPCD_SERVICE, DHCPCD_PATH,
350             DHCPCD_SERVICE, cmd);
351         if (msg == NULL) {
352                 dhcpcd_error_set(con, 0, errno);
353                 return false;
354         }
355         dbus_message_iter_init_append(msg, &args);
356         ifname = i->ifname;
357         dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &ifname);
358         if (id != -1)
359                 dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &id);
360         reply = dhcpcd_send_reply(con, msg);
361         dbus_message_unref(msg);
362         if (reply == NULL)
363                 retval = false;
364         else {
365                 dbus_message_unref(reply);
366                 retval = true;
367         }
368         return retval;
369 }