Add dhcpcd_encode which encodes raw data back into a safe string.
[dhcpcd-ui] / src / libdhcpcd / wpa.c
index 42a1f9acd4ebe00cdb852993d06e0d84b328563c..0b03b2cafa0c61456ed63be4bd799bfaa177335e 100644 (file)
@@ -46,6 +46,9 @@
        (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
 #endif
 
+#define CLAMP(x, low, high) \
+       (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+
 static int
 wpa_open(const char *ifname, char **path)
 {
@@ -140,7 +143,7 @@ dhcpcd_wpa_command_arg(DHCPCD_WPA *wpa, const char *cmd, const char *arg)
 }
 
 static bool
-dhcpcd_attach_detach(DHCPCD_WPA *wpa, int attach)
+dhcpcd_attach_detach(DHCPCD_WPA *wpa, bool attach)
 {
        char buf[10];
        ssize_t bytes;
@@ -192,9 +195,10 @@ static DHCPCD_WI_SCAN *
 dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
 {
        size_t i;
-       ssize_t bytes;
+       ssize_t bytes, dl;
        DHCPCD_WI_SCAN *wis, *w, *l;
        char *s, *p, buf[32];
+       char wssid[sizeof(w->ssid)], tssid[sizeof(w->ssid)];
 
        if (!dhcpcd_realloc(wpa->con, 2048))
                return NULL;
@@ -232,9 +236,32 @@ dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
                                dhcpcd_strtoi(&w->level.value, s + 6);
                        else if (strncmp(s, "flags=", 6) == 0)
                                strlcpy(w->flags, s + 6, sizeof(w->flags));
-                       else if (strncmp(s, "ssid=", 5) == 0)
-                               strlcpy(w->ssid, s + 5, sizeof(w->ssid));
+                       else if (strncmp(s, "ssid=", 5) == 0) {
+                               /* Decode it from \xNN to \NNN
+                                * so we're consistent */
+                               strlcpy(wssid, s + 5, sizeof(wssid));
+                               dl = dhcpcd_decode(tssid, sizeof(tssid), wssid);
+                               dhcpcd_encode(w->ssid, sizeof(w->ssid),
+                                   tssid, (size_t)dl);
+                       }
+               }
+
+               w->strength.value = w->level.value;
+               if (w->strength.value > 110 && w->strength.value < 256)
+                       /* Convert WEXT level to dBm */
+                       w->strength.value -= 256;
+
+               if (w->strength.value < 0) {
+                       /* Assume dBm */
+                       w->strength.value =
+                           abs(CLAMP(w->strength.value, -100, -40) + 40);
+                       w->strength.value =
+                           100 - ((100 * w->strength.value) / 60);
+               } else {
+                       /* Assume quality percentage */
+                       w->strength.value = CLAMP(w->strength.value, 0, 100);
                }
+
        }
        return wis;
 }
@@ -257,6 +284,7 @@ dhcpcd_wi_scans(DHCPCD_IF *i)
                w->quality.average = w->quality.value;
                w->noise.average = w->noise.value;
                w->level.average = w->level.value;
+               w->strength.average = w->strength.value;
 
                for (h = wpa->con->wi_history; h; h = h->next) {
                        if (strcmp(h->ifname, i->ifname) == 0 &&
@@ -265,6 +293,7 @@ dhcpcd_wi_scans(DHCPCD_IF *i)
                                w->quality.average += h->quality;
                                w->noise.average += h->noise;
                                w->level.average += h->level;
+                               w->strength.average += h->strength;
                                if (++nh == DHCPCD_WI_HIST_MAX) {
                                        hl->next = h->next;
                                        free(h);
@@ -278,6 +307,7 @@ dhcpcd_wi_scans(DHCPCD_IF *i)
                        w->quality.average /= nh;
                        w->noise.average /= nh;
                        w->level.average /= nh;
+                       w->strength.average /= nh;
                }
                h = malloc(sizeof(*h));
                if (h) {
@@ -286,6 +316,7 @@ dhcpcd_wi_scans(DHCPCD_IF *i)
                        h->quality = w->quality.value;
                        h->noise = w->noise.value;
                        h->level = w->level.value;
+                       h->strength = w->strength.value;
                        h->next = wpa->con->wi_history;
                        wpa->con->wi_history = h;
                }
@@ -385,8 +416,9 @@ dhcpcd_wpa_network_set(DHCPCD_WPA *wpa, int id,
 static int
 dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
 {
-       ssize_t bytes;
+       ssize_t bytes, dl, tl;
        char *s, *t, *ssid, *bssid, *flags;
+       char dssid[IF_SSIDSIZE], tssid[IF_SSIDSIZE];
        long l;
 
        dhcpcd_realloc(wpa->con, 2048);
@@ -395,6 +427,9 @@ dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
        if (bytes == 0 || bytes == -1)
                return -1;
 
+       if ((dl = dhcpcd_decode(dssid, sizeof(dssid), fssid)) == -1)
+               return -1;
+
        s = strchr(wpa->con->buf, '\n');
        if (s == NULL)
                return -1;
@@ -418,7 +453,10 @@ dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
                        errno = ERANGE;
                        break;
                }
-               if (strcmp(ssid, fssid) == 0)
+
+               if ((tl = dhcpcd_decode(tssid, sizeof(tssid), ssid)) == -1)
+                       return -1;
+               if (tl == dl && memcmp(tssid, dssid, (size_t)tl) == 0)
                        return (int)l;
        }
        errno = ENOENT;
@@ -444,14 +482,44 @@ dhcpcd_wpa_network_new(DHCPCD_WPA *wpa)
        return (int)l;
 }
 
+static const char hexstr[] = "0123456789ABCDEF";
 int
 dhcpcd_wpa_network_find_new(DHCPCD_WPA *wpa, const char *ssid)
 {
        int id;
+       char dssid[IF_SSIDSIZE], essid[IF_SSIDSIZE], *ep;
+       ssize_t dl;
 
        id = dhcpcd_wpa_network_find(wpa, ssid);
-       if (id == -1)
-               id = dhcpcd_wpa_network_new(wpa);
+       if (id != -1)
+               return id;
+
+       dl = dhcpcd_decode(dssid, sizeof(dssid), ssid);
+       if (dl == -1)
+               return -1;
+
+       ep = essid;
+       if ((size_t)dl != strlen(ssid) || memcmp(dssid, ssid, (size_t)dl)) {
+               /* Non standard characters found! Encode as hex string */
+               char *dp;
+               unsigned char c;
+
+               dp = dssid;
+               for (; dl; dl--) {
+                       c = (unsigned char)*dp++;
+                       *ep++ = hexstr[c >> 4];
+                       *ep++ = hexstr[c & 0xf];
+               }
+       } else {
+               *ep++ = '\"';
+               ep = stpcpy(ep, ssid);
+               *ep++ = '\"';
+       }
+       *ep = '\0';
+
+       id = dhcpcd_wpa_network_new(wpa);
+       if (id != -1)
+               dhcpcd_wpa_network_set(wpa, id, "ssid", essid);
        return id;
 }
 
@@ -459,13 +527,23 @@ void
 dhcpcd_wpa_close(DHCPCD_WPA *wpa)
 {
 
-       if (wpa->command_fd == -1)
+       assert(wpa);
+
+       if (wpa->command_fd == -1 || !wpa->open)
                return;
 
-       dhcpcd_attach_detach(wpa, -1);
+       wpa->open = false;
+       dhcpcd_attach_detach(wpa, false);
        shutdown(wpa->command_fd, SHUT_RDWR);
-       wpa->command_fd = -1;
        shutdown(wpa->listen_fd, SHUT_RDWR);
+
+       if (wpa->con->wpa_status_cb)
+               wpa->con->wpa_status_cb(wpa, "down",
+                   wpa->con->wpa_status_context);
+
+       close(wpa->command_fd);
+       wpa->command_fd = -1;
+       close(wpa->listen_fd);
        wpa->listen_fd = -1;
        unlink(wpa->command_path);
        free(wpa->command_path);
@@ -473,11 +551,6 @@ dhcpcd_wpa_close(DHCPCD_WPA *wpa)
        unlink(wpa->listen_path);
        free(wpa->listen_path);
        wpa->listen_path = NULL;
-
-       if (wpa->con->wpa_status_cb)
-               wpa->con->wpa_status_cb(wpa, "down",
-                   wpa->con->wpa_status_context);
-
 }
 
 DHCPCD_WPA *
@@ -489,6 +562,7 @@ dhcpcd_wpa_find(DHCPCD_CONNECTION *con, const char *ifname)
                if (strcmp(wpa->ifname, ifname) == 0)
                        return wpa;
        }
+       errno = ENOENT;
        return NULL;
 }
 
