Add dhcpcd_decode which decodes a dhcpcd encoded string variable.
authorRoy Marples <roy@marples.name>
Sun, 28 Sep 2014 11:04:03 +0000 (11:04 +0000)
committerRoy Marples <roy@marples.name>
Sun, 28 Sep 2014 11:04:03 +0000 (11:04 +0000)
Add dhcpcd_decode_shell which only decodes shell escaped characters from
a dhcpcd encoded string variable.

configure
src/libdhcpcd/Makefile
src/libdhcpcd/dhcpcd.c
src/libdhcpcd/dhcpcd.h
src/libdhcpcd/unvis.c [new file with mode: 0644]

index 879aad41c58d6cf427c93376513e8915fcd3db35..c5c732b0957d8818863b175c65b52da54068f32c 100755 (executable)
--- a/configure
+++ b/configure
@@ -303,6 +303,40 @@ if [ "$STRLCPY" = no ]; then
            >>$CONFIG_H
 fi
 
+if [ -z "$STRNUNVIS" ]; then
+       printf "Testing for strnunvis ... "
+       cat <<EOF >_strnunvis.c
+#include <vis.h>
+#ifdef __FreeBSD__
+#error FreeBSD has broken strnunvis support
+#endif
+#ifdef __NetBSD_Version__
+#  if __NetBSD_Version__ <799000200
+#  error Upgrade NetBSD to 7.99.2 to get a working strnunvis
+#  endif
+#endif
+
+int main(void) {
+       char s[10];
+       strunvis(s, 0, s);
+       return 0;
+}
+EOF
+       if $XCC _strnunvis.c -o _strnunvis 2>&3; then
+               STRNUNVIS=yes
+       else
+               STRNUNVIS=no
+       fi
+       echo "$STRNUNVIS"
+       rm -f _strnunvis.c _strnunvis
+fi
+if [ "$STRNUNVIS" = no ]; then
+       echo "UNVIS_SRC=        unvis.c" >>$CONFIG_MK
+       echo "#define strnunvis dhcpcd_strnunvis" >>$CONFIG_H
+else
+       echo "#define HAVE_VIS_H" >>$CONFIG_H
+fi
+
 if [ -z "$STRVERSCMP" ]; then
        printf "Testing for strverscmp ... "
        cat <<EOF >_strverscmp.c
index 4deacdb451bb3589c4819b0534503c88944862cd..f0c0857ec80beaf75022b9653fc505191f72723d 100644 (file)
@@ -1,6 +1,6 @@
 LIB=           dhcpcd
 SHLIB_MAJOR=   1
-SRCS=          dhcpcd.c config.c wpa.c
+SRCS=          dhcpcd.c config.c wpa.c ${UNVIS_SRC}
 INCS=          dhcpcd.h
 
 TOPDIR=                ../..
index 3413b4403f6770b5abe43449b8c4d19cfe1e8b6d..522ad18c8cd1728bc02a7a896f1764325469a552 100644 (file)
@@ -34,6 +34,7 @@
 #include <arpa/inet.h>
 
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <libintl.h>
 #define _(a) (a)
 #endif
 
+#ifdef HAVE_VIS_H
+#include <vis.h>
+#endif
+
 #ifndef SUN_LEN
 #define SUN_LEN(su) \
        (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
 #endif
 
+#ifndef iswhite
+#define iswhite(c)     (c == ' ' || c == '\t' || c == '\n')
+#endif
+
 static const char * const dhcpcd_types[] =
     { "link", "ipv4", "ra", "dhcp6", NULL };
 
@@ -242,6 +251,64 @@ dhcpcd_get_value(const DHCPCD_IF *i, const char *var)
        return get_value(i->data, i->data_len, var);
 }
 
