/*=============================================================================
  Ethernet Packet Sniffer 'GreedyDog' Version 1.30 
  The Shadow Penguin Security (http://shadowpenguin.backsection.net)
  Written by UNYUN (unewn4th@usa.net)
  [compile]
     gcc gdd.c -lnsl -D[OSNAME]
  or gcc gdd.c -D[OSNAME]
  ([OSNAME] = IRIX or SOLARIS or LINUX or FREEBSD)
  [ex]
     gcc gdd.c -lnsl -DSOLARIS
  =============================================================================
*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#ifndef FREEBSD
#include <malloc.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>

#ifdef SUNOS4
#include <sys/time.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <sys/file.h>
#include <sys/stropts.h>
#include <net/nit_if.h>
#include <net/nit_buf.h>
#endif
#ifdef LINUX
#include <ioctls.h>
#define __FAVOR_BSD
#endif
#ifdef FREEBSD
#include <fcntl.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <net/bpf.h>
#include <errno.h>
#endif
#ifdef IRIX
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <net/raw.h>
#endif
#ifdef SOLARIS
#include <sys/dlpi.h>
#include <sys/bufmod.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <sys/stropts.h>
#endif

#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>

#ifdef SUNOS4  /*--------< SUN OS4 用 パケットキャプチャ関連定数 >-----------*/
#define  NIT_DEV                "/dev/nit"        /* NITデバイス名           */ 
#define  DEFAULT_NIC            "le0"             /* NICデバイス名           */
#define  CHUNKSIZE              4096              /* チャンクサイズ          */
#endif

#ifdef LINUX   /*--------< LINUX 用 パケットキャプチャ関連定数 >-------------*/
#define  NIT_DEV                ""
#define  DEFAULT_NIC            "eth0"            /* NICデバイス名           */
#define  CHUNKSIZE              32000             /* チャンクサイズ          */
#endif

#ifdef FREEBSD /*--------< FreeBSD 用 パケットキャプチャ関連定数 >-----------*/
#define  NIT_DEV                "/dev/bpf"        /* NITデバイス名           */
#define  DEFAULT_NIC            "ed0"             /* NICデバイス名           */
#define  CHUNKSIZE              32000             /* チャンクサイズ          */
#endif

#ifdef IRIX /*-----------< IRIX 用 パケットキャプチャ関連定数 >--------------*/
#define  NIT_DEV                ""
#define  DEFAULT_NIC            ""
#define  CHUNKSIZE              60000             /* チャンクサイズ          */
#define  ETHERHDRPAD            RAW_HDRPAD(sizeof(struct ether_header))
#endif

#ifdef SOLARIS /*--------< Solaris 用 パケットキャプチャ関連定数 >-----------*/
#define  NIT_DEV                "/dev/hme"        /* NITデバイス名           */
#define  DEFAULT_NIC            "" 
#define  CHUNKSIZE              32768             /* チャンクサイズ          */
#endif

#define  S_DEBUG                                  /* DEBUGフラグ             */
#define  SIZE_OF_ETHHDR         14                /* Length of Thernet Hdr   */
#define  LOGFILE                "./snif.log"      /* 保存ログファイル名      */
#define  TMPLOG_DIR             "/tmp/"           /* 臨時ログファイルdir     */

struct conn_list{ /*------< 接続テーブルリスト >-----------------------------*/
    struct conn_list    *next_p;                  /* 次のElementへのポインタ */ 
    char                sourceIP[16],destIP[16];  /* 元、先のIPアドレス      */
    unsigned long       sourcePort,destPort;      /* 元、先のポート番号      */
};
struct conn_list        *cl;                      /* 接続テーブルリスト      */
struct conn_list        *org_cl;                  /* clの先頭アドレス        */