@@ -518,6 +592,7 @@ DHCPCD_CONNECTION *
 dhcpcd_wpa_connection(DHCPCD_WPA *wpa)
 {
 
+       assert(wpa);
        return wpa->con;
 }
 
@@ -534,8 +609,13 @@ dhcpcd_wpa_open(DHCPCD_WPA *wpa)
        int cmd_fd, list_fd = -1;
        char *cmd_path = NULL, *list_path = NULL;
 
-       if (wpa->listen_fd != -1)
+       if (wpa->listen_fd != -1) {
+               if (!wpa->open) {
+                       errno = EISCONN;
+                       return -1;
+               }
                return wpa->listen_fd;
+       }
 
        cmd_fd = wpa_open(wpa->ifname, &cmd_path);
        if (cmd_fd == -1)
@@ -545,12 +625,13 @@ dhcpcd_wpa_open(DHCPCD_WPA *wpa)
        if (list_fd == -1)
                goto fail;
 
-       wpa->attached = 0;
+       wpa->open = true;
+       wpa->attached = false;
        wpa->command_fd = cmd_fd;
        wpa->command_path = cmd_path;
        wpa->listen_fd = list_fd;
        wpa->listen_path = list_path;
-       if (!dhcpcd_attach_detach(wpa, 1)) {
+       if (!dhcpcd_attach_detach(wpa, true)) {
                dhcpcd_wpa_close(wpa);
                return -1;
        }
@@ -578,7 +659,8 @@ int
 dhcpcd_wpa_get_fd(DHCPCD_WPA *wpa)
 {
 
-       return wpa->listen_fd;
+       assert(wpa);
+       return wpa->open ? wpa->listen_fd : -1;
 }
 
 void
@@ -586,16 +668,28 @@ dhcpcd_wpa_set_scan_callback(DHCPCD_CONNECTION *con,
     void (*cb)(DHCPCD_WPA *, void *), void *context)
 {
 
+       assert(con);
        con->wi_scanresults_cb = cb;
        con->wi_scanresults_context = context;
 }
 
+
+void dhcpcd_wpa_set_status_callback(DHCPCD_CONNECTION * con,
+    void (*cb)(DHCPCD_WPA *, const char *, void *), void *context)
+{
+
+       assert(con);
+       con->wpa_status_cb = cb;
+       con->wpa_status_context = context;
+}
+
 void
 dhcpcd_wpa_dispatch(DHCPCD_WPA *wpa)
 {
        char buffer[256], *p;
        size_t bytes;
 
+       assert(wpa);
        bytes = (size_t)read(wpa->listen_fd, buffer, sizeof(buffer));
        if ((ssize_t)bytes == -1 || bytes == 0) {
                dhcpcd_wpa_close(wpa);
@@ -624,15 +718,82 @@ dhcpcd_wpa_if_event(DHCPCD_IF *i)
        DHCPCD_WPA *wpa;
 
        assert(i);
-       if (i->wireless && strcmp(i->type, "link") == 0) {
-               if (strcmp(i->reason, "STOPPED") == 0) {
+       if (strcmp(i->type, "link") == 0) {
+               if (strcmp(i->reason, "STOPPED") == 0 ||
+                   strcmp(i->reason, "DEPARTED") == 0)
+               {
                        wpa = dhcpcd_wpa_find(i->con, i->ifname);
                        if (wpa)
                                dhcpcd_wpa_close(wpa);
-               } else if (i->up) {
+               } else if (i->wireless && i->con->wpa_started) {
                        wpa = dhcpcd_wpa_new(i->con, i->ifname);
                        if (wpa && wpa->listen_fd == -1)
                                dhcpcd_wpa_open(wpa);
                }
        }
 }
+
+void
+dhcpcd_wpa_start(DHCPCD_CONNECTION *con)
+{
+       DHCPCD_IF *i;
+
+       assert(con);
+       con->wpa_started = true;
+
+       for (i = con->interfaces; i; i = i->next)
+               dhcpcd_wpa_if_event(i);
+}
+
+int
+dhcpcd_wpa_configure_psk(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s, const char *psk)
+{
+       const char *mgmt, *var;
+       int id;
+       char *npsk;
+       size_t psk_len;
+       bool r;
+
+       assert(wpa);
+       assert(s);
+
+       id = dhcpcd_wpa_network_find_new(wpa, s->ssid);
+       if (id == -1)
+               return DHCPCD_WPA_ERR;
+
+       if (strcmp(s->flags, "[WEP]") == 0) {
+               mgmt = "NONE";
+               var = "wep_key0";
+       } else {
+               mgmt = "WPA-PSK";
+               var = "psk";
+       }
+
+       if (!dhcpcd_wpa_network_set(wpa, id, "key_mgmt", mgmt))
+               return DHCPCD_WPA_ERR_SET;
+
+       if (psk)
+               psk_len = strlen(psk);
+       else
+               psk_len = 0;
+       npsk = malloc(psk_len + 3);
+       if (npsk == NULL)
+               return DHCPCD_WPA_ERR;
+       npsk[0] = '"';
+       if (psk_len)
+               memcpy(npsk + 1, psk, psk_len);
+       npsk[psk_len + 1] = '"';
+       npsk[psk_len + 2] = '\0';
+       r = dhcpcd_wpa_network_set(wpa, id, var, npsk);
+       free(npsk);
+       if (!r)
+               return DHCPCD_WPA_ERR_SET_PSK;
+
+       if (!dhcpcd_wpa_network_enable(wpa, id))
+               return DHCPCD_WPA_ERR_ENABLE;
+       if (!dhcpcd_wpa_reassociate(wpa))
+               return DHCPCD_WPA_ERR_ASSOC;
+       if (!dhcpcd_wpa_config_write(wpa))
+               return DHCPCD_WPA_ERR_WRITE;
+       return DHCPCD_WPA_SUCCESS;
+}