/* 
 Atanas Dimitrov
 Homework #5
 CS6900
 Due: 09/24/04
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <string.h>
#include <netdb.h>
#ifndef __USE_BSD
#define __USE_BSD
#endif
#include <netinet/ip.h>
#ifndef __FAVOR_BSD
#define __FAVOR_BSD
#endif
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#ifndef KERNEL
#define KERNEL
#endif
#ifndef __APPLE_API_PRIVATE
#define __APPLE_API_PRIVATE
#endif
#include <netinet/in.h>
#include <net/if.h>
#include <sys/time.h> 
#include <sys/ioctl.h> 
#include <netinet/in_systm.h> 
#include <net/bpf.h> 
#include <net/ethernet.h>
#include <fcntl.h> 
#include <string.h> 
#include <errno.h> 

//interface to sniff
#define INTERFACE "en0"

/*
 sniffing function returns sequence number in 
 the sniffed packet which matches the filter
 */
int sniff(int fd, int sip, int dip, short sport, short dport) 
{ 
        int true = 1; 
        int buflen, r; 
        struct bpf_hdr *buf; 
        struct ifreq ifreq; 

	//sniffer program/code for the special device
        struct bpf_insn insns[] = {
 
		/*
		 from thhis point on anything that doesn't match 
		 the src, dest, and ports will be dropped and not accounted for
		 */
		//go to byte 12 and see if this is IP
                BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), 
                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 11),
		
		//go to byte 23 and see if this is TCP
                BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 23), 
                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_TCP, 0, 9), 

		//filter out source ip address
                BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 26), 
                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, sip, 0, 7), 

		//filter out destination ip address
                BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 30), 
                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, dip, 0, 5), 

		//filter out source port
                BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 34), 
                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, sport, 0, 3), 

		//filter out destination port
                BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 36), 
                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, dport, 0, 1), 

		//read the packet
                BPF_STMT(BPF_RET+BPF_K, (u_int)-1), 
		
		//ignore the packet
                BPF_STMT(BPF_RET+BPF_K, 0), 

        }; 

        struct bpf_program bpf_program = { 14, (struct bpf_insn *) &insns }; 
        struct timeval timeval; 
        struct ip *iph; 
        struct tcphdr *tcph; 

	//store the interface in the required struct
        strcpy((char *) ifreq.ifr_name, INTERFACE); 

	//set the interface associated with the file
        ioctl(fd, BIOCSETIF, &ifreq);

	//set the buffer length for reading from the file
        ioctl(fd, BIOCGBLEN, &buflen); 

	//set to read immediately after the receipt of the packet
        ioctl(fd, BIOCIMMEDIATE, (u_int) &true);

	//set the timout for the remote response
        timeval.tv_sec = 1; 
        timeval.tv_usec = 0; 
        ioctl(fd, BIOCSRTIMEOUT, (struct timeval *) &timeval); 

	//initialize the sniffer code to filter out traffic related to scanning
        ioctl(fd, BIOCSETF, (struct bpf_program *) &bpf_program);

	//get and store the values
        buf = (struct bpf_hdr *) malloc(buflen); 
        bzero(buf, buflen); 
        r = read(fd, buf, buflen);
        iph = (struct ip *) ((char *) buf + buf->bh_hdrlen + sizeof(struct ether_header)); 
        tcph = (struct tcphdr *) ((char *) iph + sizeof(struct ip));
	
	//return the sequence number of the captured packet
        if (r > 0) 
                return ntohl(tcph->th_seq); 
        return 0; 
} 

/*
 Function to send and receive packets returns open port numner 
 or 0 if closed port, takes as a parameter port number.
 */
