/* $Header: ip.c,v 1.3 88/12/07 15:57:57 mar Exp $ */


#include <stdio.h>
#include <strings.h>
#include <netdb.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/if_ether.h>
#include <netinet/rvdconst.h>

#include "netwatch.h"


extern char *malloc(), *portname();

struct port {
    int	p_num;
    char *p_nam;
    int p_flg;
};


struct pchain {
    struct port *p_ent;
    struct pchain *p_nxt;
};



do_ip(pbuf, p)
char *pbuf;
unsigned char *p;
{
    char tmp[256];

    sprintf(tmp, "IN %d.%d.%d.%d -> %d.%d.%d.%d ",
	    p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19]);
    strcat(pbuf, tmp);

    switch (p[9]) {
    case IPPROTO_ICMP:
	do_icmp(pbuf, p);
	break;
    case IPPROTO_TCP:
	sprintf(tmp, "TCP %s -> ", portname((p[20] << 8) + p[21], S_TCP));
	strcat(pbuf, tmp);
	sprintf(tmp, "%s ", portname((p[22] << 8) + p[23], S_TCP));
	strcat(pbuf, tmp);
	break;
    case IPPROTO_UDP:
	sprintf(tmp, "UDP %s -> ", portname((p[20] << 8) + p[21], S_UDP));
	strcat(pbuf, tmp);
	sprintf(tmp, "%s ", portname((p[22] << 8) + p[23], S_UDP));
	strcat(pbuf, tmp);
	break;
    case IPPROTO_RVD:
	do_rvd(pbuf, p);
	break;
    default:
	sprintf(tmp, "protocol %d ", p[9]);
	strcat(pbuf, tmp);
	break;
    }
}


do_icmp(pbuf, p)
char *pbuf;
unsigned char *p;
{
    char tmp[64];

    strcat(pbuf, "ICMP ");

    switch (p[20]) {
    case ICMP_ECHOREPLY:
	strcat(pbuf, "PINGreply ");
	return;
    case ICMP_UNREACH:
	strcat(pbuf, "UNREACHABLE ");
	switch (p[21]) {
	case ICMP_UNREACH_NET:
	    strcat(pbuf, "NET ");
	    break;
	case ICMP_UNREACH_HOST:
	    strcat(pbuf, "HOST ");
	    break;
	case ICMP_UNREACH_PROTOCOL:
	    strcat(pbuf, "PROTOCOL ");
	    break;
	case ICMP_UNREACH_PORT:
	    strcat(pbuf, "PORT ");
	    break;
	case ICMP_UNREACH_NEEDFRAG:
	    strcat(pbuf, "FRAGMENT ");
	    break;
	case ICMP_UNREACH_SRCFAIL:
	    strcat(pbuf, "SRCFAIL ");
	    break;
	}
	break;
    case ICMP_SOURCEQUENCH:
	strcat(pbuf, "SRCQUENCH ");
	break;
    case ICMP_REDIRECT:
	sprintf(tmp, "REDIRECT via %d.%d.%d.%d ", p[24], p[25], p[26], p[27]);
	strcat(pbuf, tmp);
	break;
    case ICMP_ECHO:
	strcat(pbuf, "PING ");
	return;
    case ICMP_TIMXCEED:
	strcat(pbuf, "TIME_EXCEEDED ");
	break;
    case ICMP_PARAMPROB:
	strcat(pbuf, "BADHEADER ");
	break;
    case ICMP_TSTAMP:
	strcat(pbuf, "TIMESTAMP ");
	break;
    }
    strcat(pbuf, "(");
    do_ip(pbuf, &p[28]);
    strcat(pbuf, ")");
}


