Home | History | Annotate | Download | only in src
      1 /*
      2  * Redistribution and use in source and binary forms, with or without
      3  * modification, are permitted provided that: (1) source code distributions
      4  * retain the above copyright notice and this paragraph in its entirety, (2)
      5  * distributions including binary code include the above copyright notice and
      6  * this paragraph in its entirety in the documentation or other materials
      7  * provided with the distribution, and (3) all advertising materials mentioning
      8  * features or use of this software display the following acknowledgement:
      9  * ``This product includes software developed by the University of California,
     10  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
     11  * the University nor the names of its contributors may be used to endorse
     12  * or promote products derived from this software without specific prior
     13  * written permission.
     14  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
     15  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
     16  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     17  *
     18  * tcpdump/Win32 functions for reading and parsing system's Ethernet
     19  * address file:
     20  *    '%SystemRoot%/drivers/etc/ethers'  (Win-NT+)
     21  * or '%Windir%/etc/ethers'              (Win-9x/ME)
     22  *
     23  * G. Vanem <gvanem (at) yahoo.no> 2012.
     24  */
     25 
     26 #ifdef HAVE_CONFIG_H
     27 #include "config.h"
     28 #endif
     29 
     30 #include <netdissect-stdinc.h>
     31 
     32 #include "ether.h"
     33 #include "netdissect.h"
     34 #include "addrtoname.h"
     35 
     36 typedef struct ether_addr {
     37         unsigned char octet[ETHER_ADDR_LEN];
     38       } ether_address;
     39 
     40 typedef struct ether_entry {
     41         ether_address      eth_addr;  /* MAC address */
     42         char               *name;     /* name of MAC-address */
     43         struct ether_entry *next;
     44       } ether_entry;
     45 
     46 static struct ether_entry *eth0 = NULL;
     47 
     48 /*
     49  * The reason to avoid using 'pcap_next_etherent()' in addrtoname.c
     50  * are several:
     51  *   1) wpcap.dll and 'pcap_next_etherent()' could have been built in
     52  *      debug-mode (-MDd) or release-mode (-MD) and tcpdump in
     53  *      the opposite model.
     54  *   2) If this is built by MSVC, wpcap.dll could have been built by
     55  *      MingW. It has no debug-model.
     56  *   3) It may not have been exported from wpcap.dll (present in wpcap.def).
     57  *
     58  * So we shoe-horn the building of tcpdump with '-DUSE_ETHER_NTOHOST' to
     59  * make 'init_etherarray()' call the below 'ether_ntohost()' instead.
     60  */
     61 #if !defined(USE_ETHER_NTOHOST)
     62 #error "'-DUSE_ETHER_NTOHOST' must be set"
     63 #endif
     64 
     65 /*
     66  * Return TRUE if running under Win-95/98/ME.
     67  */
     68 static BOOL is_win9x (void)
     69 {
     70   OSVERSIONINFO ovi;
     71   DWORD os_ver = GetVersion();
     72   DWORD major_ver = LOBYTE (LOWORD(os_ver));
     73 
     74   return (os_ver >= 0x80000000 && major_ver >= 4);
     75 }
     76 
     77 /*
     78  * Return path to "%SystemRoot%/drivers/etc/<file>"  (Win-NT+)
     79  *          or to "%Windir%/etc/<file>"              (Win-9x/ME)
     80  */
     81 const char *etc_path (const char *file)
     82 {
     83   BOOL win9x = is_win9x();
     84   const char *env = win9x ? getenv("WinDir") : getenv("SystemRoot");
     85   static char path[MAX_PATH];
     86 
     87   if (!env)
     88     return (file);
     89 
     90   if (win9x)
     91     snprintf (path, sizeof(path), "%s\\etc\\%s", env, file);
     92   else
     93     snprintf (path, sizeof(path), "%s\\system32\\drivers\\etc\\%s", env, file);
     94 
     95   return (path);
     96 }
     97 
     98 /*
     99  * Parse a string-buf containing an MAC address and name.
    100  * Accepts MAC addresses on both "xx:xx:xx.." and "xx-xx-xx.." forms.
    101  *
    102  * We could have used pcap_ether_aton(), but problem 3) above could apply.
    103  * or we could have cut & pasted 'pcap_next_etherent(FILE *fp)' below.
    104  */
    105 #define MIN_LEN  sizeof("0:0:0:0:0:0 X")
    106 
    107 static
    108 int parse_ether_buf (const char *buf, char **result, struct ether_addr *e)
    109 {
    110   const char *fmt;
    111   char       *name;
    112   char       *str = (char*)buf;
    113   unsigned    eth [sizeof(*e)];
    114   int         i;
    115 
    116   /* Find first non-blank in 'buf' */
    117   while (str[0] && str[1] && isspace((int)str[0]))
    118        str++;
    119 
    120   if (*str == '#' || *str == ';' || *str == '\n' || strlen(str) < MIN_LEN)
    121      return (0);
    122 
    123   if (str[2] == ':')
    124     fmt = "%02x:%02x:%02x:%02x:%02x:%02x";
    125   else
    126     fmt = "%02x-%02x-%02x-%02x-%02x-%02x";
    127 
    128   if (sscanf(str, fmt, &eth[0], &eth[1], &eth[2], &eth[3], &eth[4], &eth[5]) != ETHER_ADDR_LEN)
    129      return (0);
    130 
    131   str  = strtok (str, " \t");
    132   name = strtok (NULL, " #\t\n");
    133 
    134   if (!str || !name || strlen(name) < 1)
    135      return (0);
    136 
    137   *result = name;
    138 
    139   for (i = 0; i < ETHER_ADDR_LEN; i++)
    140       e->octet[i] = eth[i];
    141 
    142   return (1);
    143 }
    144 
    145 static void free_ethers (void)
    146 {
    147   struct ether_entry *e, *next;
    148 
    149   for (e = eth0; e; e = next) {
    150     next = e->next;
    151     free(e->name);
    152     free(e);
    153   }
    154   eth0 = NULL;
    155 }
    156 
    157 static int init_ethers (void)
    158 {
    159   char  buf[BUFSIZE];
    160   FILE *fp = fopen (etc_path("ethers"), "r");
    161 
    162   if (!fp)
    163      return (0);
    164 
    165   while (fgets(buf,sizeof(buf),fp))
    166   {
    167     struct ether_entry *e;
    168     char  *name;
    169     ether_address eth;
    170 
    171     if (!parse_ether_buf(buf,&name,&eth))
    172        continue;
    173 
    174     e = calloc (sizeof(*e), 1);
    175     if (!e)
    176        break;
    177 
    178     memcpy(&e->eth_addr, &eth, ETHER_ADDR_LEN);
    179     e->name = strdup(name);
    180     if (!e->name) {
    181       free(e);
    182       break;
    183     }
    184 
    185     e->next = eth0;
    186     eth0 = e;
    187   }
    188   fclose(fp);
    189   atexit(free_ethers);
    190   return (1);
    191 }
    192 
    193 /*
    194  * Map an ethernet address 'e' to a 'name'.
    195  * Returns 0 on success.
    196  *
    197  * This function is called at startup by init_etherarray() and then
    198  * by etheraddr_string() as needed. To avoid doing an expensive fopen()
    199  * on each call, the contents of 'etc_path("ethers")' is cached here in
    200  * a linked-list 'eth0'.
    201  */
    202 int ether_ntohost (char *name, struct ether_addr *e)
    203 {
    204   const struct ether_entry *cache;
    205   static int init = 0;
    206 
    207   if (!init) {
    208     init_ethers();
    209     init = 1;
    210   }
    211 
    212   for (cache = eth0; cache; cache = cache->next)
    213      if (!memcmp(&e->octet, &cache->eth_addr, ETHER_ADDR_LEN)) {
    214        strcpy (name,cache->name);
    215        return (0);
    216      }
    217   return (1);
    218 }
    219 
    220