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