net_backend_wpa_supplicant.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /**
  2. * @file net_backend_wpa_supplicant.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * This file is part of BadVPN.
  8. *
  9. * BadVPN is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2
  11. * as published by the Free Software Foundation.
  12. *
  13. * BadVPN is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program; if not, write to the Free Software Foundation, Inc.,
  20. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. *
  22. * @section DESCRIPTION
  23. *
  24. * Wireless interface module which runs wpa_supplicant to connect to a wireless network.
  25. *
  26. * Note: wpa_supplicant does not monitor the state of rfkill switches and will fail to
  27. * start if the switch is of when it is started, and will stop working indefinitely if the
  28. * switch is turned off while it is running. Therefore, you should put a "net.backend.rfkill"
  29. * statement in front of the wpa_supplicant statement.
  30. *
  31. * Synopsis: net.backend.wpa_supplicant(string ifname, string conf, string exec, list(string) args)
  32. * Variables:
  33. * bssid - BSSID of the wireless network we connected to, or "none".
  34. * Consists of 6 capital, two-character hexadecimal numbers, separated with colons.
  35. * Example: "01:B2:C3:04:E5:F6"
  36. * ssid - SSID of the wireless network we connected to. Note that this is after what
  37. * wpa_supplicant does to it before it prints it. In particular, it replaces all bytes
  38. * outside [32, 126] with underscores.
  39. */
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <stdio.h>
  43. #include <inttypes.h>
  44. #include <misc/cmdline.h>
  45. #include <misc/string_begins_with.h>
  46. #include <misc/stdbuf_cmdline.h>
  47. #include <misc/balloc.h>
  48. #include <flow/LineBuffer.h>
  49. #include <system/BInputProcess.h>
  50. #include <ncd/NCDModule.h>
  51. #include <generated/blog_channel_ncd_net_backend_wpa_supplicant.h>
  52. #define MAX_LINE_LEN 512
  53. #define EVENT_STRING_CONNECTED "CTRL-EVENT-CONNECTED"
  54. #define EVENT_STRING_DISCONNECTED "CTRL-EVENT-DISCONNECTED"
  55. #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
  56. struct instance {
  57. NCDModuleInst *i;
  58. char *ifname;
  59. char *conf;
  60. char *exec;
  61. NCDValue *args;
  62. int dying;
  63. int up;
  64. BInputProcess process;
  65. int have_pipe;
  66. LineBuffer pipe_buffer;
  67. PacketPassInterface pipe_input;
  68. int have_info;
  69. int info_have_bssid;
  70. uint8_t info_bssid[6];
  71. char *info_ssid;
  72. };
  73. static int parse_hex_digit (uint8_t d, uint8_t *out);
  74. static int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len);
  75. static int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len);
  76. static int build_cmdline (struct instance *o, CmdLine *c);
  77. static int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len);
  78. static void free_info (struct instance *o);
  79. static void process_error (struct instance *o);
  80. static void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status);
  81. static void process_handler_closed (struct instance *o, int is_error);
  82. static void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len);
  83. static void instance_free (struct instance *o);
  84. int parse_hex_digit (uint8_t d, uint8_t *out)
  85. {
  86. switch (d) {
  87. case '0': *out = 0; return 1;
  88. case '1': *out = 1; return 1;
  89. case '2': *out = 2; return 1;
  90. case '3': *out = 3; return 1;
  91. case '4': *out = 4; return 1;
  92. case '5': *out = 5; return 1;
  93. case '6': *out = 6; return 1;
  94. case '7': *out = 7; return 1;
  95. case '8': *out = 8; return 1;
  96. case '9': *out = 9; return 1;
  97. case 'A': case 'a': *out = 10; return 1;
  98. case 'B': case 'b': *out = 11; return 1;
  99. case 'C': case 'c': *out = 12; return 1;
  100. case 'D': case 'd': *out = 13; return 1;
  101. case 'E': case 'e': *out = 14; return 1;
  102. case 'F': case 'f': *out = 15; return 1;
  103. }
  104. return 0;
  105. }
  106. int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len)
  107. {
  108. // Trying to associate with AB:CD:EF:01:23:45 (SSID='Some SSID' freq=2462 MHz)
  109. int p;
  110. if (!(p = data_begins_with(data, data_len, "Trying to associate with "))) {
  111. return 0;
  112. }
  113. data += p;
  114. data_len -= p;
  115. for (int i = 0; i < 6; i++) {
  116. uint8_t d1;
  117. uint8_t d2;
  118. if (data_len < 2 || !parse_hex_digit(data[0], &d1) || !parse_hex_digit(data[1], &d2)) {
  119. return 0;
  120. }
  121. data += 2;
  122. data_len -= 2;
  123. out_bssid[i] = ((d1 << 4) | d2);
  124. if (i != 5) {
  125. if (data_len < 1 || data[0] != ':') {
  126. return 0;
  127. }
  128. data += 1;
  129. data_len -= 1;
  130. }
  131. }
  132. if (!(p = data_begins_with(data, data_len, " (SSID='"))) {
  133. return 0;
  134. }
  135. data += p;
  136. data_len -= p;
  137. // find last '
  138. uint8_t *q = NULL;
  139. for (int i = data_len; i > 0; i--) {
  140. if (data[i - 1] == '\'') {
  141. q = &data[i - 1];
  142. break;
  143. }
  144. }
  145. if (!q) {
  146. return 0;
  147. }
  148. *out_ssid = data;
  149. *out_ssid_len = q - data;
  150. return 1;
  151. }
  152. int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len)
  153. {
  154. // Trying to associate with SSID 'Some SSID'
  155. int p;
  156. if (!(p = data_begins_with(data, data_len, "Trying to associate with SSID '"))) {
  157. return 0;
  158. }
  159. data += p;
  160. data_len -= p;
  161. // find last '
  162. uint8_t *q = NULL;
  163. for (int i = data_len; i > 0; i--) {
  164. if (data[i - 1] == '\'') {
  165. q = &data[i - 1];
  166. break;
  167. }
  168. }
  169. if (!q) {
  170. return 0;
  171. }
  172. *out_ssid = data;
  173. *out_ssid_len = q - data;
  174. return 1;
  175. }
  176. int build_cmdline (struct instance *o, CmdLine *c)
  177. {
  178. // init cmdline
  179. if (!CmdLine_Init(c)) {
  180. goto fail0;
  181. }
  182. // append stdbuf part
  183. if (!build_stdbuf_cmdline(c, o->exec)) {
  184. goto fail1;
  185. }
  186. // append user arguments
  187. NCDValue *arg = NCDValue_ListFirst(o->args);
  188. while (arg) {
  189. if (NCDValue_Type(arg) != NCDVALUE_STRING) {
  190. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  191. goto fail1;
  192. }
  193. // append argument
  194. if (!CmdLine_Append(c, NCDValue_StringValue(arg))) {
  195. goto fail1;
  196. }
  197. arg = NCDValue_ListNext(o->args, arg);
  198. }
  199. // append interface name
  200. if (!CmdLine_Append(c, "-i") || !CmdLine_Append(c, o->ifname)) {
  201. goto fail1;
  202. }
  203. // append config file
  204. if (!CmdLine_Append(c, "-c") || !CmdLine_Append(c, o->conf)) {
  205. goto fail1;
  206. }
  207. // terminate cmdline
  208. if (!CmdLine_Finish(c)) {
  209. goto fail1;
  210. }
  211. return 1;
  212. fail1:
  213. CmdLine_Free(c);
  214. fail0:
  215. return 0;
  216. }
  217. int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len)
  218. {
  219. ASSERT(!o->have_info)
  220. // set bssid
  221. o->info_have_bssid = have_bssid;
  222. if (have_bssid) {
  223. memcpy(o->info_bssid, bssid, 6);
  224. }
  225. // set ssid
  226. if (!(o->info_ssid = BAllocSize(bsize_add(bsize_fromsize(ssid_len), bsize_fromsize(1))))) {
  227. ModuleLog(o->i, BLOG_ERROR, "BAllocSize failed");
  228. return 0;
  229. }
  230. memcpy(o->info_ssid, ssid, ssid_len);
  231. o->info_ssid[ssid_len] = '\0';
  232. // set have info
  233. o->have_info = 1;
  234. return 1;
  235. }
  236. void free_info (struct instance *o)
  237. {
  238. ASSERT(o->have_info)
  239. // free ssid
  240. BFree(o->info_ssid);
  241. // set not have info
  242. o->have_info = 0;
  243. }
  244. void process_error (struct instance *o)
  245. {
  246. BInputProcess_Terminate(&o->process);
  247. }
  248. void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status)
  249. {
  250. ModuleLog(o->i, (o->dying ? BLOG_INFO : BLOG_ERROR), "process terminated");
  251. if (!o->dying) {
  252. NCDModuleInst_Backend_SetError(o->i);
  253. }
  254. // die
  255. instance_free(o);
  256. return;
  257. }
  258. void process_handler_closed (struct instance *o, int is_error)
  259. {
  260. ASSERT(o->have_pipe)
  261. if (is_error) {
  262. ModuleLog(o->i, BLOG_ERROR, "pipe error");
  263. } else {
  264. ModuleLog(o->i, BLOG_INFO, "pipe closed");
  265. }
  266. // free buffer
  267. LineBuffer_Free(&o->pipe_buffer);
  268. // free input interface
  269. PacketPassInterface_Free(&o->pipe_input);
  270. // set have no pipe
  271. o->have_pipe = 0;
  272. }
  273. void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len)
  274. {
  275. ASSERT(o->have_pipe)
  276. ASSERT(data_len > 0)
  277. // accept packet
  278. PacketPassInterface_Done(&o->pipe_input);
  279. if (o->dying) {
  280. return;
  281. }
  282. int have_bssid = 1;
  283. uint8_t bssid[6];
  284. uint8_t *ssid;
  285. int ssid_len;
  286. if (parse_trying(data, data_len, bssid, &ssid, &ssid_len) || (have_bssid = 0, parse_trying_nobssid(data, data_len, &ssid, &ssid_len))) {
  287. ModuleLog(o->i, BLOG_INFO, "trying event");
  288. if (o->up) {
  289. ModuleLog(o->i, BLOG_ERROR, "trying unexpected!");
  290. process_error(o);
  291. return;
  292. }
  293. if (o->have_info) {
  294. free_info(o);
  295. }
  296. if (!init_info(o, have_bssid, bssid, ssid, ssid_len)) {
  297. ModuleLog(o->i, BLOG_ERROR, "init_info failed");
  298. process_error(o);
  299. return;
  300. }
  301. }
  302. else if (data_begins_with((char *)data, data_len, EVENT_STRING_CONNECTED)) {
  303. ModuleLog(o->i, BLOG_INFO, "connected event");
  304. if (o->up || !o->have_info) {
  305. ModuleLog(o->i, BLOG_ERROR, "connected unexpected!");
  306. process_error(o);
  307. return;
  308. }
  309. o->up = 1;
  310. NCDModuleInst_Backend_Up(o->i);
  311. }
  312. else if (data_begins_with((char *)data, data_len, EVENT_STRING_DISCONNECTED)) {
  313. ModuleLog(o->i, BLOG_INFO, "disconnected event");
  314. if (o->have_info) {
  315. free_info(o);
  316. }
  317. if (o->up) {
  318. o->up = 0;
  319. NCDModuleInst_Backend_Down(o->i);
  320. }
  321. }
  322. }
  323. static void func_new (NCDModuleInst *i)
  324. {
  325. // allocate instance
  326. struct instance *o = malloc(sizeof(*o));
  327. if (!o) {
  328. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  329. goto fail0;
  330. }
  331. NCDModuleInst_Backend_SetUser(i, o);
  332. // init arguments
  333. o->i = i;
  334. // read arguments
  335. NCDValue *ifname_arg;
  336. NCDValue *conf_arg;
  337. NCDValue *exec_arg;
  338. NCDValue *args_arg;
  339. if (!NCDValue_ListRead(o->i->args, 4, &ifname_arg, &conf_arg, &exec_arg, &args_arg)) {
  340. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  341. goto fail1;
  342. }
  343. if (NCDValue_Type(ifname_arg) != NCDVALUE_STRING || NCDValue_Type(conf_arg) != NCDVALUE_STRING ||
  344. NCDValue_Type(exec_arg) != NCDVALUE_STRING || NCDValue_Type(args_arg) != NCDVALUE_LIST) {
  345. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  346. goto fail1;
  347. }
  348. o->ifname = NCDValue_StringValue(ifname_arg);
  349. o->conf = NCDValue_StringValue(conf_arg);
  350. o->exec = NCDValue_StringValue(exec_arg);
  351. o->args = args_arg;
  352. // set not dying
  353. o->dying = 0;
  354. // set not up
  355. o->up = 0;
  356. // build process cmdline
  357. CmdLine c;
  358. if (!build_cmdline(o, &c)) {
  359. ModuleLog(o->i, BLOG_ERROR, "failed to build cmdline");
  360. goto fail1;
  361. }
  362. // init process
  363. if (!BInputProcess_Init(&o->process, o->i->reactor, o->i->manager, o,
  364. (BInputProcess_handler_terminated)process_handler_terminated,
  365. (BInputProcess_handler_closed)process_handler_closed
  366. )) {
  367. ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Init failed");
  368. goto fail2;
  369. }
  370. // init input interface
  371. PacketPassInterface_Init(&o->pipe_input, MAX_LINE_LEN, (PacketPassInterface_handler_send)process_pipe_handler_send, o, BReactor_PendingGroup(o->i->reactor));
  372. // init buffer
  373. if (!LineBuffer_Init(&o->pipe_buffer, BInputProcess_GetInput(&o->process), &o->pipe_input, MAX_LINE_LEN, '\n')) {
  374. ModuleLog(o->i, BLOG_ERROR, "LineBuffer_Init failed");
  375. goto fail3;
  376. }
  377. // set have pipe
  378. o->have_pipe = 1;
  379. // start process
  380. if (!BInputProcess_Start(&o->process, ((char **)c.arr.v)[0], (char **)c.arr.v, NULL)) {
  381. ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Start failed");
  382. goto fail4;
  383. }
  384. // set not have info
  385. o->have_info = 0;
  386. CmdLine_Free(&c);
  387. return;
  388. fail4:
  389. LineBuffer_Free(&o->pipe_buffer);
  390. fail3:
  391. PacketPassInterface_Free(&o->pipe_input);
  392. BInputProcess_Free(&o->process);
  393. fail2:
  394. CmdLine_Free(&c);
  395. fail1:
  396. free(o);
  397. fail0:
  398. NCDModuleInst_Backend_SetError(i);
  399. NCDModuleInst_Backend_Dead(i);
  400. }
  401. void instance_free (struct instance *o)
  402. {
  403. NCDModuleInst *i = o->i;
  404. // free info
  405. if (o->have_info) {
  406. free_info(o);
  407. }
  408. if (o->have_pipe) {
  409. // free buffer
  410. LineBuffer_Free(&o->pipe_buffer);
  411. // free input interface
  412. PacketPassInterface_Free(&o->pipe_input);
  413. }
  414. // free process
  415. BInputProcess_Free(&o->process);
  416. // free instance
  417. free(o);
  418. NCDModuleInst_Backend_Dead(i);
  419. }
  420. static void func_die (void *vo)
  421. {
  422. struct instance *o = vo;
  423. ASSERT(!o->dying)
  424. // request termination
  425. BInputProcess_Terminate(&o->process);
  426. // remember dying
  427. o->dying = 1;
  428. }
  429. static int func_getvar (void *vo, const char *name, NCDValue *out)
  430. {
  431. struct instance *o = vo;
  432. ASSERT(o->up)
  433. ASSERT(o->have_info)
  434. if (!strcmp(name, "bssid")) {
  435. char str[18];
  436. if (!o->info_have_bssid) {
  437. sprintf(str, "none");
  438. } else {
  439. uint8_t *id = o->info_bssid;
  440. 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]);
  441. }
  442. if (!NCDValue_InitString(out, str)) {
  443. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
  444. return 0;
  445. }
  446. return 1;
  447. }
  448. if (!strcmp(name, "ssid")) {
  449. if (!NCDValue_InitString(out, o->info_ssid)) {
  450. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
  451. return 0;
  452. }
  453. return 1;
  454. }
  455. return 0;
  456. }
  457. static const struct NCDModule modules[] = {
  458. {
  459. .type = "net.backend.wpa_supplicant",
  460. .func_new = func_new,
  461. .func_die = func_die,
  462. .func_getvar = func_getvar
  463. }, {
  464. .type = NULL
  465. }
  466. };
  467. const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant = {
  468. .modules = modules
  469. };