/************************************************
       NICをPROMISCUSモードに設定する
  [Input]
     nit_dev    : NITデバイス名(SUNOSのみ有効)
     nic_name   : NICデバイス名
  [Output]
     -1 : PACKETソケット作成不可
     -2 : デバイス状態フラグ取得不可
     -3 : 状態フラグセット不可
     それ以外 : Socket
 ************************************************
*/
#ifdef SOLARIS
int     strgetmsg(fd, ctlp, flagsp, caller)
int     fd;
struct  strbuf  *ctlp;
int     *flagsp;
char    *caller;
{
    int     rc;
    static  char    errmsg[80];

    *flagsp = 0;
    if ((rc=getmsg(fd,ctlp,NULL,flagsp))<0) return(-2);
    if (alarm(0)<0) return(-3);
    if ((rc&(MORECTL|MOREDATA))==(MORECTL|MOREDATA)) return(-4);
    if (rc&MORECTL) return(-5);
    if (rc&MOREDATA) return(-6);
    if (ctlp->len<sizeof(long)) return(-7);
    return(0);
}
#endif
int setnic_promisc(nit_dev,nic_name)
char    *nit_dev;
char    *nic_name;
{
    int          sock;
    struct ifreq f;

#ifdef SUNOS4
    struct strioctl si;
    struct timeval  timeout;
    u_int           chunksize = CHUNKSIZE;
    u_long          if_flags  = NI_PROMISC;

    if ((sock = open(nit_dev, O_RDONLY)) < 0)     return(-1);
    if (ioctl(sock, I_SRDOPT, (char *)RMSGD) < 0) return(-2);
    si.ic_timout = INFTIM;
    if (ioctl(sock, I_PUSH, "nbuf") < 0)          return(-3);

    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    si.ic_cmd = NIOCSTIME;
    si.ic_len = sizeof(timeout);
    si.ic_dp  = (char *)&timeout;
    if (ioctl(sock, I_STR, (char *)&si) < 0)      return(-4);

    si.ic_cmd = NIOCSCHUNK;
    si.ic_len = sizeof(chunksize);
    si.ic_dp  = (char *)&chunksize;
    if (ioctl(sock, I_STR, (char *)&si) < 0)      return(-5);

    strncpy(f.ifr_name, nic_name, sizeof(f.ifr_name));
    f.ifr_name[sizeof(f.ifr_name) - 1] = '\0';
    si.ic_cmd = NIOCBIND;
    si.ic_len = sizeof(f);
    si.ic_dp  = (char *)&f;
    if (ioctl(sock, I_STR, (char *)&si) < 0)      return(-6);

    si.ic_cmd = NIOCSFLAGS;
    si.ic_len = sizeof(if_flags);
    si.ic_dp  = (char *)&if_flags;
    if (ioctl(sock, I_STR, (char *)&si) < 0)      return(-7);
    if (ioctl(sock, I_FLUSH, (char *)FLUSHR) < 0) return(-8);
#endif
#ifdef LINUX
    if ((sock=socket(AF_INET,SOCK_PACKET,768))<0) return(-1);
    strcpy(f.ifr_name, nic_name);
    if (ioctl(sock,SIOCGIFFLAGS,&f)<0) return(-2);
    f.ifr_flags |= IFF_PROMISC;
    if (ioctl(sock,SIOCSIFFLAGS,&f)<0) return(-3);
#endif
#ifdef FREEBSD
    char                device[12];
    int                 n=0;
    struct bpf_version  bv;
    unsigned int        size;

    do{
        sprintf(device,"%s%d",nit_dev,n++);
        sock=open(device,O_RDONLY);
    }while(sock<0 && errno==EBUSY);
    if(ioctl(sock,BIOCVERSION,(char *)&bv)<0) return(-2);
    if((bv.bv_major!=BPF_MAJOR_VERSION)||(bv.bv_minor<BPF_MINOR_VERSION)) return(-3);
    strncpy(f.ifr_name,nic_name,sizeof(f.ifr_name));
    if(ioctl(sock,BIOCSETIF,(char *)&f)<0) return(-4);
    ioctl(sock,BIOCPROMISC,NULL);
    if(ioctl(sock,BIOCGBLEN,(char *)&size)<0) return(-5);
#endif
#ifdef IRIX
    struct sockaddr_raw sr;
    struct snoopfilter  sf;
    int                 size=CHUNKSIZE,on=1;
    char                *interface;

    if((sock=socket(PF_RAW,SOCK_RAW,RAWPROTO_SNOOP))<0) return -1;
    sr.sr_family = AF_RAW;
    sr.sr_port = 0;
    if (!(interface=(char *)getenv("interface")))
        memset(sr.sr_ifname,0,sizeof(sr.sr_ifname));
    else
        strncpy(sr.sr_ifname,interface,sizeof(sr.sr_ifname));
    if(bind(sock,&sr,sizeof(sr))<0) return(-2);
    memset((char *)&sf,0,sizeof(sf));
    if(ioctl(sock,SIOCADDSNOOP,&sf)<0) return(-3);
    setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char *)&size,sizeof(size));
    if(ioctl(sock,SIOCSNOOPING,&on)<0) return(-4);
