

/****************************************************************************/
/*                                                                          */
/*      NNstat -- Internet Statistics Collection Package                    */
/*                                                                          */
/*            Written by: Bob Braden & Annette DeSchon                      */
/*            USC Information Sciences Institute                            */
/*            Marina del Rey, California                                    */
/*                                                                          */
/*      Copyright (c) 1991 University of Southern California.               */
/*      All rights reserved.                                                */
/*                                                                          */
/*      Redistribution and use in source and binary forms are permitted     */
/*      provided that the above copyright notice and this paragraph are     */
/*      duplicated in all such forms and that any documentation,            */
/*      advertising materials, and other materials related to such          */
/*      distribution and use acknowledge that the software was              */
/*      developed by the University of Southern California, Information     */
/*      Sciences Institute.  The name of the University may not be used     */
/*      to endorse or promote products derived from this software           */
/*      without specific prior written permission.                          */
/*      THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR        */
/*      IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR          */
/*      PURPOSE.                                                            */
/*                                                                          */
/****************************************************************************/

static char rcsid[]=
  "$Header: scan.c,v 1.4 93/10/11 18:37:16 mogul Exp $";
 
/*                      scan.c
 *
 *  This module defines the operations required to scan an "enum" file,
 *  which contains "enumerations" of label strings for particular
 *  objects.  These label strings are used in formatting displays of
 *  the values recorded by these objects, to identify the semantics of
 *  these values.  This file is used by statspy, collect, and rspy.
 *
 *  This module includes the following externally-callable routines:
 * 
 *      scan_enum(fpin)
 *      show_enums(outp, outl)
 *      IPcnv(cp, valp)
 *      hexcnv(cp)
 *      NUMcnv(cp, valp)
 *      VALcnv(token, valp)
 *
 ***************************************************************************/

/* CHANGES:
 *    17Nov88 ISI:  Add eqf (alias EQ) object
 *    29Nov88 ISI:  Truncate excessively long enum label or object name
 *    13Oct89 Merit: Access control vocab added 
 *    17Oct89 AKS/RTB: Subnet verb added
 *       Rel 2.4:
 *    01Nov89 RTB:  Add FAB, FOB, MAB, MSB objects
 *       Rel 3.0:
 *    ISI:  Add language extension reserved words
 *    ISI:  Move GenOpPtr[] here.
 *    ISI:  Add object classes BP2 and WS2.
 *    ISI:  Fix old bug in hex input conversion (NUMcnv)
 *    SGI: Add INCLUDE command, to include src text.
 *    Aug93/DECWRL: Alpha/OSF port
 *
 */
              
#include <stdio.h>
#include <netdb.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>

#include "stat.h"
#include "sobj.h"
#include "scan.h"

#define ETHER_SIZE 6  /* Number of bytes in Ethernet address */

    /* INCLUDE LEXICAL ANALYZER  */
    
int  LastToken; 
#include "cmds.lex.yy.c"
int  debug;
    
char *RsvdWds[] = { "attach", "detach",  "read", "clear",  "readclear", "show", 
                    "help", "enum", "record", "if", "else", "in",
                    "is", "isnot", "restrict", "readonly", "readwrite", 
                    "subnet", "symif", "and", "or", "select", "case", 
                    "default", "include", NULL};
                    
int   RsvdToken[] = { ATTACH, DETACH, READ, CLEAR, READCL, SHOW, 
                      HELP, ENUM, RECORD, IF, ELSE, INIS, 
                      INIS, ISNOT, RESTRICT, READONLY, READWRITE,
                      SUBNET, SYMIF, AND, OR, SELECT, CASE, DEFAULT, INCLUDE };

  
/*  class_name[], class_nick[], and GenOpPtr[] MUST be consistent with stat.h */  
char *class_name[] = {
    "",
    "freq-only",
    "hist",
    "hist-pwr2",
    "freq-all",
    "bin-pkt",
    "freq-all2",   
    "rangef",
    "setf",
    "matrix-all",
    "working-set",
    "matrix-sym",
    "matrix-all2", 
    "eqf",
    "freq-all-bytes",
    "freq-only-bytes",
    "matrix-all-bytes",
    "matrix-sym-bytes",
    "SELECT",
    "bin-pkt2",
    "working-set2",
    NULL
} ;
    
char *class_nick[] = {
    "",
    "FO",
    "HI",
    "P2",
    "FA",
    "BP",
    "FA2",   
    "RF",
    "SF",
    "MA",
    "WS",
    "MS",
    "MA2",   
    "EQ",
    "FAB",
    "FOB",
    "MAB",
    "MSB",
    "SELECT",
    "BP2",
    "WS2",
    NULL
} ;

int  is_filter[] = {
    0, 
    0, 0, 0, 0, 0, 0,
    1, 1, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 1,
    0, 0
} ; 

     /* Transfer Vectors */
extern GENERICOP FOop, HIop, P2op, FAop, BPop, FA2op, RFop, SFop, MAop, MSop, 
    MA2op, WSop, EQop, FABop, FOBop, MABop, MSBop, SELECTop, BP2op, 
    WS2op;   

GENERICOP *GenOpPtr[] = {   
             /*** MUST MATCH class_name/class_nick in scan.c ****/    
    &FOop, &HIop, &P2op, &FAop, &BPop, &FA2op, &RFop, &SFop, &MAop, &WSop,
    &MSop, &MA2op, &EQop, &FABop, &FOBop, &MABop, &MSBop, &SELECTop, &BP2op, 
    &WS2op};


long strtol();

/*
 * Convert a 48 bit ethernet number to a string representation.
 */
char *
ether_ntoa(e)
    u_char e[ETHER_SIZE];
{
    static char s[18];
    
    s[0] = 0;
    sprintf(s, "%x:%x:%x:%x:%x:%x", e[0], e[1], e[2], e[3], e[4], e[5]);
    return (s);
}


/*
 * Convert a ethernet address representation back into its 48 bits.
 */
char *
ether_aton(s)
    char *s;
{
    static char e[ETHER_SIZE];
    register int i;
    unsigned int t[ETHER_SIZE];
    
    i = sscanf(s, " %x:%x:%x:%x:%x:%x",
        &t[0], &t[1], &t[2], &t[3], &t[4], &t[5]);
    if (i != ETHER_SIZE)
        return (NULL);
    for (i = 0; i < ETHER_SIZE; i++) {
        e[i] = t[i];
    }
    return(e);
} /* ether_aton */

    /*
     *  Convert dotted-decimal char string to binary byte string (i.e.,
     *  network byte order).  With fewer than 4 segments, number is
     *  left-justified in 4 bytes.  Returns length of result in bytes,
     *  or -1 if there is a syntax error.
     */
int IPcnv(cp, valp)
    char *cp;
    union xlong *valp; 
{
    register int j, x;
        
    valp->xllong = 0;
    
    for (j=0;j<MAX_DLENG;j++) {
        if ((x = strtol(cp, &cp, 0)) > 255) return(-1);
        valp->xlbytes[j] = x;
        if (*cp++ != '.') break;
    }   
    return((j<sizeof(int32))? sizeof(int32) : (j == MAX_DLENG)? -1 : j+1);
} /* IPcnv */
    
    
    /*
     * Convert decimal, octal, or hex string to binary in network byte order.
     * Value is assumed to be a 32-bit int, except a hex number of more than 8
     * hex digits may be longer.
     */
int NUMcnv(cp, valp)
    register char *cp;
    union xlong *valp; 
{
    register int j, val;
    char *rp;
    
    valp->xllong = 0;
    