int  poke(int port)
{
	int sock, newisn, oldisn;
	int len, bpf;
	struct timeval tv1, tv2; 
	struct timezone tz1, tz2;

	int i;
	int raw_sock = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);

	char datagram[4096];
	char cksum_tcpheader[1024];

	struct ip *iph = (struct ip *) &datagram;
	struct tcphdr *tcph = (struct tcphdr *) &datagram+1;
	struct sockaddr_in sin;

	sin.sin_family = AF_INET;
	sin.sin_port = htons (port);
	sin.sin_addr.s_addr = inet_addr ("192.168.0.2");

	memset(datagram, 0x0, sizeof(datagram));
	memset(cksum_tcpheader, 0, sizeof(cksum_tcpheader));

	//IP header
	iph->ip_hl = 5;
	iph->ip_v = IPVERSION;
	iph->ip_tos = IPTOS_PREC_ROUTINE;
	iph->ip_len = htons(sizeof (struct ip) + sizeof (struct tcphdr));
	iph->ip_id = htonl(11111);
	iph->ip_off = 0x0;
	iph->ip_ttl = MAXTTL;
	iph->ip_p = 6;
	iph->ip_sum = 0x0;
	iph->ip_src.s_addr = inet_addr ("192.168.10.1");
	iph->ip_dst.s_addr = sin.sin_addr.s_addr;

	//TCP Header
	tcph->th_sport = htons(3491);
	tcph->th_dport = htons(port);
	tcph->th_seq = random();
	tcph->th_ack = 0x0;
	tcph->th_x2 = 0x0;
	tcph->th_off = sizeof(struct tcphdr) + 1;
	tcph->th_flags = TH_SYN;
	tcph->th_win = htonl(TCP_MAXWIN);
	tcph->th_sum = 0x0;
	tcph->th_urp = 0x0;

	//in_cksum() compiled in /usr/lib/libBSDPClient.A.dylib on Mac OS X
	iph->ip_sum = in_cksum ((unsigned short *) iph, sizeof(struct ip));

	//special treatment for tcp header checksum
	memcpy(&cksum_tcpheader, &(iph->ip_src.s_addr), 4); 
	memcpy(&cksum_tcpheader[4], &(iph->ip_dst.s_addr), 4); 
	cksum_tcpheader[8]=0; 
	cksum_tcpheader[9]=(u_int16_t)iph->ip_p; 
	cksum_tcpheader[10]=(u_int16_t) (sizeof(struct tcphdr) & 0xff00) >> 8; 
	cksum_tcpheader[11]=(u_int16_t) (sizeof(struct tcphdr) & 0x00ff); 
	memcpy(&cksum_tcpheader[12], tcph, sizeof(struct tcphdr));
	
	//in_cksum() compiled in /usr/lib/libBSDPClient.A.dylib on Mac OS X 
	tcph->th_sum = in_cksum((unsigned short *) (cksum_tcpheader), sizeof(struct tcphdr)+12);

	//set socket option to include the header
	int one = 1;
	const int *val = &one;
	setsockopt (raw_sock, IPPROTO_IP, IP_HDRINCL, val, sizeof (one));
	
	//open BSD Packet Filter
	bpf = open("/dev/bpf3", O_RDWR);

	//send the poking packet
	sendto(raw_sock, datagram, iph->ip_len, SO_KEEPALIVE, (struct sockaddr *) &sin, sizeof (const int *));

	//sniff the reply
        newisn=sniff(bpf, ntohl(iph->ip_dst.s_addr), ntohl(iph->ip_src.s_addr), \
				ntohs(tcph->th_dport), ntohs(tcph->th_sport)); 

	//if timed out then close out and return 0
	if (newisn==0) 
        { 
		close(raw_sock);
		close(bpf);
		return(0);
        } 
	
	//we must close these to reuse them
        oldisn=newisn;
	close(raw_sock); 
	close(bpf); 
	
	//if the port is open then return the number
	return(port);
}

#define MAX_SCAN_PORTS 1255

/*
 main function
 */
int main()
{
	//store the results from the sequential port scan in this array
	int ports[MAX_SCAN_PORTS];
	int i;
	
	//clear the array 
	for(i=0; i<MAX_SCAN_PORTS; i++)
		ports[i]=0;

	//scan the range of ports
	for (i=1; i<MAX_SCAN_PORTS; i++)
	{
		ports[i]=poke(i);
	}

	//print the results
	printf("Interesting ports: ");

	for(i=0; i<MAX_SCAN_PORTS; i++)
	{	if (ports[i]!=0)
                	printf("%d ", ports[i]);
	}

	printf("\n");
	exit(0);
}