#endif
#ifdef SOLARIS
    long                    buf[CHUNKSIZE];
    dl_attach_req_t         ar;
    dl_promiscon_req_t      pr;
    struct  strioctl        si;
    union   DL_primitives   *dp;
    dl_bind_req_t           bind_req;
    struct  strbuf          c;
    int                     flags;

    if ((sock=open(nit_dev,2))<0) return(-1);

    ar.dl_primitive=DL_ATTACH_REQ;
    ar.dl_ppa=0;
    c.maxlen=0;
    c.len=sizeof(dl_attach_req_t);
    c.buf=(char *)&ar;
    if (putmsg(sock,&c,NULL,0)<0) return(-2);

    c.maxlen=CHUNKSIZE; c.len=0; c.buf=(void *)buf;
    strgetmsg(sock,&c,&flags,"dlokack");
    dp=(union DL_primitives *)c.buf;
    if (dp->dl_primitive != DL_OK_ACK) return(-3);

    pr.dl_primitive=DL_PROMISCON_REQ;
    pr.dl_level=DL_PROMISC_PHYS;
    c.maxlen = 0;
    c.len=sizeof(dl_promiscon_req_t);
    c.buf=(char *)&pr;
    if (putmsg(sock,&c,NULL,0)<0) return(-4);

    c.maxlen=CHUNKSIZE; c.len=0; c.buf=(void *)buf;
    strgetmsg(sock,&c,&flags,"dlokack");
    dp=(union DL_primitives *)c.buf;
    if (dp->dl_primitive != DL_OK_ACK) return(-5);

    bind_req.dl_primitive=DL_BIND_REQ;
    bind_req.dl_sap=0x800;
    bind_req.dl_max_conind=0;
    bind_req.dl_service_mode=DL_CLDLS;
    bind_req.dl_conn_mgmt=0; 
    bind_req.dl_xidtest_flg=0;
    c.maxlen=0;
    c.len=sizeof(dl_bind_req_t);
    c.buf=(char *)&bind_req;
    if (putmsg(sock,&c,NULL,0)<0) return(-6);

    c.maxlen=CHUNKSIZE; c.len=0; c.buf=(void *)buf;
    strgetmsg(sock,&c,&flags,"dlbindack");
    dp=(union DL_primitives *)c.buf;
    if (dp->dl_primitive != DL_BIND_ACK) return(-7);

    si.ic_cmd=DLIOCRAW;
    si.ic_timout=-1;
    si.ic_len=0;
    si.ic_dp=NULL;
    if (ioctl(sock, I_STR, &si)<0) return(-8);

    if (ioctl(sock,I_FLUSH,FLUSHR)<0) return(-9);
#endif
    return(sock);
}

/************************************************
     接続テーブルリストclの内容を表示(DEBUG用)
 ************************************************
*/
void show_connection_table()
{
    struct conn_list    *ncl;
    int                    i;

#ifdef S_DEBUF
    return;
#endif
    ncl=org_cl;
    printf("\n>\n");
    printf("No.   SrcIP            SrcPort    DstIP            DstPort\n");
    for (i=0;;i++){
        if (ncl->next_p==NULL) break;
        printf("%-5d %-16s %-5d  ->  %-16s %-5d\n",i,
                ncl->sourceIP, ncl->sourcePort,
                ncl->destIP,   ncl->destPort);
        ncl=ncl->next_p;    
    }
    printf("\n");
}

/************************************************
  パケット中の表示可能文字だけをファイルに書く
  [Input]
     data  : パケット
     len   : パケット長
     fp    : ログファイルポインタ
 ************************************************
*/
void    printable_fwrite(data,len,fp)
char    *data;
int len;
FILE    *fp;
{
    int        i;

    for (i=0;i<len;i++)
        if (isprint(data[i]) || data[i]==10 || data[i]==13)
            putc(data[i],fp);
}

