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