    if ((*cp == '0')&&(cp[1] == 'x' || cp[1] == 'X') && (strlen(cp+2) > 8)) { 
            /* hex > 4 bytes */
        cp += 2;
        j= 0;
        while(1) {
            if (!isxdigit(*cp)) return(j);   /* End of hex digits: OK */
            if (j >= MAX_DLENG) return(-1);   /* Error: too long... */
            val = (hexcnv(cp++)<<4);
            if (!isxdigit(*cp)) return(-1);   /* Error: odd # digits */
            valp->xlbytes[j++] = val + hexcnv(cp++);
        }
    }
    else {  /* Convert as integer... */
        valp->xllong = htonl((int32)strtol(cp, &rp, 0));
        return(4);
    }
} /* NUMcnv */



int hexcnv(cp)
    register char *cp;
{
    return((isdigit(*cp))? (*cp - '0'): 
           (islower(*cp))? (*cp - 'a' + 10) :
                           (*cp - 'A' + 10) );
} /* hexcnv */


/* assume x1.x2.x3.x4/p with xi 1-bytes integer and p 2-bytes integer */
int IPSOCKcnv(cp, valp)
    char *cp;
    union xlong *valp;
{
    register int j, x;

    valp->xllong = 0;

    for (j = 0; j < 3; ++j) {
        if ((x = strtol(cp, &cp, 0)) > 255 || *cp++ != '.')
            return (-1);
        valp->xlbytes[j] = x;
    }

    if ((x = strtol(cp, &cp, 0)) > 255)
        return (-1);

    valp->xlbytes[3] = x;

    if (*cp++ != '/')
        return (-1);

    if ((x = strtol(cp, &cp, 0)) > 65535)
        return (-1);

    /*
     * This
     *    x = htons(x);
     * is wrong, because the next few lines arrange the 16 bits
     * into big-endian (network) order no matter what the native
     * byte order is
     */

    valp->xlbytes[4] = x >> 8;
    valp->xlbytes[5] = x&0xff;

    return (6);
} /* IPSOCKcnv */

    /* Convert a value, represented by 'token', to binary and store it
     *  into *valp.  Return length of value in bytes, or 0 if unknown
     *  name, or -1 for a syntax error.
     */     
int VALcnv(token, valp)
    int token;
    union xlong *valp; 
    {
    char *cp;
    
    switch (token) {
    
    case NUMBER:
        return(NUMcnv(yytext, valp));
        
    case IPADDR:
        return(IPcnv(yytext, valp));

    case IPSOCK:
        return(IPSOCKcnv(yytext, valp));
        
    case ETHERA:
        cp = (char *) ether_aton(yytext);
        if (cp == NULL) return(0);  /* error */
        bcopy(cp, valp, ETHER_SIZE);
        return(ETHER_SIZE);
                
    case IDTOKEN:
           /*
            * Convert Identifier (host name) to binary.
            */      
        return((resolve_name(yytext, valp, 1))?4:0); 
                
    default:
        return(-1);
    }
} /* VALcnv */    
 
    
/*
 *  show_enums(outp, outl) : build display of current enumerations in
 *     buffer: char outp[]; max size = outl bytes.
 *
 */         
show_enums(outp, outl)
   char *outp;
   int   outl;
{
    int *objp = NULL;
    register int n, enumsoff = 0;
    register char *cp;
    char *NextEnumObj();
        
    if (outp == NULL) return;
    if (outl < strlen(cp = "Enums defined for object specs:\n")) return;
    strcpy(outp, cp);
    
    while (cp = NextEnumObj(&objp)) {
        n = strlen(cp);
        if (enumsoff+n+3 >= outl) break;
        strcpy(&outp[enumsoff], cp) ;
        strcpy(&outp[enumsoff+=n], "  ") ;
        enumsoff += 2;
    }
    
    if (enumsoff == 0)
          strcpy(outp, "No Enumerations\n");
} /* show_enums */
        
        
/*
 *
 *
 *  scan_enum() scans input list of the form:
 *
 *        <ObjectNameSpec>   ( <value> <label>, ... <value> <label>) ,
 *           ...        
 *        <ObjectNameSpec>   ( <value> <label>, ... <value> <label>)  
 *
 *  If <label> is to contain blanks or any other character not allowed
 *  in an idenififier, it must be delimited with quotation marks.
 *  
 *  <value> may be in decimal or hex or octal or
 *  dotted-decimal; a hex or dotted decimal value can be longer
 *  than 4 bytes, up to MAX_DLENG (currently 8).
 *
 *  <ObjectNameSpec> may contain wild-cards (*'s).
 *
 *  Returns -1 for syntax error, 1 for OK, 0 for unknown symbol in value
 */

