    /* AMAP - application mapper
     * Copyright (C) 2002  DJ.Rev.Moon and vanHauser
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.    
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *    
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */
#include "amap.h"

#define PROGRAM   "Amap"
#define VERSION   "v0.95"
#define AUTHOR    "DJ.RevMoon and vanHauser"
#define EMAIL     "amap-defs@tink.org"
#define RESOURCE  "http://www.thehackerschoice.com"

#define TIMEOUT_T 		5
#define PROTO_LEN		64
#define BUFSIZE			4096
#define APP_FINGERPRINT_FILE	"appdefs"
#define RESP_EXT		".resp"
#define TRIG_EXT		".trig"
#define DEFAULT_TRIG		"\r\nHELP\r\n"

#define MAX_RETRIES	3

/* child communication commands/responses */
#define FEED_ME  1 /* Child is bored */
#define QUITTING 0 /* Child quits    */
#define EAT_THIS 2 /* Mother feeds   */
#define YOU_DIE  3 /* Mother kills child :-( */

/* child error levels */
#define SUCCESS 0 /* we got a response      */
#define NO_CONN 1 /* coulnd't connect       */
#define NO_RESP 2 /* did not get a response */
#define WAIT    3 /* waiting for input */
#define INV_IP  4 /* an invalid IP address was received */

#define MAXTASKS	200
#define TASKS		16	// 16 is a nice number ;-)
#define DO		1
#define DONT		0

/* Trigger types */
#define TRIG_STR	1
#define TRIG_HEX	2
#define TRIG_NUM	3
#define	TRIG_UNK	0
#define TRIG_DEF	4

/* Search types */
#define SUB_STR		0
#define START_S		1
#define LIT_STR		2

//-----------------------------------------

int verbose;
int bombarde;
int banner;
int dump;
int timeout_t=TIMEOUT_T;
int SSL_first;
int unrec=1;	// XXX currently set to default!
int harmful;
int tasks=TASKS;
int running_kids;
int tasks_left;
int killed;
int log_to_file;
int resp_recv;
int alarm_recv;
int fail;
pid_t pids[MAXTASKS];

char scantype = 'b';
char banner_string[256];
char *only_proto = NULL;
char *searchpath[12] = { "./", "/etc/", "/usr/etc", "/usr/local/etc/", 
                           "/opt/etc/", "/usr/local/amap/", "/opt/amap/",
                           "/usr/local/lib/", "/usr/local/lib/amap/",
                           "/usr/lib/amap/", "/usr/local/bin/", NULL };

typedef struct {
int cmd;            /* FEED_ME, DONE_TASK YOU_DIE or EAT_THIS */
char ip_ad[17];     /* the IP address */
unsigned short int port;	    /* the port to connect to */	
char proto;         /* TCP, UDP or both */
char trigger[1024]; /* the trigger to send */
int trig_type;	    /* the trigger type */
char response[1024];/* the response received */
char *orig_response;/* same as above, but will not be manipulated */
int response_length;/* the length of the response received (could contain null bytes) */
int err_level;	    /* the response code from the child */	
} comm_block;

typedef struct {
unsigned short int port;
char ip_prot;
char *appl_id;
char *unknown_response;
int unknown_response_length;
struct ll_ports *next;
} ll_ports;

typedef struct {
char *tar_host;
ll_ports *portl;
struct ll_targets *next;
} ll_targets;

typedef struct {
char *prot;
char ip_prot;
char *trigger_string;
int trig_length;
int dangerous;
int trig_type;
struct ll_trig *next;
} ll_trig;

typedef struct {
char *prot;
char *response_string;
int srch_type;
int response_type;
struct ll_resp *next;
} ll_resp;

int taskno;
int total_tasks;
ll_targets *targ_todo;
ll_ports *port_todo;
ll_trig *frst_trig;
ll_trig *trig_todo;
ll_resp *frst_resp;

//-------------------------------------------

void help() {
    printf("%s %s (c) 2002 by %s - <%s>
Syntax: %s [-i <file>] [-s<T|U>] [-b] [-r] [-u] [-v] [-d] [-o <file>]
             [-D <file>] [-t sec] [-T cons] [-S] [-p PROTO ] TARGET PORT

Options:
    -i FILE   Nmap machine readable outputfile to read ports from.
    -s T|U    Only do TCP or UDP ports. Default is both. 
    -v        verbose mode. 
    -d        Dump hex traffic (only if a response is received).
    -b        If an ascii banner is received as a response, print it.
    -H        Skip app trigs marked as potentially Harmful (may crash server).
    -T CONS   Amount of parallel connections to make (default %d, max 200).
    -p PROTO  Only test for applicational protocol PROTO (i.e.: ftp,smtp).
    -t SEC    Timeout. Seconds to wait for a response. (default %d).
    -o FILE   Write output to file.
    -D FILE   Read from Definitions FILE.[trig|resp] in stead of default.
    -h        Print this shit.
    TARGET PORT   The target address and port to scan.

%s is a tool to identify application protocols on a target.
You can always find the newest version at %s
",PROGRAM,VERSION,AUTHOR,EMAIL,PROGRAM,TASKS,TIMEOUT_T,PROGRAM,RESOURCE); 

/* unimplemented at this moment:
    -r        send random crap to the ports to elicit a response.
    -u        Supress dumping of unrecognised responses from ports. 
    -S        Try SSL handshake first for ports, then identify application.
*/

exit(-1);
}

//--------------------------------------------

void exit_err(char *messg) {
    fprintf(stderr, "Error: %s\n", messg);
    exit(-1);
}

//--------------------------------------------

void warn(char *messg) {
    fprintf(stderr, "Warning: %s, continuing...\n", messg);
}

//--------------------------------------------

int check_trig(char *trig)	{

if(strlen(trig) == 0) return TRIG_DEF;
if(strstr(trig, "0x") == trig) return TRIG_HEX;
if((trig[0] == '"')&&(trig[strlen(trig)-1] == '"')) return TRIG_STR;
if(isdigit(trig[0])) return TRIG_NUM;

return TRIG_UNK;

}

//--------------------------------------------

void trans_esc(char *orig)	{
int i,t;
char *walk;

/* currently only does \n, \t and \r escapes. Oh, and \\ for \ */

while((walk = index(orig, '\\')) != NULL)	{
	t=1;
	switch(*(walk+1))	{
		case 'n': *walk++ = '\x0a';
			break;
		case 'r': *walk++ = '\x0d'; 
			break;
		case 't': *walk++ = '\x09'; 
			break;
		case '\\': *walk++ = '\\'; 
			break;
		default: t=0;
			break;
		}
	if(t==1){
		for(i=0;walk[i] != '\0';i++)	{
			walk[i] = walk[i+1];
			}
		}
	orig=walk;
	}
}

//--------------------------------------------

void replace(char *target, int from, int to) {
register int l=0;
while((target[++l] != '\0')) {
	if(target[l] == from) target[l] = to;
	}
}
//--------------------------------------------

void make_lower(char *target, int len) {
register int l=0;
for(l=0;l<len;l++) if (target[l] != 0) target[l]=(char)tolower(target[l]);
}

//--------------------------------------------
char *amap_memstr(char *haystack, char *needle, int haystack_length, int needle_length) {
register int i;
for (i = 0; i < haystack_length; i++)
    if (memcmp(haystack + i, needle, needle_length) == 0)
            return(haystack + i);

return NULL;
}

//--------------------------------------------

char *print_banner_string(char *response, int length) {
         char *pbanner;
         int i = 0;
         int len = 0;

         memset(banner_string, 0, sizeof(banner_string));
         pbanner = banner_string;

         while (i <= length && i < 240 && len < length) {
             if (*response == '\t' || *response == '\n' || *response == '\r' || (*response != 0 && isprint(*response) && *response >= ' ' && *response <= 'z')) {
                 if (*response == '\r') {
                     strcat(banner_string, "\\r");
                     i += 2;
                     } else {
                 if (*response == '\n') {
                     strcat(banner_string, "\\n");
                     i += 2;
                     } else {
                 if (*response == '\t') {
                     strcat(banner_string, "\\t");
                     i += 2;
                     } else {
                         *(pbanner + i) = *response;
                         i++;
                         }
                     }
                     }
                 }
                 response++;
                 len++;
             }

         return banner_string;
}

//--------------------------------------------

char *print_banner(char *response, int length) {
     if (! banner)
         return "";
     else {
         char bs[sizeof(banner_string)];
         strcpy(bs, " - banner: ");
         strcat(bs, print_banner_string(response, length));
         strcpy(banner_string, bs);
         return(banner_string);
     }
}

//--------------------------------------------

void clear_block(comm_block *babble)	{

memset(babble, 0, sizeof(comm_block));
babble->cmd = FEED_ME;
strcpy(babble->ip_ad, "0.0.0.0");
babble->port = (unsigned short int)0;
babble->proto =  'b';
strcpy(babble->trigger, "");
strcpy(babble->response, "");
babble->orig_response = NULL;
babble->response_length = 5;
babble->err_level = WAIT;

}

//--------------------------------------------

void delspaces(char *target) {
/* spaces and tabs be gone! But only if it's not a string */
register int l=0;
register int k=0;

if(index(target, '\042') != NULL) return;
while((target[l] != '\0')) {
	if((target[l] == ' ') || (target[l] == '\t')) {
		k=l--;
		while(target[k++] != '\0') target[k-1] = target[k];
		}
	l++;
	}
}

//--------------------------------------------

ll_trig *read_trig(char *fnam) {
ll_trig *frst_trig = NULL, *curr_trig = NULL;
FILE *trig_file = NULL;
char *in_line;
char file_name[256];
char *readln = NULL;
char *walk = NULL;
int cnt=1, i = 0;

in_line = (char *)malloc(BUFSIZE);

while (searchpath[i] && trig_file == NULL) {
    strcpy(file_name, searchpath[i]);
    strcat(file_name, fnam);
    strcat(file_name, TRIG_EXT);
    trig_file = fopen(file_name, "r");
    i++;
    }

if(trig_file == NULL) exit_err("Error opening trigger file\n");

if (verbose) printf("Using trigger file %s\n", file_name);

while(fgets(in_line, BUFSIZE, trig_file) != NULL)	{
	if((in_line[0] != '#') && (index(in_line, ':') != NULL )) { /* we have to read this line */
		if((only_proto == NULL)||(strncmp(only_proto, in_line, (int)(strstr(in_line, ":")-in_line)) == 0)) {
			if(!((harmful == 1)&&(strstr(in_line, ":1:") != NULL))
			  &&
			   !((scantype == 'u')&&(strstr(in_line, ":t:") != NULL))
			  &&
			   !((scantype == 't')&&(strstr(in_line, ":u:") != NULL))
			  ) {
				if(cnt == 1)	{
					curr_trig=(ll_trig *)malloc(sizeof(ll_trig));
					frst_trig = curr_trig;
					cnt++;
					}
					else	{
					(ll_trig *)curr_trig->next=(ll_trig *)malloc(sizeof(ll_trig));
					curr_trig=(ll_trig *)curr_trig->next;
					cnt++;
					}
				if((readln=(char *)malloc(strlen(in_line) + 1)) == NULL) exit_err("malloc failed\n");
				in_line[strlen(in_line)-1] = '\0';
				strcpy(readln, in_line);
				curr_trig->prot = readln;
				if ((walk=index(readln, ':')) == NULL) {
				    fprintf(stderr, "Error(1): invalid line in trigger file - %s\n", readln);
				    exit(-1);
				}
				*walk = '\0';
				walk++;
				curr_trig->ip_prot=((*walk == 't')||(*walk == 'u') ? *walk : 'b');
				if ((walk=index(walk, ':')) == NULL) {
				    fprintf(stderr, "Error(2): invalid line in trigger file - %s\n", readln);
				    exit(-1);
				}
				walk++;
				curr_trig->dangerous = atoi(walk);
				if ((walk=index(walk, ':')) == NULL) {
				    fprintf(stderr, "Error(3): invalid line in trigger file - %s\n", readln);
				    exit(-1);
				}
				walk++;
				curr_trig->trigger_string = walk;
				delspaces(curr_trig->trigger_string);
				if((curr_trig->trig_type = check_trig(curr_trig->trigger_string)) == TRIG_UNK) exit_err("Incorrect trigger definition, watch what you type!\n");
				make_lower(curr_trig->prot, strlen(curr_trig->prot)); 
				}
			}
		}
	}
if(cnt == 1) exit_err("No triggers read, why not? bastard, trying to trick me, eh?\n");
return(frst_trig);
}


//--------------------------------------------


ll_resp *read_resp(char *fnam) {
ll_resp *frst_resp = NULL, *curr_resp = NULL;
FILE *resp_file = NULL;
char *in_line;
char file_name[128];
char *readln = NULL, *walk = NULL;
int cnt = 0, i = 0;

in_line = (char *)malloc(BUFSIZE);

while (searchpath[i] && resp_file == NULL) {
    strcpy(file_name, searchpath[i]);
    strcat(file_name, fnam);
    strcat(file_name, RESP_EXT);
    resp_file = fopen(file_name, "r");
    i++;
    }

if(resp_file == NULL) exit_err("Error opening response file\n");

if (verbose) printf("Using response file %s\n", file_name);

while(fgets(in_line, BUFSIZE, resp_file) != NULL)	{
	if((in_line[0] != '#') && (strstr(in_line, ":") != 0))	{ /* we have to read this line */
               if(cnt == 0)	{
               		curr_resp=(ll_resp *)malloc(sizeof(ll_resp));
   			frst_resp = curr_resp;
                       	cnt++;
                        }
              		else	{
                        curr_resp->next=malloc(sizeof(ll_resp));
                        curr_resp=(ll_resp *)curr_resp->next;
                        cnt++;
                        }
		readln=(char *)malloc(strlen(in_line) + 1);
                in_line[strlen(in_line)-1] = '\0';
		strcpy(readln, in_line);
		curr_resp->prot = readln;
		if ((walk=index(readln, ':')) == NULL) {
		    fprintf(stderr, "Error(1): invalid line in response file - %s\n", readln);
		    exit(-1);
		}
		*walk = '\0';
		walk++;
		if(*walk == '/') {
			curr_resp->srch_type = SUB_STR;
			walk++;
			} else {
			if(*walk == '^') {
				curr_resp->srch_type = START_S;
				walk++;
				} else {
					curr_resp->srch_type = LIT_STR;
				}
			}
		curr_resp->response_string = walk;
		if((curr_resp->response_type = check_trig(curr_resp->response_string)) == TRIG_UNK) exit_err("Unknown response type read.\n");
		delspaces(curr_resp->response_string);
		make_lower(curr_resp->response_string, strlen(curr_resp->response_string));
		}
	}
if(cnt <= 1) exit_err("No responses read. Response File empty? Come on, try to be a *real* hacker!\n");
return(frst_resp);
}

//--------------------------------------------

ll_ports *scan_line_4_ports(char *m, char *scanstr) {
ll_ports *frst_port = NULL, *this_port = NULL;
int cnt = 0;
char *found;

while((found = strstr(m, scanstr)) != NULL)	{
	if(cnt == 0)	{
		(ll_ports *)this_port=(ll_ports *)malloc(sizeof(ll_ports));
		frst_port = this_port;
		cnt++;
		}
		else	{
		(ll_ports *)this_port->next=(ll_ports *)malloc(sizeof(ll_ports));
		this_port = (ll_ports *)this_port->next;
		cnt++;
		}
	while(*--found != ' ' && *found != 0);
	this_port->port = (unsigned short int)atoi(found);
	this_port->appl_id = (char *)malloc(PROTO_LEN);
	strcpy(this_port->appl_id, "unidentified");
	this_port->unknown_response_length = 0;
	while(*found != 0 && *found++ != '/');
	while(*found != 0 && *found++ != '/');
	this_port->ip_prot = *found;
	if (this_port->ip_prot == 0 || this_port->port == 0) {
	    fprintf(stderr, "Error: invalid nmap line - %s\n", scanstr);
	    exit(-1);
	}
	m = found;				
	}
return(frst_port);
}

//--------------------------------------------

ll_targets *read_targ(char *filename)	{
ll_targets *this_tar = NULL, *frst_targ = NULL;
FILE *fp_in = NULL;
char *in_line;
char *l = NULL, *m = NULL;
char *walk = NULL;
char scanstr[12];
int k = 0;
int cnt = 0;

/*This whole routine RELIES on the nmap format output. if that is broken, so is this. 
  Also, it assumes that nmap lines won't be longer that 4096 chars.*/

in_line = (char *)malloc(BUFSIZE);

strcpy(scanstr, "open/");
if(scantype == 't') strcpy(scanstr, "open/tcp");
if(scantype == 'u') strcpy(scanstr, "open/udp");
if((fp_in = fopen(filename, "r")) == NULL) return(0);
	
while((fgets(in_line, BUFSIZE, fp_in)) != NULL)	{
	/* parse the line */
	l=in_line;
	k=0;
	in_line[strlen(in_line)-1]='\0';
	if((in_line[0] != '#') && (strstr(in_line, scanstr) != NULL) ) { /* skip lines starting with # and lines with no open ports*/
		if(cnt == 0)	{
			this_tar = (ll_targets *)malloc(sizeof(ll_targets));
			frst_targ = this_tar;
			cnt++;
			} else	{
	                (ll_targets *)this_tar->next=(ll_targets *)malloc(sizeof(ll_targets));
	                this_tar = (ll_targets *)this_tar->next;
                       	cnt++;
			}
		if ((walk=index(in_line, ' ')) == NULL) {
		    fprintf(stderr, "Error: invalid line in nmap file - %s\n", in_line);
		    exit(-1);
		}
		walk++;
		if ((m=index(walk, ' ')) == NULL) {
		    fprintf(stderr, "Error: invalid line in nmap file - %s\n", in_line);
		    exit(-1);
		}
		*m++='\0';
		if (verbose>1) fprintf(stderr,"DEBUG nmap target: %s\n",walk);
		this_tar->tar_host = (char *)malloc(strlen(walk) + 1);
		strcpy(this_tar->tar_host, walk);
		if (verbose>1) fprintf(stderr,"DEBUG nmap target: %s\n",this_tar->tar_host);
		/* Go find the open ports */
		this_tar->portl = scan_line_4_ports(m, scanstr);
		if(this_tar->portl == NULL) warn("huh? nmap file might be invalid");
		}
	}
if (cnt == 0)
    exit_err("invalid nmap input file, must be created with the -oM option! *sigh*");
return(frst_targ);
}

//--------------------------------------------

void die(int signo) {
int i;
for (i = 0; i < tasks; i++)
    if (pids[i] > 0)
        kill(pids[i], 9);
exit(-1);
}

//--------------------------------------------

void dead_kid(int signo) {
killed++;
}

//--------------------------------------------

void alarm_flag(int signo) {
alarm_recv=1;
if (verbose) printf("Alarm Timeout\n");
fail++;
}

//--------------------------------------------

int tohex(int a, int b)	{
if(isxdigit(a)) {
	a=tolower(a);
	}
else	{
	printf("char %c\n", a);
	warn("non-hex digit in hex trigger string, silly you");
	a='0';
	}
if(isxdigit(b)) {
	b=tolower(b);
	}
else	{
	printf("char %c\n", b);
	warn("non-hex digit in hex trigger string, silly you");
	b='0';
	}
	
isalpha(a)?(a-=87):(a-=48);

isalpha(b)?(b-=87):(b-=48);

return((a*16)+b);
}

//--------------------------------------------
int compile_line(char *trig, char *out_bytes, int type)	{
int i;
int j;
int k;
char temp1[1024];
char *temp2 = NULL;

for(i=0;i<1024;i++) out_bytes[i] = '\0';
j=strlen(trig);

switch(type)	{
	case TRIG_NUM: return(atoi(trig)); break;
	case TRIG_STR: {
		strcpy(temp1, trig);
		temp2=rindex(temp1, '"');
		*temp2 = '\0';
		temp2=index(temp1, '"');
		temp2++;
		trans_esc(temp2);
		j=sprintf(out_bytes, temp2, ""); return(j); 
		}
		break;
	case TRIG_DEF: sprintf(out_bytes, DEFAULT_TRIG); return(strlen(out_bytes)); break;
	case TRIG_HEX: {
		k=0;
		for(i=2;i<j-1;i+=2)	{
			out_bytes[k] = tohex(trig[i], trig[i+1]);
			k++;
			}
		return((j-2)/2);
		}
		break;
	default: sprintf(out_bytes, DEFAULT_TRIG); return(strlen(out_bytes)); break;
	}
}


//--------------------------------------------

int amap_conn(comm_block *babble)	{
char compiled_t[1024];
char rline[1024];
int len_t;
int err_lev=NO_CONN;
struct sockaddr_in my_tar;
struct in_addr addie;
int p, t, s = -1, ret, rlen;
fd_set fds;
struct timeval tv;

if(babble->proto == 't') p=SOCK_STREAM; else p=SOCK_DGRAM;
if(babble->proto == 't') t=IPPROTO_TCP; else t=IPPROTO_UDP;

len_t=compile_line(babble->trigger, compiled_t, babble->trig_type);
signal(SIGALRM, alarm_flag);

if((s = socket(AF_INET, p, t)) == -1) exit_err("socket creation failed");

my_tar.sin_port=htons((unsigned short int)babble->port);
my_tar.sin_family=AF_INET;
//if(inet_aton(babble->ip_ad, &addie)==0) return(INV_IP); //deprecated
if(inet_pton(AF_INET, babble->ip_ad, &addie) <= 0) return(INV_IP);
my_tar.sin_addr.s_addr = addie.s_addr;

ret=-1;
alarm_recv = 0;

while((fail < MAX_RETRIES)&&(ret < 0))	{
	alarm(timeout_t);
	ret=connect(s, (struct sockaddr *)&my_tar, sizeof(my_tar));
	alarm(0);
	if(ret != 0)	{
		fail++;
		if(fail >= MAX_RETRIES) 
			{
			close(s);
			return(NO_CONN);
			}
		sleep(1);
		}
	}
err_lev=NO_RESP;	
if(verbose) printf("Connected to %s port %d\n", babble->ip_ad, babble->port);

write(s, compiled_t, len_t); 

// whats this? usage of fcntl adds rubbish to recv received data! glibc bug?
//if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) fprintf(stderr, "fcntl error \n");

//for(fail = 0;fail < timeout_t; fail++) {
        memset(rline, 0, sizeof(rline));
        rlen = -1;

	FD_ZERO(&fds);
	FD_SET(s, &fds);
	tv.tv_sec = timeout_t;
	tv.tv_usec = 0;

if (select(s + 1, &fds, NULL, NULL, &tv) > 0)
	rlen=recv(s, rline, sizeof(rline), 0);
/*
	if(rlen > 0) break;
	sleep(1);
	}
*/

if (rlen > 0) { // saves some lines, as close+return is below anyway
	err_lev = SUCCESS;
	babble->response_length = rlen;
	memcpy(babble->response, rline, rlen);
	babble->response[rlen] = '\0';
	}

close(s);	
return(err_lev);
}

//--------------------------------------------

void do_tasks(int sock)	{
comm_block babble;

while(DO) {
memset((char*)&babble, 0, sizeof(babble));
read(sock, &babble, sizeof(babble));
	switch(babble.cmd)	{
		case EAT_THIS: {
			babble.err_level = amap_conn(&babble);
			babble.cmd = FEED_ME;
			write(sock, &babble, sizeof(babble));
			}
			break;
		case YOU_DIE: { 
			clear_block(&babble);
			if(verbose) printf("I'm going down (%d)! mom never really liked me ... :-(\n", (int)getpid());
			sleep(2);
			babble.cmd = QUITTING;
			write(sock, &babble, sizeof(babble));
			}
			return; 
			break;
		default: 
		        warn("Received unknown command (this smells deady like memory corruption - or a silly programming mistake)");
		        break;
		}
	}
}


//--------------------------------------------
    
void get_next_task(comm_block *babble)	{
int found_next=0;

if((tasks_left > 0)&&(running_kids > 0))	{

	/* Fill in target details and command */
	babble->cmd = EAT_THIS;
	strcpy(babble->ip_ad, targ_todo->tar_host);
	babble->port = port_todo->port;
	
	/* Fill in trigger details */
	strcpy(babble->trigger, trig_todo->trigger_string);
	babble->trig_type = trig_todo->trig_type;
	babble->proto = port_todo->ip_prot;	
	
	/* Forward to next trigger */
	trig_todo = (ll_trig *)trig_todo->next;
	
	tasks_left--;

	/* Forward to next appropriate trigger for this the port */
	while((found_next == 0)&&(tasks_left >= 1))	{
		if(trig_todo != NULL) { /* there is a next trigger */
			if(((port_todo->ip_prot == 't') && (trig_todo->ip_prot == 'u')) ||
			     ((port_todo->ip_prot == 'u') && (trig_todo->ip_prot == 't'))) {
			   	/* There is a mismatch, this trigger is not appropriate */
			   	trig_todo = (ll_trig *)trig_todo->next; /* ergo, go to next */
			   	}
			   else {
			   	found_next = 1;
			   	} 
			}
		else { /* at end of trigger list */
			trig_todo = frst_trig; /* rewind to start of trigger list */
			port_todo = (ll_ports *)port_todo->next; /* go to next port */
			if(port_todo == NULL) {  /* end of portlist */
				targ_todo=(ll_targets *)targ_todo->next; /* goto next target */
				if(targ_todo != NULL) { /* is there a next target */
					port_todo=(ll_ports *)targ_todo->portl; /* point to first port of this target */
					}
				else { /* No more targets */
					found_next = 1;
					if(tasks_left != 0) printf("We counted wrong. There are no more targets to scan, but tasks_left = %d.\n", tasks_left);
					}
				}
			}
		}
	}

else	{
	babble->cmd=YOU_DIE;
	}
}
//--------------------------------------------
char hp(char c, int hl)	{
int hi, lo;
char hr, lr;


hi=c/16;
lo=c-(hi*16);

hr=hi<10?hi+48:hi+87;
lr=lo<10?lo+48:lo+87;

if(hl == 0) return(hr); else return(lr);

}

//--------------------------------------------
void dump_r(comm_block *babble)	{
int i;
int rows;
int lastrow;
unsigned char lastrow_data[16];
unsigned char *q = (char *)babble->orig_response;

printf("Response received from %s port %d %s (length %d bytes):\n", babble->ip_ad, babble->port, babble->proto == 't'?"tcp":"udp", babble->response_length);
rows=babble->response_length/16;
lastrow=babble->response_length - (rows*16);
/*
if (rows > 7) {
    rows = 7;
    lastrow = 0;
}
*/

fflush(stdout);
for(i=0;i<rows;i++)	{
	printf("%04hx:\t", i*16);
	printf("%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x\n", \
		q[(i*16)], q[(i*16)+1], q[(i*16)+2], q[(i*16)+3], q[(i*16)+4], q[(i*16)+5], q[(i*16)+6], q[(i*16)+7], q[(i*16)+8], q[(i*16)+9], q[(i*16)+10], q[(i*16)+11], q[(i*16)+12], q[(i*16)+13], q[(i*16)+14], q[(i*16)+15]);
	}
if (lastrow > 0) {
	memset(lastrow_data, 0, sizeof(lastrow_data));
	memcpy(lastrow_data, q + babble->response_length - lastrow, lastrow);
	printf("%04hx:\t", rows*16);
	for (i=0; i < lastrow; i++) {
	    printf("%02x",lastrow_data[i]);
	    if (i % 2 == 1)
	        printf(" ");
	    }
	printf("\n");
        }
printf("ASCII:  \"%s\"\n", print_banner_string(babble->orig_response, babble->response_length));
}

//--------------------------------------------
ll_ports *find_mapped_port(comm_block *babble, ll_targets *tar_lst)	{
ll_ports *my_port;

while(tar_lst != NULL)	{
	if((strcmp(tar_lst->tar_host, babble->ip_ad)) == 0) {
		/* found the host. Now find the port */
		my_port = (ll_ports *)tar_lst->portl;
		while(my_port != NULL)	{
			if (babble->port == my_port->port && (babble->proto == my_port->ip_prot || my_port->ip_prot == 'b' || my_port->ip_prot == 'B')) {
				/* we found it */
				return(my_port);
				} 
			else	{
				my_port = (ll_ports *)my_port->next;
				}
			}
		} 
	else	{
		tar_lst = (ll_targets *)tar_lst->next;
		}
	}
return NULL;
	
}

//--------------------------------------------
void lookup_resp(comm_block *babble, FILE *outp_file, ll_targets *tar_lst)	{
char comp_resp[1024];
int len_r;
int gotit;
ll_resp *my_resp;
ll_ports *mapped_port;
char *orig_response;

babble->orig_response = malloc(babble->response_length);
memcpy(babble->orig_response, babble->response, babble->response_length);
orig_response = babble->orig_response;

my_resp=frst_resp;
gotit = 0;
mapped_port = find_mapped_port(babble, tar_lst);

if (strcmp(mapped_port->appl_id, "ECHO") != 0)
    while(my_resp != NULL)	{

	len_r = compile_line(my_resp->response_string, comp_resp, my_resp->response_type);

	switch(my_resp->srch_type)	{
		case SUB_STR: {
			make_lower(comp_resp, strlen(comp_resp));
			make_lower(babble->response, babble->response_length);
			if(amap_memstr(babble->response, comp_resp, babble->response_length, len_r) != NULL) {
				gotit=1;
				if((strcmp(mapped_port->appl_id, my_resp->prot)) != 0 ) {
					if((strcmp(mapped_port->appl_id, "unidentified")) == 0) strcpy(mapped_port->appl_id, my_resp->prot);
					if(log_to_file) fprintf(outp_file, "Protocol on IP %s port %d %s matches %s%s\n", babble->ip_ad,  babble->port, babble->proto == 't'?"tcp":"udp", my_resp->prot, print_banner(orig_response, babble->response_length));
					if(dump) dump_r(babble);
					printf("Protocol on IP %s port %d %s matches %s%s\n", babble->ip_ad,  babble->port, babble->proto == 't'?"tcp":"udp", my_resp->prot, print_banner(orig_response, babble->response_length));
					}
				}
			}
			break;
		case LIT_STR: {
			if(my_resp->response_type == TRIG_STR) make_lower(babble->response, len_r);
			if(memcmp(babble->response, comp_resp, babble->response_length) == 0) {
				gotit=1;
				if ((strcmp(mapped_port->appl_id, my_resp->prot)) != 0)	{
					if((strcmp(mapped_port->appl_id, "unidentified")) == 0) strcpy(mapped_port->appl_id, my_resp->prot);
					if(log_to_file) fprintf(outp_file, "Protocol on IP %s port %d %s matches %s%s\n", babble->ip_ad,  babble->port, babble->proto == 't'?"tcp":"udp", my_resp->prot, print_banner(orig_response, babble->response_length));
					if(dump) dump_r(babble);
					printf("Protocol on IP %s port %d %s matches %s%s\n", babble->ip_ad,  babble->port, babble->proto == 't'?"tcp":"udp", my_resp->prot, print_banner(orig_response, babble->response_length));
					}
				}
			}
			break;
		case START_S: {
			if(my_resp->response_type == TRIG_STR) make_lower(babble->response, len_r);
			if(memcmp(babble->response, comp_resp, len_r) == 0) { 
				gotit=1;
				if(strcmp(mapped_port->appl_id, my_resp->prot) != 0) {
					if((strcmp(mapped_port->appl_id, "unidentified")) == 0) strcpy(mapped_port->appl_id, my_resp->prot);
					if(log_to_file) fprintf(outp_file, "Protocol on IP %s port %d %s matches %s%s\n", babble->ip_ad,  babble->port, babble->proto == 't'?"tcp":"udp", my_resp->prot, print_banner(orig_response, babble->response_length));
					if(dump) dump_r(babble);
					printf("Protocol on IP %s port %d %s matches %s%s\n", babble->ip_ad,  babble->port, babble->proto == 't'?"tcp":"udp", my_resp->prot, print_banner(orig_response, babble->response_length));
					}
				}
			}
			break;
		default: break;
		}
	my_resp = (ll_resp *)my_resp->next;
	}
if ( gotit != 1 && unrec == 1 && babble->response_length > 0 && 
     strcmp(mapped_port->appl_id, "ECHO") != 0 &&
     (
      mapped_port->unknown_response_length == 0 ||
      mapped_port->unknown_response_length != babble->response_length ||
      memcmp(mapped_port->unknown_response, babble->response, babble->response_length) != 0
     )
   ) {
	printf("\nI received an unrecognized response from %s %s port %d. Please send us this and the application name + version to %s\n", babble->ip_ad, babble->proto == 't'?"tcp":"udp", babble->port, EMAIL);
	dump_r(babble);
	mapped_port->unknown_response_length = babble->response_length;
	mapped_port->unknown_response = malloc(babble->response_length);
	memcpy(mapped_port->unknown_response, babble->response, babble->response_length);
	}	
}    

//--------------------------------------------
void show_results(comm_block *babble, FILE *outp_file, ll_targets *tar_lst)	{

switch(babble->err_level)	{
	case NO_CONN: if(verbose) printf("No connection to %s port %d.\n", babble->ip_ad, babble->port); resp_recv++; break; 
	case NO_RESP: if(verbose) printf("No response from %s port %d.\n", babble->ip_ad, babble->port);  resp_recv++; break; 
	case SUCCESS: lookup_resp(babble, outp_file, tar_lst); resp_recv++; break;
	case INV_IP: printf("Not a valid IP address: %s\n", babble->ip_ad); resp_recv++; break;
	case WAIT: break;
	default: exit_err("Unknown child error level received. (this smells like memory corruption - or a simple programming error)\n"); resp_recv++; break;
	}
}
    
//--------------------------------------------

int do_scan(ll_targets *s_target, ll_trig *s_trigger, char *logfile) {

struct stat tmpstat;

FILE *outp_file = NULL;

char old_logfile[256];
char *today = NULL;

time_t t;

int total_ports=0;
int total_trig=0;
int sp[MAXTASKS][2];
int i;
int up = 0;
int procscan = 0;

comm_block babble;

/* Count tasks */
targ_todo = s_target;

while(targ_todo != NULL) {
  	port_todo = (ll_ports *)targ_todo->portl;
	while(port_todo != NULL) {
	  	trig_todo = s_trigger;
		while(trig_todo != NULL)	{
			/* now see if this task is appropriate */
			if(!(((trig_todo->ip_prot == 't') && (port_todo->ip_prot == 'u')) ||
			     ((trig_todo->ip_prot == 'u') && (port_todo->ip_prot == 't')))) total_tasks++;
			trig_todo = (ll_trig *)trig_todo->next;
			}
   		port_todo = (ll_ports *)port_todo->next;
   		}
	targ_todo = (ll_targets *)targ_todo->next;
	}

	
/* rewind to start of list */	
targ_todo = s_target;
port_todo = (ll_ports *)targ_todo->portl;	
trig_todo = s_trigger;


if(total_tasks == 0) exit_err("Nothing to do. Quitting. Why did you wake me up? Bastard.\n");
printf("Total amount of tasks to perform: %d\n", total_tasks);
if(total_tasks < tasks) tasks = total_tasks;

/* Here comes the parallelising */
/* Set up the socketpairs first */

for(i=0;i<tasks;i++)	{
	if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp[i]) != 0) {
		warn("creation of socketpair failed (too many open filedescriptors already?)");
	        sp[i][0] = -1;
	        }
	}
signal(SIGCHLD, dead_kid);

/* Fork the kiddies */
running_kids=0;    
for (i = 0; i < tasks; i++)	{
	if((pids[i] = fork()) == 0) {
		/* We are a kid, yeah! */
		do_tasks(sp[i][1]);
		exit(0);
		}
	if(pids[i] > 0) {
		/* We are the momma */
		clear_block(&babble);
		babble.cmd = FEED_ME;
		babble.err_level = WAIT;
		write(sp[i][1], &babble, sizeof(babble)); 
                fcntl(sp[i][0], F_SETFL, O_NONBLOCK);
                running_kids++;
		}
	      else {
                warn("a fork failed for a child - that's the reason I don't give them knifes");
                sp[i][0] = -1;
                }
	}

/* The momma continues. Cycle through kiddo's, 
   feeding them tasks and reading responses. */
   

t=time(NULL);
today = ctime(&t);
today[strlen(today) - 1] = '\0';

if(logfile != NULL) {
	log_to_file = 1;
	if(stat(logfile, &tmpstat) == 0) {
		warn("output file already exists. Moving to <logfile>.old ");
		strcpy(old_logfile, logfile);
		strcat(old_logfile, ".old");
		rename(logfile, old_logfile);
		}
	if((outp_file = fopen(logfile, "w")) == 0) exit_err("Couldn't open outputfile for writing\n");
	}

if (log_to_file) fprintf(outp_file, "%s %s started at %s\n", PROGRAM, VERSION, today);

printf("%s %s started at %s, stand back and keep the children away.\n", PROGRAM, VERSION, today);

if(log_to_file) fprintf(outp_file, "Ports: %d, triggers %d. Total amount of tasks to perform: %d\n", total_ports, total_trig, total_tasks);

tasks_left = total_tasks;  
while(resp_recv < total_tasks) {   
    for (i = 0; i < tasks; i++) {
	if (sp[i][0] != -1) {
            memset((char *)&babble, 0, sizeof(babble));
            if (read(sp[i][0], &babble, sizeof(babble)) > 0) {
		switch(babble.cmd) {
	            case FEED_ME: {
                         if (verbose>1) printf("Received FEED_ME from hungry child %d with pid %d\n", i, pids[i]);
			 show_results(&babble, outp_file, s_target);
			 clear_block(&babble);
			 get_next_task(&babble); 
			 write(sp[i][0], &babble, sizeof(babble)); 
                         if (verbose>1) printf("Sent EAT_THIS to fat child %d with pid %d for %s:%d/%c\n", i, pids[i], babble.ip_ad, babble.port, babble.proto);
                         break;
		    }
		    case QUITTING: {
                         if (verbose>1) printf("Received QUITTING from child %d with pid %d, never liked him anyway\n", i, pids[i]);
		         sp[i][0] = -1;
			 running_kids--;
			 if(verbose) warn("a child died - what a whimp"); 
			 break;
	            }
		    default: exit_err ("Received unknown command from child (smells like memory corruption to me boy - or we made a simple programming mistake)\n");
		}
            } else {
                if (i + 1 == tasks) { // no task ready? increase counter and wait a bit
	            procscan++;
	            usleep(100000);
	        }
	    }
	}
    }
    (void) wait3(NULL, WNOHANG, NULL);
    if (procscan >= 100) { // we check that no children was killed before it could notify us.
        if (verbose > 1 && tasks_left > 0) fprintf(stderr, "are my kids still there? I'll better go checking!\n");
        running_kids = 0;
        for (i = 0 ; i < tasks; i++) {
            if (sp[i][0] != -1) {
                if (kill(pids[i], 0) < 0)
                    sp[i][0] = -1; // the child is dead
                else
                    running_kids++;
            }
        }
        if (running_kids == 0 && tasks_left > 0) {
            fprintf(stderr, "Warning: all my children are dead, I have to go and dig some graves ... byebye (%d tasks undone)\n",tasks_left);
            exit(-1);
        }
        goto end;
    }
}

