From 230f5dfea7824e4ddb90cbe76a49444d0b349473 Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 16 Feb 2009 01:56:15 -0600 Subject: [PATCH] First commit. Client and server exchange messages, one per connection. There is a restricted list of commands that can be sent; both client and server obey. No filesystem access, yet. That is the only thing missing. --- README | 13 +++++ cftp.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ cftp.h | 18 ++++++ client.c | 125 +++++++++++++++++++++++++++++++++++++++ client.h | 7 +++ filesystem.c | 0 filesystem.h | 0 server.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++ server.h | 11 ++++ 9 files changed, 493 insertions(+) create mode 100644 README create mode 100644 cftp.c create mode 100644 cftp.h create mode 100644 client.c create mode 100644 client.h create mode 100644 filesystem.c create mode 100644 filesystem.h create mode 100644 server.c create mode 100644 server.h diff --git a/README b/README new file mode 100644 index 0000000..869dcb7 --- /dev/null +++ b/README @@ -0,0 +1,13 @@ +CFTP - a simple in-band file transfer application protocol experiment +Andrew Coleman +CSC-4200 + +To compile: + gcc -o cftp -Wall server.c client.c cftp.c + +For explanation on running: + ./cftp -h + +There is only one binary, with a switch for the server. All other options are +common and will work for either server or client. + diff --git a/cftp.c b/cftp.c new file mode 100644 index 0000000..22fd468 --- /dev/null +++ b/cftp.c @@ -0,0 +1,158 @@ +/* + * Coleman's FTP. + * + * A simple socket client and server for sending files and messages in a + * single port. + * + */ + +#include +#include +#include +#include +#include + +#include "cftp.h" +#include "server.h" +#include "client.h" + +/* + * This function will process all server requests untill killed. Takes a string + * containing the address to bind (if any) and the integer port. + * + */ +int validate_command(const char *msg) +{ + char errmsg[ERRMSGLEN]; + + /* GET file command */ + if(!strcmp(msg, "get")) + return 1; + + /* PUT file command */ + if(!strcmp(msg, "put")) + return 1; + + /* PING send back the time stamp of right now */ + if(!strcmp(msg, "ping")) + return 1; + + /* nothing else was found, so clearly this is not true */ + sprintf(errmsg, "Invalid command: %s", msg); + error(errmsg); + return 0; +} + +/* + * Splits a buffer into a command and argument. + * + */ +void split_command(char *buffer, char *command, char *argument) +{ + int c = 0, d = 0; + memset(command, '\0', CMDLEN); + memset(argument, '\0', MSGLEN); + + /* find everything up to the colon delimiter */ + while(buffer[c] != '\0' && buffer[c] != ':') + { + command[d++] = buffer[c++]; + } + /* skip the colon */ + if(buffer[c] == ':') + { + c++; + d = 0; + } + /* copy argument, if found */ + while(buffer[c] != '\0') + { + argument[d++] = buffer[c++]; + } +} + +/* + * This function will display error messages on either client or server side. + * Takes a string for the error message and prints it to stderr. + * + */ +void error(const char *msg) +{ + char msg2[ERRMSGLEN + 1]; + sprintf(msg2, "%s\n", msg); + fprintf(stderr, msg2); +} + +/* + * Prints out a debugging message to stdout. + * + */ +void debug(const char *prefix, const char *msg) +{ + printf("%s: %s\n", prefix, msg); +} + +/* + * Prints out the usage of the program to stderr. + * + */ +void usage(const char *progname) +{ + char msg[ERRMSGLEN]; + error("Coleman's FTP application"); + sprintf(msg, "Usage: %s [-h] [-s] [-a address] [-p port]", progname); + error(msg); + error(" -h for this message"); + error(" -s for server, default is client"); + error(" -a for address"); + error(" -p for port, default is 9000, no privileged ports allowed"); + exit(1); +} + +int main(int argc, char **argv) +{ + int opt = 0, portnum = 9000, server = 0; + char address[MSGLEN]; + memset(address, '\0', MSGLEN); + + /* process options using getopt */ + while((opt = getopt(argc, argv, "sa:p:h")) != -1) + { + switch(opt) + { + case 's': + server = 1; + break; + + case 'p': + portnum = atoi(optarg); + break; + + case 'a': + strcpy(address, optarg); + break; + + case 'h': + usage(argv[0]); + break; + } + } + + /* sanitization and cop outs */ + if(!server && strlen(address) == 0) + usage(argv[0]); + else if(portnum <= 1024 || portnum >= 65535) + usage(argv[0]); + + if(server) + { + run_server(address, portnum); + } + else + { + run_client(address, portnum); + } + + return 0; +} + diff --git a/cftp.h b/cftp.h new file mode 100644 index 0000000..f3283ec --- /dev/null +++ b/cftp.h @@ -0,0 +1,18 @@ +#ifndef __CFTP_H +#define __CFTP_H + +/* + * Public protocol API for CFTP. Any function defined in here will be available + * in any part of the application that needs it. + * + */ +#define MSGLEN 128 +#define ERRMSGLEN 256 +#define CMDLEN 5 +int validate_command(const char *); +void split_command(char *, char *, char *); +void error(const char *); +void debug(const char *, const char *); + +#endif + diff --git a/client.c b/client.c new file mode 100644 index 0000000..14579eb --- /dev/null +++ b/client.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cftp.h" +#include "client.h" + +/* + * Special client debug wrapper. + * + */ +void cdebug(const char *msg) +{ + debug("Client", msg); +} + +/* + * This function runs a client to the specified address/hostname and port. + * + */ +int connect_to_server(const char *address, int portnum) +{ + int skt; + struct sockaddr_in server_address; + struct hostent *host; + char msg[ERRMSGLEN]; + + memset(&server_address, 0, sizeof(server_address)); + memset(msg, '\0', ERRMSGLEN); + + /* look up hostname */ + if((host = gethostbyname(address)) == NULL) + { + sprintf(msg, "Could not look up hostname: %s", address); + error(msg); + exit(errno); + } + + /* create socket */ + if((skt = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + sprintf(msg, "Failed to make the socket: %s", strerror(errno)); + error(msg); + exit(errno); + } + + server_address.sin_family = AF_INET; + server_address.sin_addr = *((struct in_addr *) host->h_addr); + server_address.sin_port = htons(portnum); + + /* connect to the server */ + if(connect(skt, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) + { + sprintf(msg, "Could not connect to the server: %s", strerror(errno)); + error(msg); + exit(errno); + } + + return skt; +} + +/* + * Runs the command processor to the server. Handles commands and responses. + * + */ +void run_client(const char *address, int portnum) +{ + int skt; + char msg[MSGLEN], command[CMDLEN], argument[MSGLEN]; + + /* clear strings */ + memset(msg, '\0', MSGLEN); + memset(command, '\0', MSGLEN); + memset(argument, '\0', MSGLEN); + + sprintf(msg, "Using Server: %s:%d", address, portnum); + cdebug(msg); + memset(msg, '\0', MSGLEN); + + /* loop until finished */ + do { + if(strlen(msg) > 0 && validate_command(msg)) + { + skt = connect_to_server(address, portnum); + if(send(skt, msg, strlen(msg), 0) < 0) + { + sprintf(msg, "Send Error: %s", strerror(errno)); + error(msg); + } + else if(recv(skt, msg, MSGLEN, 0) < 0) + { + sprintf(msg, "Receive Error: %s", strerror(errno)); + error(msg); + } + else + { + split_command(msg, command, argument); + if(strlen(argument) > 0) + { + sprintf(msg, "Server Said: %s: %s", command, argument); + } + else + { + sprintf(msg, "Server Said: %s", command); + } + } + cdebug(msg); + memset(msg, '\0', MSGLEN); + close(skt); + } + printf("> "); + scanf("%s", msg); + } while(strcmp(msg, "quit") != 0); + + /* cleanup and quit */ + cdebug("Bye!"); +} + diff --git a/client.h b/client.h new file mode 100644 index 0000000..c74b209 --- /dev/null +++ b/client.h @@ -0,0 +1,7 @@ +#ifndef __CLIENT_H +#define __CLIENT_H + +void run_client(const char *, int); + +#endif + diff --git a/filesystem.c b/filesystem.c new file mode 100644 index 0000000..e69de29 diff --git a/filesystem.h b/filesystem.h new file mode 100644 index 0000000..e69de29 diff --git a/server.c b/server.c new file mode 100644 index 0000000..ce94dcd --- /dev/null +++ b/server.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cftp.h" +#include "server.h" + +/* + * Special server debug wrapper. + * + */ +void sdebug(const char *msg) +{ + debug("Server", msg); +} + +/* + * Proccesses a client connection and performs the necessary command. Called + * on each connection from a client. + * + */ +void handle_client(int skt, const char *clientip) +{ + char buffer[MSGLEN], msg[ERRMSGLEN], command[CMDLEN], argument[MSGLEN]; + time_t pong; + memset(buffer, '\0', MSGLEN); + memset(msg, '\0', ERRMSGLEN); + memset(command, '\0', CMDLEN); + memset(argument, '\0', MSGLEN); + + /* figure out what to do */ + if(recv(skt, buffer, MSGLEN, 0) >= 0 && validate_command(buffer)) + { + split_command(buffer, command, argument); + if(!strcmp(command, "ping")) + { + pong = time(NULL); + sprintf(buffer, "Pong:%llud", (uintmax_t)pong); + } + } + else + { + if(errno) + { + sprintf(buffer, "ERR:%s", strerror(errno)); + } + else + { + sprintf(buffer, "ERR:Invalid Command"); + } + error(buffer); + } + + /* send message to client */ + if(strlen(buffer) > 0) + { + sprintf(msg, "%s: %s", clientip, buffer); + sdebug(msg); + send(skt, buffer, strlen(buffer), 0); + } + + /* cleanup */ + close(skt); +} + +/* + * This function will process all server requests untill killed. + * Takes a string containing the address to bind (if any) and the integer port. + * + */ +void run_server(const char *address, const int portnum) +{ + int skt, client_socket; + unsigned int length; + char msg[ERRMSGLEN]; + struct sockaddr_in server_address, client_address; + + memset(&server_address, 0, sizeof(server_address)); + memset(&client_address, 0, sizeof(client_address)); + + /* create server socket */ + if((skt = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + sprintf(msg, "Socket error: %s", strerror(errno)); + error(msg); + exit(errno); + } + else + { + sdebug("Created socket"); + } + + /* build server address struct */ + server_address.sin_family = AF_INET; + server_address.sin_port = htons(portnum); + /* any network address, or 0.0.0.0 if not supplied */ + if(strlen(address) == 0) + { + server_address.sin_addr.s_addr = htonl(INADDR_ANY); + } + /* assume ip address is given */ + else + { + if(inet_aton(address, &server_address.sin_addr) == 0) + { + sprintf(msg, "Malformed address: %s", address); + error(msg); + exit(1); + } + } + + /* bind to socket */ + length = sizeof(server_address); + if(bind(skt, (struct sockaddr *)&server_address, length) < 0) + { + sprintf(msg, "Bind error: %s", strerror(errno)); + error(msg); + exit(errno); + } + else + { + sprintf(msg, "Bound to address %s", inet_ntoa(server_address.sin_addr)); + sdebug(msg); + } + + /* listen on socket */ + if(listen(skt, 10) < 0) + { + sprintf(msg, "Listen error: %s", strerror(errno)); + error(msg); + exit(errno); + } + else + { + sprintf(msg, "Listening on port %d", portnum); + sdebug(msg); + } + + /* actual server loop */ + while(1) + { + length = sizeof(client_address); + if((client_socket = accept(skt, (struct sockaddr *)&client_address, &length)) < 0) + { + sprintf(msg, "Accept error: %s", strerror(errno)); + error(msg); + exit(errno); + } + else + { + handle_client(client_socket, inet_ntoa(client_address.sin_addr)); + } + } +} diff --git a/server.h b/server.h new file mode 100644 index 0000000..1481f88 --- /dev/null +++ b/server.h @@ -0,0 +1,11 @@ +#ifndef __SERVER_H +#define __SERVER_H + +/* + * This file represents the public server api for CFTP. + * + */ +void run_server(const char *, const int); + +#endif +