#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* err, errx, warn */ #include /* errno, ENOENT, ENOTSOCK */ #include /* openat */ #include /* memcmp, strlen */ #include /* recv, send, socketpair */ #include /* waitpid */ #include /* close, fork, fsync, ftruncate, lseek, unlink, write */ /* V této úloze se vrátíme k šifrování technikou „one-time pad,“ * tentokrát v kontextu socketů. Vaším úkolem je naprogramovat * jednoduchý šifrovací server. Procedura ‹otpd› bude přijímat * datagramy, každý z nich zašifruje, a pošle zpátky odesílateli. * * Bajty klíče bude postupně načítat ze souboru zadaného popisovačem * ‹key_fd› (použije vždy právě tolik bajtů, kolik je potřeba pro * zašifrování daného datagramu). Přitom ⟦i⟧-tý bajt zašifrovaného * datagramu se vypočte jako XOR ⟦i⟧-tého bajtu vstupního datagramu * a ⟦i⟧-tého bajtu klíče. * * Odpověď bude sestávat ze dvou částí: * * • jednobajtový indikátor úspěchu, * • zašifrovaný obsah přijatého datagramu (pouze v případě * úspěchu). * * Není-li možné datagram zašifrovat (chyba čtení ze souboru, * nedostatečný počet bajtů, atp.), bude mít indikátor úspěchu * hodnotu 0, jinak 1. * * Dojde-li k chybě komunikace na socketu, procedura ihned skončí * s návratovou hodnotou -1, jinak bude výsledkem počet úspěšně * zašifrovaných datagramů. Dojde-li k chybě při čtení klíče, * procedura pokračuje ve výpočtu. * * Parametr ‹n› určuje maximální počet datagramů, které budou * zpracovány (po jejím dosažení procedura skončí). Je-li přijat * datagram delší než 1023 bajtů, přebytečné bajty ignorujte. */ int otpd( int sock_fd, int key_fd, int n ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ static void unlink_if_exists( const char *file ) { if ( unlink( file ) == -1 && errno != ENOENT ) err( 2, "unlink" ); } static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int create_file( const char *name ) { unlink_if_exists( name ); int fd; if ( ( fd = openat( AT_FDCWD, name, O_CREAT | O_TRUNC | O_RDWR, 0666 ) ) == -1 ) err( 2, "creating %s", name ); return fd; } static int open_and_write( const char *name, const char *str ) { int fd = create_file( name ); if ( write( fd, str, strlen( str ) ) == -1 ) err( 2, "writing file %s", name ); if ( lseek( fd, 0, SEEK_SET ) == ( off_t ) -1 ) err( 2, "leek on %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 void recv_or_die( int fd, void *buffer, int nbytes ) { int recvd = recv( fd, buffer, nbytes, 0 ); if ( recvd == -1 ) err( 1, "receiving %d bytes", nbytes ); if ( recvd != nbytes ) errx( 1, "unexpected short receive: %d/%d sent", recvd, nbytes ); } 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 sender( int client_fd, int file_fd ) { unsigned char buffer[ 1024 ]; /* case 1 */ unsigned char exp1[] = { 1 }; send_or_die( client_fd, NULL, 0 ); recv_or_die( client_fd, buffer, 1 ); assert( memcmp( buffer, exp1, sizeof( exp1 ) ) == 0 ); /* case 2 */ unsigned char exp2[] = { 0x01, 0x07, 0x0d, 0x00, 0x00, 0x13, 0x1a, 0x04, 0x53, 0x00, 0x54, 0x16, 0x13, 0x00, 0x0c, 0x47, 0x06, 0x0d, 0x00, 0x17, 0x42, 0x00 }; send_or_die( client_fd, "The grass was greener", 21 ); recv_or_die( client_fd, buffer , 22 ); assert( memcmp( buffer, exp2, sizeof( exp2 ) ) == 0 ); unsigned char exp3[] = { 0x01, 0x31, 0x48, 0x16, 0x48, 0x05, 0x07, 0xe, 0x06, 0x13, 0x00, 0x15, 0x13, 0x1a, 0x47, 0x0a, 0x06, 0x3a, 0x02, 0x0d, 0x54, 0x11, 0x1a }; send_or_die( client_fd, "The light was brighter", 22 ); recv_or_die( client_fd, buffer , 23 ); assert( memcmp( buffer, exp3, sizeof( exp3 ) ) == 0 ); unsigned char exp4[] = { 0x01, 0x31, 0x48, 0x16, 0x54, 0x15, 0x13, 0x00, 0x58, 0x45, 0x54, 0x1f, 0x04, 0x0a, 0x07, 0x01, 0x12, 0x45, 0x16, 0x1c, 0x0c, 0x1c }; send_or_die( client_fd, "The taste was sweeter", 21 ); recv_or_die( client_fd, buffer , 22 ); assert( memcmp( buffer, exp4, sizeof( exp4 ) ) == 0 ); const char *exp5 = "\0The nights of wonder"; send_or_die( client_fd, exp5 + 1, 20 ); recv_or_die( client_fd, buffer , 1 ); assert( buffer[ 0 ] == 0 ); close_or_warn( file_fd, "zt.p3_key" ); } int main( void ) { unlink_if_exists( "zt.p3_key" ); int fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_DGRAM, 0, fds ) == -1 ) err( 1, "socketpair" ); int server_fd = fds[ 0 ], client_fd = fds[ 1 ]; const char *key = "See the stars, they're shining bright" "See the stars, they're shin"; int file_fd = open_and_write( "zt.p3_key", key ); int client_pid = fork(); if ( client_pid == -1 ) err( 1, "fork" ); if ( client_pid == 0 ) { close_or_warn( server_fd, "server end of a socket" ); sender( client_fd, file_fd ); close_or_warn( client_fd, "client end of a socket" ); return 0; } close_or_warn( client_fd, "client end of a socket" ); /* case 1 */ assert( otpd( server_fd, file_fd, 1 ) == 1 ); /* case 2 */ assert( otpd( server_fd, file_fd, 4 ) == 3 ); assert( reap( client_pid ) == 0 ); close_or_warn( file_fd, "zt.p3_key" ); close_or_warn( server_fd, "server end of a socket" ); unlink_if_exists( "zt.p3_key" ); return 0; }