#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* uint8_t */ #include /* alarm, close, fork, read, write */ /* Podprogram ‹guess_server› bude realizovat jednoduchý proudový * protokol, kdy se klient snaží uhodnout řetězec předem neznámé * délky sestavený z nenulových bajtů a server odpovídá nápovědami. * Každý pokus ze strany klienta je ukončen jedním nulovým bajtem. * * Odpověď serveru na každý pokus sestává ze dvou částí: * * 1. jednobajtový indikátor: * ◦ 0x00 = délka hledaného řetězce je stejná, * ◦ 0x01 = hledaný řetězec je delší, * ◦ 0xff = hledaný řetězec je kratší, * 2. počet bitů ve společném prefixu, ve kterých se hledaný * řetězec liší; počet je zakódovaný jako čtyřbajtové číslo bez * znaménka, odeslané nejvýznamnějším bajtem napřed. * * Odpověď sestavená z pěti nulových bajtů tedy znamená, že klient * řetězec uhodl. Podprogram ‹guess_server› vrátí hodnotu 0, * proběhlo-li vše v pořádku a klient ukončil spojení, nebo -1 * v případě chyby. */ int guess_server( int sock_fd, const char *word ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* err, errx, warn */ #include /* exit */ #include /* memcmp */ #include /* socketpair */ #include /* waitpid */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static pid_t fork_server( int sock_fds[ 2 ], const char *word ) { int server_fd = sock_fds[ 0 ], client_fd = sock_fds[ 1 ]; pid_t pid = fork(); if ( pid == -1 ) err( 2, "fork" ); if ( pid > 0 ) return pid; alarm( 5 ); close_or_warn( client_fd, "client side of a socket" ); int rv = guess_server( server_fd, word ); close_or_warn( server_fd, "server side of a socket" ); exit( rv ); } static int reap( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 2, "wait" ); if ( WIFEXITED( status ) ) return WEXITSTATUS( status ); else return -1; } static void write_or_die( int fd, const char *buffer, int nbytes ) { int bytes_written = write( fd, buffer, nbytes ); if ( bytes_written == -1 ) err( 1, "writing %d bytes", nbytes ); if ( bytes_written != nbytes ) errx( 1, "unexpected short write: %d/%d written", bytes_written, nbytes ); } static void read_or_die( int fd, uint8_t *where, int bytes ) { int actually = read( fd, where, bytes ); if ( actually == -1 ) err( 1, "reading %d bytes", bytes ); if ( actually != bytes ) errx( 1, "unexpected short read: %d/%d\n", actually, bytes ); } int main( void ) { int sock_fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_STREAM, 0, sock_fds ) == -1 ) err( 1, "socketpair" ); int server_fd = sock_fds[ 0 ], client_fd = sock_fds[ 1 ]; pid_t pid = fork_server( sock_fds, "Lights will guide you home" ); close_or_warn( server_fd, "server side of a socket" ); uint8_t reply[ 5 ]; write_or_die( client_fd, "Nobody said it was easy", 24 ); read_or_die( client_fd, reply, 5 ); assert( memcmp( reply, "\x01\x00\x00\x00\x2d", 5 ) == 0 ); write_or_die( client_fd, "'Cause in a sky, 'cause in a sky full of stars", 47 ); read_or_die( client_fd, reply, 5 ); assert( memcmp( reply, "\xff\x00\x00\x00\x46", 5 ) == 0 ); write_or_die( client_fd, "When you try your best, but you don't succeed", 46 ); read_or_die( client_fd, reply, 5 ); assert( memcmp( reply, "\xff\x00\x00\x00\x45", 5 ) == 0 ); write_or_die( client_fd, "Lights", 7 ); read_or_die( client_fd, reply, 5 ); assert( memcmp( reply, "\x01\x00\x00\x00\x00", 5 ) == 0 ); write_or_die( client_fd, "Lights will guide", 18 ); read_or_die( client_fd, reply, 5 ); assert( memcmp( reply, "\x01\x00\x00\x00\x00", 5 ) == 0 ); write_or_die( client_fd, "Lights will guide you home", 27 ); read_or_die( client_fd, reply, 5 ); assert( memcmp( reply, "\x00\x00\x00\x00\x00", 5 ) == 0 ); close_or_warn( client_fd, "client side of a socket" ); assert(( reap( pid ) == 0 )); return 0; }