mirror of
https://github.com/caperren/school_archives.git
synced 2025-11-09 21:51:15 +00:00
223 lines
7.9 KiB
C
223 lines
7.9 KiB
C
/*
|
|
* File: ftserver.c
|
|
* Author: Corwin Perren
|
|
*
|
|
* Created on March 11, 2017, 3:11 PM
|
|
*
|
|
* This file contains all the functions needed for the server.
|
|
*/
|
|
|
|
#include "ftserver.h"
|
|
|
|
int get_listen_sfd(void){
|
|
///// Create our listening socket, check if created successfully /////
|
|
int listen_sfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if(listen_sfd < 0){
|
|
printf("Could not create listening socket! Exiting...\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
///// Return our new listening socket /////
|
|
return listen_sfd;
|
|
}
|
|
|
|
void setup_server_address_struct(struct sockaddr_in *server_address, int port){
|
|
////// Setup the server struct with port and other info /////
|
|
memset((char *)server_address, '\0', sizeof(*server_address));
|
|
server_address->sin_port = htons(port);
|
|
server_address->sin_addr.s_addr = INADDR_ANY;
|
|
server_address->sin_family = AF_INET;
|
|
}
|
|
|
|
void bind_listen_socket(int listen_sfd, struct sockaddr_in *server_address){
|
|
///// Bind server socket /////
|
|
int bind_result = bind(listen_sfd, (struct sockaddr *)server_address, sizeof(*server_address));
|
|
if(bind_result < 0){
|
|
fprintf(stderr, "Failed to bind listening port! "
|
|
"Please choose a different port! Exiting...\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void get_client_control_sfd_and_info(int listen_sfd, int *control_sfd, struct sockaddr_in *client_address){
|
|
///// This accepts a new client when a request comes in, and sets the control socket file descriptor /////
|
|
socklen_t client_length = sizeof(*client_address);
|
|
*control_sfd = accept(listen_sfd, (struct sockaddr *) client_address, &client_length);
|
|
}
|
|
|
|
int get_client_data_sfd_and_info(int port, struct sockaddr_in *control_address, struct sockaddr_in *data_address){
|
|
///// Create a new socket for data /////
|
|
int sfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if(sfd < 0){
|
|
printf("Could not create data socket! Exiting...\n");
|
|
fflush(stdout);
|
|
return -1;
|
|
}
|
|
|
|
///// Set up the server parameters, specifically the port send in /////
|
|
memset((char *)data_address, '\0', sizeof(*data_address));
|
|
data_address->sin_port = htons(port);
|
|
memcpy(&data_address->sin_addr, &control_address->sin_addr, sizeof(control_address->sin_addr));
|
|
data_address->sin_family = AF_INET;
|
|
|
|
///// Make the connection and verify it didn't fail. /////
|
|
int connect_result = connect(sfd, (struct sockaddr *)data_address, sizeof(*data_address));
|
|
if(connect_result < 0){
|
|
fprintf(stderr, "Connection failed! Is the port correct? Exiting...\n");
|
|
fflush(stdout);
|
|
return -1;
|
|
}
|
|
|
|
///// Return our new data transfer socket file descriptor /////
|
|
return sfd;
|
|
}
|
|
|
|
|
|
void send_file_listing(int control_sfd, int data_sfd){
|
|
///// Variables /////
|
|
DIR *dir;
|
|
struct dirent *directory;
|
|
struct stat s;
|
|
|
|
///// Open the current directory /////
|
|
dir = opendir(".");
|
|
|
|
///// Make and write a header to the client /////
|
|
char header[] = "########################################\n"
|
|
" Directory Listing \n"
|
|
"########################################\n";
|
|
write(data_sfd, header, strlen(header));
|
|
wait_for_ok(data_sfd);
|
|
|
|
///// Get ready to traverse directory /////
|
|
int end_reached = 0;
|
|
while(!end_reached){
|
|
///// Get the next item in the directory /////
|
|
directory = readdir(dir);
|
|
|
|
///// If it's NULL, we've reached the end, break out /////
|
|
if(directory == NULL){
|
|
end_reached = 1;
|
|
continue;
|
|
}
|
|
|
|
///// Get and send only files, no folders /////
|
|
///// Core of getting only files was taken from the link below, modified ////
|
|
///// to work on the flip servers with S_ISREG /////
|
|
///// https://cboard.cprogramming.com/linux-programming/78236-list-files-directory-without-folders.html /////
|
|
|
|
///// Get information about the current item in the directory /////
|
|
stat(directory->d_name, &s);
|
|
|
|
///// If it's a regular file, write it to the data socket /////
|
|
if(S_ISREG(s.st_mode)){
|
|
write(data_sfd, directory->d_name, strlen(directory->d_name));
|
|
write(data_sfd, "\n", 2);
|
|
wait_for_ok(data_sfd);
|
|
}
|
|
}
|
|
///// Send a final OK to let the client know we're done /////
|
|
write(data_sfd, "OK", 3);
|
|
wait_for_ok(data_sfd);
|
|
|
|
}
|
|
|
|
void send_file_if_available(char *filename, int control_sfd, int data_sfd){
|
|
///// Variables /////
|
|
char buffer[1024];
|
|
memset(buffer, '\0', 1024);
|
|
long int file_size = 0;
|
|
FILE *file_pointer;
|
|
|
|
///// Send the header /////
|
|
char header[] = "########################################\n"
|
|
" File Transfer \n"
|
|
"########################################\n";
|
|
write(data_sfd, header, strlen(header));
|
|
wait_for_ok(data_sfd);
|
|
|
|
///// Open the file /////
|
|
file_pointer = fopen(filename, "rb");
|
|
|
|
if(file_pointer != NULL){
|
|
///// Seek to the end and get the file size, then rewind to the beginning /////
|
|
fseek(file_pointer, 0L, SEEK_END);
|
|
file_size = ftell(file_pointer);
|
|
rewind(file_pointer);
|
|
|
|
///// Send the file size to the client /////
|
|
sprintf(buffer, "%ld", file_size);
|
|
write(data_sfd, buffer, strlen(buffer));
|
|
wait_for_ok(data_sfd);
|
|
|
|
///// If the client wants the file (duplicate name handling), start the transfer /////
|
|
if(should_transfer(data_sfd)){
|
|
///// Get the nubmer of 1KB blocks, and remainder, so we can be efficient about transfer /////
|
|
long int blocks = file_size / 1024;
|
|
int remainder = file_size % 1024;
|
|
|
|
///// Read 1KB block as needed and send to client/////
|
|
for(long int i = 0 ; i < blocks ; i++){
|
|
fread(buffer, sizeof(buffer), 1, file_pointer); //Reads one 1024 byte block
|
|
write(data_sfd, buffer, 1024);
|
|
wait_for_ok(data_sfd);
|
|
}
|
|
|
|
///// If the file isn't a multiple of 1KB, send the remainder /////
|
|
if(remainder > 0){
|
|
fread(buffer, remainder, 1, file_pointer); //Reads one remainder byte block
|
|
write(data_sfd, buffer, remainder);
|
|
wait_for_ok(data_sfd);
|
|
}
|
|
|
|
///// Close the file /////
|
|
fclose(file_pointer);
|
|
}
|
|
|
|
}else{
|
|
///// The requested file doesn't exist. Let the client know. /////
|
|
write(data_sfd, "0", 2);
|
|
wait_for_ok(data_sfd);
|
|
|
|
char no_file_message[] = "File not found!\n";
|
|
write(data_sfd, no_file_message, strlen(no_file_message));
|
|
wait_for_ok(data_sfd);
|
|
}
|
|
}
|
|
|
|
void send_unknown_command_received(int sfd){
|
|
///// Send the header, so the client knows it's invalid. /////
|
|
char header[] = "########################################\n"
|
|
" UNKNOWN COMMAND \n"
|
|
"########################################\n"
|
|
"Please double check your command.\n";
|
|
write(sfd, header, strlen(header));
|
|
wait_for_ok(sfd);
|
|
}
|
|
|
|
void wait_for_ok(int sfd){
|
|
///// This reads from the sfd until an OK response is received /////
|
|
char read_buffer[1024];
|
|
memset(read_buffer, '\0', 1024);
|
|
|
|
while(strcmp(read_buffer, "OK") != 0){
|
|
memset(read_buffer, '\0', 1024);
|
|
read(sfd, read_buffer, 1023);
|
|
}
|
|
}
|
|
|
|
int should_transfer(int sfd){
|
|
///// This reads an OK or EXIT from the client to determine if a transfer should occur. /////
|
|
///// EXIT will happen if the file is already at the destination and the client does not want to overwrite. /////
|
|
char read_buffer[1024];
|
|
memset(read_buffer, '\0', 1024);
|
|
read(sfd, read_buffer, 1023);
|
|
|
|
if(strcmp(read_buffer, "OK") == 0){
|
|
return 1;
|
|
}else if(strcmp(read_buffer, "EXIT") == 0){
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|