Allow a blank psk to just select the network.
[dhcpcd-ui] / src / libdhcpcd / wpa.c
index 472b2659d2d9b3fb3c235c031c1b2652954cc992..358b37333d653a69ba9fbef6012bedc57dbbf98c 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/un.h>
 
 #include <assert.h>
 #include <sys/un.h>
 
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <poll.h>
 #include <errno.h>
 #include <limits.h>
 #include <poll.h>
@@ -191,13 +192,92 @@ dhcpcd_strtoi(int *val, const char *s)
                errno = ERANGE;
 }
 
                errno = ERANGE;
 }
 
+static int
+dhcpcd_wpa_hex2num(char c)
+{
+
+       if (c >= '0' && c <= '9')
+               return c - '0';
+       if (c >= 'a' && c <= 'f')
+               return c - 'a' + 10;
+       if (c >= 'A' && c <= 'F')
+               return c - 'A' + 10;
+       return -1;
+}
+
+static int
+dhcpcd_wpa_hex2byte(const char *src)
+{
+       int h, l;
+
+       if ((h = dhcpcd_wpa_hex2num(*src++)) == -1 ||
+           (l = dhcpcd_wpa_hex2num(*src)) == -1)
+               return -1;
+       return (h << 4) | l;
+}
+
+static ssize_t
+dhcpcd_wpa_decode_ssid(char *dst, size_t dlen, const char *src)
+{
+       const char *start;
+       char c, esc;
+       int xb;
+
+       start = dst;
+       for (;;) {
+               if (*src == '\0')
+                       break;
+               if (--dlen == 0) {
+                       errno = ENOSPC;
+                       return -1;
+               }
+               c = *src++;
+               switch (c) {
+               case '\\':
+                       if (*src == '\0') {
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       esc = *src++;
+                       switch (esc) {
+                       case '\\':
+                       case '"': *dst++ = esc; break;
+                       case 'n': *dst++ = '\n'; break;
+                       case 'r': *dst++ = '\r'; break;
+                       case 't': *dst++ = '\t'; break;
+                       case 'e': *dst++ = '\033'; break;
+                       case 'x':
+                               if (src[0] == '\0' || src[1] == '\0') {
+                                       errno = EINVAL;
+                                       return -1;
+                               }
+                               if ((xb = dhcpcd_wpa_hex2byte(src)) == -1)
+                                       return -1;
+                               *dst++ = (char)xb;
+                               src += 2;
+                               break;
+                       default: errno = EINVAL; return -1;
+                       }
+               default: *dst++ = c; break;
+               }
+       }
+       if (dlen == 0) {
+               errno = ENOSPC;
+               return -1;
+       }
+       *dst = '\0';
+       return dst - start;
+}
+
 static DHCPCD_WI_SCAN *
 dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
 {
        size_t i;
 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];
        DHCPCD_WI_SCAN *wis, *w, *l;
        char *s, *p, buf[32];
+       char wssid[sizeof(w->ssid)];
+       const char *proto;
 
        if (!dhcpcd_realloc(wpa->con, 2048))
                return NULL;
 
        if (!dhcpcd_realloc(wpa->con, 2048))
                return NULL;
@@ -213,11 +293,8 @@ dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
                w = calloc(1, sizeof(*w));
                if (w == NULL)
                        break;
                w = calloc(1, sizeof(*w));
                if (w == NULL)
                        break;
-               if (wis == NULL)
-                       wis = w;
-               else
-                       l->next = w;
-               l = w;
+               dl = 0;
+               wssid[0] = '\0';
                while ((s = strsep(&p, "\n"))) {
                        if (*s == '\0')
                                continue;
                while ((s = strsep(&p, "\n"))) {
                        if (*s == '\0')
                                continue;
@@ -234,10 +311,50 @@ dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
                        else if (strncmp(s, "level=", 6) == 0)
                                dhcpcd_strtoi(&w->level.value, s + 6);
                        else if (strncmp(s, "flags=", 6) == 0)
                        else if (strncmp(s, "level=", 6) == 0)
                                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));
+                               strlcpy(w->wpa_flags, s + 6,
+                                   sizeof(w->wpa_flags));
+                       else if (strncmp(s, "ssid=", 5) == 0) {
+                               /* Decode it from \xNN to \NNN
+                                * so we're consistent */
+                               dl = dhcpcd_wpa_decode_ssid(wssid,
+                                   sizeof(wssid), s + 5);
+                               if (dl == -1)
+                                       break;
+                               dl = dhcpcd_encode_string_escape(w->ssid,
+                                   sizeof(w->ssid), wssid, (size_t)dl);
+                               if (dl == -1)
+                                       break;
+                       }
+               }
+               if (dl == -1) {
+                       free(w);
+                       break;
+               }
+
+               if (wis == NULL)
+                       wis = w;
+               else
+                       l->next = w;
+               l = w;
+
+               if ((proto = strstr(w->wpa_flags, "[WPA-")) ||
+                   (proto = strstr(w->wpa_flags, "[WPA2-")) ||
+                   (proto = strstr(w->wpa_flags, "[RSN-")))
+               {
+                       const char *endp, *psk;
+
+                       w->flags = WSF_WPA | WSF_SECURE;
+                       endp = strchr(proto, ']');
+                       if ((psk = strstr(proto, "-PSK]")) ||
+                           (psk = strstr(proto, "-PSK-")) ||
+                           (psk = strstr(proto, "-PSK+")))
+                       {
+                               if (psk < endp)
+                                       w->flags |= WSF_PSK;
+                       }
                }
                }
+               if (strstr(w->wpa_flags, "[WEP]"))
+                       w->flags = WSF_WEP | WSF_PSK | WSF_SECURE;
 
                w->strength.value = w->level.value;
                if (w->strength.value > 110 && w->strength.value < 256)
 
                w->strength.value = w->level.value;
                if (w->strength.value > 110 && w->strength.value < 256)
@@ -254,16 +371,133 @@ dhcpcd_wpa_scans_read(DHCPCD_WPA *wpa)
                        /* Assume quality percentage */
                        w->strength.value = CLAMP(w->strength.value, 0, 100);
                }
                        /* Assume quality percentage */
                        w->strength.value = CLAMP(w->strength.value, 0, 100);
                }
-
        }
        return wis;
 }
 
        }
        return wis;
 }
 
+int
+dhcpcd_wi_scan_compare(DHCPCD_WI_SCAN *a, DHCPCD_WI_SCAN *b)
+{
+       int cmp;
+
+       /* Fist sort non case sensitive, then case sensitive */
+       cmp = strcasecmp(a->ssid, b->ssid);
+       if (cmp == 0)
+               cmp = strcmp(a->ssid, b->ssid);
+
+       /* If still the same, return strongest first */
+       if (cmp == 0)
+               cmp = b->strength.value - a->strength.value;
+
+       return cmp;
+}
+
+/*
+ * This function is copyright 2001 Simon Tatham.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+static DHCPCD_WI_SCAN *
+dhcpcd_wi_scans_sort(DHCPCD_WI_SCAN *list)
+{
+       DHCPCD_WI_SCAN *p, *q, *e, *tail;
+       size_t insize, nmerges, psize, qsize, i;
+
+       /* Silly special case: if `list' was passed in as NULL, return
+        * NULL immediately. */
+       if (!list)
+               return NULL;
+
+       insize = 1;
+
+       for (;;) {
+               p = list;
+               list = tail = NULL;
+               nmerges = 0;  /* count number of merges we do in this pass */
+
+               while (p) {
+                       nmerges++;  /* there exists a merge to be done */
+                       /* step `insize' places along from p */
+                       q = p;
+                       psize = 0;
+                       for (i = 0; i < insize; i++) {
+                               psize++;
+                               q = q->next;
+                               if (!q)
+                                       break;
+                       }
+
+                       /* if q hasn't fallen off end,
+                        * we have two lists to merge */
+                       qsize = insize;
+
+                       /* now we have two lists; merge them */
+                       while (psize > 0 || (qsize > 0 && q)) {
+                               /* decide whether next element of merge comes
+                                * from p or q */
+                               if (psize == 0) {
+                                       /* p is empty; e must come from q. */
+                                       e = q; q = q->next; qsize--;
+                               } else if (qsize == 0 || !q) {
+                                       /* q is empty; e must come from p. */
+                                       e = p; p = p->next; psize--;
+                               } else if (dhcpcd_wi_scan_compare(p, q) <= 0) {
+                                       /* First element of p is lower
+                                        * (or same); e must come from p. */
+                                       e = p; p = p->next; psize--;
+                               } else {
+                                       /* First element of q is lower;
+                                        * e must come from q. */
+                                       e = q; q = q->next; qsize--;
+                               }
+
+                               /* add the next element to the merged list */
+                               if (tail)
+                                       tail->next = e;
+                               else
+                                       list = e;
+                               tail = e;
+                       }
+
+                       /* now p has stepped `insize' places along,
+                        * and q has too */
+                       p = q;
+               }
+               tail->next = NULL;
+
+               /* If we have done only one merge, we're finished. */
+               if (nmerges <= 1)   /* allow for nmerges==0, the empty list */
+                       return list;
+
+               /* Otherwise repeat, merging lists twice the size */
+               insize *= 2;
+       }
+}
+
 DHCPCD_WI_SCAN *
 dhcpcd_wi_scans(DHCPCD_IF *i)
 {
        DHCPCD_WPA *wpa;
 DHCPCD_WI_SCAN *
 dhcpcd_wi_scans(DHCPCD_IF *i)
 {
        DHCPCD_WPA *wpa;
-       DHCPCD_WI_SCAN *wis, *w;
+       DHCPCD_WI_SCAN *wis, *w, *n, *p;
        int nh;
        DHCPCD_WI_HIST *h, *hl;
 
        int nh;
        DHCPCD_WI_HIST *h, *hl;
 
@@ -271,7 +505,30 @@ dhcpcd_wi_scans(DHCPCD_IF *i)
        if (wpa == NULL)
                return NULL;
        wis = dhcpcd_wpa_scans_read(wpa);
        if (wpa == NULL)
                return NULL;
        wis = dhcpcd_wpa_scans_read(wpa);
-       for (w = wis; w; w = w->next) {
+
+       /* Sort the resultant list alphabetically and then by strength */
+       wis = dhcpcd_wi_scans_sort(wis);
+
+       p = NULL;
+       for (w = wis; w && (n = w->next, 1); w = n) {
+               /* Currently we don't support non SSID broadcasting APs */
+               if (*w->ssid == '\0') {
+                       if (p == NULL)
+                               wis = n;
+                       else
+                               p->next = n;
+                       free(w);
+                       continue;
+               }
+               /* Strip duplicated SSIDs, only show the strongest */
+               if (p && strcmp(p->ssid, w->ssid) == 0) {
+                       p->next = n;
+                       free(w);
+                       continue;
+               }
+               /* Remember this as the previos next time */
+               p = w;
+
                nh = 1;
                hl = NULL;
                w->quality.average = w->quality.value;
                nh = 1;
                hl = NULL;
                w->quality.average = w->quality.value;
@@ -319,6 +576,13 @@ dhcpcd_wi_scans(DHCPCD_IF *i)
 }
 
 bool
 }
 
 bool
+dhcpcd_wpa_reconfigure(DHCPCD_WPA *wpa)
+{
+
+       return dhcpcd_wpa_command(wpa, "RECONFIGURE");
+}
+
+bool
 dhcpcd_wpa_reassociate(DHCPCD_WPA *wpa)
 {
 
 dhcpcd_wpa_reassociate(DHCPCD_WPA *wpa)
 {
 
@@ -366,6 +630,13 @@ dhcpcd_wpa_network_enable(DHCPCD_WPA *wpa, int id)
 }
 
 bool
 }
 
 bool
+dhcpcd_wpa_network_select(DHCPCD_WPA *wpa, int id)
+{
+
+       return dhcpcd_wpa_network(wpa, "SELECT_NETWORK", id);
+}
+
+bool
 dhcpcd_wpa_network_remove(DHCPCD_WPA *wpa, int id)
 {
 
 dhcpcd_wpa_network_remove(DHCPCD_WPA *wpa, int id)
 {
 
@@ -409,8 +680,10 @@ dhcpcd_wpa_network_set(DHCPCD_WPA *wpa, int id,
 static int
 dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
 {
 static int
 dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
 {
-       ssize_t bytes;
+       ssize_t bytes, dl, tl;
+       size_t fl;
        char *s, *t, *ssid, *bssid, *flags;
        char *s, *t, *ssid, *bssid, *flags;
+       char dssid[IF_SSIDSIZE], tssid[IF_SSIDSIZE];
        long l;
 
        dhcpcd_realloc(wpa->con, 2048);
        long l;
 
        dhcpcd_realloc(wpa->con, 2048);
@@ -419,10 +692,12 @@ dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
        if (bytes == 0 || bytes == -1)
                return -1;
 
        if (bytes == 0 || bytes == -1)
                return -1;
 
+       fl = strlen(fssid);
+
        s = strchr(wpa->con->buf, '\n');
        if (s == NULL)
                return -1;
        s = strchr(wpa->con->buf, '\n');
        if (s == NULL)
                return -1;
-       while ((t = strsep(&s, "\b"))) {
+       while ((t = strsep(&s, "\b\n"))) {
                if (*t == '\0')
                        continue;
                ssid = strchr(t, '\t');
                if (*t == '\0')
                        continue;
                ssid = strchr(t, '\t');
@@ -442,7 +717,18 @@ dhcpcd_wpa_network_find(DHCPCD_WPA *wpa, const char *fssid)
                        errno = ERANGE;
                        break;
                }
                        errno = ERANGE;
                        break;
                }
-               if (strcmp(ssid, fssid) == 0)
+
+               /* Decode the wpa_supplicant SSID into raw chars and
+                * then encode into our octal escaped string to
+                * compare. */
+               dl = dhcpcd_wpa_decode_ssid(dssid, sizeof(dssid), ssid);
+               if (dl == -1)
+                       return -1;
+               tl = dhcpcd_encode_string_escape(tssid,
+                   sizeof(tssid), dssid, (size_t)dl);
+               if (tl == -1)
+                       return -1;
+               if ((size_t)tl == fl && memcmp(tssid, fssid, (size_t)tl) == 0)
                        return (int)l;
        }
        errno = ENOENT;
                        return (int)l;
        }
        errno = ENOENT;
@@ -468,14 +754,50 @@ dhcpcd_wpa_network_new(DHCPCD_WPA *wpa)
        return (int)l;
 }
 
        return (int)l;
 }
 
+static const char hexchrs[] = "0123456789abcdef";
 int
 dhcpcd_wpa_network_find_new(DHCPCD_WPA *wpa, const char *ssid)
 {
        int id;
 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, i;
+       char *dp;
 
        id = dhcpcd_wpa_network_find(wpa, ssid);
 
        id = dhcpcd_wpa_network_find(wpa, ssid);
-       if (id == -1)
-               id = dhcpcd_wpa_network_new(wpa);
+       if (id != -1)
+               return id;
+
+       dl = dhcpcd_decode_string_escape(dssid, sizeof(dssid), ssid);
+       if (dl == -1)
+               return -1;
+
+       for (i = 0; i < dl; i++) {
+               if (!isascii((int)dssid[i]) && !isprint((int)dssid[i]))
+                       break;
+       }
+       dp = dssid;
+       ep = essid;
+       if (i < dl) {
+               /* Non standard characters found! Encode as hex string */
+               unsigned char c;
+
+               for (; dl; dl--) {
+                       c = (unsigned char)*dp++;
+                       *ep++ = hexchrs[(c & 0xf0) >> 4];
+                       *ep++ = hexchrs[(c & 0x0f)];
+               }
+       } else {
+               *ep++ = '\"';
+               do
+                       *ep++ = *dp;
+               while (*++dp != '\0');
+               *ep++ = '\"';
+       }
+       *ep = '\0';
+
+       id = dhcpcd_wpa_network_new(wpa);
+       if (id != -1)
+               dhcpcd_wpa_network_set(wpa, id, "ssid", essid);
        return id;
 }
 
        return id;
 }
 
@@ -629,6 +951,16 @@ dhcpcd_wpa_set_scan_callback(DHCPCD_CONNECTION *con,
        con->wi_scanresults_context = context;
 }
 
        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)
 {
 void
 dhcpcd_wpa_dispatch(DHCPCD_WPA *wpa)
 {
@@ -664,12 +996,14 @@ dhcpcd_wpa_if_event(DHCPCD_IF *i)
        DHCPCD_WPA *wpa;
 
        assert(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);
                        wpa = dhcpcd_wpa_find(i->con, i->ifname);
                        if (wpa)
                                dhcpcd_wpa_close(wpa);
-               } else if (i->con->wpa_started) {
+               } 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);
                        wpa = dhcpcd_wpa_new(i->con, i->ifname);
                        if (wpa && wpa->listen_fd == -1)
                                dhcpcd_wpa_open(wpa);
@@ -689,55 +1023,123 @@ dhcpcd_wpa_start(DHCPCD_CONNECTION *con)
                dhcpcd_wpa_if_event(i);
 }
 
                dhcpcd_wpa_if_event(i);
 }
 
-int
-dhcpcd_wpa_configure_psk(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s, const char *psk)
+static const char *
+dhcpcd_wpa_var_psk(DHCPCD_WI_SCAN *s)
+{
+
+       if (s->flags & WSF_WEP)
+               return "wep_key0";
+       else if ((s->flags & (WSF_WPA | WSF_PSK)) == (WSF_WPA | WSF_PSK))
+               return "psk";
+       return NULL;
+}
+
+static const char *
+dhcpcd_wpa_var_mgmt(DHCPCD_WI_SCAN *s)
+{
+
+       if (s->flags & WSF_WPA) {
+               if (s->flags & WSF_PSK)
+                       return "WPA-PSK";
+       }
+       return "NONE";
+}
+
+static int
+dhcpcd_wpa_configure1(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s, const char *psk)
 {
        const char *mgmt, *var;
 {
        const char *mgmt, *var;
-       int id;
+       int id, retval;
        char *npsk;
        size_t psk_len;
        bool r;
 
        char *npsk;
        size_t psk_len;
        bool r;
 
-       assert(wpa);
-       assert(s);
+       if (!dhcpcd_wpa_disconnect(wpa))
+               return DHCPCD_WPA_ERR_DISCONN;
+
+       /* reload the configuration so that when we don't save
+        * the disabled networks to the config file. */
+       if (!dhcpcd_wpa_reconfigure(wpa))
+               return DHCPCD_WPA_ERR_RECONF;
 
        id = dhcpcd_wpa_network_find_new(wpa, s->ssid);
        if (id == -1)
                return DHCPCD_WPA_ERR;
 
 
        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";
-       }
+       mgmt = dhcpcd_wpa_var_mgmt(s);
+       var = dhcpcd_wpa_var_psk(s);
+       if (mgmt && var) {
+               if (!dhcpcd_wpa_network_set(wpa, id, "key_mgmt", mgmt))
+                       return DHCPCD_WPA_ERR_SET;
 
 
-       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 (psk)
-               psk_len = strlen(psk);
+       if (!dhcpcd_wpa_network_enable(wpa, id))
+               return DHCPCD_WPA_ERR_ENABLE;
+       if (dhcpcd_wpa_config_write(wpa))
+               retval = DHCPCD_WPA_SUCCESS;
        else
        else
-               psk_len = 0;
-       npsk = malloc(psk_len + 3);
-       if (npsk == NULL)
+               retval = DHCPCD_WPA_ERR_WRITE;
+       /* Selecting a network disbales the others.
+        * This should not be saved. */
+       if (!dhcpcd_wpa_network_select(wpa, id))
+               return DHCPCD_WPA_ERR_SELECT;
+       return retval;
+}
+
+int
+dhcpcd_wpa_configure(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s, const char *psk)
+{
+       int retval;
+
+       retval = dhcpcd_wpa_configure1(wpa, s, psk);
+       /* Always reassociate */
+       if (!dhcpcd_wpa_reassociate(wpa)) {
+               if (retval == DHCPCD_WPA_SUCCESS)
+                       retval = DHCPCD_WPA_ERR_ASSOC;
+       }
+       return retval;
+}
+
+int
+dhcpcd_wpa_select(DHCPCD_WPA *wpa, DHCPCD_WI_SCAN *s)
+{
+       int id, retval;
+
+       assert(wpa);
+       assert(s);
+
+       id = dhcpcd_wpa_network_find(wpa, s->ssid);
+       if (id == -1)
                return DHCPCD_WPA_ERR;
                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;
+       if (!dhcpcd_wpa_disconnect(wpa))
+               retval = DHCPCD_WPA_ERR_DISCONN;
+       else if (!dhcpcd_wpa_network_select(wpa, id))
+               retval = DHCPCD_WPA_ERR_SELECT;
+       else
+               retval = DHCPCD_WPA_SUCCESS;
+
+       /* Always reassociate */
+       if (!dhcpcd_wpa_reassociate(wpa)) {
+               if (retval == DHCPCD_WPA_SUCCESS)
+                       retval = DHCPCD_WPA_ERR_ASSOC;
+       }
+       return retval;
 }
 }