int scan_enum(fpin)
    FILE *fpin;
{
    char Label[MAX_LABELS], Object[MAX_OBJNAME];
    int length;
    union xlong value;
    char *strtcpy();
    
    yyin = fpin;    /* Set LEX file pointer */  
    LastToken = LEXCALL;
        
    while (1) {
        if (LastToken != IDTOKEN) {
            return(1);
        }
        strtcpy(Object, yytext, MAX_OBJNAME); 
        if (LEXCALL != '(') return(-1);
        
        while(1) {
            Label[0] = '\0';
            if ((LastToken = LEXCALL) == ')')  break;
            
            length = VALcnv(LastToken, &value);         
            if (length <= 0) {
                return(length);
            }
            if ((LastToken =  LEXCALL) == STRING)  {
                yytext[yyleng-1] = '\0';
                strtcpy(Label, yytext+1, MAX_LABELS);
            }
            else if (LastToken == IDTOKEN) {
                strtcpy(Label, yytext, MAX_LABELS);
            }
            else return(-1);
            AddEnum(Object, Label, &value, length);
        }
        if (LastToken != ')') break;
        LastToken = LEXCALL;
    }
    return(-1);
} /* scan_enum */


    /*
     *  Search linear list of names (list assumed to be short enough that
     *  binary search is unnecessary). List is null-terminated. Returns
     *  index number of entry, or -1.
     */
int search(arg, list)
    char *arg, **list;
    {
    register char **p;
    
    for (p = list; *p; p++)
        if (!strcmp(arg, *p)) return(p-list);
    return(-1);
}


#define DIGIT(x)    (isdigit(x) ? (x) - '0' : \
            islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
#define MBASE   ('z' - 'a' + 1 + 10)

long
strtol(str, ptr, base)
register char *str;
char **ptr;
register int base;
{
    register long val;
    register int c;
    int xx, neg = 0;

    if (ptr != (char **)0)
        *ptr = str; /* in case no number is formed */
    if (base < 0 || base > MBASE)
        return (0); /* base is invalid -- should be a fatal error */
    if (!isalnum(c = *str)) {
        while (isspace(c))
            c = *++str;
        switch (c) {
        case '-':
            neg++;
        case '+': /* fall-through */
            c = *++str;
        }
    }
    if (base == 0)
        if (c != '0')
            base = 10;
        else if (str[1] == 'x' || str[1] == 'X')
            base = 16;
        else
            base = 8;
    /*
     * for any base > 10, the digits incrementally following
     *  9 are assumed to be "abc...z" or "ABC...Z"
     */
    if (!isalnum(c) || (xx = DIGIT(c)) >= base)
        return (0); /* no number formed */
    if (base == 16 && c == '0' && isxdigit(str[2]) &&
        (str[1] == 'x' || str[1] == 'X'))
        c = *(str += 2); /* skip over leading "0x" or "0X" */
    for (val = -DIGIT(c); isalnum(c = *++str) && (xx = DIGIT(c)) < base; )
        /* accumulate neg avoids surprises near MAXLONG */
        val = base * val - xx;
    if (ptr != (char **)0)
        *ptr = str;
    return (neg ? val : -val);
} /* strtol() */


int lexcall()
    {
    int t = yylex();
    if (debug) printf("LEX token = %d  text= %s\n",  t, yytext);
    return(t);
}

/* String copy routine that truncates if necessary to n-1 characters, 
 *   but always null-terminates.
 */
char *strtcpy(s1, s2, n)
char *s1, *s2;
int n;
    {
    strncpy(s1, s2, n);
    *(s1+n-1) = '\0';
    return(s1);
}