/************************************************
       拾ったパケットを解析してログる
  [Input]
      packet : 拾った生パケット
      length : パケット長
 ************************************************
*/
void        packet_analysis(packet,length)
unsigned char   *packet;
int     length;
{
    struct ip        *ip_header;              /* IPヘッダ                 */
    struct tcphdr    *tcp_header;             /* TCPヘッダ                */
    char             *tcp_data;               /* TCPデータ部              */
    struct in_addr   ia;                      /* IP抽出用in_addr          */
    char             sourceIP[16],destIP[16]; /* 元、先IPアドレス         */
    unsigned long    sourcePort,destPort;     /* 元、先ポート番号         */
    int              len_data;                /* データ部長さ             */
    FILE             *fp,*fp2;
    char             buf[200];
    char             fbuf[8000]; 
    int              i;
    int              iph_len,tcph_len;
    struct conn_list *bcl,*ncl;
    static long      snif_port[]={21,23,513,110,-1}; /* ログ対象ポート */

    ip_header     = (struct ip *)(packet+SIZE_OF_ETHHDR);

    /*-----------< TCPパケットでの処理 >--------------------------*/
    if (ip_header->ip_p==IPPROTO_TCP){

#ifdef SOLARIS
        iph_len=4*(packet[SIZE_OF_ETHHDR]&0x0f);
        tcph_len=4*(packet[SIZE_OF_ETHHDR+iph_len+12]>>4);
        tcp_header = (struct tcphdr *)((char *)ip_header+iph_len);
#else
        iph_len=(ip_header->ip_hl)*4;
        tcp_header = (struct tcphdr *)((char *)ip_header+iph_len);
        tcph_len=(tcp_header->th_off)*4;
#endif
        tcp_data   = (char *)tcp_header+tcph_len;

        /*-------< ログ対象ポートかどうか検査 >-------------------*/
        for (i=0;;i++){
            if (snif_port[i]==-1) return;
            if (tcp_header->th_dport==htons(snif_port[i])
             ||  tcp_header->th_sport==htons(snif_port[i])) break;
        }    

        /*-------< ヘッダ中の有用データ抽出 >---------------------*/
        len_data   = ntohs(ip_header->ip_len)-iph_len-tcph_len;

        sourcePort = ntohs(tcp_header->th_sport);
        destPort   = ntohs(tcp_header->th_dport);
        memcpy(&ia,&(ip_header->ip_src),sizeof(struct in_addr));
        strcpy(sourceIP,(char *)inet_ntoa(ia));
        memcpy(&ia,&(ip_header->ip_dst),sizeof(struct in_addr));
        strcpy(destIP,(char *)inet_ntoa(ia));

        /*-------< 3-wayのSTEP-1 SYNでの処理 >--------------------*/
        if (tcp_header->th_flags & TH_SYN && !(tcp_header->th_flags & TH_ACK)){

            /*---< Connectionテーブルに情報をセット >-------------*/
            if ((cl->next_p=(struct conn_list *)malloc(sizeof(struct conn_list)))==NULL){
                printf("Can not allocate memory\n");
                return;
            }
            strcpy(cl->sourceIP ,sourceIP);
            strcpy(cl->destIP   ,destIP);
            cl->sourcePort = sourcePort;
            cl->destPort   = destPort;
            cl             = cl->next_p;
            cl->next_p     = NULL;
            show_connection_table();

            /*---< ログファイルにConnection情報を書く >----------*/
            sprintf(buf,"%s%s.%lu.%s.%lu", TMPLOG_DIR,
                        sourceIP, sourcePort,
                        destIP,   destPort);
            if ((fp=fopen(buf,"w"))==NULL){
                printf("Can not append to temp logfile '%s'",buf);
                return;
            }
            fprintf(fp,"\n\n\n");
            for (i=0;i<60;i++) putc('=',fp); fprintf(fp,"\n");
            fprintf(fp,"CONNECTION %s(%d) -> %s(%d)\n",
                        sourceIP,sourcePort,destIP,destPort);
            for (i=0;i<60;i++) putc('=',fp); fprintf(fp,"\n");
            fclose(fp);
            return;
        }

        /*-------< Connection切断時の処理 >-----------------------*/
        if (tcp_header->th_flags & TH_FIN || tcp_header->th_flags & TH_RST){
        
            /*---< 接続テーブルからこの接続を求める >-------------*/
            bcl=org_cl;
            ncl=org_cl->next_p;
            for (;;){
                if (ncl->sourcePort == sourcePort   && ncl->destPort == destPort
                 && !strcmp(ncl->sourceIP,sourceIP) && !strcmp(ncl->destIP,destIP))
                        break;
                bcl=ncl;
                if ((ncl=ncl->next_p)==NULL) break;
            }    
            if (ncl==NULL) return;

            /*---< 本ログファイルにTEMPログを追加する >----------*/
            sprintf(buf,"%s%s.%lu.%s.%lu", TMPLOG_DIR,
                    ncl->sourceIP, ncl->sourcePort,
                    ncl->destIP,   ncl->destPort);

            if ((fp=fopen(LOGFILE,"a"))!=NULL){
                if ((fp2=fopen(buf,"r"))!=NULL){
                    for (;;){
                        if ((i=fread(fbuf,1,8000,fp2))<=0) break;
                        fwrite(fbuf,1,i,fp);
                    }    
                    fclose(fp2);
                }else
                    printf("Can not read temp logfile '%s'",buf);
                fclose(fp);
            }else
                printf("Can not append to logfile '%s'",LOGFILE);

            /*---< TEMPログを消し、接続テーブルから除去する >-----*/
            remove(buf);
            bcl->next_p=ncl->next_p;
            free(ncl);
            show_connection_table();
            return;
        }

        /*-------< 接続テーブルからこの接続を求める >-------------*/
        ncl=org_cl->next_p;
        for (;;){
            if (ncl->sourcePort == sourcePort   && ncl->destPort == destPort
             && !strcmp(ncl->sourceIP,sourceIP) && !strcmp(ncl->destIP,destIP))
                break;
            if (ncl->sourcePort == destPort     && ncl->destPort == sourcePort
             && !strcmp(ncl->sourceIP,destIP)   && !strcmp(ncl->destIP,sourceIP))
                break;
            if ((ncl=ncl->next_p)==NULL) break;
        }
        if (ncl==NULL) return;

        /*-------< TEMPログファイルに追加する >------------------*/
        sprintf(buf,"%s%s.%lu.%s.%lu", TMPLOG_DIR,
                    ncl->sourceIP, ncl->sourcePort,
                    ncl->destIP,   ncl->destPort);
        if ((fp=fopen(buf,"a"))==NULL){
            printf("Can not append to temp logfile '%s'",buf);
            return;
        }
        printable_fwrite(tcp_data,len_data,fp);
        fclose(fp);
    }
}

