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.
commit
230f5dfea7
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef __CLIENT_H
|
||||||
|
#define __CLIENT_H
|
||||||
|
|
||||||
|
void run_client(const char *, int);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue