Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Oct 6th, 2004, 2:16 PM   #1
ghostils
Newbie
 
Join Date: Oct 2004
Posts: 5
Rep Power: 0 ghostils is on a distinguished road
Hello, I'm in the middle of writting what seemed to be a simple winsock app, however it has some bizzare issues that I can't seem to sort out.

It uses WSAEvents for winsock 2, it's a simple proxy server as of now. but it doesn't completely load webpages, its like it timesout or something even though from what I can tell from reading msdn I have WSAWaitForMulitpleEvents set to get all I/O for each event before moving on.... It sometimes loads small pages, like if you do a search for Hardocp on google.com it will load up the page and the next 10 hits but not show the ad bar on the right or some retarded crap. Anyway, here's the code. I wrote it and compiled it in MSVC6. Feel free to test it, link with ws2_32.lib


Any help would be great.

Thanks,
ghost[ils]





//-JPGScan:
//-Acts as a proxy server to scan for the JPEG Image Exploit that allows attackers to use a buffer overflow that runs a backdoor on the system embedded in a JPG Image.
//-Will Stop processing of image and replace data with NULL.
//-usage: jpgscan [ip] [port]
//-ghost[iLs]



//-Headers:
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#include <malloc.h>



//-Macros:



//-Prototypes:
void HTTPScan(char *ip, char *port);
char *parseHostname(char *data);



//-Entry:
int main(int argc, char ** argv)
{


	//-Data:
	char *ServerIp;
	char *ServerPort;
	int WSAStatus;
	WORD WSAVersionRequest = WINSOCK_VERSION;
	WSADATA wsaData;

	
	//-Get IP and PORT from CMDLINE:
	printf("---JPGSCAN---\r\n");
	if(argv[1] != NULL && argv[2] != NULL)
	{
 ServerIp = argv[1];
 printf("Server: %s\r\n", ServerIp);
 ServerPort = argv[2];
 printf("Port: %s\r\n", ServerPort);

	}
	//-Error: Show them how it works:
	else {printf("Usage: jpgscan [server ip] [server port] \r\n"); return -1;}

	

	//-Init Winsock:
	WSAStatus = WSAStartup(WSAVersionRequest, &wsaData);

	//-Winsock DLL not found or other error:
	if(WSAStatus)
	{
 printf("Error: WSAStartup(), cannot load Winsock DLL: Code: %d\r\n", WSAStatus);
 WSACleanup();
 return -1;
	}



	//-Check Version:
	if(wsaData.wVersion != WSAVersionRequest)
	{
 printf("Non compatible Winsock found\r\n");
 WSACleanup();
 return -1;
	}


	//-Start Main Loop:
	printf("Starting Scanner\r\n");
	HTTPScan(ServerIp, ServerPort);



	return 0;
}





//-Declarations:

//-Lets try this with a listen, local, remote socket vs array of sockets and events:
void HTTPScan(char *ip, char *port)
{
	#define LISTEN 0
	#define LOCAL 1
	#define REMOTE 2
	SOCKET Listen, Local, Remote;
	WSAEVENT wEvents[3]; 
	WSAEVENT newEvent;
	WSANETWORKEVENTS netEvent;
	LPHOSTENT lpHostEntry;
	sockaddr_in sockData;
	int Status = 0;
	int i = 0, index = 0;
	char *hostname;	
	char localBuffer[4096];
	char remoteBuffer[4096];
	int bytesRead = 0;



	//-Create Listen Socket:
	Listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (Listen == INVALID_SOCKET)
	{
 printf("Error: socket(), invalid socket handle\r\n");
 WSACleanup();
 return;
	}



	//-Server Address:
	sockData.sin_family = AF_INET;
	sockData.sin_addr.s_addr = inet_addr(ip);
	sockData.sin_port = htons(atoi(port));



	//-Bind Listen Socket:	
	Status = bind(Listen, (struct sockaddr *) &sockData, sizeof(sockData));
	//-Check bind:
	if(Status)
	{
 printf("Error: bind(), %s:%s\r\n", ip, port);
 closesocket(Listen);
 WSACleanup();
 return;
	}
	printf("Bound: %s:%s\r\n", ip, port);



	//-Create Event for Listen socket:
	newEvent = WSACreateEvent();
	if(newEvent == WSA_INVALID_EVENT)
	{
 printf("Error: WSACreateEvent(), invalid event handle\r\n");
 WSACleanup();
 return;
	}
	//-Associate Listen Socket with network events:
	Status = WSAEventSelect(Listen, newEvent, FD_ACCEPT|FD_CLOSE|FD_READ|FD_WRITE|FD_CONNECT);
	if(Status == SOCKET_ERROR)
	{
 printf("Error: WSAEventSelect(), socket error\r\n");
 closesocket(Listen);
 WSACleanup();
 return;
	}


	//-Start Listening:
	printf("Bringing up listen service\r\n");
	listen(Listen, SOMAXCONN);
	printf("[Started]\r\n");


	wEvents[LISTEN] = newEvent;
	wEvents[LOCAL] = WSA_INVALID_EVENT;
	wEvents[REMOTE] = WSA_INVALID_EVENT;
	

	//-Enter Main Loop:
	while(true)
	{
 //-Loop Through all events:
 for(i = 0; i < 3; i++)
 {
 	Status = WSAWaitForMultipleEvents(1, &wEvents[i], false, 0, TRUE);
 	if((Status != WSA_WAIT_FAILED) && (Status != WSA_WAIT_TIMEOUT))
 	{
  


  //-Incomming Connection Request:
  if(i == LISTEN)
  {
  	//-See what we got:
  	WSAEnumNetworkEvents(Listen, wEvents[i], &netEvent);

  	if(netEvent.lNetworkEvents & FD_ACCEPT)
  	{
   printf("Local Client: Connect\r\n");

   
   //-Accept and Create Local Socket:
   Local = accept(Listen, NULL, NULL);
   if (Local == INVALID_SOCKET)
   {
   	printf("Error: socket(), invalid socket handle\r\n");
   	WSACleanup();
   	return;
   }

   
   //-Add event for Local Socket:
   newEvent = WSACreateEvent();   
   if(newEvent == WSA_INVALID_EVENT)
   {
   	printf("Error: WSACreateEvent(), invalid event handle\r\n");
   	WSACleanup();
   	return;
   }

   
   //-Add to event list:
   wEvents[LOCAL] = newEvent;

   //-Add event to selector list:
   Status = WSAEventSelect(Local, newEvent, FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE);        	
  	}
  }



  if(i == LOCAL)
  {  	
  	//-See what we got:
  	WSAEnumNetworkEvents(Local, wEvents[i], &netEvent);


  	if(netEvent.lNetworkEvents & FD_READ)
  	{   
   //-Recieve Data: Terminate at end of bytes recieved:
   Status = recv(Local, localBuffer, 4096, 0);
   localBuffer[Status] = '\0';
   bytesRead = Status;
   printf("Local Bytes Recieved: %i\r\n", bytesRead);
   //printf("%s\r\n",localBuffer);


   //-Parse remote host from data:
   hostname = parseHostname(localBuffer);

     	
   //-Resolve hostname to DNS:
   lpHostEntry = gethostbyname(hostname);
   if(lpHostEntry == NULL)
   {
   	printf("Error: gethostbyname(), bad hostname or couldn't resolve\r\n");
   	continue;
   }


   

   //printf("Hostname: %s\r\n",hostname);

   //-Create new remote socket:
   Remote = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   if(Remote == INVALID_SOCKET)
   {
   	printf("Error: socket(), invalid socket handle\r\n");
   	closesocket(Remote);
   	continue;   	
   }


   //-Create events and add to event list:
   newEvent = WSACreateEvent();
   if(newEvent == WSA_INVALID_EVENT)
   {
   	printf("Error: WSACreateEvent(), invalid event handle\r\n");
   	continue;
   }

   

   //-Add event to list:
   wEvents[REMOTE] = newEvent;
   

   

   //-Add new remote socket to list:
   Status = WSAEventSelect(Remote, newEvent, FD_READ|FD_WRITE|FD_CLOSE|FD_CONNECT);   
   if (Status == SOCKET_ERROR)
   {
   	printf("Error: WSAEventSelect(), socket error\r\n");
   	closesocket(Remote);
   	continue;   	
   }

   

   //-Remote Host Data:
   sockData.sin_family = AF_INET;   
   sockData.sin_addr = *((LPIN_ADDR)*lpHostEntry->h_addr_list);   
   sockData.sin_port = htons(80); 

   
   
   //-Connect to Remote host: this will block now, but not in the next iteration:
   Status = connect(Remote, (struct sockaddr*)&sockData, sizeof(SOCKADDR_IN));


   //-Copy Local Buffer to Remote buffer: We will send this data on connection to Remote host next iteration:
   memcpy(remoteBuffer, localBuffer, bytesRead);
   remoteBuffer[bytesRead] = '\0';
  	
  	}
  	
  	
  	//-Close socket and events: They will be re-created on new Local Connection:
  	if(netEvent.lNetworkEvents & FD_CLOSE)
  	{
   printf("Local Client: Disconnect\r\n");
   closesocket(Local);
   WSACloseEvent(wEvents[LOCAL]);
   continue;
  	}
  	
  }


  if(i == REMOTE)
  {

  	//-See what we got:
  	WSAEnumNetworkEvents(Remote, wEvents[i], &netEvent);

  	//-Connection to remote host: Forward local proxy client data to remote:
  	if(netEvent.lNetworkEvents & FD_CONNECT)
  	{
   printf("Connect: %s\r\n", hostname);
   Status = send(Remote, remoteBuffer, strlen(remoteBuffer), 0);  	
  	}

  	//-Received data from remote: Forward it to Local:
  	if(netEvent.lNetworkEvents & FD_READ)
  	{
   
   Status = recv(Remote, remoteBuffer, 4096, 0);
   if(WSAGetLastError() == WSAENOBUFS)
   {
   	printf("Error: Remote: recv(), no buffer space\r\n");
   	continue;
   }
   else
   {
   	bytesRead = Status;
   	remoteBuffer[bytesRead] = '\0';
   }
   
   
   send(Local, remoteBuffer,strlen(remoteBuffer),0);  	
   if(WSAGetLastError() == WSAENOBUFS)
   {
   	printf("Error: Remote: send(), no buffer space\r\n");
   	continue;
   }


  	}

  	if(netEvent.lNetworkEvents & FD_CLOSE)
  	{
   printf("Remote Client: Disconnect\r\n");
   closesocket(Remote);
   WSACloseEvent(wEvents[REMOTE]);
   continue;
  	}
  }


 	}

 }
	}


	//-Cleanup:
	closesocket(Listen);
	closesocket(Local);
	closesocket(Remote);
	WSACleanup();
	return;
}