/************************************************
              プログラムエントリ
 ************************************************
*/
int main()
{
    static unsigned char    packet[CHUNKSIZE];
    int                     sock;                         
    int                     l;
#ifdef SUNOS4
    unsigned char           *buffer_p;
    struct nit_bufhdr       *nit_buffer_header; 
#endif
#ifdef FREEBSD
    unsigned char           *buffer_p;
    struct bpf_hdr          *nit_buffer_header;
#endif
#ifdef IRIX
    struct snooppacket {
        struct snoopheader snoop;
        char pad[ETHERHDRPAD];
        struct ether_header ether;
        char data[ETHERMTU];
    }*sp;
#endif
#ifdef SOLARIS
    struct  strbuf          data;
    int                     flags,result;
    
    data.buf=packet;
    data.maxlen=16000;
    data.len=0;
#endif

    /*-----------< 接続テーブル初期化 >---------------------------*/
    if ((cl=(struct conn_list *)malloc(sizeof(struct conn_list)))==NULL){
        printf("Can not allocate memory\n");
        exit(1);
    }        
    org_cl            = cl;
    cl->sourcePort    = 0;
    cl->destPort      = 0;
    strcpy(cl->sourceIP  ,"Start");
    strcpy(cl->destIP    ,"Start");
    cl->next_p        = (struct conn_list *)malloc(sizeof(struct conn_list));
    cl                = cl->next_p;
    cl->next_p        = NULL;

    show_connection_table();

    /*-----------< NICをPROMISCUSモードに設定する >---------------*/
    if ((sock=setnic_promisc(NIT_DEV,DEFAULT_NIC))<0){
        switch(sock){
            case -1 : printf("Can not create PACKET socket.\n"); break;
            case -2 : printf("Can not get device status flag.\n"); break;
            case -3 : printf("Can not set nic to promisc mode.\n"); break;
        }
    printf("%d\n",sock);
        return(-1);
    }

    /*-----------< パケットを拾って解析する >---------------------*/
    for (;;){
#ifdef SOLARIS
        result=getmsg(sock,NULL,&data,&flags);
        if (result==0 || result==MOREDATA || result ==MORECTL)
            packet_analysis(data.buf,data.len);
        {
#else
        if ((l=read(sock,packet,sizeof(packet)))>0){
#endif
#ifdef SUNOS4
            buffer_p = packet;
            while (buffer_p < packet+l) {
                nit_buffer_header = (struct nit_bufhdr *)buffer_p;
                packet_analysis(buffer_p+sizeof(struct nit_bufhdr),
                                nit_buffer_header->nhb_msglen);
                buffer_p += nit_buffer_header->nhb_totlen;
            }
#endif
#ifdef LINUX
            packet_analysis(packet,l); 
#endif
#ifdef FREEBSD
            buffer_p = packet;
            while (buffer_p < packet+l) {
                nit_buffer_header = (struct bpf_hdr *)buffer_p;
                packet_analysis(buffer_p+nit_buffer_header->bh_hdrlen,
                                sizeof(packet)-nit_buffer_header->bh_hdrlen);
                buffer_p += BPF_WORDALIGN(nit_buffer_header->bh_caplen+nit_buffer_header->bh_hdrlen);
            }
#endif
#ifdef IRIX
            sp = (struct snooppacket *)packet;
            packet_analysis((struct ether_header *)(&(sp->ether)),l); 
#endif
        }
    }
}
