commit 230f5dfea7824e4ddb90cbe76a49444d0b349473 Author: andrew Date: Mon Feb 16 01:56:15 2009 -0600 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. 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 +