do_rvd(pbuf, p)
char	*pbuf;
u_char	*p;
{
    strcat(pbuf, "RVD ");
    switch (p[20]) {
    case RVDSPIN:
	strcat(pbuf, "SPINUP ");
	break;
    case RVDSDOWN:
	strcat(pbuf, "SPINDOWN ");
	break;
    case RVDREAD:
	strcat(pbuf, "READ ");
	break;
    case RVDWRITE:
	strcat(pbuf, "WRITE ");
	break;
    case RVDSTAT:
	strcat(pbuf, "STAT ");
	break;
    case RVDHOST:
	strcat(pbuf, "HOST ");
	break;
    case RVDRESPIN:
	strcat(pbuf, "reSPINUP ");
	break;
    case RVDAUTHSPIN:
	strcat(pbuf, "authSPINUP ");
	break;
    case RVDSPACK:
	strcat(pbuf, "SPINUPack ");
	break;
    case RVDERROR:
	strcat(pbuf, "ERROR ");
	break;
    case RVDACK:
	strcat(pbuf, "SPINDOWNack ");
	break;
    case RVDBLOCK:
	strcat(pbuf, "DATA ");
	break;
    case RVDWACK:
	strcat(pbuf, "WRITEack ");
	break;
    case RVDSSTAT:
	strcat(pbuf, "sSTAT ");
	break;
    case RVDHOSTR:
	strcat(pbuf, "HOSTresp ");
	break;
    default:
	strcat(pbuf, "??? ");
    }
}


#define IPTABSIZ 97

struct pchain *ip_ntab[IPTABSIZ];
struct pchain *ip_stab[IPTABSIZ];

ip_init_serv()
{
    int i, f;
    struct servent *se;
    char **s;

    for (i = 0; i < IPTABSIZ; i++) {
	ip_ntab[i] = ip_stab[i] = NULL;
    }
    add_entry(6000, "x11:0", S_TCP);
    add_entry(6001, "x11:1", S_TCP);
    add_entry(6002, "x11:2", S_TCP);
    add_entry(520, "rip", S_UDP);
    add_entry(907, "pop-status", S_UDP);
    add_entry(2049, "nfs", S_UDP);
    setservent(1);
    while (se = getservent()) {
	if (!strcmp(se->s_proto, "udp"))
	  f = S_UDP;
	else if (!strcmp(se->s_proto, "tcp"))
	  f = S_TCP;
	else
	  f = 0;
	add_entry(htons(se->s_port), se->s_name, f);
	for (s = se->s_aliases; *s; s++)
	  add_entry(htons(se->s_port), *s, f);
    }
    endservent();
}

add_entry(port, name, flag)
unsigned int port;
char *name;
int flag;
{
    	struct pchain **pc;
	struct port *p;

    	p = (struct port *) malloc(sizeof(struct port));
	p->p_num = port;
	p->p_nam = malloc(strlen(name) + 1);
	p->p_flg = flag;
	strcpy(p->p_nam, name);

	pc = &ip_ntab[p->p_num % IPTABSIZ];
	while (*pc)
	  pc = &((*pc)->p_nxt);
	*pc = (struct pchain *) malloc(sizeof(struct pchain));
	(*pc)->p_ent = p;
	(*pc)->p_nxt = NULL;
	
	pc = &ip_stab[(p->p_nam[0] ^ p->p_nam[1] + p->p_nam[2]) % IPTABSIZ];
	while (*pc)
	  pc = &((*pc)->p_nxt);
	*pc = (struct pchain *) malloc(sizeof(struct pchain));
	(*pc)->p_ent = p;
	(*pc)->p_nxt = NULL;
    }


char *portname(p, flag)
unsigned int p;
int flag;
{
    register struct pchain *pc;
    static char buf[8];

    for (pc = ip_ntab[p % IPTABSIZ]; pc; pc = pc->p_nxt) {
	if (pc && (pc->p_ent->p_num == p) && (pc->p_ent->p_flg & flag))
	  return(pc->p_ent->p_nam);
    }
    sprintf(buf, "%d", p);
    return(buf);
}

portnum(s, flag)
char *s;
int flag;
{
    register struct pchain *pc;
    char *p;

    for (p = s; *p; p++)
      if (isupper(*p))
	*p = tolower(*p);
    for (pc = ip_stab[(s[0] ^ s[1] + s[2]) % IPTABSIZ]; pc; pc = pc->p_nxt) {
	if (pc && !strcmp(pc->p_ent->p_nam, s) && (pc->p_ent->p_flg & flag))
	  return(pc->p_ent->p_num);
    }
    return(atoi(s));
}

