2024-06-29 11:39:23 +00:00
# include <fcntl.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <errno.h>
# include <signal.h>
# include <arpa/inet.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <sys/stat.h>
# include <limits.h>
# include <time.h>
# define SEND_TIMEOUT 1
# define RECV_TIMEOUT 2
2024-06-30 22:04:57 +00:00
# define MSG1 "Server is very busy.\n"
2024-07-01 10:06:07 +00:00
# define MSG2 "File is very large.\n"
2024-06-30 22:04:57 +00:00
# define MSG3 "No such file.\n"
# define MSG4 "No namespace available.\n"
# ifndef VERSION
2024-09-28 13:55:35 +00:00
# define VERSION "1.2.2"
2024-06-30 22:04:57 +00:00
# endif
2024-06-29 11:39:23 +00:00
2024-09-15 13:45:31 +00:00
typedef struct {
/* Clients on thread */
size_t max_clients ;
2024-06-29 11:39:23 +00:00
2024-09-15 13:45:31 +00:00
/* Max file size */
size_t max_size ;
2024-06-30 22:04:57 +00:00
2024-09-15 13:45:31 +00:00
/* Buffer size for thread */
size_t buf_size ;
/* Max filename lenght */
size_t name_len ;
/* Directory for files */
char * dir ;
/* Message of the day */
char * motd ;
} CFG ;
CFG cfg = { . max_clients = 15 , . max_size = 1 , . buf_size = 32000 , . name_len = 15 , . dir = " . " } ;
2024-09-28 13:55:35 +00:00
typedef struct {
int fd ;
char * buf ;
char * filename ;
} CLIENT_THREAD ;
2024-06-29 11:39:23 +00:00
2024-09-28 13:55:35 +00:00
int ifd ;
2024-06-29 11:39:23 +00:00
int ofd ;
2024-09-28 13:55:35 +00:00
size_t clients ;
2024-06-29 11:39:23 +00:00
enum {
OUTPUT ,
INPUT
} ;
enum {
WRITE ,
SEND
} ;
2024-07-14 15:02:31 +00:00
int new_server ( const int port , const int backlog ) {
2024-06-29 11:39:23 +00:00
int listen_fd = socket ( AF_INET , SOCK_STREAM , 0 ) ;
if ( listen_fd < 0 )
return - 1 ;
struct timeval tout = { . tv_sec = RECV_TIMEOUT , . tv_usec = 0 } ;
if ( setsockopt ( listen_fd , SOL_SOCKET , SO_RCVTIMEO , & tout , sizeof ( tout ) ) < 0 )
2024-09-28 13:55:35 +00:00
goto NEW_SOCKET_ERROR ;
2024-06-29 11:39:23 +00:00
tout . tv_sec = SEND_TIMEOUT ;
if ( setsockopt ( listen_fd , SOL_SOCKET , SO_SNDTIMEO , & tout , sizeof ( tout ) ) < 0 )
2024-09-28 13:55:35 +00:00
goto NEW_SOCKET_ERROR ;
2024-06-29 11:39:23 +00:00
int reuse = 1 ;
if ( setsockopt ( listen_fd , SOL_SOCKET , SO_REUSEPORT , & reuse , sizeof ( reuse ) ) < 0 )
2024-09-28 13:55:35 +00:00
goto NEW_SOCKET_ERROR ;
2024-06-29 11:39:23 +00:00
struct sockaddr_in serv_addr ;
serv_addr . sin_family = AF_INET ;
serv_addr . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
serv_addr . sin_port = htons ( port ) ;
if ( bind ( listen_fd , ( struct sockaddr * ) & serv_addr , sizeof ( serv_addr ) ) < 0 )
2024-09-28 13:55:35 +00:00
goto NEW_SOCKET_ERROR ;
2024-06-29 11:39:23 +00:00
2024-07-14 15:02:31 +00:00
if ( listen ( listen_fd , backlog ) < 0 )
2024-09-28 13:55:35 +00:00
goto NEW_SOCKET_ERROR ;
2024-06-29 11:39:23 +00:00
return listen_fd ;
2024-09-28 13:55:35 +00:00
NEW_SOCKET_ERROR :
2024-06-29 11:39:23 +00:00
close ( listen_fd ) ;
return - 1 ;
}
2024-09-28 13:55:35 +00:00
char rand_name ( char * buf ) {
const char abc [ ] = " asdfghjklzxcvbnmqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890 " ;
2024-06-29 11:39:23 +00:00
2024-07-01 07:26:22 +00:00
for ( int i = 0 ; i < 1000 ; i + + ) {
2024-09-28 13:55:35 +00:00
for ( size_t i = 0 ; i < cfg . name_len ; i + + )
buf [ i ] = abc [ rand ( ) % ( sizeof ( abc ) - 1 ) ] ;
buf [ cfg . name_len ] = ' \n ' ;
buf [ cfg . name_len + 1 ] = ' \0 ' ;
2024-06-29 11:39:23 +00:00
struct stat sb ;
2024-09-28 13:55:35 +00:00
if ( stat ( buf , & sb ) < 0 )
return 1 ;
2024-06-29 11:39:23 +00:00
2024-09-28 13:55:35 +00:00
return 0 ;
2024-06-29 11:39:23 +00:00
}
2024-06-30 22:04:57 +00:00
2024-09-28 13:55:35 +00:00
return 1 ;
2024-06-29 11:39:23 +00:00
}
2024-06-29 20:40:42 +00:00
int send_str ( int fd , char * str , ssize_t size , int flag ) {
while ( size ) {
2024-06-29 11:39:23 +00:00
ssize_t ret = 0 ;
if ( flag )
2024-09-28 13:55:35 +00:00
ret = send ( fd , str , size , MSG_NOSIGNAL ) ;
2024-06-29 11:39:23 +00:00
else
2024-06-29 20:40:42 +00:00
ret = write ( fd , str , size ) ;
2024-06-29 11:39:23 +00:00
2024-09-28 13:55:35 +00:00
if ( ret < 0 )
2024-06-29 11:39:23 +00:00
return 1 ;
else if ( ret = = 0 )
return 0 ;
2024-06-29 20:40:42 +00:00
size - = ret ;
str + = ret ;
2024-06-29 11:39:23 +00:00
}
return 0 ;
}
2024-09-28 13:55:35 +00:00
void upload ( CLIENT_THREAD * CT ) {
if ( rand_name ( CT - > filename ) ) {
send ( CT - > fd , MSG4 , sizeof ( MSG4 ) , 0 ) ;
return ;
2024-06-30 22:04:57 +00:00
}
2024-09-28 13:55:35 +00:00
if ( send ( CT - > fd , CT - > filename , strlen ( CT - > filename ) , 0 ) < 0 )
return ;
2024-06-29 11:39:23 +00:00
2024-09-28 13:55:35 +00:00
/* Create file */
int ffd = open ( CT - > filename , O_CREAT | O_WRONLY , 0666 ) ;
2024-06-29 11:39:23 +00:00
if ( ffd < 0 )
2024-09-28 13:55:35 +00:00
goto UPLOAD_ERROR ;
2024-06-29 11:39:23 +00:00
2024-06-29 20:40:42 +00:00
size_t tbytes = 0 ;
2024-06-29 11:39:23 +00:00
while ( 1 ) {
2024-09-28 13:55:35 +00:00
ssize_t rbytes = recv ( CT - > fd , CT - > buf , cfg . buf_size , MSG_NOSIGNAL ) ;
if ( rbytes < 0 & & errno ! = EINTR )
goto UPLOAD_ERROR ;
2024-06-29 11:39:23 +00:00
else if ( rbytes = = 0 )
break ;
2024-06-29 20:40:42 +00:00
tbytes + = ( size_t ) rbytes ;
2024-09-15 13:45:31 +00:00
if ( tbytes / 1024 / 1024 > = cfg . max_size ) {
2024-09-28 13:55:35 +00:00
send_str ( CT - > fd , MSG2 , sizeof ( MSG2 ) , WRITE ) ;
2024-06-29 11:39:23 +00:00
break ;
}
2024-09-28 13:55:35 +00:00
if ( send_str ( ffd , CT - > buf , rbytes , WRITE ) )
goto UPLOAD_ERROR ;
2024-06-29 11:39:23 +00:00
}
2024-09-28 13:55:35 +00:00
UPLOAD_ERROR :
2024-06-29 20:40:42 +00:00
if ( ffd ! = - 1 )
2024-06-29 11:39:23 +00:00
close ( ffd ) ;
}
2024-09-28 13:55:35 +00:00
void load ( CLIENT_THREAD * CT ) {
ssize_t rbytes = recv ( CT - > fd , CT - > filename , cfg . name_len , 0 ) ;
2024-09-15 13:45:31 +00:00
if ( rbytes < = 0 )
2024-09-28 13:55:35 +00:00
return ;
2024-06-29 11:39:23 +00:00
2024-09-28 13:55:35 +00:00
CT - > filename [ rbytes ] = ' \0 ' ;
char * ptr = strchr ( CT - > filename , ' \n ' ) ;
2024-06-29 11:39:23 +00:00
if ( ptr )
* ptr = ' \0 ' ;
2024-09-28 13:55:35 +00:00
if ( strstr ( CT - > filename , " . " ) | | strstr ( CT - > filename , " / " ) )
return ;
2024-06-29 11:39:23 +00:00
/* INFO */
2024-09-28 13:55:35 +00:00
if ( ! strcmp ( CT - > filename , " INFO " ) ) {
2024-06-30 22:04:57 +00:00
char msg [ 336 ] ;
2024-09-28 13:55:35 +00:00
int ret = snprintf ( msg , sizeof ( msg ) , " Max file size: %zuMB \n Max clients: %zu \n Buffer size: %zu \n MOTD: %s \n " , cfg . max_size , cfg . max_clients , cfg . buf_size , ( cfg . motd ) ? cfg . motd : " " ) ;
2024-06-29 11:39:23 +00:00
2024-09-28 13:55:35 +00:00
send_str ( CT - > fd , msg , ( ssize_t ) ret , SEND ) ;
return ;
2024-06-29 11:39:23 +00:00
}
2024-06-30 22:04:57 +00:00
/* Open */
2024-09-28 13:55:35 +00:00
int ffd = open ( CT - > filename , O_RDONLY ) ;
2024-06-29 11:39:23 +00:00
if ( ffd < 0 ) {
2024-09-28 13:55:35 +00:00
send_str ( CT - > fd , MSG3 , sizeof ( MSG3 ) , SEND ) ;
goto LOAD_ERROR ;
2024-06-29 11:39:23 +00:00
}
while ( 1 ) {
2024-09-28 13:55:35 +00:00
rbytes = read ( ffd , CT - > buf , cfg . buf_size ) ;
if ( rbytes < 0 & & errno ! = EINTR )
goto LOAD_ERROR ;
2024-06-29 11:39:23 +00:00
else if ( rbytes = = 0 )
break ;
2024-09-28 13:55:35 +00:00
if ( send_str ( CT - > fd , CT - > buf , rbytes , SEND ) )
goto LOAD_ERROR ;
2024-06-29 11:39:23 +00:00
}
2024-09-28 13:55:35 +00:00
LOAD_ERROR :
2024-06-29 20:40:42 +00:00
if ( ffd ! = - 1 )
2024-06-29 11:39:23 +00:00
close ( ffd ) ;
}
void sig_handler ( int sig ) {
2024-09-28 13:55:35 +00:00
if ( sig = = SIGUSR1 & & clients )
clients - - ;
2024-06-29 11:39:23 +00:00
else {
close ( ifd ) ;
close ( ofd ) ;
exit ( 0 ) ;
}
}
void Proc ( int fd , int flag ) {
static struct sockaddr_in cli_addr ;
socklen_t length = sizeof ( cli_addr ) ;
pid_t par = getpid ( ) ;
while ( 1 ) {
int cfd = accept ( fd , ( struct sockaddr * ) & cli_addr , & length ) ;
if ( cfd < 0 )
continue ;
2024-09-28 13:55:35 +00:00
printf ( " %d \n " , clients ) ;
if ( clients < cfg . max_clients ) {
clients + + ;
2024-06-29 11:39:23 +00:00
if ( fork ( ) = = 0 ) {
close ( fd ) ;
2024-09-28 13:55:35 +00:00
/* Allocate buffers */
char * buf = malloc ( cfg . buf_size ) ;
char * filename = malloc ( cfg . name_len + 1 ) ;
if ( filename = = NULL | | buf = = NULL ) {
if ( filename ! = NULL )
free ( filename ) ;
2024-06-29 11:39:23 +00:00
2024-09-28 13:55:35 +00:00
if ( buf ! = NULL )
free ( buf ) ;
exit ( 1 ) ;
}
/* Start functions */
CLIENT_THREAD CT = { . fd = cfd , . buf = buf , . filename = filename } ;
if ( flag = = INPUT )
upload ( & CT ) ;
else if ( flag = = OUTPUT )
load ( & CT ) ;
/* Clean */
sleep ( 1 ) ;
free ( buf ) ;
close ( cfd ) ;
kill ( par , SIGUSR1 ) ;
exit ( 0 ) ;
2024-06-29 11:39:23 +00:00
}
}
2024-06-29 19:28:03 +00:00
else
2024-06-29 11:39:23 +00:00
send ( cfd , MSG1 , sizeof ( MSG1 ) , 0 ) ;
2024-06-29 19:28:03 +00:00
close ( cfd ) ;
2024-06-29 11:39:23 +00:00
}
}
int main ( int argc , char * * argv ) {
srand ( time ( NULL ) ) ;
signal ( SIGUSR1 , sig_handler ) ;
signal ( SIGINT , sig_handler ) ;
signal ( SIGTERM , sig_handler ) ;
signal ( SIGCHLD , SIG_IGN ) ;
2024-09-28 13:55:35 +00:00
signal ( SIGPIPE , SIG_IGN ) ;
2024-06-29 11:39:23 +00:00
2024-07-14 15:02:31 +00:00
int backlog = 1 ;
int iport = 8080 ;
int oport = 8081 ;
2024-09-15 13:45:31 +00:00
char daemon_flag = 0 ;
2024-07-14 15:02:31 +00:00
2024-06-29 11:39:23 +00:00
int opt ;
2024-09-15 13:45:31 +00:00
while ( ( opt = getopt ( argc , argv , " i:o:m:d:s:n:b:t:l:DV " ) ) ! = - 1 ) {
2024-06-29 11:39:23 +00:00
switch ( opt ) {
case ' i ' :
iport = atoi ( optarg ) ;
break ;
case ' o ' :
oport = atoi ( optarg ) ;
break ;
case ' m ' :
2024-09-15 13:45:31 +00:00
sscanf ( optarg , " %zu " , & cfg . max_clients ) ;
2024-06-29 11:39:23 +00:00
break ;
case ' d ' :
2024-09-15 13:45:31 +00:00
cfg . dir = optarg ;
2024-06-29 11:39:23 +00:00
break ;
case ' s ' :
2024-09-15 13:45:31 +00:00
sscanf ( optarg , " %zu " , & cfg . max_size ) ;
2024-06-29 11:39:23 +00:00
break ;
case ' n ' :
2024-09-15 13:45:31 +00:00
sscanf ( optarg , " %zu " , & cfg . name_len ) ;
2024-09-28 13:55:35 +00:00
if ( cfg . name_len < 4 ) {
fprintf ( stderr , " %s: name lenght can not be less than 4 \n " , argv [ 0 ] ) ;
return 1 ;
}
2024-06-29 11:39:23 +00:00
break ;
2024-06-30 22:04:57 +00:00
case ' b ' :
2024-09-15 13:45:31 +00:00
sscanf ( optarg , " %zu " , & cfg . buf_size ) ;
2024-06-30 22:04:57 +00:00
break ;
case ' t ' :
if ( strlen ( optarg ) > 256 ) {
2024-09-15 13:45:31 +00:00
fprintf ( stderr , " %s: MOTD very long \n " , argv [ 0 ] ) ;
2024-06-30 22:04:57 +00:00
return 1 ;
}
2024-09-15 13:45:31 +00:00
cfg . motd = optarg ;
2024-06-30 22:04:57 +00:00
break ;
2024-07-14 15:02:31 +00:00
case ' l ' :
backlog = atoi ( optarg ) ;
break ;
2024-09-15 13:45:31 +00:00
case ' D ' :
daemon_flag = 1 ;
break ;
2024-06-30 22:04:57 +00:00
case ' V ' :
puts ( " Version: " VERSION " \n Written under WTFPL License. " ) ;
return 0 ;
2024-06-29 11:39:23 +00:00
default :
2024-09-15 13:45:31 +00:00
printf ( " %s [iomdsnbtlDV] - Simple file sharing server \n \t -i NUM Input port \t default: %d \n \t -o NUM Output port \t default: %d \n \t -m NUM Max clients \t default: %zu \n \t -d STR Working dir \t default: %s \n \t -s NUM Max file size \t default: %zuMB \n \t -n NUM Filename length \t default: %zu \n \t -b NUM Buffer size \t default: %zuB \n \t -t STR Set MOTD \n \t -l listen() backlog \t default: %d \n \t -D Run as daemon \n \t -V Print version and license \n " , argv [ 0 ] , iport , oport , cfg . max_clients , cfg . dir , cfg . max_size , cfg . name_len , cfg . buf_size , backlog ) ;
2024-06-29 11:39:23 +00:00
return 0 ;
}
}
2024-09-15 13:45:31 +00:00
if ( daemon_flag )
if ( daemon ( 1 , 1 ) < 0 ) {
fprintf ( stderr , " %s: daemon: %s \n " , argv [ 0 ] , strerror ( errno ) ) ;
return 1 ;
}
if ( chdir ( cfg . dir ) < 0 ) {
fprintf ( stderr , " %s: chdir: %s \n " , argv [ 0 ] , strerror ( errno ) ) ;
2024-06-29 11:39:23 +00:00
return 1 ;
}
2024-07-14 15:02:31 +00:00
ifd = new_server ( iport , backlog ) ;
2024-06-29 11:39:23 +00:00
if ( ifd = = - 1 ) {
2024-09-15 13:45:31 +00:00
fprintf ( stderr , " %s: new_server: input socket: %s \n " , argv [ 0 ] , strerror ( errno ) ) ;
2024-06-29 11:39:23 +00:00
return 1 ;
}
2024-07-14 15:02:31 +00:00
ofd = new_server ( oport , backlog ) ;
2024-06-29 11:39:23 +00:00
if ( ofd = = - 1 ) {
2024-09-15 13:45:31 +00:00
fprintf ( stderr , " %s: new_server: output socket: %s \n " , argv [ 0 ] , strerror ( errno ) ) ;
2024-06-29 11:39:23 +00:00
return 1 ;
}
if ( fork ( ) = = 0 )
Proc ( ofd , OUTPUT ) ;
else
Proc ( ifd , INPUT ) ;
2024-07-14 15:02:31 +00:00
return 0 ;
2024-06-29 11:39:23 +00:00
}