Linux Networking

Submitted by zach on Wed, 09/16/2020 - 22:54

For my networking class we had to create a client and server to pass a file back and forth successfully. Setting up the network sockets is pretty easy, everyone has read beej's guide, but the trick was in setting up the data to send and to ensure that I was getting data I expected. I could have just sent the data and tried to deal with it but I decided I would create a basic packet header to ensure I was getting my data and the length of the data. This header is super simple and contains 3 four byte integers. The first one is actually just HEAD spelled out in ascii (which if necessary would give the endianness of the sending system, a trick I learned from the Halo 2 map file layout), the second one is the length of the message being sent in case we have to read multiple times, and the third one is a type field. All of this together allowed me to determine if the data being received originated from my server process and allowed for easier handling of data. The type field was really important for this project because we were required to emulate the way that FTP opens two connections when transferring: a command connection, and a data connection. Setting up my packet header with a type field allowed me to use the same code for both command and data connections by simply changing the type. In the end this worked out really well and proved to be a simple file transfer solution.

Check out a snippet of the code I used to create the packets with headers.

//Function: createHeader
//PRE:	type is a 4 character message type, length is the data portion length to encode in the header
//POST:	returns a 12 byte char array that contains a valid protocol header
//Description:	Creates a header with the type and length embedded
//A function to easily create a 12 byte message header
char* createHeader(std::string type, int length){
	
	//This is a really weird way to do this, but I copy-pasta'd the HEAD declaration from earlier code before this function existed so this saves me from
	//having to rewrite things
	char HEAD[12] = {(char)0x48, (char)0x45, (char)0x41, (char)0x44, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00};	

	char* lengthBytes = intToByte32(length);
	HEAD[4] = lengthBytes[0];
	HEAD[5] = lengthBytes[1];
	HEAD[6] = lengthBytes[2];
	HEAD[7] = lengthBytes[3];


	free(lengthBytes);

	if(type != "" && type.length() == 4){
		HEAD[8] = type[0];
		HEAD[9] = type[1];
		HEAD[10]  = type[2];
		HEAD[11] = type[3];
	}

	char* returnBuff = (char*) malloc(sizeof(char) * 12);
	memset(returnBuff, 0x00, 12);
	memcpy(returnBuff, HEAD, 12);

	return returnBuff;
}

//Function: createPacket
//PRE: type is a 4 character message type, message is the message to embed in the packet, length is a valid integer pointer
//POST: returns a char array that has a 12 byte header encoded with type and message.length followed by the message, sets length to the total length of the returned array
//Description: Creates a valid protocol message that can be transmitted to a file transfer client without modification
char* createPacket(std::string type, std::string message, int& length){
	char* head = createHeader(type, message.length());

	int totalLen = 12 + message.length();

	length = totalLen;

	char* buffer = (char*)malloc(sizeof(char) * totalLen);
	memset(buffer, 0x00, totalLen);
	memcpy(buffer, head, 12);	//have you figured out by now that I fucking love memset() and memcpy()??? 

	free(head);

	memcpy(buffer + 12, message.c_str(), message.length());

	return buffer;
}

//Function: createPacket
//PRE: type is a 4 character message type, data is a byte array to be packetized, length is a valid integer pointer and *must* contain the length of the data array
//POST:	returns a char array that has a 12 byte header encoded with type and message.length followed by the message, sets length to the total length of the returned array
//Description: Creates a valid protocol message that can be transmitted to a file transfer client without modification
//			   This version takes in a raw data array to send and length MUST be preset to the length of data
char* createPacket(std::string type, char* data, int& length){
	char* head = createHeader(type, length);

	int totalLen = 12 + length;

	//length = totalLen;

	char* buffer = (char*)malloc(sizeof(char) * totalLen);
	memset(buffer, 0x00, totalLen);
	memcpy(buffer, head, 12);

	free(head);

	memcpy(buffer + 12, data, length);
	
	length = totalLen;

	return buffer;
}