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.

master
andrew 2009-02-16 01:56:15 -06:00
commit 230f5dfea7
9 changed files with 493 additions and 0 deletions

13
README Normal file
View File

@ -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.

158
cftp.c Normal file
View File

@ -0,0 +1,158 @@
/*
* Coleman's FTP.
*
* A simple socket client and server for sending files and messages in a
* single port.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#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;
}

18
cftp.h Normal file
View File

@ -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

125
client.c Normal file
View File

@ -0,0 +1,125 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#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!");
}

7
client.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef __CLIENT_H
#define __CLIENT_H
void run_client(const char *, int);
#endif

0
filesystem.c Normal file
View File

0
filesystem.h Normal file
View File

161
server.c Normal file
View File

@ -0,0 +1,161 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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));
}
}
}

11
server.h Normal file
View File

@ -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