BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file net_backend_wpa_supplicant.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 * Wireless interface module which runs wpa_supplicant to connect to a wireless network.
32 *
33 * Note: wpa_supplicant does not monitor the state of rfkill switches and will fail to
34 * start if the switch is of when it is started, and will stop working indefinitely if the
35 * switch is turned off while it is running. Therefore, you should put a "net.backend.rfkill"
36 * statement in front of the wpa_supplicant statement.
37 *
38 * Synopsis: net.backend.wpa_supplicant(string ifname, string conf, string exec, list(string) args)
39 * Variables:
40 * bssid - BSSID of the wireless network we connected to, or "none".
41 * Consists of 6 capital, two-character hexadecimal numbers, separated with colons.
42 * Example: "01:B2:C3:04:E5:F6"
43 * ssid - SSID of the wireless network we connected to. Note that this is after what
44 * wpa_supplicant does to it before it prints it. In particular, it replaces all bytes
45 * outside [32, 126] with underscores.
46 */
47  
48 #include <stdlib.h>
49 #include <string.h>
50 #include <stdio.h>
51 #include <inttypes.h>
52  
53 #include <misc/cmdline.h>
54 #include <misc/string_begins_with.h>
55 #include <misc/stdbuf_cmdline.h>
56 #include <misc/balloc.h>
57 #include <misc/find_program.h>
58 #include <flow/LineBuffer.h>
59 #include <system/BInputProcess.h>
60  
61 #include <ncd/module_common.h>
62  
63 #include <generated/blog_channel_ncd_net_backend_wpa_supplicant.h>
64  
65 #define MAX_LINE_LEN 512
66 #define EVENT_STRING_CONNECTED "CTRL-EVENT-CONNECTED"
67 #define EVENT_STRING_DISCONNECTED "CTRL-EVENT-DISCONNECTED"
68  
69 struct instance {
70 NCDModuleInst *i;
71 MemRef ifname;
72 MemRef conf;
73 MemRef exec;
74 NCDValRef args;
75 int dying;
76 int up;
77 BInputProcess process;
78 int have_pipe;
79 LineBuffer pipe_buffer;
80 PacketPassInterface pipe_input;
81 int have_info;
82 int info_have_bssid;
83 uint8_t info_bssid[6];
84 char *info_ssid;
85 };
86  
87 static int parse_hex_digit (uint8_t d, uint8_t *out);
88 static int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len);
89 static int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len);
90 static int build_cmdline (struct instance *o, CmdLine *c);
91 static int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len);
92 static void free_info (struct instance *o);
93 static void process_error (struct instance *o);
94 static void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status);
95 static void process_handler_closed (struct instance *o, int is_error);
96 static void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len);
97 static void instance_free (struct instance *o, int is_error);
98  
99 int parse_hex_digit (uint8_t d, uint8_t *out)
100 {
101 switch (d) {
102 case '0': *out = 0; return 1;
103 case '1': *out = 1; return 1;
104 case '2': *out = 2; return 1;
105 case '3': *out = 3; return 1;
106 case '4': *out = 4; return 1;
107 case '5': *out = 5; return 1;
108 case '6': *out = 6; return 1;
109 case '7': *out = 7; return 1;
110 case '8': *out = 8; return 1;
111 case '9': *out = 9; return 1;
112 case 'A': case 'a': *out = 10; return 1;
113 case 'B': case 'b': *out = 11; return 1;
114 case 'C': case 'c': *out = 12; return 1;
115 case 'D': case 'd': *out = 13; return 1;
116 case 'E': case 'e': *out = 14; return 1;
117 case 'F': case 'f': *out = 15; return 1;
118 }
119  
120 return 0;
121 }
122  
123 int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len)
124 {
125 // Trying to associate with AB:CD:EF:01:23:45 (SSID='Some SSID' freq=2462 MHz)
126  
127 int p;
128 if (!(p = data_begins_with((char *)data, data_len, "Trying to associate with "))) {
129 return 0;
130 }
131 data += p;
132 data_len -= p;
133  
134 for (int i = 0; i < 6; i++) {
135 uint8_t d1;
136 uint8_t d2;
137 if (data_len < 2 || !parse_hex_digit(data[0], &d1) || !parse_hex_digit(data[1], &d2)) {
138 return 0;
139 }
140 data += 2;
141 data_len -= 2;
142 out_bssid[i] = ((d1 << 4) | d2);
143  
144 if (i != 5) {
145 if (data_len < 1 || data[0] != ':') {
146 return 0;
147 }
148 data += 1;
149 data_len -= 1;
150 }
151 }
152  
153 if (!(p = data_begins_with((char *)data, data_len, " (SSID='"))) {
154 return 0;
155 }
156 data += p;
157 data_len -= p;
158  
159 // find last '
160 uint8_t *q = NULL;
161 for (int i = data_len; i > 0; i--) {
162 if (data[i - 1] == '\'') {
163 q = &data[i - 1];
164 break;
165 }
166 }
167 if (!q) {
168 return 0;
169 }
170  
171 *out_ssid = data;
172 *out_ssid_len = q - data;
173  
174 return 1;
175 }
176  
177 int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len)
178 {
179 // Trying to associate with SSID 'Some SSID'
180  
181 int p;
182 if (!(p = data_begins_with((char *)data, data_len, "Trying to associate with SSID '"))) {
183 return 0;
184 }
185 data += p;
186 data_len -= p;
187  
188 // find last '
189 uint8_t *q = NULL;
190 for (int i = data_len; i > 0; i--) {
191 if (data[i - 1] == '\'') {
192 q = &data[i - 1];
193 break;
194 }
195 }
196 if (!q) {
197 return 0;
198 }
199  
200 *out_ssid = data;
201 *out_ssid_len = q - data;
202  
203 return 1;
204 }
205  
206 int build_cmdline (struct instance *o, CmdLine *c)
207 {
208 // init cmdline
209 if (!CmdLine_Init(c)) {
210 goto fail0;
211 }
212  
213 // find stdbuf executable
214 char *stdbuf_exec = badvpn_find_program("stdbuf");
215 if (!stdbuf_exec) {
216 ModuleLog(o->i, BLOG_ERROR, "cannot find stdbuf executable");
217 goto fail1;
218 }
219  
220 // append stdbuf part
221 int res = build_stdbuf_cmdline(c, stdbuf_exec, o->exec.ptr, o->exec.len);
222 free(stdbuf_exec);
223 if (!res) {
224 goto fail1;
225 }
226  
227 // append user arguments
228 size_t count = NCDVal_ListCount(o->args);
229 for (size_t j = 0; j < count; j++) {
230 NCDValRef arg = NCDVal_ListGet(o->args, j);
231  
232 if (!NCDVal_IsStringNoNulls(arg)) {
233 ModuleLog(o->i, BLOG_ERROR, "wrong type");
234 goto fail1;
235 }
236  
237 // append argument
238 if (!CmdLine_AppendNoNullMr(c, NCDVal_StringMemRef(arg))) {
239 goto fail1;
240 }
241 }
242  
243 // append interface name
244 if (!CmdLine_Append(c, "-i") || !CmdLine_AppendNoNullMr(c, o->ifname)) {
245 goto fail1;
246 }
247  
248 // append config file
249 if (!CmdLine_Append(c, "-c") || !CmdLine_AppendNoNullMr(c, o->conf)) {
250 goto fail1;
251 }
252  
253 // terminate cmdline
254 if (!CmdLine_Finish(c)) {
255 goto fail1;
256 }
257  
258 return 1;
259  
260 fail1:
261 CmdLine_Free(c);
262 fail0:
263 return 0;
264 }
265  
266 int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len)
267 {
268 ASSERT(!o->have_info)
269  
270 // set bssid
271 o->info_have_bssid = have_bssid;
272 if (have_bssid) {
273 memcpy(o->info_bssid, bssid, 6);
274 }
275  
276 // set ssid
277 if (!(o->info_ssid = BAllocSize(bsize_add(bsize_fromsize(ssid_len), bsize_fromsize(1))))) {
278 ModuleLog(o->i, BLOG_ERROR, "BAllocSize failed");
279 return 0;
280 }
281 memcpy(o->info_ssid, ssid, ssid_len);
282 o->info_ssid[ssid_len] = '\0';
283  
284 // set have info
285 o->have_info = 1;
286  
287 return 1;
288 }
289  
290 void free_info (struct instance *o)
291 {
292 ASSERT(o->have_info)
293  
294 // free ssid
295 BFree(o->info_ssid);
296  
297 // set not have info
298 o->have_info = 0;
299 }
300  
301 void process_error (struct instance *o)
302 {
303 BInputProcess_Terminate(&o->process);
304 }
305  
306 void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status)
307 {
308 ModuleLog(o->i, (o->dying ? BLOG_INFO : BLOG_ERROR), "process terminated");
309  
310 // die
311 instance_free(o, !o->dying);
312 return;
313 }
314  
315 void process_handler_closed (struct instance *o, int is_error)
316 {
317 ASSERT(o->have_pipe)
318  
319 if (is_error) {
320 ModuleLog(o->i, BLOG_ERROR, "pipe error");
321 } else {
322 ModuleLog(o->i, BLOG_INFO, "pipe closed");
323 }
324  
325 // free buffer
326 LineBuffer_Free(&o->pipe_buffer);
327  
328 // free input interface
329 PacketPassInterface_Free(&o->pipe_input);
330  
331 // set have no pipe
332 o->have_pipe = 0;
333 }
334  
335 void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len)
336 {
337 ASSERT(o->have_pipe)
338 ASSERT(data_len > 0)
339  
340 // accept packet
341 PacketPassInterface_Done(&o->pipe_input);
342  
343 if (o->dying) {
344 return;
345 }
346  
347 // strip "interface: " from beginning of line. Older wpa_supplicant versions (<1.0) don't add this
348 // prefix, so don't fail if there isn't one.
349 size_t l1;
350 size_t l2;
351 if (o->ifname.len > 0 && (l1 = data_begins_with_bin((char *)data, data_len, o->ifname.ptr, o->ifname.len)) && (l2 = data_begins_with((char *)data + l1, data_len - l1, ": "))) {
352 data += l1 + l2;
353 data_len -= l1 + l2;
354 }
355  
356 int have_bssid = 1;
357 uint8_t bssid[6];
358 uint8_t *ssid;
359 int ssid_len;
360 if (parse_trying(data, data_len, bssid, &ssid, &ssid_len) || (have_bssid = 0, parse_trying_nobssid(data, data_len, &ssid, &ssid_len))) {
361 ModuleLog(o->i, BLOG_INFO, "trying event");
362  
363 if (o->up) {
364 ModuleLog(o->i, BLOG_ERROR, "trying unexpected!");
365 process_error(o);
366 return;
367 }
368  
369 if (o->have_info) {
370 free_info(o);
371 }
372  
373 if (!init_info(o, have_bssid, bssid, ssid, ssid_len)) {
374 ModuleLog(o->i, BLOG_ERROR, "init_info failed");
375 process_error(o);
376 return;
377 }
378 }
379 else if (data_begins_with((char *)data, data_len, EVENT_STRING_CONNECTED)) {
380 ModuleLog(o->i, BLOG_INFO, "connected event");
381  
382 if (o->up || !o->have_info) {
383 ModuleLog(o->i, BLOG_ERROR, "connected unexpected!");
384 process_error(o);
385 return;
386 }
387  
388 o->up = 1;
389 NCDModuleInst_Backend_Up(o->i);
390 }
391 else if (data_begins_with((char *)data, data_len, EVENT_STRING_DISCONNECTED)) {
392 ModuleLog(o->i, BLOG_INFO, "disconnected event");
393  
394 if (o->have_info) {
395 free_info(o);
396 }
397  
398 if (o->up) {
399 o->up = 0;
400 NCDModuleInst_Backend_Down(o->i);
401 }
402 }
403 }
404  
405 static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
406 {
407 struct instance *o = vo;
408 o->i = i;
409  
410 // read arguments
411 NCDValRef ifname_arg;
412 NCDValRef conf_arg;
413 NCDValRef exec_arg;
414 NCDValRef args_arg;
415 if (!NCDVal_ListRead(params->args, 4, &ifname_arg, &conf_arg, &exec_arg, &args_arg)) {
416 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
417 goto fail0;
418 }
419 if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsStringNoNulls(conf_arg) ||
420 !NCDVal_IsStringNoNulls(exec_arg) || !NCDVal_IsList(args_arg)) {
421 ModuleLog(o->i, BLOG_ERROR, "wrong type");
422 goto fail0;
423 }
424  
425 o->ifname = NCDVal_StringMemRef(ifname_arg);
426 o->conf = NCDVal_StringMemRef(conf_arg);
427 o->exec = NCDVal_StringMemRef(exec_arg);
428 o->args = args_arg;
429  
430 // set not dying
431 o->dying = 0;
432  
433 // set not up
434 o->up = 0;
435  
436 // build process cmdline
437 CmdLine c;
438 if (!build_cmdline(o, &c)) {
439 ModuleLog(o->i, BLOG_ERROR, "failed to build cmdline");
440 goto fail0;
441 }
442  
443 // init process
444 if (!BInputProcess_Init(&o->process, o->i->params->iparams->reactor, o->i->params->iparams->manager, o,
445 (BInputProcess_handler_terminated)process_handler_terminated,
446 (BInputProcess_handler_closed)process_handler_closed
447 )) {
448 ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Init failed");
449 goto fail1;
450 }
451  
452 // init input interface
453 PacketPassInterface_Init(&o->pipe_input, MAX_LINE_LEN, (PacketPassInterface_handler_send)process_pipe_handler_send, o, BReactor_PendingGroup(o->i->params->iparams->reactor));
454  
455 // init buffer
456 if (!LineBuffer_Init(&o->pipe_buffer, BInputProcess_GetInput(&o->process), &o->pipe_input, MAX_LINE_LEN, '\n')) {
457 ModuleLog(o->i, BLOG_ERROR, "LineBuffer_Init failed");
458 goto fail2;
459 }
460  
461 // set have pipe
462 o->have_pipe = 1;
463  
464 // start process
465 if (!BInputProcess_Start(&o->process, ((char **)c.arr.v)[0], (char **)c.arr.v, NULL)) {
466 ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Start failed");
467 goto fail3;
468 }
469  
470 // set not have info
471 o->have_info = 0;
472  
473 CmdLine_Free(&c);
474 return;
475  
476 fail3:
477 LineBuffer_Free(&o->pipe_buffer);
478 fail2:
479 PacketPassInterface_Free(&o->pipe_input);
480 BInputProcess_Free(&o->process);
481 fail1:
482 CmdLine_Free(&c);
483 fail0:
484 NCDModuleInst_Backend_DeadError(i);
485 }
486  
487 void instance_free (struct instance *o, int is_error)
488 {
489 // free info
490 if (o->have_info) {
491 free_info(o);
492 }
493  
494 if (o->have_pipe) {
495 // free buffer
496 LineBuffer_Free(&o->pipe_buffer);
497  
498 // free input interface
499 PacketPassInterface_Free(&o->pipe_input);
500 }
501  
502 // free process
503 BInputProcess_Free(&o->process);
504  
505 if (is_error) {
506 NCDModuleInst_Backend_DeadError(o->i);
507 } else {
508 NCDModuleInst_Backend_Dead(o->i);
509 }
510 }
511  
512 static void func_die (void *vo)
513 {
514 struct instance *o = vo;
515 ASSERT(!o->dying)
516  
517 // request termination
518 BInputProcess_Terminate(&o->process);
519  
520 // remember dying
521 o->dying = 1;
522 }
523  
524 static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out)
525 {
526 struct instance *o = vo;
527 ASSERT(o->up)
528 ASSERT(o->have_info)
529  
530 if (!strcmp(name, "bssid")) {
531 char str[18];
532  
533 if (!o->info_have_bssid) {
534 sprintf(str, "none");
535 } else {
536 uint8_t *id = o->info_bssid;
537 sprintf(str, "%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8, id[0], id[1], id[2], id[3], id[4], id[5]);
538 }
539  
540 *out = NCDVal_NewString(mem, str);
541 return 1;
542 }
543  
544 if (!strcmp(name, "ssid")) {
545 *out = NCDVal_NewString(mem, o->info_ssid);
546 return 1;
547 }
548  
549 return 0;
550 }
551  
552 static struct NCDModule modules[] = {
553 {
554 .type = "net.backend.wpa_supplicant",
555 .func_new2 = func_new,
556 .func_die = func_die,
557 .func_getvar = func_getvar,
558 .alloc_size = sizeof(struct instance)
559 }, {
560 .type = NULL
561 }
562 };
563  
564 const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant = {
565 .modules = modules
566 };