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-07-14 15:02:31 +00:00
# define VERSION "1.2"
2024-06-30 22:04:57 +00:00
# endif
2024-06-29 11:39:23 +00:00
size_t max_clients = 15 ;
2024-06-30 22:04:57 +00:00
size_t max_size = 1 ;
size_t buf_size = 32000 ;
2024-06-29 11:39:23 +00:00
size_t name_len = 15 ;
2024-06-30 22:04:57 +00:00
char * dir = " . " ;
2024-06-29 11:39:23 +00:00
char * prog_name ;
2024-06-30 22:04:57 +00:00
char * motd ;
2024-06-29 11:39:23 +00:00
int ifd ;
2024-06-29 11:43:19 +00:00
size_t ifd_clients ;
2024-06-29 11:39:23 +00:00
int ofd ;
2024-06-29 11:43:19 +00:00
size_t ofd_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 )
goto NS_ERROR ;
tout . tv_sec = SEND_TIMEOUT ;
if ( setsockopt ( listen_fd , SOL_SOCKET , SO_SNDTIMEO , & tout , sizeof ( tout ) ) < 0 )
goto NS_ERROR ;
int reuse = 1 ;
if ( setsockopt ( listen_fd , SOL_SOCKET , SO_REUSEPORT , & reuse , sizeof ( reuse ) ) < 0 )
goto NS_ERROR ;
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 )
goto NS_ERROR ;
2024-07-14 15:02:31 +00:00
if ( listen ( listen_fd , backlog ) < 0 )
2024-06-29 11:39:23 +00:00
goto NS_ERROR ;
return listen_fd ;
NS_ERROR :
close ( listen_fd ) ;
return - 1 ;
}
char * rand_string ( void ) {
char abc [ ] = " asdfghjklzxcvbnmqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890 " ;
char * res = malloc ( name_len + 1 ) ;
if ( res = = NULL )
return NULL ;
2024-06-29 11:43:19 +00:00
for ( size_t i = 0 ; i < name_len ; i + + )
2024-06-29 11:39:23 +00:00
res [ i ] = abc [ rand ( ) % ( sizeof ( abc ) - 1 ) ] ;
res [ name_len ] = ' \0 ' ;
return res ;
}
char * rand_name ( void ) {
2024-07-01 07:26:22 +00:00
for ( int i = 0 ; i < 1000 ; i + + ) {
2024-06-29 11:39:23 +00:00
char * filename = rand_string ( ) ;
if ( filename = = NULL )
continue ;
struct stat sb ;
if ( stat ( filename , & sb ) < 0 )
return filename ;
free ( filename ) ;
}
2024-06-30 22:04:57 +00:00
return NULL ;
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-06-29 20:40:42 +00:00
ret = send ( fd , str , size , 0 ) ;
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
if ( ret < 0 & & ( errno ! = EAGAIN | | errno ! = EINTR ) )
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 ;
}
void upload ( int cfd , pid_t par ) {
int ret = - 1 ;
2024-06-29 20:40:42 +00:00
int ffd = - 1 ;
2024-06-30 22:04:57 +00:00
char * buf = NULL ;
2024-06-29 11:39:23 +00:00
2024-06-30 22:04:57 +00:00
/* Send and create filename */
2024-06-29 11:39:23 +00:00
char * filename = rand_name ( ) ;
2024-06-30 22:04:57 +00:00
if ( filename = = NULL ) {
send ( cfd , MSG4 , sizeof ( MSG4 ) , 0 ) ;
goto UPL_ERROR ;
}
2024-06-29 11:39:23 +00:00
if ( send ( cfd , filename , strlen ( filename ) , 0 ) < 0 )
goto UPL_ERROR ;
if ( send ( cfd , " \n " , 1 , 0 ) < 0 )
goto UPL_ERROR ;
ffd = open ( filename , O_CREAT | O_WRONLY , 0666 ) ;
if ( ffd < 0 )
goto UPL_ERROR ;
2024-06-30 22:04:57 +00:00
/* Sending */
buf = malloc ( buf_size + 1 ) ;
if ( buf = = NULL )
goto UPL_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-06-30 22:04:57 +00:00
ssize_t rbytes = recv ( cfd , buf , buf_size , 0 ) ;
2024-06-29 11:39:23 +00:00
if ( rbytes < 0 & & ( errno ! = EAGAIN | | errno ! = EINTR ) )
goto UPL_ERROR ;
else if ( rbytes = = 0 )
break ;
2024-06-29 20:40:42 +00:00
tbytes + = ( size_t ) rbytes ;
if ( tbytes / 1024 / 1024 > = max_size ) {
2024-06-29 11:39:23 +00:00
send_str ( cfd , MSG2 , sizeof ( MSG2 ) , WRITE ) ;
break ;
}
if ( send_str ( ffd , buf , rbytes , WRITE ) )
goto UPL_ERROR ;
}
ret = 0 ;
2024-06-30 22:04:57 +00:00
/* Clear */
2024-06-29 11:39:23 +00:00
UPL_ERROR :
2024-07-01 07:26:22 +00:00
if ( buf )
2024-06-30 22:04:57 +00:00
free ( buf ) ;
2024-06-29 20:40:42 +00:00
if ( ffd ! = - 1 )
2024-06-29 11:39:23 +00:00
close ( ffd ) ;
if ( ret = = - 1 )
2024-07-01 07:26:22 +00:00
fprintf ( stderr , " %s: input thread: %s: %s \n " , prog_name , ( filename ) ? filename : " NULL " , strerror ( errno ) ) ;
if ( filename )
free ( filename ) ;
2024-06-30 22:04:57 +00:00
close ( cfd ) ;
2024-06-29 11:39:23 +00:00
kill ( par , SIGUSR1 ) ;
exit ( 0 ) ;
}
void load ( int cfd , pid_t par ) {
int ret = - 1 ;
2024-06-29 20:40:42 +00:00
int ffd = - 1 ;
2024-06-30 22:04:57 +00:00
char * buf = NULL ;
2024-06-29 11:39:23 +00:00
2024-07-05 08:34:28 +00:00
/* For INFO cmd */
name_len + = 4 ;
2024-06-29 11:39:23 +00:00
/* Get filename */
char * filename = malloc ( name_len + 1 ) ;
if ( filename = = NULL )
goto LD_ERROR ;
ssize_t rbytes = recv ( cfd , filename , name_len , 0 ) ;
if ( rbytes < = 0 ) {
if ( rbytes = = 0 )
ret = 0 ;
goto LD_ERROR ;
}
filename [ rbytes ] = ' \0 ' ;
char * ptr = strchr ( filename , ' \n ' ) ;
if ( ptr )
* ptr = ' \0 ' ;
if ( strstr ( filename , " . " ) | | strstr ( filename , " / " ) )
goto LD_ERROR ;
/* INFO */
if ( ! strcmp ( filename , " INFO " ) ) {
2024-06-30 22:04:57 +00:00
char msg [ 336 ] ;
int ret2 = snprintf ( msg , sizeof ( msg ) , " Max file size: %zuMB \n Max clients in thread: %zu \n Buffer size: %zu \n MOTD: %s \n " , max_size , max_clients , buf_size , ( motd ) ? motd : " " ) ;
2024-06-29 11:39:23 +00:00
if ( send_str ( cfd , msg , ( ssize_t ) ret2 , SEND ) )
goto LD_ERROR ;
ret = 0 ;
goto LD_ERROR ;
}
2024-06-30 22:04:57 +00:00
/* Open */
2024-06-29 11:39:23 +00:00
ffd = open ( filename , O_RDONLY ) ;
if ( ffd < 0 ) {
2024-06-30 22:04:57 +00:00
send_str ( cfd , MSG3 , sizeof ( MSG3 ) , SEND ) ;
2024-06-29 11:39:23 +00:00
goto LD_ERROR ;
}
2024-06-30 22:04:57 +00:00
/* Sending */
buf = malloc ( buf_size + 1 ) ;
if ( buf = = NULL )
goto LD_ERROR ;
2024-06-29 11:39:23 +00:00
while ( 1 ) {
2024-06-30 22:04:57 +00:00
rbytes = read ( ffd , buf , buf_size ) ;
2024-06-29 11:39:23 +00:00
if ( rbytes < 0 & & ( errno ! = EAGAIN | | errno ! = EINTR ) )
goto LD_ERROR ;
else if ( rbytes = = 0 )
break ;
if ( send_str ( cfd , buf , rbytes , SEND ) )
goto LD_ERROR ;
}
ret = 0 ;
2024-06-30 22:04:57 +00:00
/* Clear */
2024-06-29 11:39:23 +00:00
LD_ERROR :
2024-07-01 07:26:22 +00:00
if ( buf )
2024-06-30 22:04:57 +00:00
free ( buf ) ;
2024-06-29 20:40:42 +00:00
if ( ffd ! = - 1 )
2024-06-29 11:39:23 +00:00
close ( ffd ) ;
if ( ret = = - 1 )
2024-07-01 07:26:22 +00:00
fprintf ( stderr , " %s: output thread: %s: %s \n " , prog_name , ( filename ) ? filename : " NULL " , strerror ( errno ) ) ;
if ( filename )
free ( filename ) ;
2024-06-30 22:04:57 +00:00
sleep ( 1 ) ;
close ( cfd ) ;
2024-06-29 11:39:23 +00:00
kill ( par , SIGUSR2 ) ;
exit ( 0 ) ;
}
void sig_handler ( int sig ) {
2024-07-01 10:06:07 +00:00
if ( sig = = SIGUSR1 ) {
if ( ifd_clients )
ifd_clients - - ;
}
2024-06-29 11:39:23 +00:00
2024-07-01 10:06:07 +00:00
else if ( sig = = SIGUSR2 ) {
if ( ofd_clients )
ofd_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 ;
if ( flag = = INPUT & & ifd_clients < max_clients ) {
ifd_clients + + ;
if ( fork ( ) = = 0 ) {
close ( fd ) ;
upload ( cfd , par ) ;
}
}
else if ( flag = = OUTPUT & & ofd_clients < max_clients ) {
ofd_clients + + ;
if ( fork ( ) = = 0 ) {
close ( fd ) ;
load ( cfd , par ) ;
}
}
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 ) ) ;
prog_name = argv [ 0 ] ;
signal ( SIGUSR1 , sig_handler ) ;
signal ( SIGUSR2 , sig_handler ) ;
signal ( SIGINT , sig_handler ) ;
signal ( SIGTERM , sig_handler ) ;
signal ( SIGCHLD , SIG_IGN ) ;
2024-07-14 15:02:31 +00:00
int backlog = 1 ;
int iport = 8080 ;
int oport = 8081 ;
2024-06-29 11:39:23 +00:00
int opt ;
2024-07-14 15:02:31 +00:00
while ( ( opt = getopt ( argc , argv , " i:o:m:d:s:n:b:t:l:V " ) ) ! = - 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 ' :
sscanf ( optarg , " %zu " , & max_clients ) ;
break ;
case ' d ' :
dir = optarg ;
break ;
case ' s ' :
sscanf ( optarg , " %zu " , & max_size ) ;
break ;
case ' n ' :
sscanf ( optarg , " %zu " , & name_len ) ;
break ;
2024-06-30 22:04:57 +00:00
case ' b ' :
sscanf ( optarg , " %zu " , & buf_size ) ;
break ;
case ' t ' :
if ( strlen ( optarg ) > 256 ) {
fprintf ( stderr , " %s: MOTD very long \n " , prog_name ) ;
return 1 ;
}
motd = optarg ;
break ;
2024-07-14 15:02:31 +00:00
case ' l ' :
backlog = atoi ( optarg ) ;
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-07-14 15:02:31 +00:00
printf ( " %s [iomdsnbtlV] - 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 -V Print version and license \n " , prog_name , iport , oport , max_clients , dir , max_size , name_len , buf_size , backlog ) ;
2024-06-29 11:39:23 +00:00
return 0 ;
}
}
if ( chdir ( dir ) < 0 ) {
fprintf ( stderr , " %s: chdir: %s \n " , prog_name , strerror ( errno ) ) ;
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 ) {
fprintf ( stderr , " %s: new_server: input socket: %s \n " , prog_name , strerror ( errno ) ) ;
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 ) {
fprintf ( stderr , " %s: new_server: output socket: %s \n " , prog_name , strerror ( errno ) ) ;
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
}