OSDN Git Service

v28
[android-x86/external-wireless-tools.git] / wireless_tools / iwlib.c
index b78882f..7d5f1a4 100644 (file)
@@ -177,7 +177,8 @@ iw_get_ifname(char *        name,   /* Where to store the name */
   memcpy(name, buf, (end - buf));
   name[end - buf] = '\0';
 
-  return(end + 2);
+  /* Return value currently unused, just make sure it's non-NULL */
+  return(end);
 }
 
 /*------------------------------------------------------------------*/
@@ -201,7 +202,7 @@ iw_enum_devices(int         skfd,
   int          i;
 
 #ifndef IW_RESTRIC_ENUM
-  /* Check if /proc/net/wireless is available */
+  /* Check if /proc/net/dev is available */
   fh = fopen(PROC_NET_DEV, "r");
 #else
   /* Check if /proc/net/wireless is available */
@@ -222,16 +223,23 @@ iw_enum_devices(int               skfd,
          char  name[IFNAMSIZ + 1];
          char *s;
 
+         /* Skip empty or almost empty lines. It seems that in some
+          * cases fgets return a line with only a newline. */
+         if((buff[0] == '\0') || (buff[1] == '\0'))
+           continue;
+
          /* Extract interface name */
          s = iw_get_ifname(name, sizeof(name), buff);
 
          if(!s)
-           /* Failed to parse, complain and continue */
+           {
+             /* Failed to parse, complain and continue */
 #ifndef IW_RESTRIC_ENUM
-           fprintf(stderr, "Cannot parse " PROC_NET_DEV "\n");
+             fprintf(stderr, "Cannot parse " PROC_NET_DEV "\n");
 #else
-           fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
+             fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
 #endif
+           }
          else
            /* Got it, print info about this interface */
            (*fn)(skfd, name, args, count);
@@ -510,7 +518,7 @@ iw_get_range_info(int               skfd,
 
       /* We don't like future versions of WE, because we can't cope with
        * the unknown */
-      if(range->we_version_compiled > WE_VERSION)
+      if(range->we_version_compiled > WE_MAX_VERSION)
        {
          fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled);
          fprintf(stderr, "of Wireless Extension, while this program supports up to version %d.\n", WE_VERSION);
@@ -790,9 +798,14 @@ iw_set_basic_config(int                    skfd,
    */
   if(info->has_essid)
     {
+      int              we_kernel_version;
+      we_kernel_version = iw_get_kernel_we_version();
+
       wrq.u.essid.pointer = (caddr_t) info->essid;
       wrq.u.essid.length = strlen(info->essid) + 1;
       wrq.u.data.flags = info->essid_on;
+      if(we_kernel_version > 20)
+       wrq.u.essid.length--;
 
       if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
        {
@@ -1203,6 +1216,7 @@ iw_print_txpower(char *                   buffer,
 /*------------------------------------------------------------------*/
 /*
  * Read /proc/net/wireless to get the latest statistics
+ * Note : strtok not thread safe, not used in WE-12 and later.
  */
 int
 iw_get_stats(int               skfd,
@@ -1308,10 +1322,12 @@ iw_print_stats(char *           buffer,
    * Relative/percent values are always encoded unsigned, between 0 and 255.
    * Absolute/dBm values are always encoded negative, between -255 and 0.
    *
-   * How do we separate relative from absolute values ? We use the
-   * range to do that. The range allow to specify the real min/max
-   * of the value. As the range struct only specify one bound of the
-   * value, we assume that the other bound is 0 (zero).
+   * How do we separate relative from absolute values ?
+   * The old way is to use the range to do that. As of WE-19, we have
+   * an explicit IW_QUAL_DBM flag in updated...
+   * The range allow to specify the real min/max of the value. As the
+   * range struct only specify one bound of the value, we assume that
+   * the other bound is 0 (zero).
    * For relative values, range is [0 ; range->max].
    * For absolute values, range is [range->max ; 0].
    *
@@ -1323,8 +1339,11 @@ iw_print_stats(char *            buffer,
    * Jean II
    */
 
-  /* Just do it */
-  if(has_range && (qual->level != 0))
+  /* Just do it...
+   * The old way to detect dBm require both the range and a non-null
+   * level (which confuse the test). The new way can deal with level of 0
+   * because it does an explicit test on the flag. */
+  if(has_range && ((qual->level != 0) || (qual->updated & IW_QUAL_DBM)))
     {
       /* Deal with quality : always a relative value */
       if(!(qual->updated & IW_QUAL_QUAL_INVALID))
@@ -1336,8 +1355,9 @@ iw_print_stats(char *             buffer,
          buflen -= len;
        }
 
-      /* If the statistics are in dBm or relative */
-      if(qual->level > range->max_qual.level)
+      /* Check if the statistics are in dBm or relative */
+      if((qual->updated & IW_QUAL_DBM)
+        || (qual->level > range->max_qual.level))
        {
          /* Deal with signal level in dBm  (absolute power measurement) */
          if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
@@ -1462,6 +1482,7 @@ iw_pass_key(const char *  input,
 /*
  * Parse a key from the command line.
  * Return size of the key, or 0 (no key) or -1 (error)
+ * If the key is too long, it's simply truncated...
  */
 int
 iw_in_key(const char *         input,
@@ -1476,7 +1497,7 @@ iw_in_key(const char *            input,
       keylen = strlen(input + 2);              /* skip "s:" */
       if(keylen > IW_ENCODING_TOKEN_MAX)
        keylen = IW_ENCODING_TOKEN_MAX;
-      strncpy(key, input + 2, keylen);
+      memcpy(key, input + 2, keylen);
     }
   else
     if(!strncmp(input, "p:", 2))
@@ -1486,56 +1507,66 @@ iw_in_key(const char *          input,
       }
     else
       {
-       char *  buff;
-       char *  hex;
-       char *  out;
-       char *  p;
+       const char *    p;
+       int             dlen;   /* Digits sequence length */
+       unsigned char   out[IW_ENCODING_TOKEN_MAX];
 
        /* Third case : as hexadecimal digits */
-       buff = malloc(IW_ENCODING_TOKEN_MAX + strlen(input) + 1);
-       if(buff == NULL)
-         {
-           fprintf(stderr, "Malloc failed (string too long ?)\n");
-           return(-1);
-         }
-       /* Preserve original buffers (both in & out) */
-       hex = buff + IW_ENCODING_TOKEN_MAX;
-       strcpy(hex, input);                             /* Size checked */
-       out = buff;
-
-       /* Parse */
-       p = strtok(hex, "-:;.,");
-       while((p != (char *) NULL) && (keylen < IW_ENCODING_TOKEN_MAX))
+       p = input;
+       dlen = -1;
+
+       /* Loop until we run out of chars in input or overflow the output */
+       while(*p != '\0')
          {
            int temph;
            int templ;
            int count;
-           int len;
+           /* No more chars in this sequence */
+           if(dlen <= 0)
+             {
+               /* Skip separator */
+               if(dlen == 0)
+                 p++;
+               /* Calculate num of char to next separator */
+               dlen = strcspn(p, "-:;.,");
+             }
            /* Get each char separatly (and not by two) so that we don't
             * get confused by 'enc' (=> '0E'+'0C') and similar */
            count = sscanf(p, "%1X%1X", &temph, &templ);
            if(count < 1)
              return(-1);               /* Error -> non-hex char */
            /* Fixup odd strings such as '123' is '01'+'23' and not '12'+'03'*/
-           len = strlen(p);
-           if(len % 2)
+           if(dlen % 2)
              count = 1;
-           /* Put back two chars as one byte */
+           /* Put back two chars as one byte and output */
            if(count == 2)
              templ |= temph << 4;
            else
              templ = temph;
            out[keylen++] = (unsigned char) (templ & 0xFF);
-           /* Check where to get next char from */
-           if(len > count)     /* Token not finished yet */
-             p += count;
-           else
-             p = strtok((char *) NULL, "-:;.,");
+           /* Check overflow in output */
+           if(keylen >= IW_ENCODING_TOKEN_MAX)
+             break;
+           /* Move on to next chars */
+           p += count;
+           dlen -= count;
          }
+       /* We use a temporary output buffer 'out' so that if there is
+        * an error, we don't overwrite the original key buffer.
+        * Because of the way iwconfig loop on multiple key/enc arguments
+        * until it finds an error in here, this is necessary to avoid
+        * silently corrupting the encryption key... */
        memcpy(key, out, keylen);
-       free(buff);
       }
 
+#ifdef DEBUG
+  {
+    char buf[IW_ENCODING_TOKEN_MAX * 3];
+    iw_print_key(buf, sizeof(buf), key, keylen, 0);
+    printf("Got key : %d [%s]\n", keylen, buf);
+  }
+#endif
+
   return(keylen);
 }
 
@@ -1566,7 +1597,7 @@ iw_in_key_full(int                skfd,
       memcpy(key, input + 2, keylen);
 
       /* Separate the two strings */
-      p = strchr(key, ':');
+      p = strchr((char *) key, ':');
       if(p == NULL)
        {
          fprintf(stderr, "Error: Invalid login format\n");
@@ -1768,16 +1799,17 @@ iw_print_retry_value(char *     buffer,
  * Inspired from irdadump...
  */
 void
-iw_print_timeval(char *                        buffer,
-                int                    buflen,
-                const struct timeval * time)
+iw_print_timeval(char *                                buffer,
+                int                            buflen,
+                const struct timeval *         timev,
+                const struct timezone *        tz)
 {
         int s;
 
-       s = (time->tv_sec) % 86400;
-       snprintf(buffer, buflen, "%02d:%02d:%02d.%06u ", 
+       s = (timev->tv_sec - tz->tz_minuteswest * 60) % 86400;
+       snprintf(buffer, buflen, "%02d:%02d:%02d.%06u", 
                s / 3600, (s % 3600) / 60, 
-               s % 60, (u_int32_t) time->tv_usec);
+               s % 60, (u_int32_t) timev->tv_usec);
 }
 
 /*********************** ADDRESS SUBROUTINES ************************/
@@ -1793,14 +1825,15 @@ iw_print_timeval(char *                 buffer,
  */
 int
 iw_check_mac_addr_type(int             skfd,
-                      char *           ifname)
+                      const char *     ifname)
 {
   struct ifreq         ifr;
 
   /* Get the type of hardware address */
   strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
   if((ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) ||
-     (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER))
+     ((ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
+      && (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211)))
     {
       /* Deep trouble... */
       fprintf(stderr, "Interface %s doesn't support MAC addresses\n",
@@ -1809,8 +1842,11 @@ iw_check_mac_addr_type(int               skfd,
     }
 
 #ifdef DEBUG
-  printf("Hardware : %d - %s\n", ifr.ifr_hwaddr.sa_family,
-        iw_ether_ntoa((struct ether_addr *) ifr.ifr_hwaddr.sa_data));
+  {
+    char buf[20];
+    printf("Hardware : %d - %s\n", ifr.ifr_hwaddr.sa_family,
+          iw_saether_ntop(&ifr.ifr_hwaddr, buf));
+  }
 #endif
 
   return(0);
@@ -1823,7 +1859,7 @@ iw_check_mac_addr_type(int                skfd,
  */
 int
 iw_check_if_addr_type(int              skfd,
-                     char *            ifname)
+                     const char *      ifname)
 {
   struct ifreq         ifr;
 
@@ -1866,6 +1902,7 @@ iw_check_addr_type(int            skfd,
 }
 #endif
 
+#if 0
 /*------------------------------------------------------------------*/
 /*
  * Ask the kernel for the MAC address of an interface.
@@ -1890,13 +1927,40 @@ iw_get_mac_addr(int                     skfd,
   *ptype = ifr.ifr_hwaddr.sa_family;
   return(ret);
 }
+#endif
+
+/*------------------------------------------------------------------*/
+/*
+ * Display an arbitrary length MAC address in readable format.
+ */
+char *
+iw_mac_ntop(const unsigned char *      mac,
+           int                         maclen,
+           char *                      buf,
+           int                         buflen)
+{
+  int  i;
+
+  /* Overflow check (don't forget '\0') */
+  if(buflen < (maclen * 3 - 1 + 1))
+    return(NULL);
+
+  /* First byte */
+  sprintf(buf, "%02X", mac[0]);
+
+  /* Other bytes */
+  for(i = 1; i < maclen; i++)
+    sprintf(buf + (i * 3) - 1, ":%02X", mac[i]);
+  return(buf);
+}
 
 /*------------------------------------------------------------------*/
 /*
  * Display an Ethernet address in readable format.
  */
 void
-iw_ether_ntop(const struct ether_addr* eth, char* buf)
+iw_ether_ntop(const struct ether_addr *        eth,
+             char *                    buf)
 {
   sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
          eth->ether_addr_octet[0], eth->ether_addr_octet[1],
@@ -1906,70 +1970,111 @@ iw_ether_ntop(const struct ether_addr* eth, char* buf)
 
 /*------------------------------------------------------------------*/
 /*
- * Display an Ethernet address in readable format.
- * Same with a static buffer
+ * Display an Wireless Access Point Socket Address in readable format.
+ * Note : 0x44 is an accident of history, that's what the Orinoco/PrismII
+ * chipset report, and the driver doesn't filter it.
  */
 char *
-iw_ether_ntoa(const struct ether_addr* eth)
+iw_sawap_ntop(const struct sockaddr *  sap,
+             char *                    buf)
 {
-  static char buf[20];
-  iw_ether_ntop(eth, buf);
-  return buf;
+  const struct ether_addr ether_zero = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }};
+  const struct ether_addr ether_bcast = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }};
+  const struct ether_addr ether_hack = {{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }};
+  const struct ether_addr * ether_wap = (const struct ether_addr *) sap->sa_data;
+
+  if(!iw_ether_cmp(ether_wap, &ether_zero))
+    sprintf(buf, "Not-Associated");
+  else
+    if(!iw_ether_cmp(ether_wap, &ether_bcast))
+      sprintf(buf, "Invalid");
+    else
+      if(!iw_ether_cmp(ether_wap, &ether_hack))
+       sprintf(buf, "None");
+      else
+       iw_ether_ntop(ether_wap, buf);
+  return(buf);
 }
 
 /*------------------------------------------------------------------*/
 /*
- * Input an Ethernet address and convert to binary.
+ * Input an arbitrary length MAC address and convert to binary.
+ * Return address size.
  */
 int
-iw_ether_aton(const char *orig, struct ether_addr *eth)
+iw_mac_aton(const char *       orig,
+           unsigned char *     mac,
+           int                 macmax)
 {
-  const char *bufp;
-  int i;
-
-  i = 0;
-  for(bufp = orig; *bufp != '\0'; ++bufp) {
-       unsigned int val;
-       unsigned char c = *bufp++;
-       if (isdigit(c)) val = c - '0';
-       else if (c >= 'a' && c <= 'f') val = c - 'a' + 10;
-       else if (c >= 'A' && c <= 'F') val = c - 'A' + 10;
-       else break;
-
-       val <<= 4;
-       c = *bufp++;
-       if (isdigit(c)) val |= c - '0';
-       else if (c >= 'a' && c <= 'f') val |= c - 'a' + 10;
-       else if (c >= 'A' && c <= 'F') val |= c - 'A' + 10;
-       else break;
-
-       eth->ether_addr_octet[i] = (unsigned char) (val & 0377);
-       if(++i == ETH_ALEN) {
-               /* That's it.  Any trailing junk? */
-               if (*bufp != '\0') {
+  const char * p = orig;
+  int          maclen = 0;
+
+  /* Loop on all bytes of the string */
+  while(*p != '\0')
+    {
+      int      temph;
+      int      templ;
+      int      count;
+      /* Extract one byte as two chars */
+      count = sscanf(p, "%1X%1X", &temph, &templ);
+      if(count != 2)
+       break;                  /* Error -> non-hex chars */
+      /* Output two chars as one byte */
+      templ |= temph << 4;
+      mac[maclen++] = (unsigned char) (templ & 0xFF);
+
+      /* Check end of string */
+      p += 2;
+      if(*p == '\0')
+       {
 #ifdef DEBUG
-                       fprintf(stderr, "iw_ether_aton(%s): trailing junk!\n", orig);
-                       errno = EINVAL;
-                       return(0);
+         char buf[20];
+         iw_ether_ntop((const struct ether_addr *) mac, buf);
+         fprintf(stderr, "iw_mac_aton(%s): %s\n", orig, buf);
 #endif
-               }
+         return(maclen);               /* Normal exit */
+       }
+
+      /* Check overflow */
+      if(maclen >= macmax)
+       {
 #ifdef DEBUG
-               fprintf(stderr, "iw_ether_aton(%s): %s\n",
-                       orig, ether_ntoa(eth));
+         fprintf(stderr, "iw_mac_aton(%s): trailing junk!\n", orig);
 #endif
-               return(1);
+         errno = E2BIG;
+         return(0);                    /* Error -> overflow */
        }
-       if (*bufp != ':')
-               break;
-  }
 
+      /* Check separator */
+      if(*p != ':')
+       break;
+      p++;
+    }
+
+  /* Error... */
 #ifdef DEBUG
-  fprintf(stderr, "iw_ether_aton(%s): invalid ether address!\n", orig);
+  fprintf(stderr, "iw_mac_aton(%s): invalid ether address!\n", orig);
 #endif
   errno = EINVAL;
   return(0);
 }
 
+/*------------------------------------------------------------------*/
+/*
+ * Input an Ethernet address and convert to binary.
+ */
+int
+iw_ether_aton(const char *orig, struct ether_addr *eth)
+{
+  int  maclen;
+  maclen = iw_mac_aton(orig, (unsigned char *) eth, ETH_ALEN);
+  if((maclen > 0) && (maclen < ETH_ALEN))
+    {
+      errno = EINVAL;
+      maclen = 0;
+    }
+  return(maclen);
+}
 
 /*------------------------------------------------------------------*/
 /*
@@ -2015,7 +2120,7 @@ iw_in_inet(char *name, struct sockaddr *sap)
  */
 int
 iw_in_addr(int         skfd,
-          char *       ifname,
+          const char * ifname,
           char *       bufp,
           struct sockaddr *sap)
 {
@@ -2062,8 +2167,11 @@ iw_in_addr(int           skfd,
             sizeof(struct sockaddr));
 
 #ifdef DEBUG
-      printf("IP Address %s => Hw Address = %s\n",
-            bufp, iw_ether_ntoa((struct ether_addr *) sap->sa_data));
+      {
+       char buf[20];
+       printf("IP Address %s => Hw Address = %s\n",
+              bufp, iw_saether_ntop(sap, buf));
+      }
 #endif
     }
   else /* If it's an hardware address */
@@ -2076,7 +2184,7 @@ iw_in_addr(int            skfd,
        }
 
       /* Get the hardware address */
-      if(iw_in_ether(bufp, sap) < 0)
+      if(iw_saether_aton(bufp, sap) == 0)
        {
          fprintf(stderr, "Invalid hardware address %s\n", bufp);
          return(-1);
@@ -2084,7 +2192,10 @@ iw_in_addr(int           skfd,
     }
 
 #ifdef DEBUG
-  printf("Hw Address = %s\n", iw_ether_ntoa((struct ether_addr *) sap->sa_data));
+  {
+    char buf[20];
+    printf("Hw Address = %s\n", iw_saether_ntop(sap, buf));
+  }
 #endif
 
   return(0);
@@ -2125,6 +2236,8 @@ iw_get_priv_size(int      args)
  * this case.
  */
 
+/* -------------------------- CONSTANTS -------------------------- */
+
 /* Type of headers we know about (basically union iwreq_data) */
 #define IW_HEADER_TYPE_NULL    0       /* Not available */
 #define IW_HEADER_TYPE_CHAR    2       /* char [IFNAMSIZ] */
@@ -2135,69 +2248,315 @@ iw_get_priv_size(int   args)
 #define IW_HEADER_TYPE_PARAM   9       /* struct iw_param */
 #define IW_HEADER_TYPE_QUAL    10      /* struct iw_quality */
 
-/* Headers for the various requests */
-static const char standard_ioctl_hdr[] = {
-       IW_HEADER_TYPE_NULL,    /* SIOCSIWCOMMIT */
-       IW_HEADER_TYPE_CHAR,    /* SIOCGIWNAME */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWNWID */
-       IW_HEADER_TYPE_PARAM,   /* SIOCGIWNWID */
-       IW_HEADER_TYPE_FREQ,    /* SIOCSIWFREQ */
-       IW_HEADER_TYPE_FREQ,    /* SIOCGIWFREQ */
-       IW_HEADER_TYPE_UINT,    /* SIOCSIWMODE */
-       IW_HEADER_TYPE_UINT,    /* SIOCGIWMODE */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWSENS */
-       IW_HEADER_TYPE_PARAM,   /* SIOCGIWSENS */
-       IW_HEADER_TYPE_NULL,    /* SIOCSIWRANGE */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWRANGE */
-       IW_HEADER_TYPE_NULL,    /* SIOCSIWPRIV */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWPRIV */
-       IW_HEADER_TYPE_NULL,    /* SIOCSIWSTATS */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWSTATS */
-       IW_HEADER_TYPE_POINT,   /* SIOCSIWSPY */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWSPY */
-       IW_HEADER_TYPE_POINT,   /* SIOCSIWTHRSPY */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWTHRSPY */
-       IW_HEADER_TYPE_ADDR,    /* SIOCSIWAP */
-       IW_HEADER_TYPE_ADDR,    /* SIOCGIWAP */
-       IW_HEADER_TYPE_NULL,    /* -- hole -- */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWAPLIST */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWSCAN */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWSCAN */
-       IW_HEADER_TYPE_POINT,   /* SIOCSIWESSID */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWESSID */
-       IW_HEADER_TYPE_POINT,   /* SIOCSIWNICKN */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWNICKN */
-       IW_HEADER_TYPE_NULL,    /* -- hole -- */
-       IW_HEADER_TYPE_NULL,    /* -- hole -- */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWRATE */
-       IW_HEADER_TYPE_PARAM,   /* SIOCGIWRATE */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWRTS */
-       IW_HEADER_TYPE_PARAM,   /* SIOCGIWRTS */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWFRAG */
-       IW_HEADER_TYPE_PARAM,   /* SIOCGIWFRAG */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWTXPOW */
-       IW_HEADER_TYPE_PARAM,   /* SIOCGIWTXPOW */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWRETRY */
-       IW_HEADER_TYPE_PARAM,   /* SIOCGIWRETRY */
-       IW_HEADER_TYPE_POINT,   /* SIOCSIWENCODE */
-       IW_HEADER_TYPE_POINT,   /* SIOCGIWENCODE */
-       IW_HEADER_TYPE_PARAM,   /* SIOCSIWPOWER */
-       IW_HEADER_TYPE_PARAM,   /* SIOCGIWPOWER */
+/* Handling flags */
+/* Most are not implemented. I just use them as a reminder of some
+ * cool features we might need one day ;-) */
+#define IW_DESCR_FLAG_NONE     0x0000  /* Obvious */
+/* Wrapper level flags */
+#define IW_DESCR_FLAG_DUMP     0x0001  /* Not part of the dump command */
+#define IW_DESCR_FLAG_EVENT    0x0002  /* Generate an event on SET */
+#define IW_DESCR_FLAG_RESTRICT 0x0004  /* GET : request is ROOT only */
+                               /* SET : Omit payload from generated iwevent */
+#define IW_DESCR_FLAG_NOMAX    0x0008  /* GET : no limit on request size */
+/* Driver level flags */
+#define IW_DESCR_FLAG_WAIT     0x0100  /* Wait for driver event */
+
+/* ---------------------------- TYPES ---------------------------- */
+
+/*
+ * Describe how a standard IOCTL looks like.
+ */
+struct iw_ioctl_description
+{
+       __u8    header_type;            /* NULL, iw_point or other */
+       __u8    token_type;             /* Future */
+       __u16   token_size;             /* Granularity of payload */
+       __u16   min_tokens;             /* Min acceptable token number */
+       __u16   max_tokens;             /* Max acceptable token number */
+       __u32   flags;                  /* Special handling of the request */
+};
+
+/* -------------------------- VARIABLES -------------------------- */
+
+/*
+ * Meta-data about all the standard Wireless Extension request we
+ * know about.
+ */
+static const struct iw_ioctl_description standard_ioctl_descr[] = {
+       [SIOCSIWCOMMIT  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCGIWNAME    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_CHAR,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWNWID    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+               .flags          = IW_DESCR_FLAG_EVENT,
+       },
+       [SIOCGIWNWID    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWFREQ    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_FREQ,
+               .flags          = IW_DESCR_FLAG_EVENT,
+       },
+       [SIOCGIWFREQ    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_FREQ,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWMODE    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_UINT,
+               .flags          = IW_DESCR_FLAG_EVENT,
+       },
+       [SIOCGIWMODE    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_UINT,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWSENS    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWSENS    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWRANGE   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCGIWRANGE   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = sizeof(struct iw_range),
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWPRIV    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCGIWPRIV    - SIOCIWFIRST] = { /* (handled directly by us) */
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCSIWSTATS   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCGIWSTATS   - SIOCIWFIRST] = { /* (handled directly by us) */
+               .header_type    = IW_HEADER_TYPE_NULL,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWSPY     - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct sockaddr),
+               .max_tokens     = IW_MAX_SPY,
+       },
+       [SIOCGIWSPY     - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct sockaddr) +
+                                 sizeof(struct iw_quality),
+               .max_tokens     = IW_MAX_SPY,
+       },
+       [SIOCSIWTHRSPY  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct iw_thrspy),
+               .min_tokens     = 1,
+               .max_tokens     = 1,
+       },
+       [SIOCGIWTHRSPY  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct iw_thrspy),
+               .min_tokens     = 1,
+               .max_tokens     = 1,
+       },
+       [SIOCSIWAP      - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+       },
+       [SIOCGIWAP      - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWMLME    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = sizeof(struct iw_mlme),
+               .max_tokens     = sizeof(struct iw_mlme),
+       },
+       [SIOCGIWAPLIST  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct sockaddr) +
+                                 sizeof(struct iw_quality),
+               .max_tokens     = IW_MAX_AP,
+               .flags          = IW_DESCR_FLAG_NOMAX,
+       },
+       [SIOCSIWSCAN    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = 0,
+               .max_tokens     = sizeof(struct iw_scan_req),
+       },
+       [SIOCGIWSCAN    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_SCAN_MAX_DATA,
+               .flags          = IW_DESCR_FLAG_NOMAX,
+       },
+       [SIOCSIWESSID   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ESSID_MAX_SIZE + 1,
+               .flags          = IW_DESCR_FLAG_EVENT,
+       },
+       [SIOCGIWESSID   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ESSID_MAX_SIZE + 1,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWNICKN   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ESSID_MAX_SIZE + 1,
+       },
+       [SIOCGIWNICKN   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ESSID_MAX_SIZE + 1,
+       },
+       [SIOCSIWRATE    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWRATE    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWRTS     - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWRTS     - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWFRAG    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWFRAG    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWTXPOW   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWTXPOW   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWRETRY   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWRETRY   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWENCODE  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ENCODING_TOKEN_MAX,
+               .flags          = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
+       },
+       [SIOCGIWENCODE  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ENCODING_TOKEN_MAX,
+               .flags          = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
+       },
+       [SIOCSIWPOWER   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWPOWER   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWGENIE   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [SIOCGIWGENIE   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [SIOCSIWAUTH    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWAUTH    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = sizeof(struct iw_encode_ext),
+               .max_tokens     = sizeof(struct iw_encode_ext) +
+                                 IW_ENCODING_TOKEN_MAX,
+       },
+       [SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = sizeof(struct iw_encode_ext),
+               .max_tokens     = sizeof(struct iw_encode_ext) +
+                                 IW_ENCODING_TOKEN_MAX,
+       },
+       [SIOCSIWPMKSA - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = sizeof(struct iw_pmksa),
+               .max_tokens     = sizeof(struct iw_pmksa),
+       },
 };
-static const unsigned int standard_ioctl_num = sizeof(standard_ioctl_hdr);
+static const unsigned int standard_ioctl_num = (sizeof(standard_ioctl_descr) /
+                                               sizeof(struct iw_ioctl_description));
 
 /*
  * Meta-data about all the additional standard Wireless Extension events
  * we know about.
  */
-static const char      standard_event_hdr[] = {
-       IW_HEADER_TYPE_ADDR,    /* IWEVTXDROP */
-       IW_HEADER_TYPE_QUAL,    /* IWEVQUAL */
-       IW_HEADER_TYPE_POINT,   /* IWEVCUSTOM */
-       IW_HEADER_TYPE_ADDR,    /* IWEVREGISTERED */
-       IW_HEADER_TYPE_ADDR,    /* IWEVEXPIRED */
+static const struct iw_ioctl_description standard_event_descr[] = {
+       [IWEVTXDROP     - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+       },
+       [IWEVQUAL       - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_QUAL,
+       },
+       [IWEVCUSTOM     - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_CUSTOM_MAX,
+       },
+       [IWEVREGISTERED - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+       },
+       [IWEVEXPIRED    - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR, 
+       },
+       [IWEVGENIE      - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [IWEVMICHAELMICFAILURE  - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT, 
+               .token_size     = 1,
+               .max_tokens     = sizeof(struct iw_michaelmicfailure),
+       },
+       [IWEVASSOCREQIE - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [IWEVASSOCRESPIE        - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [IWEVPMKIDCAND  - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = sizeof(struct iw_pmkid_cand),
+       },
 };
-static const unsigned int standard_event_num = sizeof(standard_event_hdr);
+static const unsigned int standard_event_num = (sizeof(standard_event_descr) /
+                                               sizeof(struct iw_ioctl_description));
 
 /* Size (in bytes) of various events */
 static const int event_type_size[] = {
@@ -2214,10 +2573,6 @@ static const int event_type_size[] = {
        IW_EV_QUAL_LEN,         /* IW_HEADER_TYPE_QUAL */
 };
 
-/* Forward compatibility with WE-19 */
-#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \
-                         (char *) NULL)
-
 /*------------------------------------------------------------------*/
 /*
  * Initialise the struct stream_descr so that we can extract
@@ -2245,6 +2600,7 @@ iw_extract_event_stream(struct stream_descr *     stream, /* Stream of events */
                        struct iw_event *       iwe,    /* Extracted event */
                        int                     we_version)
 {
+  const struct iw_ioctl_description *  descr = NULL;
   int          event_type = 0;
   unsigned int event_len = 1;          /* Invalid */
   char *       pointer;
@@ -2258,7 +2614,7 @@ iw_extract_event_stream(struct stream_descr *     stream, /* Stream of events */
   if((stream->current + IW_EV_LCP_LEN) > stream->end)
     return(0);
 
-#if 0
+#if DEBUG
   printf("DBG - stream->current = %p, stream->value = %p, stream->end = %p\n",
         stream->current, stream->value, stream->end);
 #endif
@@ -2267,7 +2623,7 @@ iw_extract_event_stream(struct stream_descr *     stream, /* Stream of events */
    * Note : the event may be unaligned, therefore copy... */
   memcpy((char *) iwe, stream->current, IW_EV_LCP_LEN);
 
-#if 0
+#if DEBUG
   printf("DBG - iwe->cmd = 0x%X, iwe->len = %d\n",
         iwe->cmd, iwe->len);
 #endif
@@ -2281,19 +2637,21 @@ iw_extract_event_stream(struct stream_descr *   stream, /* Stream of events */
     {
       cmd_index = iwe->cmd - SIOCIWFIRST;
       if(cmd_index < standard_ioctl_num)
-       event_type = standard_ioctl_hdr[cmd_index];
+       descr = &(standard_ioctl_descr[cmd_index]);
     }
   else
     {
       cmd_index = iwe->cmd - IWEVFIRST;
       if(cmd_index < standard_event_num)
-       event_type = standard_event_hdr[cmd_index];
+       descr = &(standard_event_descr[cmd_index]);
     }
+  if(descr != NULL)
+    event_type = descr->header_type;
   /* Unknown events -> event_type=0 => IW_EV_LCP_LEN */
   event_len = event_type_size[event_type];
-  /* Fixup for later version of WE */
-  if((we_version > 18) && (event_type == IW_HEADER_TYPE_POINT))
-    event_len -= IW_EV_POINT_OFF;
+  /* Fixup for earlier version of WE */
+  if((we_version <= 18) && (event_type == IW_HEADER_TYPE_POINT))
+    event_len += IW_EV_POINT_OFF;
 
   /* Check if we know about this event */
   if(event_len <= IW_EV_LCP_LEN)
@@ -2310,7 +2668,7 @@ iw_extract_event_stream(struct stream_descr *     stream, /* Stream of events */
   else
     pointer = stream->current + IW_EV_LCP_LEN; /* First value in event */
 
-#if 0
+#if DEBUG
   printf("DBG - event_type = %d, event_len = %d, pointer = %p\n",
         event_type, event_len, pointer);
 #endif
@@ -2322,7 +2680,7 @@ iw_extract_event_stream(struct stream_descr *     stream, /* Stream of events */
       stream->current += iwe->len;
       return(-2);
     }
-  /* Fixup for later version of WE */
+  /* Fixup for WE-19 and later : pointer no longer in the stream */
   if((we_version > 18) && (event_type == IW_HEADER_TYPE_POINT))
     memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
           pointer, event_len);
@@ -2336,9 +2694,40 @@ iw_extract_event_stream(struct stream_descr *    stream, /* Stream of events */
   if(event_type == IW_HEADER_TYPE_POINT)
     {
       /* Check the length of the payload */
-      if((iwe->len - (event_len + IW_EV_LCP_LEN)) > 0)
-       /* Set pointer on variable part (warning : non aligned) */
-       iwe->u.data.pointer = pointer;
+      unsigned int     extra_len = iwe->len - (event_len + IW_EV_LCP_LEN);
+      if(extra_len > 0)
+       {
+         /* Set pointer on variable part (warning : non aligned) */
+         iwe->u.data.pointer = pointer;
+
+         /* Check that we have a descriptor for the command */
+         if(descr == NULL)
+           /* Can't check payload -> unsafe... */
+           iwe->u.data.pointer = NULL; /* Discard paylod */
+         else
+           {
+             /* Those checks are actually pretty hard to trigger,
+              * because of the checks done in the kernel... */
+
+             /* Discard bogus events which advertise more tokens than
+              * what they carry... */
+             unsigned int      token_len = iwe->u.data.length * descr->token_size;
+             if(token_len > extra_len)
+               iwe->u.data.pointer = NULL;     /* Discard paylod */
+             /* Check that the advertised token size is not going to
+              * produce buffer overflow to our caller... */
+             if((iwe->u.data.length > descr->max_tokens)
+                && !(descr->flags & IW_DESCR_FLAG_NOMAX))
+               iwe->u.data.pointer = NULL;     /* Discard paylod */
+             /* Same for underflows... */
+             if(iwe->u.data.length < descr->min_tokens)
+               iwe->u.data.pointer = NULL;     /* Discard paylod */
+#if DEBUG
+             printf("DBG - extra_len = %d, token_len = %d, token = %d, max = %d, min = %d\n",
+                    extra_len, token_len, iwe->u.data.length, descr->max_tokens, descr->min_tokens);
+#endif
+           }
+       }
       else
        /* No data */
        iwe->u.data.pointer = NULL;
@@ -2439,9 +2828,9 @@ iw_process_scanning_token(struct iw_event *               event,
     case SIOCGIWESSID:
       wscan->b.has_essid = 1;
       wscan->b.essid_on = event->u.data.flags;
+      memset(wscan->b.essid, '\0', IW_ESSID_MAX_SIZE+1);
       if((event->u.essid.pointer) && (event->u.essid.length))
        memcpy(wscan->b.essid, event->u.essid.pointer, event->u.essid.length);
-      wscan->b.essid[event->u.essid.length] = '\0';
       break;
     case SIOCGIWENCODE:
       wscan->b.has_key = 1;
@@ -2455,7 +2844,7 @@ iw_process_scanning_token(struct iw_event *               event,
     case IWEVQUAL:
       /* We don't get complete stats, only qual */
       wscan->has_stats = 1;
-      memcpy(&wscan->stats.qual, &event->u.qual, sizeof(iwstats));
+      memcpy(&wscan->stats.qual, &event->u.qual, sizeof(struct iw_quality));
       break;
     case SIOCGIWRATE:
       /* Scan may return a list of bitrates. Should we really bother with
@@ -2489,9 +2878,9 @@ iw_process_scan(int                       skfd,
   int                  buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */
   unsigned char *      newbuf;
 
-  /* Don't waste too much time on interfaces (50 * 100 = 5s) */
+  /* Don't waste too much time on interfaces (150 * 100 = 15s) */
   context->retry++;
-  if(context->retry > 50)
+  if(context->retry > 150)
     {
       errno = ETIME;
       return(-1);
@@ -2570,7 +2959,7 @@ iw_process_scan(int                       skfd,
       struct stream_descr      stream;
       struct wireless_scan *   wscan = NULL;
       int                      ret;
-#if 0
+#if DEBUG
       /* Debugging code. In theory useless, because it's debugged ;-) */
       int      i;
       printf("Scan result [%02X", buffer[0]);
@@ -2580,7 +2969,7 @@ iw_process_scan(int                       skfd,
 #endif
 
       /* Init */
-      iw_init_event_stream(&stream, buffer, wrq.u.data.length);
+      iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length);
       /* This is dangerous, we may leak user data... */
       context->result = NULL;