//-parseHostname(...):
//-Parses the hostname from the GET... request.
char *parseHostname(char *data)
{

	char *hostname = (char*) malloc(sizeof(char*) * 512);
	int i = 0;
	
	//-Skip 'GET http://'
	for(i = 0; i < 11; i++)
 {*data++;}
	
	//-Read until first '/' character, this is the end of the hostname.
	i = 0;
	while(*data != '/')
	{
 hostname[i++] = *data;	
 *data++;
	}

	hostname[i++] = '\0';
	return hostname;
}
ghostils is offline   Reply With Quote
Old Oct 6th, 2004, 5:39 PM   #2
kurifu
Expert Programmer
 
kurifu's Avatar
 
Join Date: Jul 2004
Location: Halifax, Nova Scotia (Canada)
Posts: 784
Rep Power: 5 kurifu is on a distinguished road
Send a message via ICQ to kurifu Send a message via MSN to kurifu
The problem you are having is because you probably are not turning off TCP naggling... a little feature of Windows which will take multiple TCP packets and transmit them as one. This feature is great in reducing local network overhead, and probably would be beneficial to keep it enabled, but you will need to look up how to flush an nagled data that remains (if this is possible).

Otherwise google turning off the TCP nagle and your problem will likely go away.

PS: If you are PROPERLY closing you TCP connections with the local machine, the naggle buffers should be flushed before the virtual circuit is closed. You could be doing something wrong there, are you making sure your receive buffer is empty when receiving a shutdown on the client socket?
__________________
Clifford Matthew Roche &lt;geek@cliffordroche.com&gt;
Web Hosting: http://www.crd-hosting.com
Consulting: http://www.crdev-consulting.com
kurifu is offline   Reply With Quote
Old Oct 6th, 2004, 6:13 PM   #3
ghostils
Newbie
 
