| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- /**
- * @file net_backend_wpa_supplicant.c
- * @author Ambroz Bizjak <ambrop7@gmail.com>
- *
- * @section LICENSE
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the author nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * @section DESCRIPTION
- *
- * Wireless interface module which runs wpa_supplicant to connect to a wireless network.
- *
- * Note: wpa_supplicant does not monitor the state of rfkill switches and will fail to
- * start if the switch is of when it is started, and will stop working indefinitely if the
- * switch is turned off while it is running. Therefore, you should put a "net.backend.rfkill"
- * statement in front of the wpa_supplicant statement.
- *
- * Synopsis: net.backend.wpa_supplicant(string ifname, string conf, string exec, list(string) args)
- * Variables:
- * bssid - BSSID of the wireless network we connected to, or "none".
- * Consists of 6 capital, two-character hexadecimal numbers, separated with colons.
- * Example: "01:B2:C3:04:E5:F6"
- * ssid - SSID of the wireless network we connected to. Note that this is after what
- * wpa_supplicant does to it before it prints it. In particular, it replaces all bytes
- * outside [32, 126] with underscores.
- */
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <inttypes.h>
- #include <misc/cmdline.h>
- #include <misc/string_begins_with.h>
- #include <misc/stdbuf_cmdline.h>
- #include <misc/balloc.h>
- #include <misc/find_program.h>
- #include <flow/LineBuffer.h>
- #include <system/BInputProcess.h>
- #include <ncd/NCDModule.h>
- #include <generated/blog_channel_ncd_net_backend_wpa_supplicant.h>
- #define MAX_LINE_LEN 512
- #define EVENT_STRING_CONNECTED "CTRL-EVENT-CONNECTED"
- #define EVENT_STRING_DISCONNECTED "CTRL-EVENT-DISCONNECTED"
- #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
- struct instance {
- NCDModuleInst *i;
- const char *ifname;
- size_t ifname_len;
- const char *conf;
- size_t conf_len;
- const char *exec;
- size_t exec_len;
- NCDValRef args;
- int dying;
- int up;
- BInputProcess process;
- int have_pipe;
- LineBuffer pipe_buffer;
- PacketPassInterface pipe_input;
- int have_info;
- int info_have_bssid;
- uint8_t info_bssid[6];
- char *info_ssid;
- };
- static int parse_hex_digit (uint8_t d, uint8_t *out);
- static int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len);
- static int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len);
- static int build_cmdline (struct instance *o, CmdLine *c);
- static int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len);
- static void free_info (struct instance *o);
- static void process_error (struct instance *o);
- static void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status);
- static void process_handler_closed (struct instance *o, int is_error);
- static void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len);
- static void instance_free (struct instance *o, int is_error);
- int parse_hex_digit (uint8_t d, uint8_t *out)
- {
- switch (d) {
- case '0': *out = 0; return 1;
- case '1': *out = 1; return 1;
- case '2': *out = 2; return 1;
- case '3': *out = 3; return 1;
- case '4': *out = 4; return 1;
- case '5': *out = 5; return 1;
- case '6': *out = 6; return 1;
- case '7': *out = 7; return 1;
- case '8': *out = 8; return 1;
- case '9': *out = 9; return 1;
- case 'A': case 'a': *out = 10; return 1;
- case 'B': case 'b': *out = 11; return 1;
- case 'C': case 'c': *out = 12; return 1;
- case 'D': case 'd': *out = 13; return 1;
- case 'E': case 'e': *out = 14; return 1;
- case 'F': case 'f': *out = 15; return 1;
- }
-
- return 0;
- }
- int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len)
- {
- // Trying to associate with AB:CD:EF:01:23:45 (SSID='Some SSID' freq=2462 MHz)
-
- int p;
- if (!(p = data_begins_with((char *)data, data_len, "Trying to associate with "))) {
- return 0;
- }
- data += p;
- data_len -= p;
-
- for (int i = 0; i < 6; i++) {
- uint8_t d1;
- uint8_t d2;
- if (data_len < 2 || !parse_hex_digit(data[0], &d1) || !parse_hex_digit(data[1], &d2)) {
- return 0;
- }
- data += 2;
- data_len -= 2;
- out_bssid[i] = ((d1 << 4) | d2);
-
- if (i != 5) {
- if (data_len < 1 || data[0] != ':') {
- return 0;
- }
- data += 1;
- data_len -= 1;
- }
- }
-
- if (!(p = data_begins_with((char *)data, data_len, " (SSID='"))) {
- return 0;
- }
- data += p;
- data_len -= p;
-
- // find last '
- uint8_t *q = NULL;
- for (int i = data_len; i > 0; i--) {
- if (data[i - 1] == '\'') {
- q = &data[i - 1];
- break;
- }
- }
- if (!q) {
- return 0;
- }
-
- *out_ssid = data;
- *out_ssid_len = q - data;
-
- return 1;
- }
- int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len)
- {
- // Trying to associate with SSID 'Some SSID'
-
- int p;
- if (!(p = data_begins_with((char *)data, data_len, "Trying to associate with SSID '"))) {
- return 0;
- }
- data += p;
- data_len -= p;
-
- // find last '
- uint8_t *q = NULL;
- for (int i = data_len; i > 0; i--) {
- if (data[i - 1] == '\'') {
- q = &data[i - 1];
- break;
- }
- }
- if (!q) {
- return 0;
- }
-
- *out_ssid = data;
- *out_ssid_len = q - data;
-
- return 1;
- }
- int build_cmdline (struct instance *o, CmdLine *c)
- {
- // init cmdline
- if (!CmdLine_Init(c)) {
- goto fail0;
- }
-
- // find stdbuf executable
- char *stdbuf_exec = badvpn_find_program("stdbuf");
- if (!stdbuf_exec) {
- ModuleLog(o->i, BLOG_ERROR, "cannot find stdbuf executable");
- goto fail1;
- }
-
- // append stdbuf part
- int res = build_stdbuf_cmdline(c, stdbuf_exec, o->exec, o->exec_len);
- free(stdbuf_exec);
- if (!res) {
- goto fail1;
- }
-
- // append user arguments
- size_t count = NCDVal_ListCount(o->args);
- for (size_t j = 0; j < count; j++) {
- NCDValRef arg = NCDVal_ListGet(o->args, j);
-
- if (!NCDVal_IsStringNoNulls(arg)) {
- ModuleLog(o->i, BLOG_ERROR, "wrong type");
- goto fail1;
- }
-
- // append argument
- if (!CmdLine_AppendNoNull(c, NCDVal_StringData(arg), NCDVal_StringLength(arg))) {
- goto fail1;
- }
- }
-
- // append interface name
- if (!CmdLine_Append(c, "-i") || !CmdLine_AppendNoNull(c, o->ifname, o->ifname_len)) {
- goto fail1;
- }
-
- // append config file
- if (!CmdLine_Append(c, "-c") || !CmdLine_AppendNoNull(c, o->conf, o->conf_len)) {
- goto fail1;
- }
-
- // terminate cmdline
- if (!CmdLine_Finish(c)) {
- goto fail1;
- }
-
- return 1;
-
- fail1:
- CmdLine_Free(c);
- fail0:
- return 0;
- }
- int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len)
- {
- ASSERT(!o->have_info)
-
- // set bssid
- o->info_have_bssid = have_bssid;
- if (have_bssid) {
- memcpy(o->info_bssid, bssid, 6);
- }
-
- // set ssid
- if (!(o->info_ssid = BAllocSize(bsize_add(bsize_fromsize(ssid_len), bsize_fromsize(1))))) {
- ModuleLog(o->i, BLOG_ERROR, "BAllocSize failed");
- return 0;
- }
- memcpy(o->info_ssid, ssid, ssid_len);
- o->info_ssid[ssid_len] = '\0';
-
- // set have info
- o->have_info = 1;
-
- return 1;
- }
- void free_info (struct instance *o)
- {
- ASSERT(o->have_info)
-
- // free ssid
- BFree(o->info_ssid);
-
- // set not have info
- o->have_info = 0;
- }
- void process_error (struct instance *o)
- {
- BInputProcess_Terminate(&o->process);
- }
- void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status)
- {
- ModuleLog(o->i, (o->dying ? BLOG_INFO : BLOG_ERROR), "process terminated");
-
- // die
- instance_free(o, !o->dying);
- return;
- }
- void process_handler_closed (struct instance *o, int is_error)
- {
- ASSERT(o->have_pipe)
-
- if (is_error) {
- ModuleLog(o->i, BLOG_ERROR, "pipe error");
- } else {
- ModuleLog(o->i, BLOG_INFO, "pipe closed");
- }
-
- // free buffer
- LineBuffer_Free(&o->pipe_buffer);
-
- // free input interface
- PacketPassInterface_Free(&o->pipe_input);
-
- // set have no pipe
- o->have_pipe = 0;
- }
- void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len)
- {
- ASSERT(o->have_pipe)
- ASSERT(data_len > 0)
-
- // accept packet
- PacketPassInterface_Done(&o->pipe_input);
-
- if (o->dying) {
- return;
- }
-
- // strip "interface: " from beginning of line. Older wpa_supplicant versions (<1.0) don't add this
- // prefix, so don't fail if there isn't one.
- size_t l1;
- size_t l2;
- if (o->ifname_len > 0 && (l1 = data_begins_with_bin((char *)data, data_len, o->ifname, o->ifname_len)) && (l2 = data_begins_with((char *)data + l1, data_len - l1, ": "))) {
- data += l1 + l2;
- data_len -= l1 + l2;
- }
-
- int have_bssid = 1;
- uint8_t bssid[6];
- uint8_t *ssid;
- int ssid_len;
- if (parse_trying(data, data_len, bssid, &ssid, &ssid_len) || (have_bssid = 0, parse_trying_nobssid(data, data_len, &ssid, &ssid_len))) {
- ModuleLog(o->i, BLOG_INFO, "trying event");
-
- if (o->up) {
- ModuleLog(o->i, BLOG_ERROR, "trying unexpected!");
- process_error(o);
- return;
- }
-
- if (o->have_info) {
- free_info(o);
- }
-
- if (!init_info(o, have_bssid, bssid, ssid, ssid_len)) {
- ModuleLog(o->i, BLOG_ERROR, "init_info failed");
- process_error(o);
- return;
- }
- }
- else if (data_begins_with((char *)data, data_len, EVENT_STRING_CONNECTED)) {
- ModuleLog(o->i, BLOG_INFO, "connected event");
-
- if (o->up || !o->have_info) {
- ModuleLog(o->i, BLOG_ERROR, "connected unexpected!");
- process_error(o);
- return;
- }
-
- o->up = 1;
- NCDModuleInst_Backend_Up(o->i);
- }
- else if (data_begins_with((char *)data, data_len, EVENT_STRING_DISCONNECTED)) {
- ModuleLog(o->i, BLOG_INFO, "disconnected event");
-
- if (o->have_info) {
- free_info(o);
- }
-
- if (o->up) {
- o->up = 0;
- NCDModuleInst_Backend_Down(o->i);
- }
- }
- }
- static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
- {
- struct instance *o = vo;
- o->i = i;
-
- // read arguments
- NCDValRef ifname_arg;
- NCDValRef conf_arg;
- NCDValRef exec_arg;
- NCDValRef args_arg;
- if (!NCDVal_ListRead(params->args, 4, &ifname_arg, &conf_arg, &exec_arg, &args_arg)) {
- ModuleLog(o->i, BLOG_ERROR, "wrong arity");
- goto fail0;
- }
- if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsStringNoNulls(conf_arg) ||
- !NCDVal_IsStringNoNulls(exec_arg) || !NCDVal_IsList(args_arg)) {
- ModuleLog(o->i, BLOG_ERROR, "wrong type");
- goto fail0;
- }
-
- o->ifname = NCDVal_StringData(ifname_arg);
- o->ifname_len = NCDVal_StringLength(ifname_arg);
- o->conf = NCDVal_StringData(conf_arg);
- o->conf_len = NCDVal_StringLength(conf_arg);
- o->exec = NCDVal_StringData(exec_arg);
- o->exec_len = NCDVal_StringLength(exec_arg);
- o->args = args_arg;
-
- // set not dying
- o->dying = 0;
-
- // set not up
- o->up = 0;
-
- // build process cmdline
- CmdLine c;
- if (!build_cmdline(o, &c)) {
- ModuleLog(o->i, BLOG_ERROR, "failed to build cmdline");
- goto fail0;
- }
-
- // init process
- if (!BInputProcess_Init(&o->process, o->i->params->iparams->reactor, o->i->params->iparams->manager, o,
- (BInputProcess_handler_terminated)process_handler_terminated,
- (BInputProcess_handler_closed)process_handler_closed
- )) {
- ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Init failed");
- goto fail1;
- }
-
- // init input interface
- PacketPassInterface_Init(&o->pipe_input, MAX_LINE_LEN, (PacketPassInterface_handler_send)process_pipe_handler_send, o, BReactor_PendingGroup(o->i->params->iparams->reactor));
-
- // init buffer
- if (!LineBuffer_Init(&o->pipe_buffer, BInputProcess_GetInput(&o->process), &o->pipe_input, MAX_LINE_LEN, '\n')) {
- ModuleLog(o->i, BLOG_ERROR, "LineBuffer_Init failed");
- goto fail2;
- }
-
- // set have pipe
- o->have_pipe = 1;
-
- // start process
- if (!BInputProcess_Start(&o->process, ((char **)c.arr.v)[0], (char **)c.arr.v, NULL)) {
- ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Start failed");
- goto fail3;
- }
-
- // set not have info
- o->have_info = 0;
-
- CmdLine_Free(&c);
- return;
-
- fail3:
- LineBuffer_Free(&o->pipe_buffer);
- fail2:
- PacketPassInterface_Free(&o->pipe_input);
- BInputProcess_Free(&o->process);
- fail1:
- CmdLine_Free(&c);
- fail0:
- NCDModuleInst_Backend_DeadError(i);
- }
- void instance_free (struct instance *o, int is_error)
- {
- // free info
- if (o->have_info) {
- free_info(o);
- }
-
- if (o->have_pipe) {
- // free buffer
- LineBuffer_Free(&o->pipe_buffer);
-
- // free input interface
- PacketPassInterface_Free(&o->pipe_input);
- }
-
- // free process
- BInputProcess_Free(&o->process);
-
- if (is_error) {
- NCDModuleInst_Backend_DeadError(o->i);
- } else {
- NCDModuleInst_Backend_Dead(o->i);
- }
- }
- static void func_die (void *vo)
- {
- struct instance *o = vo;
- ASSERT(!o->dying)
-
- // request termination
- BInputProcess_Terminate(&o->process);
-
- // remember dying
- o->dying = 1;
- }
- static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out)
- {
- struct instance *o = vo;
- ASSERT(o->up)
- ASSERT(o->have_info)
-
- if (!strcmp(name, "bssid")) {
- char str[18];
-
- if (!o->info_have_bssid) {
- sprintf(str, "none");
- } else {
- uint8_t *id = o->info_bssid;
- 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]);
- }
-
- *out = NCDVal_NewString(mem, str);
- return 1;
- }
-
- if (!strcmp(name, "ssid")) {
- *out = NCDVal_NewString(mem, o->info_ssid);
- return 1;
- }
-
- return 0;
- }
- static struct NCDModule modules[] = {
- {
- .type = "net.backend.wpa_supplicant",
- .func_new2 = func_new,
- .func_die = func_die,
- .func_getvar = func_getvar,
- .alloc_size = sizeof(struct instance)
- }, {
- .type = NULL
- }
- };
- const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant = {
- .modules = modules
- };
|