end:
if(verbose) printf("%d responses received in total for %d tasks.\n", resp_recv, total_tasks);

targ_todo = s_target;
printf("Unidentified ports: ");
if (log_to_file) fprintf(outp_file, "Unidentified ports: ");
while (targ_todo != NULL) {
  	port_todo = (ll_ports *)targ_todo->portl;
	while(port_todo != NULL) {
		if(strncmp(port_todo->appl_id, "unidentified", 12) == 0)	{
			up++;
			printf("%d:%s ", port_todo->port, port_todo->ip_prot=='t'?"tcp":"udp");
			if(log_to_file) fprintf(outp_file, "%d:%s", port_todo->port, port_todo->ip_prot=='t'?"tcp":"udp"); 
			}
   		port_todo = (ll_ports *)port_todo->next;
   		}
	targ_todo = (ll_targets *)targ_todo->next;
	}

if (up == 0)	{
	if(log_to_file) fprintf(outp_file, "None.\n"); 
	printf("None.\n");
	}
	else	{
	if(log_to_file) fprintf(outp_file, "(total %d).\n", up);
	printf("(total %d).\n", up);
	}

/* All done, clean up */

for (i = 0; i<tasks; i++) kill(pids[i], 15);
usleep(250000);
for (i = 0; i<tasks; i++) kill(pids[i], 9);

t=time(NULL);
today = ctime(&t);
today[strlen(today) - 1] = '\0';
	
printf("%s %s ended at %s\n", PROGRAM, VERSION, today);
if(log_to_file) fprintf(outp_file, "%s %s ended at %s\n", PROGRAM, VERSION, today);
if(log_to_file) fclose(outp_file);

return 0;
}

//--------------------------------------------

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

int i;
int have_targetsfile = 0;
ll_targets *frst_targ = NULL;
ll_ports *frst_port;
struct in_addr in;
char out[16];

char *nmap_infile = NULL, *output_file = NULL; // otherwise logfiles with weird filenames will be created ...
char *deffile = NULL;
memset(banner_string, 0, sizeof(banner_string));
memset(&pids, 0, sizeof(pids));

deffile=(char *)malloc(strlen(APP_FINGERPRINT_FILE)+1);

strcpy(deffile, APP_FINGERPRINT_FILE);

    while ((i = getopt(argc, argv, "SbruvdhHi:s:T:p:o:D:t:")) >= 0) {
        switch (i) {
            case 'd': dump = 1; break;
            case 'v': verbose++; break;
	    case 'S': printf("Option -S not yet implemented\n"); SSL_first = 1; break;
            case 'r': printf("Option -r not yet implemented\n"); bombarde = 1; break;
            case 'b': banner = 1; break;
	    case 'H': harmful = 1; break;
	    case 'u': unrec = 1; break;
            case 'h': help(); break;
            case 'T': tasks = atoi(optarg); break;
	    case 'i': nmap_infile = optarg; have_targetsfile=1; break;
	    case 's': scantype=tolower(*optarg); break;
	    case 'p': only_proto = optarg; make_lower(only_proto, strlen(only_proto)); break;
            case 'o': output_file = optarg; break;
            case 'D': deffile = optarg; break;
	    case 't': timeout_t = atoi(optarg); break;
            default: fprintf(stderr,"Error: unknown option -%c\n", i); help();
        }
    }
    
    if(tasks > MAXTASKS) exit_err("Too many tasks man, lighten up! Ever *tried* to read the help output, hmm?\n");

    if ((optind + 2 != argc) && (have_targetsfile != 1))
	help();

    if (optind + 2 == argc) {
        if (inet_addr(argv[argc-2]) == -1) {
            struct hostent *target;
            if ((target = gethostbyname(argv[argc-2])) == NULL) exit_err("Error resolving target, ever heard of DNS and IP?\n");
            memcpy(&in, target->h_addr, target->h_length);
            //argv[argc-2] = inet_ntoa(in); //deprecated
            argv[argc-2] = (char*)inet_ntop(AF_INET, &in, (char*)&out, sizeof(out));
        }
        if ((frst_targ = (ll_targets *)malloc(sizeof(ll_targets))) == NULL) exit_err("Malloc failed\n");
        if ((frst_port = (ll_ports *)malloc(sizeof(ll_ports))) == NULL) exit_err("Malloc failed\n");
        (ll_ports *)frst_targ->portl = frst_port;
        frst_targ->tar_host = malloc(strlen(argv[argc-2])+1);
        strcpy(frst_targ->tar_host, argv[argc-2]);
        frst_port->port = atoi(argv[argc-1]);
        if (scantype == 'b') exit_err("You want me to *guess* whether you mean tcp or udp? Come on, give me at least a -sT or -sU....");
        frst_port->ip_prot = scantype;
        frst_port->unknown_response_length = 0;
        frst_port->appl_id = malloc(PROTO_LEN);
	strcpy(frst_port->appl_id, "unidentified");
	frst_port->next = NULL;
        if (frst_port->port <= 0)
               exit_err("Only one port is currently supported, no ranges! sorry ...\n");
        }

    if (have_targetsfile == 1) {
	/* Read in the targets file */
	if((frst_targ = read_targ(nmap_infile)) == NULL) 
		exit_err("No targets read. Empty or maybe incorrect protocol type? Anyway, I'm going home.\n");
	}
    
   /* Read in the triggers and the responses */
   if((frst_trig = read_trig(deffile)) == 0) exit_err("Couldn't read the triggers file (not that I am illiterate, but ...)\n");
   if((frst_resp = read_resp(deffile)) == 0) exit_err("Couldn't read the responses file (not that I am illiterate, but ...)\n");
   
   signal(SIGHUP, die);
   signal(SIGINT, die);
   signal(SIGTERM, die);
   (void) setvbuf(stdout, NULL, _IONBF, 0);
   return (do_scan(frst_targ, frst_trig, output_file));
}