Join Date: Oct 2004
Posts: 5
Rep Power: 0 ghostils is on a distinguished road
Woot thanks for pointing me in the right direction, what you say totally makes sense. I've seen data show up on its own after the program quits and I restart it again on some connections. This points to what you said.


Thanks,

-ghost[iLs]
ghostils is offline   Reply With Quote
Old Oct 7th, 2004, 11:10 AM   #4
kurifu
Expert Programmer
 
kurifu's Avatar
 
Join Date: Jul 2004
Location: Halifax, Nova Scotia (Canada)
Posts: 784
Rep Power: 5 kurifu is on a distinguished road
Send a message via ICQ to kurifu Send a message via MSN to kurifu
TCP naggle is something that gets a lot of people just starting to explore winsocks I have seen and heard this problem describe is probably a 1000 different ways, lol.

Anyway, no problem. Good luck with the coding.
__________________
Clifford Matthew Roche &lt;geek@cliffordroche.com&gt;
Web Hosting: http://www.crd-hosting.com
Consulting: http://www.crdev-consulting.com
kurifu is offline   Reply With Quote
Old Oct 7th, 2004, 5:36 PM   #5
ghostils
Newbie
 
Join Date: Oct 2004
Posts: 5
Rep Power: 0 ghostils is on a distinguished road
Well as it turns out, I left the nagle algorithm on, and what seems to be the problem is how my server is handling connections.

I backed off a sigificant portion of the code to just the server receiving data from the browser, and then faking that it is an HTTP server and sending out some bs simple HTML to the client. I still had the same symtoms. Ie the browser would not know that we were done sending data to it.


What I've found is: I've been completely backasswards on my code:
I was checking the FD_READ event for the initial data from the browser, which seemed to work... however if I check the FD_WRITE event right at the beggining of the loop after the listen socket creates our local connection, (FD_WRITE is called) first...... if I call recv() in the FD_WRITE handler PRESTO I get the HTTP GET REQUEST from the browser and the other Headers it sends with it... and if I call send() in the same handler to send the BS HTML code to it then call shutdown(Local, SD_SEND); PRESTO the browser gets its data, shows it on the screen, and then the browser sends me back my termination signal and FD_CLOSE handler is envoked closing our sockets with a graceful shutdown.

After my experiment with the code, I went ahead and tested one of my other theories, browsers create several connections to the HTTP server to get every piece of content on the page..... so I went ahead and added a simple <img> tag to my BS HTML code, and bam the browser connected, got the html, disconnected, then connected again right after that to try and get the non existant JPEG image it saw in the tag.... the disconnected.... The connected again to report the error to the server.


So a combination of things were wrong with my approach it seems. I'd still be in waiting for data mode in the browser, and it would have seen the <img> tags in the other remote html code (in the full setup I posted), and disconnected, connected again, and left the whole thing hanging on Data because we never sent an SD_SEND shutdown message to the remote server and again to the browser everytime we got data.


I guess the question is, since I'm do'n an almost complete re-write of the code, should I stick with the WSA Event code, or move over to a threaded server? I'm sure I could setup a simple Listener Signal system in the existing WSA Event code, so that we don't try and re create a new local connection right off the bat while we still have data pending in the current ones... Then accept the connections into a Local Socket queue, wait for the DONE Signal, and then start the process all over again. Does this sound logical?

With the WSAWaitForMultipleEvents I can set a small sleep time of 100ms to cut down on the CPU time of the select style server...

With threads I can spool off a new local/remote forwarder with every accept call, but I would have to work with some global data...

Or maybe a combination of both styles.


Advice?


Thanks,
ghost[iLs]
ghostils is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 4:07 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC