Encryption Client-Server

Assigned during fall 2005 for csci4061 — Introduction to Operating Systems, this is a client and server combination that encrypts files using a simple shift cipher. Obviously the practical applications for such a client and server are virtually non-existent, but it was an easy proof of concept of sorts, in network protocols, sockets, multithreading, and file I/O (in C). The README is pretty comprehensive on this project, so I won’t waste much more breath here, and just let you read it for yourself:

README »

Below is the code from the encrypt client, which connects to the server, sends a file and shift key, and then saves the result it receives from the server (the encrypted file).

/* CSci4061 F2005 Assignment 5
* section: 3
* login: hedg0029
* date: 12/07/05
* name: David R. Hedges
* id: 2836226 */
 
/*
Name:David R. Hedges
S ID: 2836226
x500: hedg0029
Sec : 003
 
Ver : 2005-12-05T2315-0600 drh encrypt-client.c
Par : 2005-11-23T1853-0600 drh encrypt.c
 
Desc: modify encrypt.c to take some of the reader thread functionality inside of the main (non-threaded) operation
 
*/
 
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "messages.h"
 
typedef struct {
	char in_file[1024];
	int integer_add;
} request;
 
static int client_log_fd;
int debug = 0;
 
//prototypes
int readinRequest (request *tempRequest);
int get_filesize(int fd);
 
 
int main(int argc, char **argv) {
	//variables
	int port_number, sockfd, in_fd, out_fd, fileSize, x;
	struct sockaddr_in serv_addr;
	struct hostent *host_addr;
	char readMsg[1024];
	char writeMsg[1024];
	char *errorMsg;
	char encFilename[1024];
	char *writePos;
	request tempRequest;
 
 
	if (argc != 3 && argc != 4) {
		printf("(#00): argc = %d.\\n", argc);
		printf("(#01): Usage: ./encrypt-client host_name port_number [-v]\\n\\n");
		return -1;
	}
 
	//check for -v (verbose) flag
	if (argc == 4 && strcmp(argv[3],"-v") == 0) {
		debug = 1;
	}
 
	//lookup hostname, error if not found
	if ((host_addr = gethostbyname(argv[1])) == NULL) {
		printf("(#31): Host lookup failed for hostname '%s'\\n", argv[1]);
		return -1;
	}
 
	//convert third argument from string to int port_number
	port_number = atoi(argv[2]);
	if (port_number < 1 || port_number > 65535) {
		printf("(#02): port_number must be between 1 and 65,535, inclusive. %d is not in this range.\\n\\n", port_number);
		return -1;
	}
 
	//open logfile, don't need to worry about mutex or perror, since no one else is around yet
	if ((client_log_fd = creat("client_log", 0666)) < 0) {
		perror("(#03): Couldn't create file 'client_log'");
		return -1;
	}
 
	//if we've gotten this far, then "all systems are go." create the socket and connect
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0) {
		errorMsg = strerror(errno);
		printf("(#32): Unable to open socket: '%s'\\n", errorMsg);
		return -1;
	}
 
	if(debug) {
		printf("(%s:%d): Attempting memset.\\n", __FILE__, __LINE__);
	}
 
	memset((char *) &serv_addr, '0', sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
 
	if(debug) {
		printf("(%s:%d): Attempting memcpy.\\n", __FILE__, __LINE__);
	}
 
	//copy the ip address from the gethostbyname lookup into the connection info
	memcpy((char *) &serv_addr.sin_addr.s_addr, (char *)host_addr->h_addr, host_addr->h_length);
 
	serv_addr.sin_port = htons(port_number);
 
	if(debug) {
		printf("(%s:%d): Attempting connect.\\n", __FILE__, __LINE__);
	}
 
	if(connect(sockfd,(struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
		errorMsg = strerror(errno);
		printf("(#33): Unable to connect(): '%s'\\n", errorMsg);
		return -1;
	} //at this point, we're connected.
 
	if(debug) {
		printf("(%s:%d): Client is connected.\\n", __FILE__, __LINE__);
	}
 
 
	/// main processing loop
 
	if (debug) {
		printf("(%s:%d): Entering main loop.\\n", __FILE__, __LINE__);
	}
	int retValHolder = 0; //, runOnce = 0;
	retValHolder = readinRequest(&tempRequest);
	while (retValHolder != 0) {
		if (debug) {
			printf("(%s:%d): Got past readinRequest call.\\n", __FILE__, __LINE__);
		}
 
		if (retValHolder == -1) {
			//malformed command
			printf("(#35): A malformed command was encountered. See logfile, or run again with -v for details. Attempting next request.\\n");
		}
		else {
			//good command, so now we actually want to write to the sockfd per notes
 
			//attempt to open the file
			if ((in_fd = open(tempRequest.in_file,O_RDONLY)) < 0) {
				errorMsg = strerror_r(errno, writeMsg, 1024);
				snprintf(writeMsg, 1024, "%s : %s\\n", errorMsg, tempRequest.in_file);
				if(debug) {
					printf("(#10): Error opening file %s. Error: '%s'\\n", tempRequest.in_file, errorMsg);
				}
				write(client_log_fd, writeMsg, strlen(writeMsg));
				retValHolder = readinRequest(&tempRequest);
				continue;	//try next request
			}
			if (debug) {
				printf("(%s:%d): Successfully opened the file %s.\\n", __FILE__, __LINE__, tempRequest.in_file);
			}
 
/*			if(runOnce) {
				//notdone
				x = htons(NOTDONE);
				memcpy(writeMsg, &x, sizeof(int));
				write(sockfd, writeMsg, sizeof(int));
 
				if (debug) {
					printf("(%s:%d): Sent NOTDONE.\\n", __FILE__, __LINE__);
				}
			}
*/
 
			//wait for READY
			read(sockfd, readMsg, sizeof(int));
			memcpy(&x, readMsg, sizeof(int));
			if(debug) {
				printf("(%s:%d): ntohs(x) is %d\\n", __FILE__, __LINE__, ntohs(x));
			}
			if(ntohs(x) != READY) {
				if(debug) {
					printf("(#34): Non-READY command received. Message received was %d (or %d)\\n.", ntohs(atoi(readMsg)), atoi(readMsg));
				}
				close(sockfd);
				close(client_log_fd);
				return -1;
			}
			else if (debug) {
				printf("(%s:%d): READY command received.\\n", __FILE__, __LINE__);
			}
 
			x = htons(ENCRYPT);
			memcpy(writeMsg, &x, sizeof(int));
			x = write(sockfd, writeMsg, sizeof(int));
 
			if (debug) {
				printf("(%s:%d): (%d) Sent ENCRYPT.\\n", __FILE__, __LINE__, x);
			}
 
 
			//write encryptionKey
			x = htonl(tempRequest.integer_add);
			memcpy(writeMsg, &x, sizeof(int));
			x = write(sockfd, writeMsg, sizeof(int));
 
			if (debug) {
				printf("(%s:%d): (%d) Sent encryptionKey (%d).\\n", __FILE__, __LINE__, x, tempRequest.integer_add);
			}
 
			fileSize = get_filesize(in_fd);
			x = htons(fileSize);
			memcpy(writeMsg, &x, sizeof(int));
			x = write(sockfd, writeMsg, sizeof(int));
 
			if (debug) {
				printf("(%s:%d): (%d) Sent filesize (%d).\\n", __FILE__, __LINE__, x, fileSize);
			}
 
			//attempt to read in 1024 bytes at a time from in_file, and then write as many bytes as were read to sockfd each time around
			ssize_t readbytes, writebytes;
			while ((readbytes = read(in_fd, writeMsg, 1024)) > 0) {	//currently assuming that we'll only get <= 0 when EOF arrives, not an error
				writePos = writeMsg;
				while ((writebytes = write(sockfd, writePos, readbytes)) < readbytes) {
					if (debug) {
						printf("(%s:%d): Writebytes was %d.\\n", __FILE__, __LINE__, writebytes);
					}
					//we didn't get to write the whole buffer to the new file, so we need to write the rest
					readbytes -= writebytes;
					writePos += writebytes;
				}	// done writing the buffer to the socket
				if (debug) {
					printf("(%s:%d): Wrote %d bytes to sockfd.\\n", __FILE__, __LINE__, writebytes);
				}
			} //end b/c no more bytes to read locally
 
			if (debug) {
				printf("(%s:%d): Finished reading/sending file.\\n", __FILE__, __LINE__);
			}
 
			//close in_fd
			if (close(in_fd) < 0) {
				errorMsg = strerror_r(errno, writeMsg, 1024);
				snprintf(writeMsg, 1024, "%s\\n", errorMsg);
				if(debug) {
					printf("(#11): Error closing file '%s'. Error: '%s'\\n", tempRequest.in_file, writeMsg);
				}
				write(client_log_fd, writeMsg, strlen(writeMsg));
			}
 
			if (debug) {
				printf("(%s:%d): Closed in_fd '%s'.\\n", __FILE__, __LINE__, tempRequest.in_file);
			}
 
			//now the ball is in the server's court, and we have to wait for it to send back encrypted data, which we will write to in_file.encrypt
 
			//create in_file.encrypt
			snprintf(encFilename, 1024, "%s.encrypt", tempRequest.in_file);
			if ((out_fd = creat(encFilename, 0666)) < 0) {				//open output file
				errorMsg = strerror_r(errno, writeMsg, 1024);
				snprintf(writeMsg, 1024, "%s\\n", errorMsg);
				if(debug) {
					printf("(#11): Error creating file '%s.encrypt'. Error: '%s'\\n", tempRequest.in_file, writeMsg);
				}
				write(client_log_fd, writeMsg, strlen(writeMsg));
			}
 
			if (debug) {
				printf("(%s:%d): Created file '%s'\\n", __FILE__, __LINE__, encFilename);
			}
 
			//wait for RESULT
			read(sockfd, readMsg, sizeof(int));
			memcpy(&x, readMsg, sizeof(int));
			if(ntohs(x) != RESULT) {
				if(debug) {
					printf("(#36): Non-RESULT command received. Message received was %d (or %d)\\n.", ntohs(atoi(readMsg)), atoi(readMsg));
				}
				close(out_fd);
			}
 
			if (debug) {
				printf("(%s:%d): RESULT command received from server.\\n", __FILE__, __LINE__);
			}
 
			//read in Size of Data
			read(sockfd, readMsg, sizeof(int));
			memcpy(&x, readMsg, sizeof(int));
			if(ntohs(x) != fileSize) {
				if(debug) {
					printf("(#37): Return filesize (%d) not equal to sent filesize (%d). o_0\\n.", ntohs(atoi(readMsg)), fileSize);
				}
				fileSize = ntohs(x);
			}
 
			if (debug) {
				printf("(%s:%d): filesize (%d) received from server.\\n", __FILE__, __LINE__, ntohs(x));
			}
 
			//read in the actual data
			ssize_t fileProgress = 0;
			int bytesToRead;
			while (fileProgress < fileSize) {
				if (fileSize - fileProgress > 1024) {
					bytesToRead = 1024;
				}
				else {
					bytesToRead = fileSize - fileProgress;
				}
				readbytes = read(sockfd, writeMsg, bytesToRead);
				if (debug) {
					printf("(%s:%d): readbytes was %d.\\n", __FILE__, __LINE__, readbytes);
				}
				writePos = writeMsg;
				fileProgress += readbytes;
				//write the bytes from the network, into the local file
				while ((writebytes = write(out_fd, writePos, readbytes)) < readbytes) {
					readbytes -= writebytes;
					writePos += writebytes;
				}
			}
 
			if (debug) {
				printf("(%s:%d): Encrypted file received, and written to out_fd.\\n", __FILE__, __LINE__);
			}
 
			//close out_fd
			if (close(out_fd) < 0) {
				errorMsg = strerror_r(errno, writeMsg, 1024);
				snprintf(writeMsg, 1024, "%s\\n", errorMsg);
				if(debug) {
					printf("(#38): Error closing file '%s'. Error: '%s'\\n", encFilename, writeMsg);
				}
				write(client_log_fd, writeMsg, strlen(writeMsg));
			}
 
		} // finished with one successful request, now do anything necessary to prepare for next loop through
 
		if(debug) {
			printf("(%s:%d): Closed out_fd, setting runOnce.\\n", __FILE__, __LINE__);
		}
 
//		runOnce = 1;
		retValHolder = readinRequest(&tempRequest);
		if(retValHolder > 0) {
			//notdone
			x = htons(NOTDONE);
			memcpy(writeMsg, &x, sizeof(int));
			write(sockfd, writeMsg, sizeof(int));
			if (debug) {
				printf("(%s:%d): Sent NOTDONE.\\n", __FILE__, __LINE__);
			}
		}
 
	} //close main while loop since we received eof on requests
 
 
	//tell the server we're DONE, and the server will close the connection
 
	x = htons(DONE);
	memcpy(writeMsg, &x, sizeof(int));
	write(sockfd, writeMsg, sizeof(int));
 
	if (debug) {
		printf("(%s:%d): Sent DONE message to server.\\n", __FILE__, __LINE__);
	}
 
	//close logfile (don't care about mutex, threads are gone)
	if (close(client_log_fd) != 0) {
		perror("(#08): Error closing client_log.");
		return -1;
	}
 
	return 1;
}
 
 
 
//peripheral functions
 
int readinRequest (request *tempRequest) {
	/*return values
		returns 0 on eof
		returns 1 on success
		returns -1 on malformed command
	*/
 
	char lineBuf[1024];		//where we put fgets
	char *space;
	int returnVal = 0;
 
 
	if (fgets(lineBuf, 1024, stdin) != NULL) {
		if(debug) {
			printf("(#17): lineBuf is '%s'.\\n", lineBuf);
		}
		space = index(lineBuf, ' ');
 
		// check for no space in line, or space is at end, or item after space isn't %d or -%d
		if(space == NULL ||
		   (space+1) == '\\0' ||
		   (!isdigit(*(space+1))
		   && *(space+1) != '-'
		   && (space+2) != NULL
		   && !isdigit(*(space+2)))) {
			char writeMsg[1024];
 
			//fix the user input so when we print it out (file and/or screen), there's no newline at the end
			space = index(lineBuf, '\\n');
			*space = '\\0';
 
			snprintf(writeMsg, 1024, "[reader]: Malformed command '%s'\\n", lineBuf);
			if(debug) {
				printf("(#21): [reader]: Malformed command '%s'\\n", lineBuf);
			}
			write(client_log_fd, writeMsg, strlen(writeMsg));
			returnVal = -1;
		}
		//else, deemed to be a good command, so process it into our request struct for return
		else {
			strcpy((*tempRequest).in_file, strtok(lineBuf, " "));
			(*tempRequest).integer_add = atoi(strtok(NULL, " "));
			if(debug) {
				printf("(#09):\\ttempRequest.in_file is '%s'\\n", (*tempRequest).in_file);
				printf("(#09):\\ttempRequest.integer_add is '%d'\\n", (*tempRequest).integer_add);
			}
			returnVal = 1;
		}
	}
 
	else {
		returnVal = 0;
	}
 
 
	return returnVal;
}
 
 
int get_filesize(int fd) {
//takes a fd of an already opened file, and returns its size (in bytes)
//returns -1 on error
/*	struct stat fileInfo;
	if (fstat(fd, &fileInfo) != -1) {
		return (int) fileInfo.st_size;
	}
	else {
		//error
		return -1;
	}
*/
//the above code was returning erroneous file sizes, so i'm trying lseek instead
	int i;
	i = lseek(fd, 0, SEEK_END);
	lseek(fd, 0, SEEK_SET);		//oops. have to set this back to the beginning of the file (hopefully we don't call it anywhere but from the beginning)
 
	return i;
}
 
 
///to do, time permitting:
/*
 
	replace error messages (#33): and such with __FILE__:__LINE__ (in function __FUNCTION__):
	~(#34) log error to file, too
	add more file logging
 
*/
 

Download this code: encrypt/encrypt-client.c