/*
 *
 * Name: 404print.c
 * Purpose: Detect IIS Patch/SP level based on 404 Content Length
 * Author: Erik Parker <erik.parker@digitaldefense.net>
 * Copyright: Copyright (C) 2003 Digital Defense Inc.
 * Distribution: This code may not be redistributed.
 * Revision: 1.1
 * Released: 06/11/2003
 * Download: http://www.digitaldefense.net/labs/securitytools.html
 *
 * This idea is based off of code from [rob (at) robmann.org] (iis404id.pl),
 * who credits unknown sources for giving him the idea. Some of the Content-Lengths
 * were also obtained from Rob. iis404id.pl is available at:
 * http://brokenminds.org/tools
 *
 * Thanks to HD Moore and Orlando Padilla for code review.
 *
 */

#define VER 1.1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <getopt.h>
#include <unistd.h>
#include <errno.h>

int request(char *ip, int port, char *string);
void usage(char *argv0);
void checkport(int port);

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

	int errorflag = 0;
	int port = 80;
	int c;
	char *string = "DDI-BLAH.FOO";

	if ((argc < 2) || (argc > 5))
		usage(argv[0]);

	while ((c=getopt(argc, argv, "vp:hs:")) != EOF) {
		switch(c) {
			case 'p':
				port = strtol(optarg, NULL, 10);
				checkport(port);
				fprintf(stderr, "Using port: %s\n", optarg);
				break;
			case 'h':
				usage(argv[0]);
				break;
			case 's':
				if (strlen(optarg) >= 101) {
				fprintf(stderr, "Use a filename 100 characters or less\n");
				exit(1); } 
				string = optarg;
				break;
			case 'v':
				fprintf(stderr, "IIS 404 Fingerprinter Version %.2f\n", VER);
				fprintf(stderr, "Copyright 2003 Digital Defense, Inc.\n");
				fprintf(stderr, "Written by: Erik Parker <erik.parker@digitaldefense.net>\n\n");
				exit(0);
			case '?':
				fprintf(stderr, "Unrecognized option: -%c\n", optopt);
				errorflag++;
		}
	}

				

	if (errorflag) {
		usage(argv[0]);
	}


	request(argv[argc-1], port, string);
	return 0;
}

void usage(char* argv0)
{
	fprintf(stderr, "\n\e[0;1mIIS 404 Fingerprinter\e[0m\n\n");
	fprintf(stderr, "Copyright 2003 Digital Defense, Inc.\n");
	fprintf(stderr, "Written By: Erik Parker <erik.parker@digitaldefense.net>\n");
	fprintf(stderr, "Usage: %s [options] IP\n\n", argv0);
	fprintf(stderr,
			"-h \tPrint a summary of the options\n"
			"-v \tPrint Version information\n"
			"-p \tPort To use\n"
			"-s \tFile to request (Default: DDI-BLAH.FOO)\n");
			exit(1);
			
}


int request(char *ip, int port, char *string)
{

	int s, r, j;
	int x = 1;
	int c = 0;
	char *d = NULL;
	char response_string[300];
	char reqstring[117];
	char reqhost[267];
	char *p = NULL;

	int iis5[] = { 3243, 2352, 4040, 1711 };
	char *iis5t[] = { "No Service Pack", "Service Pack 2 or SRP1", "Service Pack 3 or 4", "null" };

	int iis6[] = { 2166, 1635, 1711 };
	char *iis6t[] = { "No Service Pack", "w2k3 build 3790", "null"};

	int iis51[] = { 1330, 4040, 1711 };
	char *iis51t[] = { "Service Pack 2", "No Service Pack", "null"};
	
	int iis4[] = { 451, 461, 1711 };
	char *iis4t[] = { "Service Pack 6", "Service Pack 3", "null"};

	struct sockaddr_in addr;
	struct hostent *hp;
	memset((char *) &addr, '\0', sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(ip);
	addr.sin_port= htons(port);


	/* build request string */
    	snprintf(reqstring, (sizeof(reqstring)-1), "GET /%s HTTP/1.0\r\n", string);
    	snprintf(reqhost, (sizeof(reqhost)-1), "Host: %s\r\n\r\n", ip);


        if ((hp = gethostbyname(ip)) == NULL) {
		fprintf(stderr, "Unknown host: %s\n", ip);
		exit(1);
        }

        if ((hp = gethostbyname(ip)) != NULL) {
                if (hp->h_length > sizeof(addr.sin_addr)) {
                        hp->h_length = sizeof(addr.sin_addr); }

                 memcpy((char *) &addr.sin_addr, hp->h_addr, hp->h_length);
        }
        else {
                if ((addr.sin_addr.s_addr = inet_addr(ip)) < 0) {
                        return(0);
                 }
        }

        s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	if (s == -1) {
		perror("socket() failed");
		return 2; }

        r = connect(s, (struct sockaddr *) &addr, sizeof(addr));

	if (r == -1) {
		perror("connect() failed");
		return 2; }

	write(s, reqstring, strlen(reqstring));
	write(s, reqhost, strlen(reqhost));
	memset(response_string, '\0', sizeof(response_string));

	read(s, response_string, sizeof(response_string));

	if (strstr(response_string, "404") != NULL) {

	if ((p = strstr(response_string, "Content-Length: ")) != NULL)
	{
		p +=  16;
		c = atoi(p);
	}

	if (strstr(response_string, "Server: Microsoft-IIS/5.0") != NULL) {
		d = "Microsoft-IIS/5.0"; 

		fprintf(stderr, "Server: %s\n", d);

		for(j=0; iis5[j] != 1711 ; j++) {
			if (c == iis5[j]) {
				fprintf(stderr, "%s\n", iis5t[j]);
				x = 0;
			}
		}
	}

	else if (strstr(response_string, "Server: Microsoft-IIS/5.1") != NULL) {
		d = "Microsoft-IIS/5.1";
		fprintf(stderr, "Server: %s\n", d);

		for(j=0; iis51[j] != 1711 ; j++) {
			if (c == iis51[j]) {
				fprintf(stderr, "%s\n", iis51t[j]);
				x = 0;
			}
		}
	}

	else if (strstr(response_string, "Server: Microsoft-IIS/4.0") != NULL) {
			d = "Microsoft-IIS/4.0";
			fprintf(stderr, "Server: %s\n", d);

		for(j=0; iis4[j] != 1711 ; j++) {
			if (c == iis4[j])  {
				fprintf(stderr, "%s\n", iis4t[j]);
				x = 0;
			}
		}
	}

	else if (strstr(response_string, "Server: Microsoft-IIS/6.0") != NULL) {
			d = "Microsoft-IIS/6.0";
			fprintf(stderr, "Server: %s\n", d);

		for(j=0; iis6[j] != 1711 ; j++) {
			if (c == iis6[j]) {
				fprintf(stderr, "%s\n", iis6t[j]);
				x = 0;
			}
		}
	}

	else if (strstr(response_string, "Server: Microsoft-IIS/3.0") != NULL) {
			d = "Microsoft-IIS 3.0";
			fprintf(stderr, "Server: %s\n", d);
			fprintf(stderr, "Microsoft IIS 3.0 does not support Content-Length, hence this tool won't work");
	}
	else {

		fprintf(stderr, "Server Not Supported\n");
		exit(0);
	}

	if(x)
		fprintf(stderr, "Unknown Content-Length: %d\n", c);

	}
	else {
		fprintf(stderr, "No 404 message encountered. File exists or custom configuration\n");
	}



	    return 0;
}

void checkport(int port)
{
	if(port > 65535) {
		fprintf(stderr, "Invalid port\n");
		exit(1); 
	}
}
