// SqlPing.cpp
// 
// by ChipAndrews, Rajiv Delwadia, Michael Choi
// SQLPing came out of our curiosity and observations of SQL Server's discovery
// mechanisms.  When a SQL Server 2000 client wishes to connect to a server it 
// first attempts a pre-connection query against UDP 1434 (unregistered listener 
// service on any SQL Server 2000 server).  Upon seeing the handshake (packet payload 0x02), the SQL Server
// replies with details about all named instances installed on the server including 
// instance name, version, clustering info, net-libs, and net-lib details (ports, pipe
// names, etc.).  Using this tool, you can reveal this information as well as send
// discovery packets to entire networks (i.e. 192.168.0.255) for mass interrogation.
// Enjoy. 


#include "stdafx.h"
#include "stdio.h"
#include "string.h"

#define reverse16(x) ( (((x)&0xFF) << 8) | (((x)&0xFF00) >> 8) )
#define reverse32(x) ( (((x)&0xFF) << 24) | (((x)&0xFF00) << 8) | (((x)&0xFF0000) >> 8) | (((x)&0xFF000000) >> 24) )


unsigned long ResolveIP (const char* ipstring)
{
	unsigned long addr;

	// try interpreting as dotted quartet
	addr = inet_addr(ipstring);
	if (addr != INADDR_NONE)
		return addr;

	// if that fails, try resolving hostname
	HOSTENT* phost = gethostbyname(ipstring);
	if (phost != NULL)
		return ((in_addr*)(phost->h_addr))->S_un.S_addr;

	return 0xFFFFFFFF;
}	

void decode_resp (char *buf, int size) 
{
	int index;
	int counter = 0;

	for (index = 3; index < size; index++)
	{
		if ((buf[index] == ';') && (buf[index+1] != ';')) 
		{  
			//Look for a semi-colon and check for end of record (;;)
			if ((counter % 2) == 0) 
			{ 
				printf(":"); 
				counter++; 
			}
			else
			{ 
				printf("\n"); 
				counter++; 
			}
		}
		else 
		{
			if (buf[index] != ';') 
			{  
				// If an end of record (;;), then double-space for next instance
				printf("%c",buf[index]);
			}
			else
			{
				printf("\n");
			}
		}
	}
	  
	printf("\n");
}

void listenerfunction (void* v)
{
	// big buffer
	static const unsigned int buffersize = 64000;
	static char buffer [buffersize];

	// convert void* argument to SOCKET
	SOCKET s = (SOCKET)v;

	printf("Listening....\n\n");

	for (;;)
	{
		struct sockaddr_in udpfrom;
		int udpfromlen = sizeof(udpfrom);
		int n = recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&udpfrom, &udpfromlen);
		int e = WSAGetLastError();
		
		if (n > 0 && e == 0) 
			decode_resp(buffer, n);

	} 

	printf("SQLPing Complete.\n");
}

int PrintBanner ()
{
	static const char* banner = "\nSQLPing v1.2 (3/9/2001)\n\n"
		"Chip Andrews, Michael Choi, and Rajiv Delwadia\n"
		"http://www.sqlsecurity.com  chip@sqlsecurity.com\n"
		"SQLPing is a utility for querying SQL Servers (2000+) listening on UDP 1434 to\n"
		"return detailed information about the instances installed.  Note that broadcast\n"
		"addresses may return multiple results.\n"
		"\nUsage: sqlping target_ip or host_name\n";
	printf(banner);
	return 0;
}	

int main(int argc, char* argv[])
{	
	int e;

	// if no command line then just show usage
	if (argc < 2)
		return PrintBanner();

	// initialize sockets lib
	WSADATA wsadata;
	WSAStartup(0x0002, &wsadata);

	// create IP address structure
	sockaddr_in hostaddr = {AF_INET, reverse16(1434)};
	unsigned long ip = hostaddr.sin_addr.S_un.S_addr = ResolveIP(argv[1]);
	printf("SQL-Pinging %u.%u.%u.%u\n", ip & 0xFF, (ip>>8) & 0xFF, (ip>>16) & 0xFF, (ip>>24) & 0xFF);

	// create a socket
	SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	e = WSAGetLastError();

	const int SNDBUF = 0;
	const int TCPNODELAY = true;
	const int BROADCAST = true;
	
	e = setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*)&SNDBUF, sizeof(SNDBUF));
	e = setsockopt(s, SOL_SOCKET, TCP_NODELAY, (const char*)&TCPNODELAY, sizeof(TCPNODELAY));
	e = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*)&BROADCAST, sizeof(BROADCAST));
	e = WSAGetLastError();

	//e = connect(s, (sockaddr*) &hostaddr, sizeof(hostaddr));
	//e = WSAGetLastError();

	// start listener thread to receive replies
	HANDLE listener = (HANDLE) _beginthread(listenerfunction, 0, (void*)s);

	// send ping
	// 02 works as well as 03 and 04.  The difference? who knows
	// If you find out please drop me a line

	e = sendto(s, "\02", 1, 0,(sockaddr*) &hostaddr, sizeof(hostaddr));
	e = WSAGetLastError();

	// wait a little while for listener thread to do his job
	WaitForSingleObject(listener, 5000);

	// shut down sockets lib
	WSACleanup();
	return 0;
}