+ssize_t
+dhcpcd_decode(char *dst, size_t dlen, const char *src)
+{
+
+       assert(dst);
+       assert(src);
+       return strnunvis(dst, dlen, src);
+}
+
+ssize_t
+dhcpcd_decode_shell(char *dst, size_t dlen, const char *src)
+{
+       char *tmp, *p, c, *e, *d;
+       ssize_t l;
+
+       assert(dst);
+       assert(src);
+
+       tmp = malloc(dlen);
+       if (tmp == NULL)
+               return -1;
+       if ((l = dhcpcd_decode(tmp, dlen, src)) == -1) {
+               free(tmp);
+               return -1;
+       }
+
+       p = tmp;
+       e = tmp + l;
+       d = dst;
+       while (p < e) {
+               c = *p++;
+
+               if (isascii(c) && (isgraph(c) || iswhite(c))) {
+                       if (dlen < 2) {
+                               errno = ENOSPC;
+                               return -1;
+                       }
+                       *d++ = c;
+                       dlen--;
+                       continue;
+               }
+               
+               if (dlen < 5) {
+                       errno = ENOSPC;
+                       return -1;
+               }
+               *d++ = '\\';
+               *d++ = (((unsigned char)c >> 6) & 03) + '0';
+               *d++ = (((unsigned char)c >> 3) & 07) + '0';
+               *d++ = ( (unsigned char)c       & 07) + '0';
+               dlen -= 4;
+       }
+       *d = '\0';
+       free(tmp);
+
+       return d - dst;
+}
+
 const char *
 dhcpcd_get_prefix_value(const DHCPCD_IF *i, const char *prefix, const char *var)
 {
@@ -350,7 +417,7 @@ dhcpcd_get_if(DHCPCD_CONNECTION *con, const char *ifname, const char *type)
 static DHCPCD_IF *
 dhcpcd_new_if(DHCPCD_CONNECTION *con, char *data, size_t len)
 {
-       const char *ifname, *ifclass, *reason, *type, *order, *flags;
+       const char *ifname, *ifclass, *reason, *type, *order, *flags, *ssid;
        char *orderdup, *o, *p;
        DHCPCD_IF *e, *i, *l, *n, *nl;
        int ti;
@@ -474,7 +541,10 @@ dhcpcd_new_if(DHCPCD_CONNECTION *con, char *data, size_t len)
        else
                i->up = strtobool(dhcpcd_get_value(i, "if_up="));
        i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless="));
-       i->ssid = dhcpcd_get_value(i, i->up ? "new_ssid=" : "old_ssid=");
+       ssid = dhcpcd_get_value(i, i->up ? "new_ssid=" : "old_ssid=");
+       if (ssid == NULL ||
+           dhcpcd_decode_shell(i->ssid, sizeof(i->ssid), ssid) == -1)
+               *i->ssid = '\0';
 
        /* Sort! */
        n = nl = NULL;
index 68f9a88eb1360d7cca647fd284dfb77b6613ee25..2adb4da676ca8b244d308e1853ba73b83bae0756 100644 (file)
@@ -84,7 +84,7 @@ typedef struct dhcpcd_if {
        unsigned int flags;
        bool up;
        bool wireless;
-       const char *ssid;
+       char ssid[IF_SSIDSIZE];
 
        char *data;
        size_t data_len;
@@ -103,7 +103,7 @@ typedef struct dhcpcd_if {
        int flags;
        bool up;
        bool wireless;
-       const char *ssid;
+       char ssid[IF_SSIDSIZE];
 } DHCPCD_IF;
 #endif
 
@@ -200,6 +200,12 @@ DHCPCD_CONNECTION * dhcpcd_if_connection(DHCPCD_IF *);
 const char *dhcpcd_get_value(const DHCPCD_IF *, const char *);
 const char *dhcpcd_get_prefix_value(const DHCPCD_IF *, const char *,
     const char *);
+#ifdef IN_LIBDHCPCD
+/* This function only exists if libc does not provide a working version */
+int dhcpcd_strnunvis(char *dst, size_t dlen, const char *src);
+#endif
+ssize_t dhcpcd_decode(char *dst, size_t dlen, const char *src);
+ssize_t dhcpcd_decode_shell(char *dst, size_t dlen, const char *src);
 char * dhcpcd_if_message(DHCPCD_IF *i, bool *new_msg);
 
 ssize_t dhcpcd_command(DHCPCD_CONNECTION *, const char *, char **);
diff --git a/src/libdhcpcd/unvis.c b/src/libdhcpcd/unvis.c
new file mode 100644 (file)
index 0000000..e143e36
--- /dev/null
@@ -0,0 +1,331 @@
+/*     $NetBSD: unvis.c,v 1.44 2014/09/26 15:43:36 roy Exp $   */
+
+/*-
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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 University 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 REGENTS 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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define IN_LIBDHCPCD
+#include "dhcpcd.h"
+
+#ifndef __arraycount
+#define __arraycount(__x)       (sizeof(__x) / sizeof(__x[0]))
+#endif
+
+/*
+ * decode driven by state machine
+ */
+#define        S_GROUND        0       /* haven't seen escape char */
+#define        S_START         1       /* start decoding special sequence */
+#define        S_META          2       /* metachar started (M) */
+#define        S_META1         3       /* metachar more, regular char (-) */
+#define        S_CTRL          4       /* control char started (^) */
+#define        S_OCTAL2        5       /* octal digit 2 */
+#define        S_OCTAL3        6       /* octal digit 3 */
+#define        S_HEX           7       /* mandatory hex digit */
+#define        S_HEX1          8       /* http hex digit */
+#define        S_HEX2          9       /* http hex digit 2 */
+
+#define        isoctal(c)      (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define        xtod(c)         (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
+
+#define _VIS_END        0x0800  /* for unvis */
+#define UNVIS_END      _VIS_END
+
+/*
+ * unvis return codes
+ */
+#define        UNVIS_VALID      1      /* character valid */
+#define        UNVIS_VALIDPUSH  2      /* character valid, push back passed char */
+#define        UNVIS_NOCHAR     3      /* valid sequence, no character produced */
+#define        UNVIS_SYNBAD    -1      /* unrecognized escape sequence */
+#define        UNVIS_ERROR     -2      /* decoder in unknown state (unrecoverable) */
+
+/*
+ * unvis - decode characters previously encoded by vis
+ */
+static int
+unvis(char *cp, char c, int *astate, int flag)
+{
+       unsigned char uc = (unsigned char)c;
+       unsigned char st;
+
+/*
+ * Bottom 8 bits of astate hold the state machine state.
+ * Top 8 bits hold the current character in the http 1866 nv string decoding
+ */
+#define GS(a)          ((a) & 0xff)
+#define SS(a, b)       (((uint32_t)(a) << 24) | (b))
+#define GI(a)          ((uint32_t)(a) >> 24)
+
+       st = GS(*astate);
+
+       if (flag & UNVIS_END) {
+               switch (st) {
+               case S_OCTAL2:
+               case S_OCTAL3:
+               case S_HEX2:
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case S_GROUND:
+                       return UNVIS_NOCHAR;
+               default:
+                       return UNVIS_SYNBAD;
+               }
+       }
+
+       switch (st) {
+
+       case S_GROUND:
+               *cp = 0;
+               if (c == '\\') {
+                       *astate = SS(0, S_START);
+                       return UNVIS_NOCHAR;
+               }
+               *cp = c;
+               return UNVIS_VALID;
+
+       case S_START:
+               switch(c) {
+               case '\\':
+                       *cp = c;
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case '0': case '1': case '2': case '3':
+               case '4': case '5': case '6': case '7':
+                       *cp = (c - '0');
+                       *astate = SS(0, S_OCTAL2);
+                       return UNVIS_NOCHAR;
+               case 'M':
+                       *cp = (char)0200;
+                       *astate = SS(0, S_META);
+                       return UNVIS_NOCHAR;
+               case '^':
+                       *astate = SS(0, S_CTRL);
+                       return UNVIS_NOCHAR;
+               case 'n':
+                       *cp = '\n';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 'r':
+                       *cp = '\r';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 'b':
+                       *cp = '\b';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 'a':
+                       *cp = '\007';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 'v':
+                       *cp = '\v';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 't':
+                       *cp = '\t';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 'f':
+                       *cp = '\f';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 's':
+                       *cp = ' ';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 'E':
+                       *cp = '\033';
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_VALID;
+               case 'x':
+                       *astate = SS(0, S_HEX);
+                       return UNVIS_NOCHAR;
+               case '\n':
+                       /*
+                        * hidden newline
+                        */
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_NOCHAR;
+               case '$':
+                       /*
+                        * hidden marker
+                        */
+                       *astate = SS(0, S_GROUND);
+                       return UNVIS_NOCHAR;
+               default:
+                       if (isgraph(c)) {
+                               *cp = c;
+                               *astate = SS(0, S_GROUND);
+                               return UNVIS_VALID;
+                       }
+               }
+               goto bad;
+
+       case S_META:
+               if (c == '-')
+                       *astate = SS(0, S_META1);
+               else if (c == '^')
+                       *astate = SS(0, S_CTRL);
+               else 
+                       goto bad;
+               return UNVIS_NOCHAR;
+
+       case S_META1:
+               *astate = SS(0, S_GROUND);
+               *cp |= c;
+               return UNVIS_VALID;
+
+       case S_CTRL:
+               if (c == '?')
+                       *cp |= 0177;
+               else
+                       *cp |= c & 037;
+               *astate = SS(0, S_GROUND);
+               return UNVIS_VALID;
+
+       case S_OCTAL2:  /* second possible octal digit */
+               if (isoctal(uc)) {
+                       /*
+                        * yes - and maybe a third
+                        */
+                       *cp = (char)((*cp << 3) + (c - '0'));
+                       *astate = SS(0, S_OCTAL3);
+                       return UNVIS_NOCHAR;
+               }
+               /*
+                * no - done with current sequence, push back passed char
+                */
+               *astate = SS(0, S_GROUND);
+               return UNVIS_VALIDPUSH;
+
+       case S_OCTAL3:  /* third possible octal digit */
+               *astate = SS(0, S_GROUND);
+               if (isoctal(uc)) {
+                       *cp = (char)((*cp << 3) + (c - '0'));
+                       return UNVIS_VALID;
+               }
+               /*
+                * we were done, push back passed char
+                */
+               return UNVIS_VALIDPUSH;
+
+       case S_HEX:
+               if (!isxdigit(uc))
+                       goto bad;
+               /*FALLTHROUGH*/
+       case S_HEX1:
+               if (isxdigit(uc)) {
+                       *cp = (char)xtod(uc);
+                       *astate = SS(0, S_HEX2);
+                       return UNVIS_NOCHAR;
+               }
+               /*
+                * no - done with current sequence, push back passed char
+                */
+               *astate = SS(0, S_GROUND);
+               return UNVIS_VALIDPUSH;
+
+       case S_HEX2:
+               *astate = S_GROUND;
+               if (isxdigit(uc)) {
+                       *cp = (char)(xtod(uc) | (*cp << 4));
+                       return UNVIS_VALID;
+               }
+               return UNVIS_VALIDPUSH;
+
+       default:
+       bad:
+               /*
+                * decoder in unknown state - (probably uninitialized)
+                */
+               *astate = SS(0, S_GROUND);
+               return UNVIS_SYNBAD;
+       }
+}
+
+/*
+ * strnunvisx - decode src into dst
+ *
+ *     Number of chars decoded into dst is returned, -1 on error.
+ *     Dst is null terminated.
+ */
+
+int
+dhcpcd_strnunvis(char *dst, size_t dlen, const char *src)
+{
+       char c;
+       char t = '\0', *start = dst;
+       int state = 0;
+
+#define CHECKSPACE() \
+       do { \
+               if (dlen-- == 0) { \
+                       errno = ENOSPC; \
+                       return -1; \
+               } \
+       } while (/*CONSTCOND*/0)
+
+       while ((c = *src++) != '\0') {
+ again:
+               switch (unvis(&t, c, &state, 0)) {
+               case UNVIS_VALID:
+                       CHECKSPACE();
+                       *dst++ = t;
+                       break;
+               case UNVIS_VALIDPUSH:
+                       CHECKSPACE();
+                       *dst++ = t;
+                       goto again;
+               case 0:
+               case UNVIS_NOCHAR:
+                       break;
+               case UNVIS_SYNBAD:
+                       errno = EINVAL;
+                       return -1;
+               default:
+                       errno = EINVAL;
+                       return -1;
+               }
+       }
+       if (unvis(&t, c, &state, UNVIS_END) == UNVIS_VALID) {
+               CHECKSPACE();
+               *dst++ = t;
+       }
+       CHECKSPACE();
+       *dst = '\0';
+       return (int)(dst - start);
+}