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:
- encrypt.tbz
- /encrypt (Index of files
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