/*****************************************************************************
 * 
 *     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 3 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, see <http://www.gnu.org/licenses/>
 * 
 *****************************************************************************  
 * 
 * Sockcheck.c By BigDawg dawg@nuthin.nu - [ http://www.rootshell.com/ ]
 * Use: put a list of ips (one per line) in ips.in then run sockcheck. 
 * The list of unsecure socks servers will be saved to ips.out 
 * Compile: gcc sockcheck.c -o sockcheck
 * 
 * [20:01] <Skeeter> put my name in the source ;) 
 * 
 * I'd like to say thanks to all who have helped me throughout the past years.
 * 
 *****************************************************************************
 * Notes from Mr.HinkyDink 8/24/2007
 * 
 *  There were a few things I didn't like about the original.
 * 
 *  I'd rather be able to put the IP & port on the command line
 *  and script the list than hassle with this infile/outfile crap.
 * 
 *  For example...
 * 
 *  for ADDR in $(<sockslist) ; do sockcheck $ADDR ; done
 * 
 *  You can pipe that out to a file and grep for "GRANTED" to get the good
 *  SOCKS servers.
 * 
 *  So anyway that's gone.  Sorry, Dawg.
 * 
 *  It's silly to assume the port is always going to be 1080, but that
 *  will be used as the default if no port is explicitly given.
 * 
 *  Also the hard-coded destination address and port was stupid.
 *   
 *  I don't think that server even exists anymore.  So now the default
 *  is www.google.com, but you can specify your own destination address
 *  and port if you want.  Remember, just because you can connect on
 *  port 80 doesn't mean you can access just any old port.  Those days,
 *  sadly, are gone.  So far gone, in fact, that the SOCKS servers you
 *  may find could be honeypots.  Be aware of that.  And if you decide
 *  to use the servers you find - for educational purposes, of course -
 *  use one outside your country, if only to cover your ass.  It might be
 *  advisable to stay away from German servers since that country has
 *  recently (2007) enacted strict anti-hacking laws.  If you are in
 *  Germany at this very moment it would be a good idea to delete this
 *  file NOW.
 * 
 *  Some firewall jockeys and security wannabees like to tarpit TCP traffic 
 *  so I scaled back on the timeout values and messed with the SIGALRM handler.  
 *  If you can't connect in 10 seconds it's probably a junk server anyway.
 * 
 *  You can override the default 10 second timeout by defining an environment 
 *  variable named "SOCKWAIT".  For example:
 * 
 *  SOCKWAIT=1 ; export SOCKWAIT ; ./sockcheck example.com:1081
 * 
 *  A 1 second timeout really flies compared to BigDawg's defaults, but
 *  if you really want to use some NetZero user's dialup proxy in 
 *  Bumfuck Zimbabwe you can adjust it as high as you want.  Take a nap,
 *  it'll finish eventually.
 * 
 *  And if you ARE that guy in Zimbabwe with a NetZero account, crank it
 *  up to at least 30 seconds.
 * 
 *  In testing I lost ~15% "good" connections using a timeout of 1 second 
 *  compared to BigDawg's defaults.
 * 
 *  I have looked at the SOCKS v5 standard but I don't want to fuck with
 *  it.  Ditto with v4a, so you're stuck with v4.
 * 
 *  Check the "unsupported version" results with a version code equal to
 *  0x05.  These just might be v5 proxies, but, from the RFCs, it appears
 *  that the response of a SOCKS v5 server to a SOCKS v4x request is 
 *  "undefined".
 * 
 *  So anyway I changed the code and added all these boring comments.
 * 
 *  The original can be found here (last time I checked)...
 * 
 *  http://packetstormsecurity.nl/UNIX/scanners/sockcheck.c
 * 
 *  Note that the original is considered an "exploit" by some
 *  commercial anti-malware packages.  Dumb fuckers.
 * 
 *  You can get a number of SOCKS proxy lists on the Web, but
 *  they mostly steal from each other.  I get scanned for open
 *  proxy ports 24x7, mostly from Chinese IP addresses, so there
 *  is a lot of "work" going on in this area.  ISPs despise
 *  open proxies since they perceive it as stolen bandwidth.
 * 
 *  I pulled 750 addresses off one site and got a 13.5% success
 *  rate (101 good SOCKS servers - or functional honeypots?).  YMMV, 
 *  but in my experience that's about average for a typical 
 *  http/CERN proxy list.  
 * 
 *  Thanks to BigDawg for starting this.  There are no good 
 *  *FREE* SOCKS proxy scanners anywhere.  Period.  This one
 *  sucked, too, but I think I made it better.
 * 
 *  (NOTE: not long after making that statement I stumbled across
 *  proxycheck, which is an option in most Linux distros.  I felt 
 *  kind of stupid afterwards, but in view of the horrific 
 *  CLI {command line interface} of proxycheck I still feel that
 *  the qualifier "good" is appropriate.  Proxycheck is far too
 *  complex and tries to do too many things.  It fails miserably
 *  in the "ease of use" category.  Other than that I'm sure it's
 *  well written and functional.)
 *  
 *  I have only tested this on Debian 4.0r0 with GCC 4.1.2 and Cygwin
 *  on Windows XP SP2 (cygwin.dll v1.5.24 & GCC v3.4.4).
 * 
 *  I also added the GPL notice.
 * 
 *  dink@mrhinkydink.com
 * 
 *  That address should be good until 2009.
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <stdarg.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>

#define _GNU_SOURCE

#define DEFAULTWAIT (unsigned short)10
#define HTTPlen 8192
#define TMPlen 1024
#define GETlen 512
#define VIAMAX 64

typedef struct Sreq 
{
   char  vers;
   char  command;
   unsigned short port;
   char  addr[4];
   char  name[16];
} Sreq;


typedef struct S5req
{
	char vers;
	char command;
	char rsv;
	char atyp;
	char addr[4];  // IPv4 addresses only : (
	unsigned short port;
} S5req;


typedef struct S5resp
{
	char vers;
	char rep;
	char rsv;
	char atyp;
	char baddr[4];
	unsigned short bport;
} S5resp;

char *S5error[10]={
"GRANTED",
"general SOCKS server failure",
"connection not allowed by ruleset",
"network unreachable",
"host unreachable",
"connection refused",
"TTL expired",
"command not supported",
"address type not supported",
"unassigned"
};
             

int sockcheck(char *host, unsigned short port, char *dhost, unsigned short dport);

static int sockfd2;
static int aflag, killsock;

void sigalrm_handler3(int sig)
{
	if( killsock ) {
		killsock=0;
		close(sockfd2);
	}
	aflag++;
}

// memmem is part of the GNU C library 
// Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc.
void *
memmem(const void *block, size_t blen, const void *pat, size_t plen) 
{
	const unsigned char *bp, *pp, *endp;

	if (plen == 0)
		return NULL;

	if (blen < plen)
		return NULL;

	bp = block;
	pp = pat;
	endp = bp + (blen - plen) + 1;

	while (bp < endp) {
		if ((*bp == *pp) && (memcmp(bp, pp, plen) == 0))
			return (void *)bp;
		bp++;
	}

	return NULL;
}


// end of memmem

void findVIA( char *a, char *via )
{
  	while( (*a!='\r') ) {
		if(*a==',') break;
		*via++ = *a++;
	} 
	*via=0;
}


int main(int argc, char **argv)
{
  char sockip[32];
  char destip[32];
  short sockport;
  short destport;
  char *p;
  
  sockport=destport=0;
  destip[0]='\0';  
   
  if( argc<2 || argc>3 )
   {	
     printf("\n=============================\n");
     printf("sockcheck 0.4 by Mr.HinkyDink\n");
     printf("   based on sockcheck 0.3\n");
     printf("         by BigDawg\n");
     printf("=============================\n\n");
     printf("Useage:\n");
     printf("      sockcheck SOCKSSERVER<:port> <TARGET<:port>>\n\n");
     printf("Default SOCKS  port = 1080\n");
     printf("Default TARGET port =   80\n");
     printf("Default TARGET      = www.google.com\n");

     return 1;
   }
   
   
  strcpy( sockip, argv[1] );   
  
  if( (p=strchr(sockip,':'))==NULL)
    sockport=1080;
  else {
    sockport=(unsigned short)atoi((const char*)p+1);
    *p='\0';
  }

  sockport=sockport==0?1080:sockport;
  
  if ( argv[2]==NULL )
  { 
	  return sockcheck( sockip, sockport, "www.google.com\0", 80 );
  }
  else
  {
	  strcpy( destip, argv[2] );
   
	  if( (p=strchr(destip,':'))==NULL )
		  destport=80;
	  else {
		  destport=(unsigned short)atoi((const char*)p+1);
		  *p='\0';
	  }

  destport=destport==0?80:destport;

  return sockcheck( sockip, sockport, destip, destport ); 
  }

}



int sockcheck(char *host, unsigned short port, char *dhost, unsigned short dport)
{
   int d,e,f,retcode,idx,pos;
   struct Sreq S;
   struct S5req S5;
   struct S5resp S5r;
   char tmpstr2[TMPlen]; 
   char HTTP_buffer[HTTPlen];
   char HTTP[GETlen]="GET / HTTP/1.1\xd\xaHost: \0";
   char GETcheck[]="(12202)"; // not prone to language issues
   char VIAcheck[]="Via:";
   char S5ask[3] = "\x5\x1\0";
   char S5tell[2] = "\0\0";
   fd_set gateset;
   struct timeval tv;
   struct hostent *he;
   struct sockaddr_in sin;
   unsigned short sockwait;
   char *env;
   char conDios[VIAMAX]="UNKNOWN\0";
   void *p;
   
   if( dport==80 ){
   strncat(HTTP, dhost, GETlen-strlen(HTTP));
   strncat(HTTP, "\xd\xa\xd\xa\0", GETlen-strlen(HTTP));
   }
   
   aflag=killsock=0;
    
   if ( (env=getenv("SOCKWAIT"))==NULL)
	   sockwait=DEFAULTWAIT;
   else
	   sockwait=(unsigned short)atoi((const char*)env);
   
   sockwait=sockwait?sockwait:DEFAULTWAIT;
   
   sockfd2 = socket(AF_INET, SOCK_STREAM, 0);
   sin.sin_family = AF_INET;
   sin.sin_port = htons(port);
   sin.sin_addr.s_addr = inet_addr(host);
   
   if(sin.sin_addr.s_addr == INADDR_NONE)
    {
     he = gethostbyname(host);
     if(!he)
       {
        close(sockfd2); 
        printf("DNS error - can't resolve \"%s\"!\n", host);
	return 0;
       }
     memcpy(&sin.sin_addr, he->h_addr, he->h_length);
    }
   
   signal(SIGALRM, (void*)sigalrm_handler3);
   
   alarm(sockwait); killsock=1;  // SIGALRM closes sockfd2
   
   e = connect(sockfd2, (struct sockaddr *)&sin, sizeof(sin));
   
   if ( aflag ) {
	   printf("%s:%u - connect killed by SIGALRM", host, port);
	   if (sockwait==DEFAULTWAIT)
		   printf("\n");
	   else
		   printf(" (SOCKWAIT = %d sec.)\n", sockwait);
	   return 0;
   }
   
   if (e < 0)
     {
	  printf("%s:%u - %s\n", host, port, strerror(errno));
	  //perror("connect");
	  close(sockfd2);
      return 1;
     } 
   
   alarm(sockwait);
   
   FD_ZERO(&gateset);
   FD_SET(sockfd2, &gateset);
   tv.tv_sec = 10;
   tv.tv_usec = 0;
   
   d = select(sockfd2+1, NULL, &gateset, NULL, &tv); 
   
   if(d == 0 || aflag!=0 ) // seems to be dead code here, never dies on select
     { 
	   printf("%s:%d - %s\n", host, port, strerror(errno));
	   close(sockfd2);
       return 1;
     }
   
   memset( &S, 0, sizeof(struct Sreq));
   memset( &S5, 0, sizeof(struct S5req));
   memset( &S5r, 0, sizeof(struct S5resp));
   
   S.vers=4; S5.vers=5;      
   S.command=S5.command=1;
   S.port=S5.port=htons(dport);
   S5.rsv=0;
   S5.atyp=1;
   
   if ( (he = gethostbyname(dhost))==NULL ) 
   {
	   printf("Can't resolve destination host \"%s\"\n", dhost);
	   close( sockfd2 );
	   return 1;
   }
   
   memcpy( &S.addr, he->h_addr, he->h_length );
   memcpy( &S5.addr, he->h_addr, he->h_length );
   
   strcpy(S.name,"BathHouseJohn\0");  // YOUR NAME HERE
   memset( tmpstr2, 0, 1024);
 
   send(sockfd2, &S, sizeof(struct Sreq), 0);
   tv.tv_sec = 10;
   tv.tv_usec = 0;

   f = select(sockfd2+2, &gateset, NULL, NULL, &tv); 
   
   if(f>=0)   
     {
      
	  alarm(sockwait); killsock=1; // SIGALRM closes sockfd2
	  
	  idx=pos=0;
		 
      read(sockfd2, tmpstr2, TMPlen);
      
      if( aflag ) {
    	  printf("%s:%u - Read killed by SIGALRM (connect successful)\n", host, port);
    	  return 1;
      }
 
      if ( tmpstr2[0]==0x00 )
        {
    	  switch (tmpstr2[1])
    	  {
    	  case 0x5a:	// check for ISA server - toss out a GET request
    		  			if (dport==80){
    		  				memset( HTTP_buffer, 0, HTTPlen);
    		  				e=send(sockfd2, &HTTP, strlen(HTTP), 0);
							
							idx=pos=0;
							do {
								idx=read(sockfd2, HTTP_buffer+pos, HTTPlen-pos);
								if ( idx<0 ) break;
								pos+=idx;
							}
							while(idx);
								
							if (e<0) {
								printf("%s:%u - %s (after access granted)\n", host, port, strerror(errno));
    		  					retcode=1;
								break;
							}
    		  				p=memmem((const void*)HTTP_buffer,(size_t)HTTPlen,(const void*)GETcheck, (size_t)strlen(GETcheck));
    		  				
    		  				if ( p==NULL ){
    		  					printf("%s:%u - ****GRANTED anonymous SOCKSv4 request****\n",host,port );
    		  					retcode=0;
    		  					}
    		  				else {
								
								p=memmem((const void*)HTTP_buffer,(size_t)HTTPlen,(const void*)VIAcheck, (size_t)strlen(VIAcheck));
								if(p) findVIA(p+8,conDios);
								
    		  					printf("%s:%u - ISA 2000 server \"%s\" denied http request (other ports may work)\n",host,port,conDios );
    		  					retcode=1;
    		  					}
    	  				}
    	  				else {
    	  			    printf("%s:%u - ****GRANTED anonymous SOCKSv4 request to port %d****\n",host,port,dport );	
    	  			    retcode=0;		
    	  				}
    	  				break; 
    	  				
    	  				// A note on ISA Server:
    	  				// =====================
    	  				// You really have to go out of your way to expose
    	  				// an ISA 2004/2006 server to the Internet.  I was surprised
    	  				// to find as many as I did.  The problem with http & ISA
    	  				// SOCKS support is you can't run the Web (http) proxy at the
    	  				// same time.  It eats up all the "Web" ports.  However, you
    	  				// can use SOCKS for any other TCP ports the ISA server allows
    	  				// unauthenticated access to.  These could be substantial
    	  				// since if an ISA admin is stupid enough to expose the 
    	  				// server to the Internet, he has very likely also created an
    	  				// "any port anywhere" rule. 
    	  				//						-- Mr.HinkyDink
    	  				
    	  case 0x5b:	printf("%s:%u - returned explicit FAIL/DENY\n",host,port);
    	  				retcode=1;
    	  				break;
    	  case 0x5c:	printf("%s:%u - requires identd (not anonymous).\n",host,port);
    	  				retcode=2;
    	                break;  
    	  case 0x5d:	printf("%s:%u - returned SUX2BU (bad user ID).\n",host,port);
    	  				retcode=3;
    	  				break;  
    	  default:		if ( errno )
			  				printf("%s:%d - %s after SOCKS request.\n",host,port,strerror(errno));
		  				else	 			
		  					printf("%s:%u - Got JUNK (0x00,0x%0.2X,0x%0.2X).\n", host, port,(unsigned char)tmpstr2[1],(unsigned char)tmpstr2[2]);
    	  				retcode=4;
						
    	  				break;  
    	  }				
	      close(sockfd2);
	      return retcode;
	   }
     else
       {
    	if ( tmpstr2[0]==0x05 && tmpstr2[1]==0x01 && tmpstr2[2]==0x00 && tmpstr2[3]==0x01 ) {
			close(sockfd2);
			// server will have FIN'd us by now
			// can't use same connection, so make it new
			// no SIGALRM stuff because been there, done that, and I'm lazy
			// SOCKS 5 packets previously stuffed just in case
			sockfd2 = socket(AF_INET, SOCK_STREAM, 0);
            connect(sockfd2, (struct sockaddr *)&sin, sizeof(sin));
            FD_ZERO(&gateset);
            FD_SET(sockfd2, &gateset);
            tv.tv_sec = 10;
            tv.tv_usec = 0;
            select(sockfd2+1, NULL, &gateset, NULL, &tv); 
			send(sockfd2, &S5ask, 3, 0); // SOCKS 5 greeting
			read(sockfd2, &S5tell, 2 );  // SOCKS 5 response
			if (S5tell[1]==0) {
    			send(sockfd2, &S5, sizeof(struct S5req), 0);
				read(sockfd2, &S5r, sizeof(struct S5resp) );
				if ( S5r.rep > 9 ) S5r.rep=9;
				printf("%s:%d - SOCKS v5 server response \"%s\"\n",host,port,S5error[S5r.rep]);
			}
			else // dead code???
				printf("%s:%d - SOCKS v5 server requires authentication\"%s\"\n",host,port);
			
			close(sockfd2);
			
			return 1;
		}
    	else
	        printf("%s:%d - Unknown SOCKS version or CRAP (0x%0.2X,0x%0.2X,0x%0.2X) or ERROR 0x%0.2X (%s).\n",host,port,(unsigned char)tmpstr2[0],(unsigned char)tmpstr2[1],(unsigned char)tmpstr2[2], errno, strerror(errno));
	    close(sockfd2);
	    return 1;
       }
     }   

   // fall through
   printf("%s:%d - Bizarre outcome with errno = 0x%0.2X (%s).\n",host,port,errno,strerror(errno));
   close(sockfd2);
   return 0;
}

