#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* err, errx, warn */ #include /* errno, EBADF, ENOENT */ #include /* bool */ #include /* exit */ #include /* memcmp */ #include /* send, socketpair */ #include /* open */ #include /* open */ #include /* waitpid */ #include /* alarm, close, fork, unlink */ /* Naprogramujte proceduru ‹window›, která obdrží: * * • ‹sock_fd›, popisovač datagramového socketu, * • ‹file_fd›, popisovač obyčejného souboru. * * Na ‹sock_fd› budou přicházet datagramy, složené z: * * 1. čtyřbajtové hlavičky: * a. offset fragmentu, * b. délka fragmentu, * c. délka zprávy, * d. nulový bajt, * 2. obsah fragmentu. * * Procedura ‹window› sestaví zprávu ze získaných fragmentů, zapíše * ji do souboru ‹file_fd› a skončí s návratovou hodnotou: * * • 0 proběhlo-li vše v pořádku, * • -1 nastane-li systémová chyba, * • -2 nastane-li chyba protokolu. * * Možné chyby protokolu: * * • fragmenty se překrývají, * • fragmenty se neshodují na délce zprávy, * • fragment přesahuje za konec zprávy, * • obsah fragmentu nemá správnou délku, * • hlavička nemá správný formát. */ int window( int sock_fd, int file_fd ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ static void unlink_if_exists( const char* file ) { if ( unlink( file ) == -1 && errno != ENOENT ) err( 2, "unlinking %s", file ); } static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int open_or_die( const char *name, int mode ) { int fd = open( name, O_CREAT | mode, 0666 ); if ( fd == -1 ) err( 2, "opening %s", name ); return fd; } static void send_or_die( int fd, const void *buffer, int nbytes ) { int bytes_sent = send( fd, buffer, nbytes, 0 ); if ( bytes_sent == -1 ) err( 1, "sending %d bytes", nbytes ); if ( bytes_sent != nbytes ) errx( 1, "unexpected short send: %d/%d sent", bytes_sent, nbytes ); } static int reap( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 2, "wait" ); return WIFEXITED( status ) ? WEXITSTATUS( status ) : -1; } static bool file_contains( const char *file, const char *contents, int size ) { int fd = open_or_die( file, O_RDONLY ); char buf[ 256 ]; ssize_t bytes_read = read( fd, buf, sizeof( buf ) ); if ( bytes_read == -1 ) err( 1, "reading from %s", file ); close_or_warn( fd, file ); return memcmp( buf, contents, size ) == 0; } static int fork_server( int *client_fd, void( *server )( int ), int close_fd ) { int fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_DGRAM, 0, fds ) == -1 ) err( 1, "socketpair" ); int server_fd = fds[ 0 ]; *client_fd = fds[ 1 ]; int server_pid = fork(); if ( server_pid == -1 ) err( 1, "fork" ); if ( server_pid > 0 ) { close_or_warn( server_fd, "server end of the socket" ); return server_pid; } close_or_warn( close_fd, "client state file" ); close_or_warn( *client_fd, "client end of the socket" ); alarm( 5 ); server( server_fd ); close_or_warn( server_fd, "server end of the socket" ); exit( 0 ); } /* Correct protocol. */ static void server_1( int fd ) { const char *frag[] = { "\x1d\x1f\x47\0Never cared for what they know\n", "\x3c\x0b\x47\0But I know\n", "\x00\x1d\x47\0Never cared for what they do\n" }; const char size[] = { 35, 15, 33 }; for ( int i = 0; i < 3; ++ i ) send_or_die( fd, frag[ i ], size[ i ] ); } /* Missing zero byte on the 3rd position. */ static void server_2( int fd ) { char *data = "\x00\x01\x01\x2az"; send_or_die( fd, data, 5 ); } /* Overlapping fragments. */ static void server_3( int fd ) { char *data1 = "\x00\x05\x80\0Zivot"; send_or_die( fd, data1, 9 ); char *data2 = "\x01\x02\x80\0je"; send_or_die( fd, data2, 6 ); } /* Different message size. */ static void server_4( int fd ) { char *data1 = "\x00\x05\x80\0Zivot"; send_or_die( fd, data1, 9 ); char *data2 = "\x06\x02\x2a\0je"; send_or_die( fd, data2, 6 ); } /* Fragment is longer than message size. */ static void server_5( int fd ) { char *data = "\x00\x05\x02\0Zivot"; send_or_die( fd, data, 9 ); } static void server_6( int fd ) { char *data = "\x00\x05\x05\0Zivot"; send_or_die( fd, data, 9 ); } int main( void ) { const char *file_name = "zt.r2_out"; int client_fd, server_pid; int file_fd = open_or_die( file_name, O_TRUNC | O_WRONLY ); /* Test case 1. */ const char *out = "Never cared for what they do\n" "Never cared for what they know\n" "But I know\n"; server_pid = fork_server( &client_fd, server_1, file_fd ); assert( window( client_fd, file_fd ) == 0 ); assert( reap( server_pid ) == 0 ); assert( file_contains( file_name, out, strlen( out ) ) ); close_or_warn( client_fd, "client side of a socket" ); /* Test case 2. */ server_pid = fork_server( &client_fd, server_2, file_fd ); assert( window( client_fd, file_fd ) == -2 ); assert( reap( server_pid ) == 0 ); close_or_warn( client_fd, "client side of a socket" ); /* Test case 3. */ server_pid = fork_server( &client_fd, server_3, file_fd ); assert( window( client_fd, file_fd ) == -2 ); assert( reap( server_pid ) == 0 ); close_or_warn( client_fd, "client side of a socket" ); /* Test case 4. */ server_pid = fork_server( &client_fd, server_4, file_fd ); assert( window( client_fd, file_fd ) == -2 ); assert( reap( server_pid ) == 0 ); close_or_warn( client_fd, "client side of a socket" ); /* Test case 5. */ server_pid = fork_server( &client_fd, server_5, file_fd ); assert( window( client_fd, file_fd ) == -2 ); assert( reap( server_pid ) == 0 ); close_or_warn( client_fd, "client side of a socket" ); /* Test case 6. */ server_pid = fork_server( &client_fd, server_6, file_fd ); close_or_warn( file_fd, file_name ); assert( window( client_fd, file_fd ) == -1 ); assert( reap( server_pid ) == 0 ); close_or_warn( client_fd, "client side of a socket" ); /* Test case 7. */ file_fd = open_or_die( file_name, O_TRUNC | O_WRONLY ); assert( window( client_fd, file_fd ) == -1 ); assert( errno == EBADF ); close_or_warn( file_fd, file_name ); unlink_if_exists( file_name ); return 0; }