From e8ccc0ec93c27752d44a7c2f955b52d5e5050871 Mon Sep 17 00:00:00 2001 From: Fabio Buracchi <45599613+buracchi@users.noreply.github.com> Date: Mon, 8 Apr 2024 01:03:14 +0200 Subject: [PATCH] Update --- .gitignore | 307 + CMakeLists.txt | 2 + client/CMakeLists.txt | 19 +- client/src/cmdline.c | 30 - client/src/cmdline.h | 25 - client/src/connector.c | 98 - client/src/connector.h | 16 - client/src/main.c | 618 +- client/src/service_handler.c | 44 - client/src/service_handler.h | 17 - docs/Dettagli/Go-Back N.txt | 26 + docs/Dettagli/Traccia.txt | 28 + docs/Model.mdj | 9179 ----------------- docs/{ => RFC}/rfcs-compliance-metadata.csv | 92 +- docs/{ => RFC}/rfcs-compliance-results.md | 92 +- docs/{ => RFC}/rfcs_compliance-test.py | 0 docs/chapters/ambiente_sviluppo.tex | 4 + docs/chapters/architettura.tex | 10 + docs/chapters/architettura/client.tex | 1 + .../architettura/perdita_pacchetti.tex | 1 + docs/chapters/architettura/server.tex | 1 + docs/chapters/architettura/tftp.tex | 1 + docs/chapters/architettura/timeout.tex | 1 + docs/chapters/esempi_funzionamento.tex | 1 + docs/chapters/implementazione.tex | 3 + docs/chapters/implementazione/client.tex | 0 docs/chapters/implementazione/server.tex | 0 docs/chapters/limitazioni.tex | 3 + docs/chapters/manuale.tex | 3 + docs/chapters/prestazioni.tex | 5 + docs/chapters/traccia.tex | 81 + docs/documentazione.tex | 49 + docs/header.tex | 13 + docs/media/image1.png | Bin 0 -> 10000 bytes docs/media/image2.png | Bin 0 -> 6835 bytes docs/rfc1350_req.txt | 198 - docs/rfc2090_req.txt | 158 - docs/rfc2347_req.txt | 123 - docs/rfc2348_req.txt | 44 - docs/rfc2349_req.txt | 96 - docs/rfc7440_req.txt | 101 - docs/style.tex | 152 + docs/title.tex | 40 + logger/CMakeLists.txt | 9 + logger/include/logger.h | 57 + logger/src/logger.c | 106 + logger/test/CMakeLists.txt | 7 + logger/test/test_logger.c | 17 + network_utils/CMakeLists.txt | 6 - network_utils/include/inet_utils.h | 4 +- network_utils/src/inet_utils.c | 58 +- server/CMakeLists.txt | 36 +- server/src/acceptor.c | 285 - server/src/acceptor.h | 45 - server/src/cmdline.c | 20 - server/src/cmdline.h | 24 - server/src/dispatcher.c | 80 - server/src/dispatcher.h | 24 - server/src/main.c | 228 +- server/src/server_stats.c | 40 + server/src/server_stats.h | 34 + server/src/service_handler.c | 100 - server/src/service_handler.h | 46 - server/src/service_handler_factory.h | 17 - server/src/session_stats.c | 26 + server/src/session_stats.h | 36 + server/src/tftp_handler.c | 716 ++ server/src/tftp_handler.h | 98 + server/src/tftp_server.c | 484 + server/src/tftp_server.h | 71 + server/src/uring_thread_pool.c | 106 + server/src/uring_thread_pool.h | 35 + server/test/CMakeLists.txt | 14 +- server/test/test.c | 31 + server/test/test_acceptor.c | 59 - tftp/CMakeLists.txt | 10 + tftp/include/tftp.h | 174 + tftp/src/tftp.c | 351 + tftp/test/CMakeLists.txt | 7 + tftp/test/test.c | 5 + vcpkg.json | 2 +- 81 files changed, 4040 insertions(+), 11110 deletions(-) delete mode 100644 client/src/cmdline.c delete mode 100644 client/src/cmdline.h delete mode 100644 client/src/connector.c delete mode 100644 client/src/connector.h delete mode 100644 client/src/service_handler.c delete mode 100644 client/src/service_handler.h create mode 100644 docs/Dettagli/Go-Back N.txt create mode 100644 docs/Dettagli/Traccia.txt delete mode 100644 docs/Model.mdj rename docs/{ => RFC}/rfcs-compliance-metadata.csv (69%) rename docs/{ => RFC}/rfcs-compliance-results.md (76%) rename docs/{ => RFC}/rfcs_compliance-test.py (100%) create mode 100644 docs/chapters/ambiente_sviluppo.tex create mode 100644 docs/chapters/architettura.tex create mode 100644 docs/chapters/architettura/client.tex create mode 100644 docs/chapters/architettura/perdita_pacchetti.tex create mode 100644 docs/chapters/architettura/server.tex create mode 100644 docs/chapters/architettura/tftp.tex create mode 100644 docs/chapters/architettura/timeout.tex create mode 100644 docs/chapters/esempi_funzionamento.tex create mode 100644 docs/chapters/implementazione.tex create mode 100644 docs/chapters/implementazione/client.tex create mode 100644 docs/chapters/implementazione/server.tex create mode 100644 docs/chapters/limitazioni.tex create mode 100644 docs/chapters/manuale.tex create mode 100644 docs/chapters/prestazioni.tex create mode 100644 docs/chapters/traccia.tex create mode 100644 docs/documentazione.tex create mode 100644 docs/header.tex create mode 100644 docs/media/image1.png create mode 100644 docs/media/image2.png delete mode 100644 docs/rfc1350_req.txt delete mode 100644 docs/rfc2090_req.txt delete mode 100644 docs/rfc2347_req.txt delete mode 100644 docs/rfc2348_req.txt delete mode 100644 docs/rfc2349_req.txt delete mode 100644 docs/rfc7440_req.txt create mode 100644 docs/style.tex create mode 100644 docs/title.tex create mode 100644 logger/CMakeLists.txt create mode 100644 logger/include/logger.h create mode 100644 logger/src/logger.c create mode 100644 logger/test/CMakeLists.txt create mode 100644 logger/test/test_logger.c delete mode 100644 server/src/acceptor.c delete mode 100644 server/src/acceptor.h delete mode 100644 server/src/cmdline.c delete mode 100644 server/src/cmdline.h delete mode 100644 server/src/dispatcher.c delete mode 100644 server/src/dispatcher.h create mode 100644 server/src/server_stats.c create mode 100644 server/src/server_stats.h delete mode 100644 server/src/service_handler.c delete mode 100644 server/src/service_handler.h delete mode 100644 server/src/service_handler_factory.h create mode 100644 server/src/session_stats.c create mode 100644 server/src/session_stats.h create mode 100644 server/src/tftp_handler.c create mode 100644 server/src/tftp_handler.h create mode 100644 server/src/tftp_server.c create mode 100644 server/src/tftp_server.h create mode 100644 server/src/uring_thread_pool.c create mode 100644 server/src/uring_thread_pool.h create mode 100644 server/test/test.c delete mode 100644 server/test/test_acceptor.c create mode 100644 tftp/CMakeLists.txt create mode 100644 tftp/include/tftp.h create mode 100644 tftp/src/tftp.c create mode 100644 tftp/test/CMakeLists.txt create mode 100644 tftp/test/test.c diff --git a/.gitignore b/.gitignore index 24f0722..faa81e5 100644 --- a/.gitignore +++ b/.gitignore @@ -365,3 +365,310 @@ FodyWeavers.xsd /.idea docs/build + +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync +*.rubbercache +rubber.cache + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hypdoc +*.hd + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib diff --git a/CMakeLists.txt b/CMakeLists.txt index 9970a7e..e1daf22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ endif () # Subproject #add_subdirectory("ftcp") +add_subdirectory("logger") +add_subdirectory("tftp") add_subdirectory("network_utils") add_subdirectory("server") add_subdirectory("client") diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 1a1efd0..8565ce8 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,15 +1,6 @@ -find_package(buracchi-argparser CONFIG REQUIRED) -find_package(buracchi-common CONFIG REQUIRED) -find_package(libevent CONFIG REQUIRED) - -add_executable(client - "src/main.c" - "src/cmdline.c" - "src/connector.c" - "src/service_handler.c") -target_link_libraries(client PRIVATE buracchi::argparser::argparser) -target_link_libraries(client PRIVATE buracchi::common::logger) -target_link_libraries(client PRIVATE buracchi::common::utilities) -target_link_libraries(client PRIVATE libevent::core) -target_link_libraries(client PRIVATE network_utils) +add_executable(client src/main.c) target_link_libraries(client INTERFACE $) +target_link_libraries(client PRIVATE network_utils) +target_link_libraries(client PRIVATE logger) +target_link_libraries(client PRIVATE network_utils) +target_link_libraries(client PRIVATE tftp) diff --git a/client/src/cmdline.c b/client/src/cmdline.c deleted file mode 100644 index 0f81853..0000000 --- a/client/src/cmdline.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "cmdline.h" - -#include - -#include -#include - -// Parses command-line arguments and fills in the fields of a cmdline_args struct -// Returns 0 on success, 1 on failure -int parse_cmdline_args(int argc, const char *argv[static const argc + 1], cmdline_args_t *args) { - argparser_t parser; - argparser_t list_parser; - argparser_t get_parser; - argparser_t put_parser; - try(parser = argparser_init(argc, argv), NULL, fail); - argparser_set_description(parser, "File transfer client."); - try(argparser_add_argument(parser, &args->host, { .name = "host", .help = "the file transfer server address" }), 1, fail); - try(argparser_add_argument(parser, &args->port, { .flag = "p", .long_flag = "port", .default_value = "1234", .help = "specify the listening port number (the default value is \"1234\")" }), 1, fail); - argparser_set_subparsers_options(parser, (struct argparser_subparsers_options){ .required = true, .title = "commands" }); - try(list_parser = argparser_add_subparser(parser, &args->command, "list", "List available file on the remote server"), NULL, fail); - try(get_parser = argparser_add_subparser(parser, &args->command, "get", "Download a file from the remote server"), NULL, fail); - try(argparser_add_argument(get_parser, &args->filename, { .name = "filename", .help = "The file to download" }), 1, fail); - try(put_parser = argparser_add_subparser(parser, &args->command, "put", "Upload a file to the remote server"), NULL, fail); - try(argparser_add_argument(put_parser, &args->filename, { .name = "filename", .help = "The file to upload" }), 1, fail); - try(argparser_parse_args(parser), 1, fail); - argparser_destroy(parser); - return 0; -fail: - return 1; -} diff --git a/client/src/cmdline.h b/client/src/cmdline.h deleted file mode 100644 index f1ba991..0000000 --- a/client/src/cmdline.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -typedef struct cmdline_args { - char *host; // IP address or hostname of the server to connect to - char *port; // port number to use for the connection - char const *command; // command to send to the server (e.g. "list", "get", "put") - char *filename; // file to send or receive (used with the "get" and "put" commands) - uint32_t window_size; // size of the dispatch window to use for the Go-Back N protocol - long timeout_duration; // duration of the timeout to use for the Go-Back N protocol, in milliseconds - bool adaptive_timeout; // flag to use an adaptive timeout calculated dynamically based on network delays - double loss_probability; // probability of packet loss to simulate - int verbose_level; // verbose level to output additional information -} cmdline_args_t; - -/** - * Parses command-line arguments and fills in the fields of a `cmdline_args` struct. - * @param argc The number of command-line arguments passed to the program. - * @param argv An array of `char*` representing the command-line arguments passed to the program. - * @param args A pointer to a `cmdline_args` struct to store the parsed arguments. - * @return 0 on success, 1 on failure. - */ -int parse_cmdline_args(int argc, const char *argv[static const argc + 1], cmdline_args_t *args); diff --git a/client/src/connector.c b/client/src/connector.c deleted file mode 100644 index bcc2ef0..0000000 --- a/client/src/connector.c +++ /dev/null @@ -1,98 +0,0 @@ -#include "connector.h" - -#include -#include - -#include "inet_utils.h" - -static struct evutil_addrinfo *get_server_addrinfo_list(char const *node, char const *service); -static int attempt_connection(evutil_socket_t *sockfd, struct addrinfo *addrinfo); - -// Attempts to connect to a server at the specified host and port -// Returns 0 on success, 1 on failure -extern int connector_connect(evutil_socket_t *sockfd, char const *host, char const *port) { - struct evutil_addrinfo *server_addrinfo_list; - cmn_logger_log_info("Establishing server connection."); - try(server_addrinfo_list = get_server_addrinfo_list(host, port), NULL, fail); - for (struct addrinfo *addrinfo = server_addrinfo_list; addrinfo != NULL; addrinfo = addrinfo->ai_next) { - if (attempt_connection(sockfd, addrinfo)) { - continue; - } - break; - } - freeaddrinfo(server_addrinfo_list); - if (*sockfd == EVUTIL_INVALID_SOCKET) { - cmn_logger_log_error("Connection to the server refused."); - return 1; - } - print_inet_address_detail(*sockfd); - return 0; -fail: - return 1; -} - -// Resolves the specified node and service and returns a list of addrinfo -// structures containing information about the resulting addresses. -// On error, returns NULL. -static struct evutil_addrinfo *get_server_addrinfo_list(char const *node, char const *service) { - struct evutil_addrinfo *result = NULL; - struct evutil_addrinfo hints = { - .ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED, - .ai_family = AF_INET6, - .ai_socktype = SOCK_DGRAM, - .ai_protocol = 0, - .ai_addrlen = 0, - .ai_addr = NULL, - .ai_canonname = NULL, - .ai_next = NULL - }; - int gai_ret; - if ((gai_ret = getaddrinfo(node, service, &hints, &result))) { - // According to the POSIX 2008 standard, if getaddrinfo returns - // a non-zero error value, the content of the res parameter - // is undefined. - // Therefore, it is not guaranteed that the res parameter will - // not be assigned in the event of a getaddrinfo failure. - cmn_logger_log_error("getaddrinfo: %s", gai_strerror(gai_ret)); - if (result) { - freeaddrinfo(result); - } - result = NULL; - } - return result; -} - -// Attempts to establish a connection using the given address info structure -// Returns 0 on success, 1 on failure -static int attempt_connection(evutil_socket_t *sockfd, struct addrinfo *addrinfo) { - char server_addr_str[INET6_ADDRSTRLEN] = { 0 }; - uint16_t server_addr_port; - struct sockaddr_storage sock_addr; - socklen_t sock_addr_length = sizeof sock_addr; - char sock_addr_str[INET6_ADDRSTRLEN] = { 0 }; - uint16_t sock_port; - try(*sockfd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol), EVUTIL_INVALID_SOCKET, socket_fail); - try(inet_ntop_address(addrinfo->ai_addr, server_addr_str, &server_addr_port), NULL, inet_ntop_address_fail); - cmn_logger_log_debug("Connecting to %s on port %hu", server_addr_str, server_addr_port); - try(connect(*sockfd, addrinfo->ai_addr, addrinfo->ai_addrlen), -1, connect_fail); - getsockname(*sockfd, (struct sockaddr *)&sock_addr, &sock_addr_length); - try(inet_ntop_address((struct sockaddr *)&sock_addr, sock_addr_str, &sock_port), NULL, inet_ntop_address_fail); - cmn_logger_log_debug("Connected from host %s on port %hu", sock_addr_str, sock_port); - return 0; -socket_fail: - cmn_logger_log_debug("socket: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - return 1; -connect_fail: - cmn_logger_log_debug("connect: %s.", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - try(evutil_closesocket(*sockfd), -1, close_fail); - *sockfd = EVUTIL_INVALID_SOCKET; - return 1; -inet_ntop_address_fail: - cmn_logger_log_error("inet_ntop_address failed"); - try(evutil_closesocket(*sockfd), -1, close_fail); - *sockfd = EVUTIL_INVALID_SOCKET; - return 1; -close_fail: - cmn_logger_log_error("close: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - return 1; -} diff --git a/client/src/connector.h b/client/src/connector.h deleted file mode 100644 index 585caac..0000000 --- a/client/src/connector.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -/** - * Connects to a server at the specified host and port. - * The sockfd parameter must be set to EVUTIL_INVALID_SOCKET before calling this function. - * On success, sockfd will be set to the socket descriptor of the connection. - * On failure, sockfd will be left unchanged. - * - * @param sockfd a pointer to the socket descriptor to set - * @param host the hostname or IP address of the server - * @param port the port number of the server - * @return 0 on success, 1 on failure. - */ -extern int connector_connect(evutil_socket_t *sockfd, char const *host, char const *port); diff --git a/client/src/main.c b/client/src/main.c index b08885a..aecae0e 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -1,60 +1,580 @@ #include #include #include +#include -#include +#include +#include +#include +#include +#include +#include -#include -#include +#define UINT8_STRLEN 4 +#define UINT16_STRLEN 6 +#define SIZE_STRLEN 21 -#include "cmdline.h" -#include "connector.h" -#include "service_handler.h" +enum client_command { + CLIENT_COMMAND_LIST, + CLIENT_COMMAND_GET, + CLIENT_COMMAND_PUT, +}; -void set_log_level(int verbose_level); +struct arguments { + const char *host; // IP address or hostname of the server to connect to + const char *port; // port number to use for the connection + enum client_command command; // command to send to the server (e.g. "list", "get", "put") + enum tftp_mode mode; // transfer mode to use for the file transfer + const char *filename; // file to send or receive (used with the "get" and "put" commands) + uint8_t retries; // number of retries to attempt before giving up + uint8_t timeout_s; // duration of the timeout to use for the Go-Back N protocol, in seconds + uint16_t block_size; // size of the data block to use for the file transfer + uint16_t window_size; // size of the dispatch window to use for the Go-Back N protocol + bool use_tsize; // + bool adaptive_timeout; // flag to use an adaptive timeout calculated dynamically based on network delays + double loss_probability; // probability of packet loss to simulate + enum logger_log_level verbose_level; // verbose level to output additional information +}; + +struct tftp_client { + struct logger *logger; + + int sockfd; + struct sockaddr_storage server_addr_storage; + socklen_t server_addr_len; + struct sockaddr_storage sock_addr_storage; + socklen_t sock_addr_len; + char server_addr_str[INET6_ADDRSTRLEN]; + uint16_t server_port; + char sock_addr_str[INET6_ADDRSTRLEN]; + uint16_t sock_port; + + enum client_command command; + enum tftp_mode mode; + const char *mode_str; + const char *filename; + uint8_t retries; + uint8_t retransmits; + + struct tftp_rrq_packet rrq_packet; + ssize_t rrq_packet_size; + + struct tftp_option options[TFTP_OPTION_TOTAL_OPTIONS]; + bool use_options; + char options_str[tftp_option_formatted_string_max_size]; + + bool is_timeout_s_default; + uint8_t timeout_s; + char* timeout_s_str[UINT8_STRLEN]; + + bool is_block_size_default; + uint16_t block_size; + char* block_size_str[UINT16_STRLEN]; + + bool is_window_size_default; + uint16_t window_size; + char* window_size_str[UINT16_STRLEN]; + + bool use_tsize; + size_t tsize; + char* tsize_str[SIZE_STRLEN]; + + bool adaptive_timeout; + double loss_probability; + + size_t file_bytes_received; +}; + +static inline struct arguments get_arguments(); + +static bool run(struct tftp_client client[static 1]); +static bool get(struct tftp_client client[static 1]); +static bool put(struct tftp_client client[static 1]); +static bool list(struct tftp_client client[static 1]); + +static bool send_rrq_packet(struct tftp_client client[static 1]); +static bool send_ack_packet(struct tftp_client client[static 1], uint16_t received_block_number); + +static bool send_read_request(struct tftp_client client[static 1]); +static bool receive_file(struct tftp_client client[static 1], FILE file[static 1]); + +static bool listener_init(struct tftp_client client[static 1], const char *host, const char *service); +static void options_init(struct tftp_client client[static 1]); + +static void print_stats(struct tftp_client client[static 1], struct timespec start_time[static 1], struct timespec end_time[static 1]); +static inline size_t get_buffer_size(struct tftp_client client[static 1]); + +static inline long double timespec_to_double(struct timespec timespec[static 1]); int main(int argc, char *argv[argc + 1]) { - cmdline_args_t args; - evutil_socket_t sockfd = EVUTIL_INVALID_SOCKET; - try(parse_cmdline_args(argc, (const char**)argv, &args), 1, argparser_fail); - set_log_level(args.verbose_level); - try(connector_connect(&sockfd, args.host, args.port), 1, fail); - try(service_handler_handle_request( - &(service_handler_t){ - .command = args.command, - .filename = args.filename, - .window_size = args.window_size, - .timeout_duration = args.timeout_duration, - .adaptive_timeout = args.adaptive_timeout, - .loss_probability = args.loss_probability }, - sockfd), - 1, - fail); - cmn_logger_log_info("Closing connection."); - try(evutil_closesocket(sockfd), -1, close_fail); - return EXIT_SUCCESS; -argparser_fail: - cmn_logger_log_error("Can't parse CLI arguments."); - return EXIT_FAILURE; -close_fail: - cmn_logger_log_error("close: %s\n.", strerror(errno)); - return EXIT_FAILURE; + struct arguments args = get_arguments(); + struct logger logger; + if (!logger_init(&logger, logger_default_config)) { + return EXIT_FAILURE; + } + logger.config.default_level = args.verbose_level; + struct tftp_client client = { + .sockfd = -1, + .sock_addr_len = sizeof client.sock_addr_storage, + .logger = &logger, + .command = args.command, + .mode = args.mode, + .mode_str = tftp_mode_to_string(args.mode), + .filename = args.filename, + .retries = args.retries, + .timeout_s = args.timeout_s, + .block_size = args.block_size, + .window_size = args.window_size, + .use_tsize = args.use_tsize, + .adaptive_timeout = args.adaptive_timeout, + .loss_probability = args.loss_probability, + }; + options_init(&client); + if (!listener_init(&client, args.host, args.port)) { + return EXIT_FAILURE; + } + if (!run(&client)) { + return EXIT_FAILURE; + } + logger_log_debug(client.logger, "Closing connection."); + if (close(client.sockfd)) { + logger_log_error(client.logger, "close: %s", strerror(errno)); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static inline struct arguments get_arguments() { + return (struct arguments) { + .host = "::1", + .port = "6969", + .filename = "pippo", + .command = CLIENT_COMMAND_GET, + .retries = 5, + .timeout_s = 1, + .block_size = 0, + .window_size = 0, + .use_tsize = false, + .adaptive_timeout = false, + .loss_probability = 0.0, + .verbose_level = LOGGER_LOG_LEVEL_ALL, + }; +} + +static bool run(struct tftp_client client[static 1]) { + switch (client->command) { + case CLIENT_COMMAND_LIST: + return list(client); + case CLIENT_COMMAND_GET: + return get(client); + case CLIENT_COMMAND_PUT: + return put(client); + } + return true; +} + +static bool get(struct tftp_client client[static 1]) { + struct timespec start_time; + struct timespec end_time; + FILE *file = fopen(client->filename, "wb"); + if (file == nullptr) { + logger_log_error(client->logger, "Could not open file %s for writing. %s", client->filename, strerror(errno)); + return false; + } + logger_log_info(client->logger, "Getting file %s from %s:%hu [%s]", client->filename, client->server_addr_str, client->server_port, client->mode_str); + if (clock_gettime(CLOCK_MONOTONIC, &start_time) == -1) { + logger_log_error(client->logger, "Failed to get start time. %s", strerror(errno)); + fclose(file); + return false; + } + if (!send_read_request(client)) { + fclose(file); + return false; + } + if (!receive_file(client, file)) { + fclose(file); + return false; + } + if (clock_gettime(CLOCK_MONOTONIC, &end_time) == -1) { + logger_log_error(client->logger, "Failed to get end time. %s", strerror(errno)); + fclose(file); + return false; + } + print_stats(client, &start_time, &end_time); + fclose(file); + return true; +} + +static bool put(struct tftp_client client[static 1]) { + return false; +} + +static bool list(struct tftp_client client[static 1]) { + return false; +} + +static bool send_rrq_packet(struct tftp_client client[static 1]) { + if (sendto(client->sockfd, &client->rrq_packet, client->rrq_packet_size, 0, (struct sockaddr *) &client->server_addr_storage, client->server_addr_len) < 0) { + logger_log_error(client->logger, "Failed to send RRQ packet. %s", strerror(errno)); + return false; + } + logger_log_trace(client->logger, "Sent RRQ ", client->filename, client->mode_str, client->use_options ? ", options=" : "", client->use_options ? client->options_str : ""); + return true; +} + +static bool send_ack_packet(struct tftp_client client[static 1], uint16_t received_block_number) { + struct tftp_ack_packet ack_packet = { + .opcode = htons(TFTP_OPCODE_ACK), + .block_number = htons(received_block_number), + }; + if (sendto(client->sockfd, &ack_packet, sizeof ack_packet, 0, (struct sockaddr *) &client->server_addr_storage, client->server_addr_len) < 0) { + logger_log_error(client->logger, "Failed to send ACK packet. %s", strerror(errno)); + return false; + } + logger_log_trace(client->logger, "Sent ACK ", received_block_number); + return true; +} + +static bool send_read_request(struct tftp_client client[static 1]) { + client->rrq_packet_size = tftp_rrq_packet_init(&client->rrq_packet, + strlen(client->filename) + 1, + client->filename, + client->mode, + client->options); + if (client->rrq_packet_size < 0) { + logger_log_error(client->logger, "Failed to create RRQ packet"); + return false; + } + if (!send_rrq_packet(client)) { + return false; + } + if (getsockname(client->sockfd, (struct sockaddr *) &client->sock_addr_storage, &client->sock_addr_len) == -1) { + logger_log_error(client->logger, "Failed to get the socket name. %s", strerror(errno)); + return false; + } + if (inet_ntop_address((struct sockaddr *) &client->sock_addr_storage, client->sock_addr_str, &client->sock_port) == nullptr) { + logger_log_error(client->logger, "Could not translate binding address to a string representation. %s", strerror(errno)); + return false; + } + logger_log_debug(client->logger, "Bound to %s:%hu", client->sock_addr_str, client->sock_port); + return true; +} + +static bool receive_file(struct tftp_client client[static 1], FILE file[static 1]) { + bool transfer_complete = false; + uint16_t expected_next_block = 1; + uint16_t last_block_received = 0; + size_t buffer_size = get_buffer_size(client); + uint8_t *buffer = malloc(buffer_size); + if (buffer == nullptr) { + logger_log_error(client->logger, "Failed to allocate memory for the buffer. %s", strerror(errno)); + return false; + } + while (!transfer_complete) { + memset(buffer, 0, buffer_size); + ssize_t bytes_received; + struct sockaddr_storage addr_storage = {}; + socklen_t addr_len = sizeof addr_storage; + if (expected_next_block == 1) { + client->server_addr_len = sizeof client->server_addr_storage; + while (true) { + bytes_received = recvfrom(client->sockfd, buffer, buffer_size, 0, (struct sockaddr *) &client->server_addr_storage, &client->server_addr_len); + if (bytes_received != -1) { + break; + } + if (errno == EWOULDBLOCK || errno == EAGAIN) { + if (++client->retransmits < client->retries) { + if (!send_rrq_packet(client)) { + return false; + } + continue; + } + logger_log_error(client->logger, "Transfer timed out.", strerror(errno)); + return false; + } + logger_log_error(client->logger, "Failed to receive data. %s", strerror(errno)); + return false; + } + client->retransmits = 0; + } + else { + while (true) { + bytes_received = recvfrom(client->sockfd, buffer, buffer_size, 0, (struct sockaddr *) &addr_storage, &addr_len); + if (bytes_received != -1) { + break; + } + if (errno == EWOULDBLOCK || errno == EAGAIN) { + if (++client->retransmits < client->retries) { + if (!send_ack_packet(client, last_block_received)) { + return false; + } + continue; + } + logger_log_error(client->logger, "Transfer timed out.", strerror(errno)); + return false; + } + logger_log_error(client->logger, "Failed to receive data. %s", strerror(errno)); + return false; + } + client->retransmits = 0; + if (addr_len != client->server_addr_len || memcmp(&client->server_addr_storage, &addr_storage, addr_len) != 0) { + char addr_str[INET6_ADDRSTRLEN]; + uint16_t port; + inet_ntop_address((struct sockaddr *) &addr_storage, addr_str, &port); + logger_log_warn(client->logger, "Unexpected peer: '%s:%d', expected peer: '%s:%d'. Ignoring packet.", addr_str, port, client->server_addr_str, client->server_port); + continue; + } + } + enum tftp_opcode opcode = ntohs(*(uint16_t *) buffer); + switch (opcode) { + case TFTP_OPCODE_OACK: { + struct tftp_oack_packet *oack_packet = (struct tftp_oack_packet *) buffer; + struct tftp_option ackd_options[TFTP_OPTION_TOTAL_OPTIONS] = {}; + char ackd_options_str[tftp_option_formatted_string_max_size]; + uint16_t block_size = client->block_size; + uint16_t timeout = client->timeout_s; + uint16_t window_size = client->window_size; + size_t file_size = 0; + bool contains_unrequested_options = false; + tftp_parse_options(ackd_options, bytes_received - sizeof *oack_packet, (char *) oack_packet->options_values); + tftp_format_options(ackd_options, ackd_options_str); + logger_log_trace(client->logger, "Received OACK %s", ackd_options_str); + for (enum tftp_option_recognized o = 0; o < TFTP_OPTION_TOTAL_OPTIONS; o++) { + if (ackd_options[o].is_active) { + if (!client->options[o].is_active) { + contains_unrequested_options = true; + break; + } + switch (o) { + case TFTP_OPTION_BLKSIZE: + block_size = strtoul(ackd_options[o].value, nullptr, 10); + break; + case TFTP_OPTION_TIMEOUT: + timeout = strtoul(ackd_options[o].value, nullptr, 10); + break; + case TFTP_OPTION_TSIZE: + file_size = strtoul(ackd_options[o].value, nullptr, 10); + break; + case TFTP_OPTION_WINDOWSIZE: + window_size = strtoul(ackd_options[o].value, nullptr, 10); + break; + default: // unreachable + break; + } + } + } + contains_unrequested_options = true; + if (contains_unrequested_options) { + logger_log_error(client->logger, "Received OACK with unrequested options."); + const struct tftp_error_packet_info *error_info = &tftp_error_packet_info[TFTP_ERROR_INVALID_OPTIONS]; + if (sendto(client->sockfd, error_info->packet, error_info->size, 0, (struct sockaddr *) &client->server_addr_storage, client->server_addr_len) < 0) { + logger_log_error(client->logger, "Failed to send ERROR packet. %s", strerror(errno)); + } + return false; + } + if (block_size != client->block_size || window_size != client->window_size) { + client->block_size = block_size; + client->window_size = window_size; + buffer_size = get_buffer_size(client); + uint8_t *new_buffer = realloc(buffer, buffer_size); + if (new_buffer == nullptr) { + logger_log_error(client->logger, "Failed to reallocate memory for the buffer. %s", strerror(errno)); + return false; + } + buffer = new_buffer; + } + if (timeout != client->timeout_s) { + client->timeout_s = timeout; + struct timeval new_timeout = {.tv_sec = client->timeout_s,}; + if (setsockopt(client->sockfd, SOL_SOCKET, SO_RCVTIMEO, &new_timeout, sizeof new_timeout) == -1) { + logger_log_error(client->logger, "Could not set listener socket options for the server. %s", strerror(errno)); + return false; + } + } + // TODO: handle ackd_options errors + if (!send_ack_packet(client, last_block_received)) { + return false; + } + break; + } + case TFTP_OPCODE_DATA: { + // TODO: If no OACK set default values for options + struct tftp_data_packet *data_packet = (struct tftp_data_packet *) buffer; + last_block_received = ntohs(data_packet->block_number); + if (last_block_received == expected_next_block) { + expected_next_block++; + size_t block_size = bytes_received - sizeof *data_packet; + fwrite(data_packet->data, 1, block_size, file); + client->file_bytes_received += block_size; + logger_log_trace(client->logger, "Received DATA ", last_block_received, block_size); + if (!send_ack_packet(client, last_block_received)) { + return false; + } + if (block_size < client->block_size) { + transfer_complete = true; + } + } + break; + } + case TFTP_OPCODE_ERROR: { + // TODO: Attempt to repeat the request without the options if the previous sent was the RRQ packet + struct tftp_error_packet *error_packet = (struct tftp_error_packet *) buffer; + uint16_t error_code = ntohs(error_packet->error_code); + const char *error_message = (const char *) error_packet->error_message; + logger_log_error(client->logger, "Received error %d: %s", error_code, error_message); + return false; + } + default: + logger_log_error(client->logger, "Unexpected opcode %d", opcode); + return false; + } + } + free(buffer); + return true; +} + +static void print_stats(struct tftp_client client[static 1], struct timespec start_time[static 1], struct timespec end_time[static 1]) { + long double duration = timespec_to_double(&(struct timespec) { + .tv_sec = end_time->tv_sec - start_time->tv_sec, + .tv_nsec = end_time->tv_nsec - start_time->tv_nsec, + }); + long double bit_speed = (long double) client->file_bytes_received * 8 / duration; + long double byte_speed = (long double) client->file_bytes_received / duration; + static const char *unit_prefix[] = {"", "K", "M", "G", "T", "P", "E", "Z", "Y"}; + long double display_speed = byte_speed; + size_t prefix_index = 0; + while (display_speed > 1024 && prefix_index < sizeof unit_prefix) { + display_speed /= 1024; + prefix_index++; + } + logger_log_info(client->logger, "Received %zu bytes in %.3Lf seconds [%.2Lf %sB/s, %.0Lf bit/s]", + client->file_bytes_received, + duration, + display_speed, + unit_prefix[prefix_index], + bit_speed); +} + +static bool listener_init(struct tftp_client client[static 1], const char *host, const char *service) { + struct addrinfo *addrinfo_list = nullptr; + struct timeval timeout = {.tv_sec = client->timeout_s,}; + static const struct addrinfo hints = { + .ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED, + .ai_family = AF_INET6, + .ai_socktype = SOCK_DGRAM, + .ai_addr = nullptr, + .ai_canonname = nullptr, + .ai_next = nullptr + }; + int gai_ret = getaddrinfo(host, service, &hints, &addrinfo_list); + if (gai_ret) { + logger_log_error(client->logger, "Could not translate the host and service required to a valid internet address. %s", gai_strerror(gai_ret)); + if (addrinfo_list != nullptr) { + freeaddrinfo(addrinfo_list); + } + return false; + } + struct addrinfo *addrinfo = addrinfo_list; + client->sockfd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol); + if (inet_ntop_address(addrinfo->ai_addr, client->server_addr_str, &client->server_port) == nullptr) { + logger_log_error(client->logger, "Could not translate binding address to a string representation. %s", strerror(errno)); + goto fail; + } + if (client->sockfd == -1) { + logger_log_error(client->logger, "Could not create a socket for the server. %s", strerror(errno)); + goto fail; + } + if (setsockopt(client->sockfd, SOL_SOCKET, SO_REUSEADDR, &(int) {true}, sizeof(int)) == -1 || + setsockopt(client->sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &(int) {false}, sizeof(int)) == -1 || + setsockopt(client->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout) == -1) { + logger_log_error(client->logger, "Could not set listener socket options for the server. %s", strerror(errno)); + goto fail2; + } + memcpy(&client->server_addr_storage, addrinfo->ai_addr, addrinfo->ai_addrlen); + client->server_addr_len = addrinfo->ai_addrlen; + freeaddrinfo(addrinfo_list); + return true; +fail2: + close(client->sockfd); fail: - return EXIT_FAILURE; -} - -void set_log_level(int verbose_level) { - switch (verbose_level) { - case 1: - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_WARN); - break; - case 2: - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_INFO); - break; - case 3: - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_DEBUG); - break; - default: - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_ALL); - } + freeaddrinfo(addrinfo_list); + return false; +} + +static void options_init(struct tftp_client client[static 1]) { + if (client->timeout_s == 0) { + client->timeout_s = 2; + client->is_timeout_s_default = true; + } + else { + sprintf((char *) client->timeout_s_str, "%hhu", client->timeout_s); + } + if (client->block_size == 0) { + client->block_size = tftp_default_blksize; + client->is_block_size_default = true; + } + else { + sprintf((char *) client->block_size_str, "%hu", client->block_size); + } + if (client->window_size == 0) { + client->window_size = tftp_default_window_size; + client->is_window_size_default = true; + } + else { + sprintf((char *) client->window_size_str, "%hu", client->window_size); + } + sprintf((char *) client->tsize_str, "0"); + + memcpy(client->options, + &(struct tftp_option[TFTP_OPTION_TOTAL_OPTIONS]) { + [TFTP_OPTION_BLKSIZE] = {.is_active = !client->is_block_size_default, .value = (const char *) client->block_size_str}, + [TFTP_OPTION_TIMEOUT] = {.is_active = !client->is_timeout_s_default, .value = (const char *) client->timeout_s_str}, + [TFTP_OPTION_TSIZE] = {.is_active = client->use_tsize, .value = (const char *) client->tsize_str}, + [TFTP_OPTION_WINDOWSIZE] = {.is_active = !client->is_window_size_default, . value = (const char *) client->window_size_str}, + }, + sizeof client->options); + for (size_t i = 0; i < TFTP_OPTION_TOTAL_OPTIONS; i++) { + if (client->options[i].is_active) { + client->use_options = true; + break; + } + } + if (client->use_options) { + tftp_format_options(client->options, client->options_str); + } +} + +static inline size_t get_buffer_size(struct tftp_client client[static 1]) { + static size_t min_buffer_size = sizeof(struct tftp_rrq_packet) - 4; + size_t required_buffer_size = sizeof(struct tftp_data_packet) + client->block_size; + return required_buffer_size < min_buffer_size ? min_buffer_size : required_buffer_size; +} + +static inline long double timespec_to_double(struct timespec timespec[static 1]) { + int64_t sec_as_nsec = timespec->tv_sec * 1'000'000'000LL; + return (sec_as_nsec + timespec->tv_nsec) / 1e9L; +} + +/* +int parse_cmdline_args(int argc, const char *argv[static const argc + 1], cmdline_args_t *args) { + argparser_t parser; + argparser_t list_parser; + argparser_t get_parser; + argparser_t put_parser; + try(parser = argparser_init(argc, argv), NULL, fail); + argparser_set_description(parser, "File transfer client."); + try(argparser_add_argument(parser, &args->host, { .name = "host", .help = "the file transfer server address" }), 1, fail); + try(argparser_add_argument(parser, &args->port, { .flag = "p", .long_flag = "port", .default_value = "1234", .help = "specify the listening port number (the default value is \"1234\")" }), 1, fail); + argparser_set_subparsers_options(parser, (struct argparser_subparsers_options){ .required = true, .title = "commands" }); + try(list_parser = argparser_add_subparser(parser, &args->command, "list", "List available file on the remote server"), NULL, fail); + try(get_parser = argparser_add_subparser(parser, &args->command, "get", "Download a file from the remote server"), NULL, fail); + try(argparser_add_argument(get_parser, &args->filename, { .name = "filename", .help = "The file to download" }), 1, fail); + try(put_parser = argparser_add_subparser(parser, &args->command, "put", "Upload a file to the remote server"), NULL, fail); + try(argparser_add_argument(put_parser, &args->filename, { .name = "filename", .help = "The file to upload" }), 1, fail); + try(argparser_parse_args(parser), 1, fail); + argparser_destroy(parser); + return 0; + fail: + return 1; } +*/ diff --git a/client/src/service_handler.c b/client/src/service_handler.c deleted file mode 100644 index 04ebddc..0000000 --- a/client/src/service_handler.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "service_handler.h" - -#include - -#include -#include - -int service_handler_handle_request(service_handler_t* service_handler, evutil_socket_t sockfd) { - cmn_logger_log_info("Handling the user's request."); - if (!strcmp(service_handler->command, "list") || !strcmp(service_handler->command, "put")) { - cmn_logger_log_error("Command not implemented yet."); - return 1; - } - char buff[256] = { 0 }; - ssize_t bytes_read; - ssize_t bytes_sent; - uint8_t *packet; - size_t packet_len; - packet_len = 2 + strlen(service_handler->filename) + 1 + sizeof "octet"; - packet = malloc(packet_len); - memcpy(packet, &(uint8_t[]){ 0x0, 0x1 }, 2); - memcpy(packet + 2, service_handler->filename, strlen(service_handler->filename) + 1); - memcpy(packet + 2 + strlen(service_handler->filename) + 1, "octet", sizeof "octet"); - try((bytes_sent = send(sockfd, packet, packet_len, 0)) == -1, true, send_fail); - printf("client: sent %zu bytes\n", bytes_sent); - printf("client: waiting reply from server...\n"); - try((bytes_read = recv(sockfd, buff, sizeof(buff), 0)) == -1, true, recv_fail); - printf("client: received %zd bytes, packet content is \"%s\"\n", bytes_read, buff); - printf("client: replying to server...\n"); - char _message[] = "ACK"; - bytes_sent = 0; - while (bytes_sent != sizeof _message) { - int n; - try((n = send(sockfd, _message + bytes_sent, strlen(_message + bytes_sent) + 1, 0)) == -1, true, send_fail); - bytes_sent += n; - } - return 0; -recv_fail: - fprintf(stderr, "recv: %s\n", strerror(errno)); - return 1; -send_fail: - fprintf(stderr, "send: %s\n", strerror(errno)); - return 1; -} diff --git a/client/src/service_handler.h b/client/src/service_handler.h deleted file mode 100644 index cacc901..0000000 --- a/client/src/service_handler.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -#include - -typedef struct service_handler { - char const *command; // command to send to the server (e.g. "list", "get", "put") - char *filename; // file to send or receive (used with the "get" and "put" commands) - uint32_t window_size; // size of the dispatch window to use for the Go-Back N protocol - long timeout_duration; // duration of the timeout to use for the Go-Back N protocol, in milliseconds - bool adaptive_timeout; // flag to use an adaptive timeout calculated dynamically based on network delays - double loss_probability; // probability of message loss to simulate -} service_handler_t; - -int service_handler_handle_request(service_handler_t* service_handler, evutil_socket_t sockfd); diff --git a/docs/Dettagli/Go-Back N.txt b/docs/Dettagli/Go-Back N.txt new file mode 100644 index 0000000..69bfbcb --- /dev/null +++ b/docs/Dettagli/Go-Back N.txt @@ -0,0 +1,26 @@ +The Go-Back N (GBN-ARQ) protocol described in Kurose & Ross “Computer Networking: A Top-Down Approach” is here reported: + +In a Go-Back-N (GBN) protocol, the sender is allowed to transmit multiple packets (when available) without waiting for an acknowledgment, but is constrained to have no more than some maximum allowable number, "N", of unacknowledged packets in the pipeline. +We define "base" to be the sequence number of the oldest unacknowledged packet and "nextseqnum" to be the smallest unused sequence number (that is, the sequence number of the next packet to be sent), then four intervals in the range of sequence numbers can be identified. +Sequence numbers in the interval [0, base-1] correspond to packets that have already been transmitted and acknowledged. +The interval [base, nextseqnum-1] corresponds to packets that have been sent but not yet acknowledged. +Sequence numbers in the interval [nextseqnum, base+N-1] can be used for packets that can be sent immediately, should data arrive from the upper layer. +Finally, sequence numbers greater than or equal to base+N cannot be used until an unacknowledged packet currently in the pipeline (specifically, the packet with sequence number base ) has been acknowledged. +The range of permissible sequence numbers for transmitted but not yet acknowledged packets can be viewed as a window of size N over the range of sequence numbers. +As the protocol operates, this window slides forward over the sequence number space. +A packet’s sequence number is carried in a fixed-length field in the packet header. +If k is the number of bits in the packet sequence number field, the range of sequence numbers is thus [0, 2k - 1]. +With a finite range of sequence numbers, all arithmetic involving sequence numbers must then be done using modulo 2^k arithmetic. (That is, the sequence number space can be thought of as a ring of size 2^k , where sequence number 2^{k - 1} is immediately followed by sequence number 0.) +The GBN sender must respond to three types of events: +- Invocation from above. When the reliable send function is called from above, the sender first checks to see if the window is full, that is, whether there are N outstanding, unacknowledged packets. +If the window is not full, a packet is created and sent, and variables are appropriately updated. +If the window is full, the sender simply returns the data back to the upper layer, an implicit indication that the window is full. The upper layer would presumably then have to try again later. +In a real implementation, the sender would more likely have either buffered (but not immediately sent) this data, or would have a synchronization mechanism (for example, a semaphore or a flag) that would allow the upper layer to call the reliable send function only when the window is not full. +- Receipt of an ACK. In our GBN protocol, an acknowledgment for a packet with sequence number n will be taken to be a cumulative acknowledgment, indicating that all packets with a sequence number up to and including n have been correctly received at the receiver. We’ll come back to this issue shortly when we examine the receiver side of GBN. +- A timeout event. If a timeout occurs, the sender resends all packets that have been previously sent but that have not yet been acknowledged. +If an ACK is received but there are still additional transmitted but not yet acknowledged packets, the timer is restarted. If there are no outstanding, unacknowledged +packets, the timer is stopped. +If a packet with sequence number n is received correctly and is in order (that is, the data last delivered to the upper layer came from a packet with sequence number n - 1), +the receiver sends an ACK for packet n and delivers the data portion of the packet to the upper layer. +In all other cases, the receiver discards the packet and resends an ACK for the most recently received in-order packet. +The receiver maintain the sequence number of the next in-order packet in a variable "expectedseqnum". \ No newline at end of file diff --git a/docs/Dettagli/Traccia.txt b/docs/Dettagli/Traccia.txt new file mode 100644 index 0000000..e7d3960 --- /dev/null +++ b/docs/Dettagli/Traccia.txt @@ -0,0 +1,28 @@ +Write in the C programming language a client/server file transfer application using a connectionless network service (SOCK_DGRAM type of the Berkeley sockets API, i.e. UDP as transport layer protocol) which provides the following functions: + +- Unauthenticated client-server connection +- Reliable file transfer +- A communication protocol between the client and the server providing the exchange of two types of messages: + *command messages: sent from the client to the server to request the execution of the various operations + *response messages: sent from the server to the client in response to a command with the outcome of the operation +- The execution in the user space without requiring root privileges + +The server (multithreaded) listening on a default port will provide the following functions: + +- Send the response message to the list command with the filelist (the list of file names available for sharing) +- Send the response message to the get command with the required file, if any, or an error message +- Sending the response message to the put command with the outcome of the operation +- Configure the listening port + +The client provides the user with the following functions: + +- Show the files available on the server (via the list command) +- Download a file from the server (via the get command) +- Upload a file on the server (via the put command) + +The exchange of messages takes place using an unreliable communication service. In order to ensure the correct sending / receiving of messages and files both clients and servers implement at the application level the Go-Back N protocol. + +During the tests to simulate the loss of messages over the network (a very unlikely event in a local network let alone when client and server are running on the same host), it is assumed that each message is discarded by the sender with probability p. + +The size N of the dispatch window, the probability p of loss of messages, and the duration T of the timeout, are three configurable constants and the same for all processes. In addition to the use of a fixed timeout, it is possible to choose the use of a value for the adaptive timeout calculated dynamically based on the evolution of the observed network delays. + diff --git a/docs/Model.mdj b/docs/Model.mdj deleted file mode 100644 index fc566c2..0000000 --- a/docs/Model.mdj +++ /dev/null @@ -1,9179 +0,0 @@ -{ - "_type": "Project", - "_id": "AAAAAAFF+h6SjaM2Hec=", - "name": "Untitled", - "ownedElements": [ - { - "_type": "UMLModel", - "_id": "AAAAAAFF+qBWK6M3Z8Y=", - "_parent": { - "$ref": "AAAAAAFF+h6SjaM2Hec=" - }, - "name": "Model", - "ownedElements": [ - { - "_type": "UMLClassDiagram", - "_id": "AAAAAAFF+qBtyKM79qY=", - "_parent": { - "$ref": "AAAAAAFF+qBWK6M3Z8Y=" - }, - "name": "Main", - "defaultDiagram": true - }, - { - "_type": "UMLStateMachine", - "_id": "AAAAAAF71Zx6n9buWyM=", - "_parent": { - "$ref": "AAAAAAFF+qBWK6M3Z8Y=" - }, - "name": "StateMachine", - "ownedElements": [ - { - "_type": "UMLStatechartDiagram", - "_id": "AAAAAAF71Zx6oNbwmYg=", - "_parent": { - "$ref": "AAAAAAF71Zx6n9buWyM=" - }, - "name": "StatechartDiagram", - "ownedViews": [ - { - "_type": "UMLPseudostateView", - "_id": "AAAAAAF71ZyFD9b2Zik=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71ZyFD9b1lBU=" - }, - "subViews": [ - { - "_type": "NodeLabelView", - "_id": "AAAAAAF71ZyFD9b3Qgw=", - "_parent": { - "$ref": "AAAAAAF71ZyFD9b2Zik=" - }, - "model": { - "$ref": "AAAAAAF71ZyFD9b1lBU=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 18.5, - "top": 378, - "height": 13, - "alpha": 2.356194490192345, - "distance": 20 - }, - { - "_type": "NodeLabelView", - "_id": "AAAAAAF71ZyFD9b4+vQ=", - "_parent": { - "$ref": "AAAAAAF71ZyFD9b2Zik=" - }, - "model": { - "$ref": "AAAAAAF71ZyFD9b1lBU=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 8.5, - "top": 368, - "height": 13, - "alpha": 2.356194490192345, - "distance": 35 - }, - { - "_type": "NodeLabelView", - "_id": "AAAAAAF71ZyFENb5qYM=", - "_parent": { - "$ref": "AAAAAAF71ZyFD9b2Zik=" - }, - "model": { - "$ref": "AAAAAAF71ZyFD9b1lBU=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 18.5, - "top": 407, - "height": 13, - "alpha": -2.356194490192345, - "distance": 20 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 24, - "top": 390, - "width": 20, - "height": 20, - "nameLabel": { - "$ref": "AAAAAAF71ZyFD9b3Qgw=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71ZyFD9b4+vQ=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71ZyFENb5qYM=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71ZyLwNcH0cE=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71ZyLwNcIr+U=", - "_parent": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "model": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71ZyLwNcJB6c=", - "_parent": { - "$ref": "AAAAAAF71ZyLwNcIr+U=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -384, - "top": 208, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71ZyLwNcKtak=", - "_parent": { - "$ref": "AAAAAAF71ZyLwNcIr+U=" - }, - "font": "Arial;13;1", - "left": 166, - "top": 387, - "width": 54.1708984375, - "height": 13, - "text": "CLOSED" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71ZyLwNcL00k=", - "_parent": { - "$ref": "AAAAAAF71ZyLwNcIr+U=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -384, - "top": 208, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71ZyLwNcM670=", - "_parent": { - "$ref": "AAAAAAF71ZyLwNcIr+U=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -384, - "top": 208, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 161, - "top": 380, - "width": 64.1708984375, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71ZyLwNcJB6c=" - }, - "nameLabel": { - "$ref": "AAAAAAF71ZyLwNcKtak=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71ZyLwNcL00k=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71ZyLwNcM670=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71ZyLwNcN1mE=", - "_parent": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "model": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -192, - "top": 104, - "width": 10, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71ZyLwdcOGKA=", - "_parent": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "model": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -192, - "top": 104, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71ZyLwdcPMAA=", - "_parent": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "model": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "font": "Arial;13;0", - "left": 161, - "top": 405, - "width": 64.1708984375 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 161, - "top": 380, - "width": 64.1708984375, - "height": 40, - "nameCompartment": { - "$ref": "AAAAAAF71ZyLwNcIr+U=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71ZyLwNcN1mE=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71ZyLwdcOGKA=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71ZyLwdcPMAA=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71Zy/8NdLthg=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Zy/8NdKhrk=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71Zy/8NdMRtM=", - "_parent": { - "$ref": "AAAAAAF71Zy/8NdLthg=" - }, - "model": { - "$ref": "AAAAAAF71Zy/8NdKhrk=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71Zy/8NdNRc8=", - "_parent": { - "$ref": "AAAAAAF71Zy/8NdMRtM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -464, - "top": -400, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Zy/8NdOmoM=", - "_parent": { - "$ref": "AAAAAAF71Zy/8NdMRtM=" - }, - "font": "Arial;13;1", - "left": 147, - "top": 150, - "width": 91.04443359375, - "height": 13, - "text": "LISTEN" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Zy/8NdPiRo=", - "_parent": { - "$ref": "AAAAAAF71Zy/8NdMRtM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -464, - "top": -400, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Zy/8NdQycc=", - "_parent": { - "$ref": "AAAAAAF71Zy/8NdMRtM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -464, - "top": -400, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 142, - "top": 143, - "width": 101.04443359375, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71Zy/8NdNRc8=" - }, - "nameLabel": { - "$ref": "AAAAAAF71Zy/8NdOmoM=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71Zy/8NdPiRo=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Zy/8NdQycc=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71Zy/8NdRKAw=", - "_parent": { - "$ref": "AAAAAAF71Zy/8NdLthg=" - }, - "model": { - "$ref": "AAAAAAF71Zy/8NdKhrk=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 142, - "top": 168, - "width": 101.04443359375, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71Zy/8NdSM48=", - "_parent": { - "$ref": "AAAAAAF71Zy/8NdLthg=" - }, - "model": { - "$ref": "AAAAAAF71Zy/8NdKhrk=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -232, - "top": -200, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71Zy/8NdTZjI=", - "_parent": { - "$ref": "AAAAAAF71Zy/8NdLthg=" - }, - "model": { - "$ref": "AAAAAAF71Zy/8NdKhrk=" - }, - "font": "Arial;13;0", - "left": 142, - "top": 168, - "width": 101.04443359375 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 142, - "top": 143, - "width": 101.04443359375, - "height": 50, - "nameCompartment": { - "$ref": "AAAAAAF71Zy/8NdMRtM=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71Zy/8NdRKAw=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71Zy/8NdSM48=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71Zy/8NdTZjI=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71ZzQTddxhV8=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71ZzQTddyj28=", - "_parent": { - "$ref": "AAAAAAF71ZzQTddxhV8=" - }, - "model": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71ZzQTddzEUg=", - "_parent": { - "$ref": "AAAAAAF71ZzQTddyj28=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 960, - "top": -832, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71ZzQTdd067g=", - "_parent": { - "$ref": "AAAAAAF71ZzQTddyj28=" - }, - "font": "Arial;13;1", - "left": 889, - "top": 131, - "width": 70.79541015625, - "height": 13, - "text": "SYN_RCVD" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71ZzQTdd15gs=", - "_parent": { - "$ref": "AAAAAAF71ZzQTddyj28=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 960, - "top": -832, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71ZzQTdd2wpg=", - "_parent": { - "$ref": "AAAAAAF71ZzQTddyj28=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 960, - "top": -832, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 884, - "top": 124, - "width": 80.79541015625, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71ZzQTddzEUg=" - }, - "nameLabel": { - "$ref": "AAAAAAF71ZzQTdd067g=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71ZzQTdd15gs=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71ZzQTdd2wpg=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71ZzQTdd3Jqw=", - "_parent": { - "$ref": "AAAAAAF71ZzQTddxhV8=" - }, - "model": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 576, - "top": -416, - "width": 10, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71ZzQTdd4SIM=", - "_parent": { - "$ref": "AAAAAAF71ZzQTddxhV8=" - }, - "model": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 576, - "top": -416, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71ZzQTdd5bVk=", - "_parent": { - "$ref": "AAAAAAF71ZzQTddxhV8=" - }, - "model": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - }, - "font": "Arial;13;0", - "left": 884, - "top": 149, - "width": 80.79541015625 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 884, - "top": 124, - "width": 80.79541015625, - "height": 40, - "nameCompartment": { - "$ref": "AAAAAAF71ZzQTddyj28=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71ZzQTdd3Jqw=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71ZzQTdd4SIM=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71ZzQTdd5bVk=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71ZzlqdeXtDA=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71ZzlqdeWGqM=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71ZzlqdeY8QE=", - "_parent": { - "$ref": "AAAAAAF71ZzlqdeXtDA=" - }, - "model": { - "$ref": "AAAAAAF71ZzlqdeWGqM=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71ZzlqdeZbgE=", - "_parent": { - "$ref": "AAAAAAF71ZzlqdeY8QE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -576, - "top": -16, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71ZzlqdeaEf0=", - "_parent": { - "$ref": "AAAAAAF71ZzlqdeY8QE=" - }, - "font": "Arial;13;1", - "left": 365, - "top": 387, - "width": 68.630859375, - "height": 13, - "text": "SYN_SENT" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71ZzlqtebGVQ=", - "_parent": { - "$ref": "AAAAAAF71ZzlqdeY8QE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -576, - "top": -16, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Zzlqteca1k=", - "_parent": { - "$ref": "AAAAAAF71ZzlqdeY8QE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -576, - "top": -16, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 360, - "top": 380, - "width": 78.630859375, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71ZzlqdeZbgE=" - }, - "nameLabel": { - "$ref": "AAAAAAF71ZzlqdeaEf0=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71ZzlqtebGVQ=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Zzlqteca1k=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71Zzlqted/WE=", - "_parent": { - "$ref": "AAAAAAF71ZzlqdeXtDA=" - }, - "model": { - "$ref": "AAAAAAF71ZzlqdeWGqM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -288, - "top": -8, - "width": 10, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71ZzlqteefqY=", - "_parent": { - "$ref": "AAAAAAF71ZzlqdeXtDA=" - }, - "model": { - "$ref": "AAAAAAF71ZzlqdeWGqM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -288, - "top": -8, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71ZzlqtefI5I=", - "_parent": { - "$ref": "AAAAAAF71ZzlqdeXtDA=" - }, - "model": { - "$ref": "AAAAAAF71ZzlqdeWGqM=" - }, - "font": "Arial;13;0", - "left": 360, - "top": 405, - "width": 78.630859375 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 360, - "top": 380, - "width": 78.630859375, - "height": 40, - "nameCompartment": { - "$ref": "AAAAAAF71ZzlqdeY8QE=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71Zzlqted/WE=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71ZzlqteefqY=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71ZzlqtefI5I=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71Z0EUNe9T24=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71Z0EUNe+u/Q=", - "_parent": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "model": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71Z0EUNe/P0k=", - "_parent": { - "$ref": "AAAAAAF71Z0EUNe+u/Q=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 448, - "top": -224, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z0EUNfAXNA=", - "_parent": { - "$ref": "AAAAAAF71Z0EUNe+u/Q=" - }, - "font": "Arial;13;1", - "left": 836, - "top": 343, - "width": 176, - "height": 13, - "text": "ESTABILISHED" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z0EUNfB5SM=", - "_parent": { - "$ref": "AAAAAAF71Z0EUNe+u/Q=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 448, - "top": -224, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z0EUNfCvlU=", - "_parent": { - "$ref": "AAAAAAF71Z0EUNe+u/Q=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 448, - "top": -224, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 831, - "top": 336, - "width": 186, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71Z0EUNe/P0k=" - }, - "nameLabel": { - "$ref": "AAAAAAF71Z0EUNfAXNA=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71Z0EUNfB5SM=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Z0EUNfCvlU=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71Z0EUNfDJFM=", - "_parent": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "model": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 320, - "top": -112, - "width": 10, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71Z0EUNfEqZo=", - "_parent": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "model": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 320, - "top": -112, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71Z0EUNfFp1Q=", - "_parent": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "model": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "font": "Arial;13;0", - "left": 831, - "top": 361, - "width": 186 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 831, - "top": 336, - "width": 186, - "height": 128, - "nameCompartment": { - "$ref": "AAAAAAF71Z0EUNe+u/Q=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71Z0EUNfDJFM=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71Z0EUNfEqZo=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71Z0EUNfFp1Q=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71Z1Lrtgvbps=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Z1LrtguLzI=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71Z1LrtgwKm8=", - "_parent": { - "$ref": "AAAAAAF71Z1Lrtgvbps=" - }, - "model": { - "$ref": "AAAAAAF71Z1LrtguLzI=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71Z1Lrtgxx4o=", - "_parent": { - "$ref": "AAAAAAF71Z1LrtgwKm8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 524, - "top": -304, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z1LrtgyaJI=", - "_parent": { - "$ref": "AAAAAAF71Z1LrtgwKm8=" - }, - "font": "Arial;13;1", - "left": 941, - "top": 553, - "width": 76.5654296875, - "height": 13, - "text": "FIN_WAIT_1" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z1LrtgzMI0=", - "_parent": { - "$ref": "AAAAAAF71Z1LrtgwKm8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 524, - "top": -304, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z1Lrtg0oQ0=", - "_parent": { - "$ref": "AAAAAAF71Z1LrtgwKm8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 524, - "top": -304, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 936, - "top": 546, - "width": 86.5654296875, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71Z1Lrtgxx4o=" - }, - "nameLabel": { - "$ref": "AAAAAAF71Z1LrtgyaJI=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71Z1LrtgzMI0=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Z1Lrtg0oQ0=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71Z1Lrtg17JY=", - "_parent": { - "$ref": "AAAAAAF71Z1Lrtgvbps=" - }, - "model": { - "$ref": "AAAAAAF71Z1LrtguLzI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 398, - "top": -152, - "width": 10, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71Z1Lrtg2YN4=", - "_parent": { - "$ref": "AAAAAAF71Z1Lrtgvbps=" - }, - "model": { - "$ref": "AAAAAAF71Z1LrtguLzI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 398, - "top": -152, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71Z1Lrtg3qoY=", - "_parent": { - "$ref": "AAAAAAF71Z1Lrtgvbps=" - }, - "model": { - "$ref": "AAAAAAF71Z1LrtguLzI=" - }, - "font": "Arial;13;0", - "left": 936, - "top": 571, - "width": 86.5654296875 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 936, - "top": 546, - "width": 86.5654296875, - "height": 40, - "nameCompartment": { - "$ref": "AAAAAAF71Z1LrtgwKm8=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71Z1Lrtg17JY=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71Z1Lrtg2YN4=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71Z1Lrtg3qoY=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71Z1c2dhVZ9M=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Z1c2dhUU7k=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71Z1c2dhWrfY=", - "_parent": { - "$ref": "AAAAAAF71Z1c2dhVZ9M=" - }, - "model": { - "$ref": "AAAAAAF71Z1c2dhUU7k=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71Z1c2dhXUpI=", - "_parent": { - "$ref": "AAAAAAF71Z1c2dhWrfY=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -26, - "top": -40, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z1c2dhY7nw=", - "_parent": { - "$ref": "AAAAAAF71Z1c2dhWrfY=" - }, - "font": "Arial;13;1", - "left": 941, - "top": 675, - "width": 76.5654296875, - "height": 13, - "text": "FIN_WAIT_2" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z1c2dhZgSw=", - "_parent": { - "$ref": "AAAAAAF71Z1c2dhWrfY=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -26, - "top": -40, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z1c2dhaBgc=", - "_parent": { - "$ref": "AAAAAAF71Z1c2dhWrfY=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -26, - "top": -40, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 936, - "top": 668, - "width": 86.5654296875, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71Z1c2dhXUpI=" - }, - "nameLabel": { - "$ref": "AAAAAAF71Z1c2dhY7nw=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71Z1c2dhZgSw=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Z1c2dhaBgc=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71Z1c2dhbjIM=", - "_parent": { - "$ref": "AAAAAAF71Z1c2dhVZ9M=" - }, - "model": { - "$ref": "AAAAAAF71Z1c2dhUU7k=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 123, - "top": -20, - "width": 10, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71Z1c2dhc2Ls=", - "_parent": { - "$ref": "AAAAAAF71Z1c2dhVZ9M=" - }, - "model": { - "$ref": "AAAAAAF71Z1c2dhUU7k=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 123, - "top": -20, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71Z1c2dhdbW4=", - "_parent": { - "$ref": "AAAAAAF71Z1c2dhVZ9M=" - }, - "model": { - "$ref": "AAAAAAF71Z1c2dhUU7k=" - }, - "font": "Arial;13;0", - "left": 936, - "top": 693, - "width": 86.5654296875 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 936, - "top": 668, - "width": 86.5654296875, - "height": 40, - "nameCompartment": { - "$ref": "AAAAAAF71Z1c2dhWrfY=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71Z1c2dhbjIM=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71Z1c2dhc2Ls=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71Z1c2dhdbW4=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71Z2HCNh8Q0E=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71Z2HCNh972s=", - "_parent": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "model": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71Z2HCNh+O5w=", - "_parent": { - "$ref": "AAAAAAF71Z2HCNh972s=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 92, - "top": 592, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z2HCNh/HDw=", - "_parent": { - "$ref": "AAAAAAF71Z2HCNh972s=" - }, - "font": "Arial;13;1", - "left": 941, - "top": 797, - "width": 75.84814453125, - "height": 13, - "text": "TIME_WAIT" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z2HCNiAKik=", - "_parent": { - "$ref": "AAAAAAF71Z2HCNh972s=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 92, - "top": 592, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z2HCNiBxEg=", - "_parent": { - "$ref": "AAAAAAF71Z2HCNh972s=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 92, - "top": 592, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 936, - "top": 790, - "width": 85.84814453125, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71Z2HCNh+O5w=" - }, - "nameLabel": { - "$ref": "AAAAAAF71Z2HCNh/HDw=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71Z2HCNiAKik=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Z2HCNiBxEg=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71Z2HCNiCxG4=", - "_parent": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "model": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 174, - "top": 296, - "width": 10, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71Z2HCNiDv5g=", - "_parent": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "model": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 174, - "top": 296, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71Z2HCNiE6ZQ=", - "_parent": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "model": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "font": "Arial;13;0", - "left": 936, - "top": 815, - "width": 85.84814453125 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 936, - "top": 790, - "width": 85.84814453125, - "height": 65, - "nameCompartment": { - "$ref": "AAAAAAF71Z2HCNh972s=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71Z2HCNiCxG4=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71Z2HCNiDv5g=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71Z2HCNiE6ZQ=" - } - }, - { - "_type": "UMLStateView", - "_id": "AAAAAAF71Z2dgNiiQNE=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Z2dgNiheI4=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71Z2dgNijfKo=", - "_parent": { - "$ref": "AAAAAAF71Z2dgNiiQNE=" - }, - "model": { - "$ref": "AAAAAAF71Z2dgNiheI4=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71Z2dgNikoj4=", - "_parent": { - "$ref": "AAAAAAF71Z2dgNijfKo=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -240, - "top": 528, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z2dgNiln5U=", - "_parent": { - "$ref": "AAAAAAF71Z2dgNijfKo=" - }, - "font": "Arial;13;1", - "left": 829, - "top": 615, - "width": 69.34814453125, - "height": 13, - "text": "LAST_ACK" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z2dgNimaxU=", - "_parent": { - "$ref": "AAAAAAF71Z2dgNijfKo=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -240, - "top": 528, - "width": 38.27001953125, - "height": 13, - "text": "(from )" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71Z2dgNinq+Y=", - "_parent": { - "$ref": "AAAAAAF71Z2dgNijfKo=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -240, - "top": 528, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 824, - "top": 608, - "width": 79.34814453125, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71Z2dgNikoj4=" - }, - "nameLabel": { - "$ref": "AAAAAAF71Z2dgNiln5U=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71Z2dgNimaxU=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Z2dgNinq+Y=" - } - }, - { - "_type": "UMLInternalActivityCompartmentView", - "_id": "AAAAAAF71Z2dgNioxIU=", - "_parent": { - "$ref": "AAAAAAF71Z2dgNiiQNE=" - }, - "model": { - "$ref": "AAAAAAF71Z2dgNiheI4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -64, - "top": 264, - "width": 10, - "height": 10 - }, - { - "_type": "UMLInternalTransitionCompartmentView", - "_id": "AAAAAAF71Z2dgNipJLI=", - "_parent": { - "$ref": "AAAAAAF71Z2dgNiiQNE=" - }, - "model": { - "$ref": "AAAAAAF71Z2dgNiheI4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -64, - "top": 264, - "width": 10, - "height": 10 - }, - { - "_type": "UMLDecompositionCompartmentView", - "_id": "AAAAAAF71Z2dgdiq4Ts=", - "_parent": { - "$ref": "AAAAAAF71Z2dgNiiQNE=" - }, - "model": { - "$ref": "AAAAAAF71Z2dgNiheI4=" - }, - "font": "Arial;13;0", - "left": 824, - "top": 633, - "width": 79.34814453125 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 824, - "top": 608, - "width": 79.34814453125, - "height": 40, - "nameCompartment": { - "$ref": "AAAAAAF71Z2dgNijfKo=" - }, - "internalActivityCompartment": { - "$ref": "AAAAAAF71Z2dgNioxIU=" - }, - "internalTransitionCompartment": { - "$ref": "AAAAAAF71Z2dgNipJLI=" - }, - "decompositionCompartment": { - "$ref": "AAAAAAF71Z2dgdiq4Ts=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71Z3SZtjJ8pE=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Z3SZdjI3Dk=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z3SZtjKbDQ=", - "_parent": { - "$ref": "AAAAAAF71Z3SZtjJ8pE=" - }, - "model": { - "$ref": "AAAAAAF71Z3SZdjI3Dk=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 102, - "top": 378, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71Z3SZtjJ8pE=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z3SZtjLe3c=", - "_parent": { - "$ref": "AAAAAAF71Z3SZtjJ8pE=" - }, - "model": { - "$ref": "AAAAAAF71Z3SZdjI3Dk=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 102, - "top": 363, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71Z3SZtjJ8pE=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z3SZtjMGjM=", - "_parent": { - "$ref": "AAAAAAF71Z3SZtjJ8pE=" - }, - "model": { - "$ref": "AAAAAAF71Z3SZdjI3Dk=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 102, - "top": 408, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71Z3SZtjJ8pE=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "tail": { - "$ref": "AAAAAAF71ZyFD9b2Zik=" - }, - "lineStyle": 1, - "points": "44:399;160:399", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71Z3SZtjKbDQ=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71Z3SZtjLe3c=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Z3SZtjMGjM=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71Z4W9NjeHR8=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Z4W9NjdKoA=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z4W9NjfUCI=", - "_parent": { - "$ref": "AAAAAAF71Z4W9NjeHR8=" - }, - "model": { - "$ref": "AAAAAAF71Z4W9NjdKoA=" - }, - "font": "Arial;13;0", - "left": 224, - "top": 350, - "width": 133.6435546875, - "height": 13, - "alpha": -4.66480611466427, - "distance": 42.04759208325728, - "hostEdge": { - "$ref": "AAAAAAF71Z4W9NjeHR8=" - }, - "edgePosition": 1, - "text": "CONNECT / send SYN" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z4W9NjgqEI=", - "_parent": { - "$ref": "AAAAAAF71Z4W9NjeHR8=" - }, - "model": { - "$ref": "AAAAAAF71Z4W9NjdKoA=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 292, - "top": 363, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71Z4W9NjeHR8=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z4W9NjhE7Q=", - "_parent": { - "$ref": "AAAAAAF71Z4W9NjeHR8=" - }, - "model": { - "$ref": "AAAAAAF71Z4W9NjdKoA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 292, - "top": 408, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71Z4W9NjeHR8=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71ZzlqdeXtDA=" - }, - "tail": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "lineStyle": 1, - "points": "225:399;359:399", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71Z4W9NjfUCI=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71Z4W9NjgqEI=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Z4W9NjhE7Q=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71Z6vLNjzO2k=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71Z6vLNjyd/I=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z6vLNj0LpY=", - "_parent": { - "$ref": "AAAAAAF71Z6vLNjzO2k=" - }, - "model": { - "$ref": "AAAAAAF71Z6vLNjyd/I=" - }, - "font": "Arial;13;0", - "left": 103, - "top": 283, - "width": 52.72998046875, - "height": 13, - "alpha": -4.648645547943322, - "distance": 47.095647357266465, - "hostEdge": { - "$ref": "AAAAAAF71Z6vLNjzO2k=" - }, - "edgePosition": 1, - "text": "ACCEPT" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z6vLNj1exY=", - "_parent": { - "$ref": "AAAAAAF71Z6vLNjzO2k=" - }, - "model": { - "$ref": "AAAAAAF71Z6vLNjyd/I=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 146, - "top": 279, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71Z6vLNjzO2k=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71Z6vLNj2UKg=", - "_parent": { - "$ref": "AAAAAAF71Z6vLNjzO2k=" - }, - "model": { - "$ref": "AAAAAAF71Z6vLNjyd/I=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 190, - "top": 280, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71Z6vLNjzO2k=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Zy/8NdLthg=" - }, - "tail": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "points": "176:380;176:192", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71Z6vLNj0LpY=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71Z6vLNj1exY=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71Z6vLNj2UKg=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71bwXtdywQlk=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71bwXtNyvzN4=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71bwXtdyxzS8=", - "_parent": { - "$ref": "AAAAAAF71bwXtdywQlk=" - }, - "model": { - "$ref": "AAAAAAF71bwXtNyvzN4=" - }, - "font": "Arial;13;0", - "left": 932, - "top": 240, - "width": 322.8291015625, - "height": 13, - "alpha": 1.5884414367692108, - "distance": 170.02646852769718, - "hostEdge": { - "$ref": "AAAAAAF71bwXtdywQlk=" - }, - "edgePosition": 1, - "text": "SOCKET_READABLE [get_type(recvpkt) = DATA_ACK]" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71bwXtdyyjnY=", - "_parent": { - "$ref": "AAAAAAF71bwXtdywQlk=" - }, - "model": { - "$ref": "AAAAAAF71bwXtNyvzN4=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 952, - "top": 242, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71bwXtdywQlk=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71bwXtdyz4/c=", - "_parent": { - "$ref": "AAAAAAF71bwXtdywQlk=" - }, - "model": { - "$ref": "AAAAAAF71bwXtNyvzN4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 908, - "top": 243, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71bwXtdywQlk=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "tail": { - "$ref": "AAAAAAF71ZzQTddxhV8=" - }, - "lineStyle": 1, - "points": "923:164;923:335", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71bwXtdyxzS8=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71bwXtdyyjnY=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71bwXtdyz4/c=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71b01ZdzFW7g=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71b01ZdzER2I=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71b01ZdzG1Lc=", - "_parent": { - "$ref": "AAAAAAF71b01ZdzFW7g=" - }, - "model": { - "$ref": "AAAAAAF71b01ZdzER2I=" - }, - "font": "Arial;13;0", - "left": 895, - "top": 87, - "width": 58.49365234375, - "height": 13, - "alpha": 1.2924960074125513, - "distance": 14.560219778561036, - "hostEdge": { - "$ref": "AAAAAAF71b01ZdzFW7g=" - }, - "edgePosition": 1, - "text": "TIMEOUT" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71b01ZdzHniw=", - "_parent": { - "$ref": "AAAAAAF71b01ZdzFW7g=" - }, - "model": { - "$ref": "AAAAAAF71b01ZdzER2I=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 920, - "top": 71, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71b01ZdzFW7g=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71b01ZdzI/6w=", - "_parent": { - "$ref": "AAAAAAF71b01ZdzFW7g=" - }, - "model": { - "$ref": "AAAAAAF71b01ZdzER2I=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 920, - "top": 116, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71b01ZdzFW7g=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71ZzQTddxhV8=" - }, - "tail": { - "$ref": "AAAAAAF71ZzQTddxhV8=" - }, - "points": "896:124;896:107;944:107;944:124", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71b01ZdzG1Lc=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71b01ZdzHniw=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71b01ZdzI/6w=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71b7TddzbM5g=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71b7TddzamsM=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71b7Tdtzc1eg=", - "_parent": { - "$ref": "AAAAAAF71b7TddzbM5g=" - }, - "model": { - "$ref": "AAAAAAF71b7TddzamsM=" - }, - "font": "Arial;13;0", - "left": 439, - "top": 351, - "width": 386.18505859375, - "height": 13, - "alpha": -4.663647372398203, - "distance": 41.048751503547585, - "hostEdge": { - "$ref": "AAAAAAF71b7TddzbM5g=" - }, - "edgePosition": 1, - "text": "SOCKET_READABLE [get_type(recvpkt) = SYN_ACK] / send ACK" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71b7TdtzdEek=", - "_parent": { - "$ref": "AAAAAAF71b7TddzbM5g=" - }, - "model": { - "$ref": "AAAAAAF71b7TddzamsM=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 634, - "top": 363, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71b7TddzbM5g=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71b7TdtzehME=", - "_parent": { - "$ref": "AAAAAAF71b7TddzbM5g=" - }, - "model": { - "$ref": "AAAAAAF71b7TddzamsM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 634, - "top": 408, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71b7TddzbM5g=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "tail": { - "$ref": "AAAAAAF71ZzlqdeXtDA=" - }, - "lineStyle": 1, - "points": "439:399;830:399", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71b7Tdtzc1eg=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71b7TdtzdEek=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71b7TdtzehME=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71d3wYd0pcXw=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71d3wYd0o95E=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d3wYt0q088=", - "_parent": { - "$ref": "AAAAAAF71d3wYd0pcXw=" - }, - "model": { - "$ref": "AAAAAAF71d3wYd0o95E=" - }, - "font": "Arial;13;0", - "left": 992, - "top": 728, - "width": 374.60693359375, - "height": 13, - "alpha": 1.6403355121252206, - "distance": 201.48697228356974, - "hostEdge": { - "$ref": "AAAAAAF71d3wYd0pcXw=" - }, - "edgePosition": 1, - "text": "SOCKET_READABLE [get_type(recvpkt) = FIN] / send FIN_ACK" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d3wYt0rEWI=", - "_parent": { - "$ref": "AAAAAAF71d3wYd0pcXw=" - }, - "model": { - "$ref": "AAAAAAF71d3wYd0o95E=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 1007, - "top": 741, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71d3wYd0pcXw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d3wYt0sQcs=", - "_parent": { - "$ref": "AAAAAAF71d3wYd0pcXw=" - }, - "model": { - "$ref": "AAAAAAF71d3wYd0o95E=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 963, - "top": 742, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71d3wYd0pcXw=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "tail": { - "$ref": "AAAAAAF71Z1c2dhVZ9M=" - }, - "lineStyle": 1, - "points": "978:708;978:789", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71d3wYt0q088=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71d3wYt0rEWI=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71d3wYt0sQcs=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71d35kt06rHA=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71d35kt05RPQ=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d35kt07AOk=", - "_parent": { - "$ref": "AAAAAAF71d35kt06rHA=" - }, - "model": { - "$ref": "AAAAAAF71d35kt05RPQ=" - }, - "font": "Arial;13;0", - "left": 994, - "top": 616, - "width": 311.02880859375, - "height": 13, - "alpha": 1.5941836293043063, - "distance": 171.04677722775136, - "hostEdge": { - "$ref": "AAAAAAF71d35kt06rHA=" - }, - "edgePosition": 1, - "text": "SOCKET_READABLE [get_type(recvpkt) = FIN_ACK]" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d35kt08/Es=", - "_parent": { - "$ref": "AAAAAAF71d35kt06rHA=" - }, - "model": { - "$ref": "AAAAAAF71d35kt05RPQ=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 1007, - "top": 619, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71d35kt06rHA=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d35kt09TdE=", - "_parent": { - "$ref": "AAAAAAF71d35kt06rHA=" - }, - "model": { - "$ref": "AAAAAAF71d35kt05RPQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 963, - "top": 620, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71d35kt06rHA=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z1c2dhVZ9M=" - }, - "tail": { - "$ref": "AAAAAAF71Z1Lrtgvbps=" - }, - "lineStyle": 1, - "points": "978:586;978:667", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71d35kt07AOk=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71d35kt08/Es=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71d35kt09TdE=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71d4DBt1LZmw=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71d4DBt1KPU4=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4DBt1M1Bo=", - "_parent": { - "$ref": "AAAAAAF71d4DBt1LZmw=" - }, - "model": { - "$ref": "AAAAAAF71d4DBt1KPU4=" - }, - "font": "Arial;13;0", - "left": 992, - "top": 488, - "width": 107.64990234375, - "height": 13, - "alpha": 1.7147215175159791, - "distance": 69.72087205421343, - "hostEdge": { - "$ref": "AAAAAAF71d4DBt1LZmw=" - }, - "edgePosition": 1, - "text": "CLOSE / send FIN" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4DBt1NIww=", - "_parent": { - "$ref": "AAAAAAF71d4DBt1LZmw=" - }, - "model": { - "$ref": "AAAAAAF71d4DBt1KPU4=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 1005, - "top": 497, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71d4DBt1LZmw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4DBt1OwSs=", - "_parent": { - "$ref": "AAAAAAF71d4DBt1LZmw=" - }, - "model": { - "$ref": "AAAAAAF71d4DBt1KPU4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 961, - "top": 498, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71d4DBt1LZmw=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z1Lrtgvbps=" - }, - "tail": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "points": "976:463;976:546", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71d4DBt1M1Bo=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71d4DBt1NIww=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71d4DBt1OwSs=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71d4Mid1cFnE=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71d4Mid1bbXo=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4Mid1d3iw=", - "_parent": { - "$ref": "AAAAAAF71d4Mid1cFnE=" - }, - "model": { - "$ref": "AAAAAAF71d4Mid1bbXo=" - }, - "font": "Arial;13;0", - "left": 440, - "top": 511, - "width": 411.4423828125, - "height": 13, - "alpha": -1.6482666824831247, - "distance": 219.65882636488797, - "hostEdge": { - "$ref": "AAAAAAF71d4Mid1cFnE=" - }, - "edgePosition": 1, - "text": "SOCKET_READABLE [get_type(recvpkt) = FIN] / send FIN_ACK & FIN" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4Mid1e+Zo=", - "_parent": { - "$ref": "AAAAAAF71d4Mid1cFnE=" - }, - "model": { - "$ref": "AAAAAAF71d4Mid1bbXo=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 893, - "top": 528, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71d4Mid1cFnE=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4Mid1fo18=", - "_parent": { - "$ref": "AAAAAAF71d4Mid1cFnE=" - }, - "model": { - "$ref": "AAAAAAF71d4Mid1bbXo=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 849, - "top": 529, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71d4Mid1cFnE=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z2dgNiiQNE=" - }, - "tail": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "points": "864:463;864:608", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71d4Mid1d3iw=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71d4Mid1e+Zo=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71d4Mid1fo18=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71d4b991tCUI=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71d4b991sS6A=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4b991upaE=", - "_parent": { - "$ref": "AAAAAAF71d4b991tCUI=" - }, - "model": { - "$ref": "AAAAAAF71d4b991sS6A=" - }, - "font": "Arial;13;0", - "left": 359, - "top": 606, - "width": 351.13330078125, - "height": 13, - "alpha": -1.512757561683065, - "distance": 327.55152266475574, - "hostEdge": { - "$ref": "AAAAAAF71d4b991tCUI=" - }, - "edgePosition": 1, - "text": "SOCKET_READABLE [get_type(recvpkt) = FIN_ACK]" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4b991vJmw=", - "_parent": { - "$ref": "AAAAAAF71d4b991tCUI=" - }, - "model": { - "$ref": "AAAAAAF71d4b991sS6A=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 178, - "top": 625, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71d4b991tCUI=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4b991wGrM=", - "_parent": { - "$ref": "AAAAAAF71d4b991tCUI=" - }, - "model": { - "$ref": "AAAAAAF71d4b991sS6A=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 223, - "top": 626, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71d4b991tCUI=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "tail": { - "$ref": "AAAAAAF71Z2dgNiiQNE=" - }, - "points": "824:632;208:632;208:419", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71d4b991upaE=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71d4b991vJmw=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71d4b991wGrM=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71d4rTd1+lEg=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71d4rTd19w0Q=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4rTd1/ihY=", - "_parent": { - "$ref": "AAAAAAF71d4rTd1+lEg=" - }, - "model": { - "$ref": "AAAAAAF71d4rTd19w0Q=" - }, - "font": "Arial;13;0", - "left": 439, - "top": 782, - "width": 311.02880859375, - "height": 13, - "alpha": -1.54350779146244, - "distance": 403.15009611805874, - "hostEdge": { - "$ref": "AAAAAAF71d4rTd1+lEg=" - }, - "edgePosition": 1, - "text": "SOCKET_READABLE [get_type(recvpkt) = FIN_ACK]" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4rTd2A8go=", - "_parent": { - "$ref": "AAAAAAF71d4rTd1+lEg=" - }, - "model": { - "$ref": "AAAAAAF71d4rTd19w0Q=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 162, - "top": 793, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71d4rTd1+lEg=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d4rTd2Ba9g=", - "_parent": { - "$ref": "AAAAAAF71d4rTd1+lEg=" - }, - "model": { - "$ref": "AAAAAAF71d4rTd19w0Q=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 207, - "top": 794, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71d4rTd1+lEg=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "tail": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "points": "936:800;192:800;192:419", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71d4rTd1/ihY=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71d4rTd2A8go=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71d4rTd2Ba9g=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71d5le92PpDo=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71d5le92OJ3s=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d5le92Qn3E=", - "_parent": { - "$ref": "AAAAAAF71d5le92PpDo=" - }, - "model": { - "$ref": "AAAAAAF71d5le92OJ3s=" - }, - "font": "Arial;13;0", - "left": 551, - "top": 830, - "width": 103.263671875, - "height": 13, - "alpha": -1.5450408763636045, - "distance": 427.1416626834709, - "hostEdge": { - "$ref": "AAAAAAF71d5le92PpDo=" - }, - "edgePosition": 1, - "text": "FINAL_TIMEOUT" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d5le92RTdE=", - "_parent": { - "$ref": "AAAAAAF71d5le92PpDo=" - }, - "model": { - "$ref": "AAAAAAF71d5le92OJ3s=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 146, - "top": 841, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71d5le92PpDo=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71d5le92Snrk=", - "_parent": { - "$ref": "AAAAAAF71d5le92PpDo=" - }, - "model": { - "$ref": "AAAAAAF71d5le92OJ3s=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 191, - "top": 842, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71d5le92PpDo=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "tail": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "points": "936:848;176:848;176:419", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71d5le92Qn3E=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71d5le92RTdE=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71d5le92Snrk=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71ekM1eAXQak=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71ekM1OAWpGA=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71ekM1eAYl1g=", - "_parent": { - "$ref": "AAAAAAF71ekM1eAXQak=" - }, - "model": { - "$ref": "AAAAAAF71ekM1OAWpGA=" - }, - "font": "Arial;13;0", - "left": 1063, - "top": 813, - "width": 156.03173828125, - "height": 13, - "alpha": 1.581104738878441, - "distance": 97.0051545022222, - "hostEdge": { - "$ref": "AAAAAAF71ekM1eAXQak=" - }, - "edgePosition": 1, - "text": "TIMEOUT / send FIN_ACK" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71ekM1eAZegU=", - "_parent": { - "$ref": "AAAAAAF71ekM1eAXQak=" - }, - "model": { - "$ref": "AAAAAAF71ekM1OAWpGA=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 1073, - "top": 813, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71ekM1eAXQak=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71ekM1eAaRi4=", - "_parent": { - "$ref": "AAAAAAF71ekM1eAXQak=" - }, - "model": { - "$ref": "AAAAAAF71ekM1OAWpGA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 1029, - "top": 814, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71ekM1eAXQak=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "tail": { - "$ref": "AAAAAAF71Z2HCNh8Q0E=" - }, - "points": "1021:800;1044:800;1044:840;1021:840", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71ekM1eAYl1g=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71ekM1eAZegU=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71ekM1eAaRi4=" - } - }, - { - "_type": "UMLPseudostateView", - "_id": "AAAAAAF71e79/+BjNCI=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71e79/+BifxY=" - }, - "subViews": [ - { - "_type": "NodeLabelView", - "_id": "AAAAAAF71e79/+BkjQU=", - "_parent": { - "$ref": "AAAAAAF71e79/+BjNCI=" - }, - "model": { - "$ref": "AAAAAAF71e79/+BifxY=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 787.5, - "top": 146, - "height": 13, - "alpha": 2.356194490192345, - "distance": 20 - }, - { - "_type": "NodeLabelView", - "_id": "AAAAAAF71e79/+BlLFA=", - "_parent": { - "$ref": "AAAAAAF71e79/+BjNCI=" - }, - "model": { - "$ref": "AAAAAAF71e79/+BifxY=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 777.5, - "top": 136, - "height": 13, - "alpha": 2.356194490192345, - "distance": 35 - }, - { - "_type": "NodeLabelView", - "_id": "AAAAAAF71e79/+BmPjA=", - "_parent": { - "$ref": "AAAAAAF71e79/+BjNCI=" - }, - "model": { - "$ref": "AAAAAAF71e79/+BifxY=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 787.5, - "top": 175, - "height": 13, - "alpha": -2.356194490192345, - "distance": 20 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 800, - "top": 127, - "width": 6, - "height": 82, - "nameLabel": { - "$ref": "AAAAAAF71e79/+BkjQU=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71e79/+BlLFA=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71e79/+BmPjA=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71e8a7+B0unI=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71e8a7+BzdQI=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71e8a7+B1UIM=", - "_parent": { - "$ref": "AAAAAAF71e8a7+B0unI=" - }, - "model": { - "$ref": "AAAAAAF71e8a7+BzdQI=" - }, - "font": "Arial;13;0", - "left": 326, - "top": 119, - "width": 386.18505859375, - "height": 13, - "alpha": -4.66480611466427, - "distance": 42.04759208325728, - "hostEdge": { - "$ref": "AAAAAAF71e8a7+B0unI=" - }, - "edgePosition": 1, - "text": "SOCKET_READABLE [get_type(recvpkt) = SYN] / send SYN_ACK" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71e8a7+B2T+w=", - "_parent": { - "$ref": "AAAAAAF71e8a7+B0unI=" - }, - "model": { - "$ref": "AAAAAAF71e8a7+BzdQI=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 521, - "top": 132, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71e8a7+B0unI=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71e8a7+B3LFs=", - "_parent": { - "$ref": "AAAAAAF71e8a7+B0unI=" - }, - "model": { - "$ref": "AAAAAAF71e8a7+BzdQI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 521, - "top": 177, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71e8a7+B0unI=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71e79/+BjNCI=" - }, - "tail": { - "$ref": "AAAAAAF71Zy/8NdLthg=" - }, - "points": "242:168;800:168", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71e8a7+B1UIM=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71e8a7+B2T+w=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71e8a7+B3LFs=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71e88EOCFK5k=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71e88EOCEujQ=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71e88EOCG878=", - "_parent": { - "$ref": "AAAAAAF71e88EOCFK5k=" - }, - "model": { - "$ref": "AAAAAAF71e88EOCEujQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 844, - "top": 123, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71e88EOCFK5k=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71e88EOCHBMI=", - "_parent": { - "$ref": "AAAAAAF71e88EOCFK5k=" - }, - "model": { - "$ref": "AAAAAAF71e88EOCEujQ=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 844, - "top": 108, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71e88EOCFK5k=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71e88EOCI7wI=", - "_parent": { - "$ref": "AAAAAAF71e88EOCFK5k=" - }, - "model": { - "$ref": "AAAAAAF71e88EOCEujQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 844, - "top": 153, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71e88EOCFK5k=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71ZzQTddxhV8=" - }, - "tail": { - "$ref": "AAAAAAF71e79/+BjNCI=" - }, - "points": "805:144;884:144", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71e88EOCG878=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71e88EOCHBMI=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71e88EOCI7wI=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71fO7nODfy3s=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71fO7nODezH4=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71fO7nODgbr8=", - "_parent": { - "$ref": "AAAAAAF71fO7nODfy3s=" - }, - "model": { - "$ref": "AAAAAAF71fO7nODezH4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 871, - "top": 233, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71fO7nODfy3s=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71fO7nODhRhQ=", - "_parent": { - "$ref": "AAAAAAF71fO7nODfy3s=" - }, - "model": { - "$ref": "AAAAAAF71fO7nODezH4=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 871, - "top": 248, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71fO7nODfy3s=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71fO7nODiCnk=", - "_parent": { - "$ref": "AAAAAAF71fO7nODfy3s=" - }, - "model": { - "$ref": "AAAAAAF71fO7nODezH4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 872, - "top": 203, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71fO7nODfy3s=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71ZyLwNcH0cE=" - }, - "tail": { - "$ref": "AAAAAAF71e79/+BjNCI=" - }, - "points": "805:192;872:192;872:224;208:224;208:380", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71fO7nODgbr8=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71fO7nODhRhQ=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71fO7nODiCnk=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71kqnVOGiwcw=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71kqnU+Gh48Y=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71kqnVOGj/q0=", - "_parent": { - "$ref": "AAAAAAF71kqnVOGiwcw=" - }, - "model": { - "$ref": "AAAAAAF71kqnU+Gh48Y=" - }, - "font": "Arial;13;0", - "left": 1047, - "top": 353, - "width": 36.1181640625, - "height": 13, - "alpha": 1.620754072579196, - "distance": 20.024984394500787, - "hostEdge": { - "$ref": "AAAAAAF71kqnVOGiwcw=" - }, - "edgePosition": 1, - "text": "SEND" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71kqnVOGkOKg=", - "_parent": { - "$ref": "AAAAAAF71kqnVOGiwcw=" - }, - "model": { - "$ref": "AAAAAAF71kqnU+Gh48Y=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 1074, - "top": 353, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71kqnVOGiwcw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71kqnVeGlQhE=", - "_parent": { - "$ref": "AAAAAAF71kqnVOGiwcw=" - }, - "model": { - "$ref": "AAAAAAF71kqnU+Gh48Y=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 1030, - "top": 354, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71kqnVOGiwcw=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "tail": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "points": "1016:344;1045:344;1045:376;1016:376", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71kqnVOGj/q0=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71kqnVOGkOKg=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71kqnVeGlQhE=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71kriK+G1FJ8=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71kriK+G0xEA=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71kriK+G2dho=", - "_parent": { - "$ref": "AAAAAAF71kriK+G1FJ8=" - }, - "model": { - "$ref": "AAAAAAF71kriK+G0xEA=" - }, - "font": "Arial;13;0", - "left": 1047, - "top": 393, - "width": 36.1181640625, - "height": 13, - "alpha": 1.620754072579196, - "distance": 20.024984394500787, - "hostEdge": { - "$ref": "AAAAAAF71kriK+G1FJ8=" - }, - "edgePosition": 1, - "text": "RECV" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71kriLOG3Ctg=", - "_parent": { - "$ref": "AAAAAAF71kriK+G1FJ8=" - }, - "model": { - "$ref": "AAAAAAF71kriK+G0xEA=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 1074, - "top": 393, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71kriK+G1FJ8=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71kriLOG4fgM=", - "_parent": { - "$ref": "AAAAAAF71kriK+G1FJ8=" - }, - "model": { - "$ref": "AAAAAAF71kriK+G0xEA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 1030, - "top": 394, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71kriK+G1FJ8=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "tail": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "points": "1016:384;1045:384;1045:416;1016:416", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71kriK+G2dho=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71kriLOG3Ctg=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71kriLOG4fgM=" - } - }, - { - "_type": "UMLTransitionView", - "_id": "AAAAAAF71ksKH+HIanQ=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71ksKH+HHLH4=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71ksKH+HJbVw=", - "_parent": { - "$ref": "AAAAAAF71ksKH+HIanQ=" - }, - "model": { - "$ref": "AAAAAAF71ksKH+HHLH4=" - }, - "font": "Arial;13;0", - "left": 1047, - "top": 433, - "width": 58.49365234375, - "height": 13, - "alpha": 1.6030425738351435, - "distance": 31.016124838541646, - "hostEdge": { - "$ref": "AAAAAAF71ksKH+HIanQ=" - }, - "edgePosition": 1, - "text": "TIMEOUT" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71ksKH+HKOSA=", - "_parent": { - "$ref": "AAAAAAF71ksKH+HIanQ=" - }, - "model": { - "$ref": "AAAAAAF71ksKH+HHLH4=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 1074, - "top": 433, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71ksKH+HIanQ=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71ksKIOHL68s=", - "_parent": { - "$ref": "AAAAAAF71ksKH+HIanQ=" - }, - "model": { - "$ref": "AAAAAAF71ksKH+HHLH4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 1030, - "top": 434, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71ksKH+HIanQ=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "tail": { - "$ref": "AAAAAAF71Z0EUNe9T24=" - }, - "points": "1016:424;1045:424;1045:456;1016:456", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71ksKH+HJbVw=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71ksKH+HKOSA=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71ksKIOHL68s=" - } - }, - { - "_type": "HyperlinkView", - "_id": "AAAAAAF71oXYXuWFwXY=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71oXYXeWDyOE=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71oXYXuWGJdU=", - "_parent": { - "$ref": "AAAAAAF71oXYXuWFwXY=" - }, - "font": "Arial;13;0", - "left": 277.39453125, - "top": 373, - "width": 58.53173828125, - "height": 13, - "text": "send SYN" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71oXYXuWHv2Q=", - "_parent": { - "$ref": "AAAAAAF71oXYXuWFwXY=" - }, - "font": "Arial;13;1", - "left": 245, - "top": 373, - "width": 23.39453125, - "height": 13, - "text": "link" - } - ], - "font": "Arial;13;0", - "left": 240, - "top": 368, - "width": 101.92626953125, - "height": 23, - "nameLabel": { - "$ref": "AAAAAAF71oXYXuWGJdU=" - }, - "typeLabel": { - "$ref": "AAAAAAF71oXYXuWHv2Q=" - } - }, - { - "_type": "HyperlinkView", - "_id": "AAAAAAF71oa71uWUp8s=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71oa71uWSfsE=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71oa71+WVaZU=", - "_parent": { - "$ref": "AAAAAAF71oa71uWUp8s=" - }, - "font": "Arial;13;0", - "left": 493.39453125, - "top": 141, - "width": 92.49169921875, - "height": 13, - "text": "send SYN_ACK" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71oa71+WWb6w=", - "_parent": { - "$ref": "AAAAAAF71oa71uWUp8s=" - }, - "font": "Arial;13;1", - "left": 461, - "top": 141, - "width": 23.39453125, - "height": 13, - "text": "link" - } - ], - "font": "Arial;13;0", - "left": 456, - "top": 136, - "width": 135.88623046875, - "height": 23, - "nameLabel": { - "$ref": "AAAAAAF71oa71+WVaZU=" - }, - "typeLabel": { - "$ref": "AAAAAAF71oa71+WWb6w=" - } - }, - { - "_type": "HyperlinkView", - "_id": "AAAAAAF71rAAwuePpig=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF71rAAwueNolo=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71rAAwueQIME=", - "_parent": { - "$ref": "AAAAAAF71rAAwuePpig=" - }, - "font": "Arial;13;0", - "left": 613.39453125, - "top": 373, - "width": 58.53173828125, - "height": 13, - "text": "send ACK" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71rAAw+eRIrQ=", - "_parent": { - "$ref": "AAAAAAF71rAAwuePpig=" - }, - "font": "Arial;13;1", - "left": 581, - "top": 373, - "width": 23.39453125, - "height": 13, - "text": "link" - } - ], - "font": "Arial;13;0", - "left": 576, - "top": 368, - "width": 101.92626953125, - "height": 23, - "nameLabel": { - "$ref": "AAAAAAF71rAAwueQIME=" - }, - "typeLabel": { - "$ref": "AAAAAAF71rAAw+eRIrQ=" - } - }, - { - "_type": "HyperlinkView", - "_id": "AAAAAAF72PQo9OfMeRA=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF72PQo9OfKZlA=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF72PQo9efNEuE=", - "_parent": { - "$ref": "AAAAAAF72PQo9OfMeRA=" - }, - "font": "Arial;13;0", - "left": 597.39453125, - "top": 533, - "width": 123.5380859375, - "height": 13, - "text": "send FIN_ACK & FIN" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF72PQo9efO/Wo=", - "_parent": { - "$ref": "AAAAAAF72PQo9OfMeRA=" - }, - "font": "Arial;13;1", - "left": 565, - "top": 533, - "width": 23.39453125, - "height": 13, - "text": "link" - } - ], - "font": "Arial;13;0", - "left": 560, - "top": 528, - "width": 166.9326171875, - "height": 23, - "nameLabel": { - "$ref": "AAAAAAF72PQo9efNEuE=" - }, - "typeLabel": { - "$ref": "AAAAAAF72PQo9efO/Wo=" - } - }, - { - "_type": "HyperlinkView", - "_id": "AAAAAAF72PZFZufj72o=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF72PZFZufhnUI=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF72PZFZ+fkrbo=", - "_parent": { - "$ref": "AAAAAAF72PZFZufj72o=" - }, - "font": "Arial;13;0", - "left": 1029.39453125, - "top": 517, - "width": 52.74267578125, - "height": 13, - "text": "send FIN" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF72PZFZ+flIpY=", - "_parent": { - "$ref": "AAAAAAF72PZFZufj72o=" - }, - "font": "Arial;13;1", - "left": 997, - "top": 517, - "width": 23.39453125, - "height": 13, - "text": "link" - } - ], - "font": "Arial;13;0", - "left": 992, - "top": 512, - "width": 100, - "height": 23, - "nameLabel": { - "$ref": "AAAAAAF72PZFZ+fkrbo=" - }, - "typeLabel": { - "$ref": "AAAAAAF72PZFZ+flIpY=" - } - }, - { - "_type": "HyperlinkView", - "_id": "AAAAAAF72PoLE+gAhi0=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbwmYg=" - }, - "model": { - "$ref": "AAAAAAF72PoLEuf+KRM=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF72PoLE+gBIKk=", - "_parent": { - "$ref": "AAAAAAF72PoLE+gAhi0=" - }, - "font": "Arial;13;0", - "left": 1141.39453125, - "top": 765, - "width": 86.70263671875, - "height": 13, - "text": "send FIN_ACK" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF72PoLE+gCKzs=", - "_parent": { - "$ref": "AAAAAAF72PoLE+gAhi0=" - }, - "font": "Arial;13;1", - "left": 1109, - "top": 765, - "width": 23.39453125, - "height": 13, - "text": "link" - } - ], - "font": "Arial;13;0", - "left": 1104, - "top": 760, - "width": 130.09716796875, - "height": 23, - "nameLabel": { - "$ref": "AAAAAAF72PoLE+gBIKk=" - }, - "typeLabel": { - "$ref": "AAAAAAF72PoLE+gCKzs=" - } - } - ] - }, - { - "_type": "Hyperlink", - "_id": "AAAAAAF71oXYXeWDyOE=", - "_parent": { - "$ref": "AAAAAAF71Zx6n9buWyM=" - }, - "name": "Hyperlink1", - "reference": { - "$ref": "AAAAAAF71nwZDOSR3is=" - } - }, - { - "_type": "Hyperlink", - "_id": "AAAAAAF71oa71uWSfsE=", - "_parent": { - "$ref": "AAAAAAF71Zx6n9buWyM=" - }, - "name": "Hyperlink2", - "reference": { - "$ref": "AAAAAAF71m6fguKhHgM=" - } - }, - { - "_type": "Hyperlink", - "_id": "AAAAAAF71rAAwueNolo=", - "_parent": { - "$ref": "AAAAAAF71Zx6n9buWyM=" - }, - "name": "Hyperlink3", - "reference": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - } - }, - { - "_type": "Hyperlink", - "_id": "AAAAAAF72PQo9OfKZlA=", - "_parent": { - "$ref": "AAAAAAF71Zx6n9buWyM=" - }, - "name": "Hyperlink4", - "reference": { - "$ref": "AAAAAAF72PO2refG4WQ=" - } - }, - { - "_type": "Hyperlink", - "_id": "AAAAAAF72PZFZufhnUI=", - "_parent": { - "$ref": "AAAAAAF71Zx6n9buWyM=" - }, - "name": "Hyperlink5", - "reference": { - "$ref": "AAAAAAF72PaBxOftVjs=" - } - }, - { - "_type": "Hyperlink", - "_id": "AAAAAAF72PoLEuf+KRM=", - "_parent": { - "$ref": "AAAAAAF71Zx6n9buWyM=" - }, - "name": "Hyperlink6", - "reference": { - "$ref": "AAAAAAF72PnUXef6Fgo=" - } - } - ], - "regions": [ - { - "_type": "UMLRegion", - "_id": "AAAAAAF71Zx6oNbvFXQ=", - "_parent": { - "$ref": "AAAAAAF71Zx6n9buWyM=" - }, - "vertices": [ - { - "_type": "UMLPseudostate", - "_id": "AAAAAAF71ZyFD9b1lBU=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "kind": "initial" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71ZyLv9cG+m8=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "CLOSED" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71Zy/8NdKhrk=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "LISTEN" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71ZzQTddw1Ps=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "SYN_RCVD" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71ZzlqdeWGqM=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "SYN_SENT" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71Z0EUNe8u34=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "ESTABILISHED" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71Z0dCNfikD8=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "SENDING" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71Z0wKdgIQC4=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "RECEIVING" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71Z1LrtguLzI=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "FIN_WAIT_1" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71Z1c2dhUU7k=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "FIN_WAIT_2" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71Z2HCNh7G7o=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "TIME_WAIT" - }, - { - "_type": "UMLState", - "_id": "AAAAAAF71Z2dgNiheI4=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "name": "LAST_ACK" - }, - { - "_type": "UMLPseudostate", - "_id": "AAAAAAF71e79/+BifxY=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "kind": "fork" - } - ], - "transitions": [ - { - "_type": "UMLTransition", - "_id": "AAAAAAF71Z3SZdjI3Dk=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71ZyFD9b1lBU=" - }, - "target": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - } - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71Z4W9NjdKoA=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "target": { - "$ref": "AAAAAAF71ZzlqdeWGqM=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71Z4futjvp2U=", - "_parent": { - "$ref": "AAAAAAF71Z4W9NjdKoA=" - }, - "name": "CONNECT", - "kind": "call" - } - ], - "effects": [ - { - "_type": "UMLActivity", - "_id": "AAAAAAF71npkouSGWL4=", - "_parent": { - "$ref": "AAAAAAF71Z4W9NjdKoA=" - }, - "name": "send SYN", - "ownedElements": [ - { - "_type": "UMLActivityDiagram", - "_id": "AAAAAAF71nwZDOSR3is=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "name": "send SYN", - "ownedViews": [ - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71nwp/eSWGtQ=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71nwp/eSU2jo=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 646, - "top": 224, - "width": 20, - "height": 20 - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71nyRIuSb+ys=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71nyRIeSZTiQ=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71nyRIuScpmA=", - "_parent": { - "$ref": "AAAAAAF71nyRIuSb+ys=" - }, - "model": { - "$ref": "AAAAAAF71nyRIeSZTiQ=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71nyRIuSdTJw=", - "_parent": { - "$ref": "AAAAAAF71nyRIuScpmA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71nyRIuSe5Ww=", - "_parent": { - "$ref": "AAAAAAF71nyRIuScpmA=" - }, - "font": "Arial;13;1", - "left": 631, - "top": 313, - "width": 50, - "height": 13, - "text": "bind" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71nyRIuSfg4Q=", - "_parent": { - "$ref": "AAAAAAF71nyRIuScpmA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "width": 96.8017578125, - "height": 13, - "text": "(from send SYN)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71nyRIuSgZv0=", - "_parent": { - "$ref": "AAAAAAF71nyRIuScpmA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 626, - "top": 306, - "width": 60, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71nyRIuSdTJw=" - }, - "nameLabel": { - "$ref": "AAAAAAF71nyRIuSe5Ww=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71nyRIuSfg4Q=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71nyRIuSgZv0=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 626, - "top": 306, - "width": 60, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71nyRIuScpmA=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71nylYOS1Qjs=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71nylX+Szzfc=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71nylYOS2EMg=", - "_parent": { - "$ref": "AAAAAAF71nylYOS1Qjs=" - }, - "model": { - "$ref": "AAAAAAF71nylX+Szzfc=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71nylYOS38uc=", - "_parent": { - "$ref": "AAAAAAF71nylYOS2EMg=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71nylYOS45X4=", - "_parent": { - "$ref": "AAAAAAF71nylYOS2EMg=" - }, - "font": "Arial;13;1", - "left": 588, - "top": 416, - "width": 135.36376953125, - "height": 13, - "text": "client_isn 🠔 random()" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71nylYOS5lro=", - "_parent": { - "$ref": "AAAAAAF71nylYOS2EMg=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "width": 96.8017578125, - "height": 13, - "text": "(from send SYN)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71nylYOS6DzE=", - "_parent": { - "$ref": "AAAAAAF71nylYOS2EMg=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 583, - "top": 409, - "width": 145.36376953125, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71nylYOS38uc=" - }, - "nameLabel": { - "$ref": "AAAAAAF71nylYOS45X4=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71nylYOS5lro=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71nylYOS6DzE=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 583, - "top": 409, - "width": 145.36376953125, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71nylYOS2EMg=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71n4RpeTl8Co=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71n4RpeTjn6w=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71n4RpeTmazw=", - "_parent": { - "$ref": "AAAAAAF71n4RpeTl8Co=" - }, - "model": { - "$ref": "AAAAAAF71n4RpeTjn6w=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71n4RpeTnogw=", - "_parent": { - "$ref": "AAAAAAF71n4RpeTmazw=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -112, - "top": 32, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71n4RpeToatQ=", - "_parent": { - "$ref": "AAAAAAF71n4RpeTmazw=" - }, - "font": "Arial;13;1", - "left": 532, - "top": 519, - "width": 247, - "height": 13, - "text": "pkt 🠔 make_pkt(SYN, client_isn, NULL)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71n4RpeTpNGs=", - "_parent": { - "$ref": "AAAAAAF71n4RpeTmazw=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -112, - "top": 32, - "width": 96.8017578125, - "height": 13, - "text": "(from send SYN)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71n4RpeTqpmE=", - "_parent": { - "$ref": "AAAAAAF71n4RpeTmazw=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -112, - "top": 32, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 527, - "top": 512, - "width": 257, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71n4RpeTnogw=" - }, - "nameLabel": { - "$ref": "AAAAAAF71n4RpeToatQ=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71n4RpeTpNGs=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71n4RpeTqpmE=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 527, - "top": 512, - "width": 257, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71n4RpeTmazw=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71n+z1eUHcg8=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71n+z1OUF8/g=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71n+z1eUISSg=", - "_parent": { - "$ref": "AAAAAAF71n+z1eUHcg8=" - }, - "model": { - "$ref": "AAAAAAF71n+z1OUF8/g=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71n+z1eUJjEs=", - "_parent": { - "$ref": "AAAAAAF71n+z1eUISSg=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71n+z1eUKIAk=", - "_parent": { - "$ref": "AAAAAAF71n+z1eUISSg=" - }, - "font": "Arial;13;1", - "left": 577, - "top": 622, - "width": 158.9072265625, - "height": 13, - "text": "udp_send(pkt, dest_addr)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71n+z1eULiD4=", - "_parent": { - "$ref": "AAAAAAF71n+z1eUISSg=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "width": 96.8017578125, - "height": 13, - "text": "(from send SYN)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71n+z1eUMV/Y=", - "_parent": { - "$ref": "AAAAAAF71n+z1eUISSg=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -32, - "top": 112, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 572, - "top": 615, - "width": 168.9072265625, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71n+z1eUJjEs=" - }, - "nameLabel": { - "$ref": "AAAAAAF71n+z1eUKIAk=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71n+z1eULiD4=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71n+z1eUMV/Y=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 572, - "top": 615, - "width": 168.9072265625, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71n+z1eUISSg=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71oBSSOUjFAw=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71oBSSOUixQM=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oBSSOUktL0=", - "_parent": { - "$ref": "AAAAAAF71oBSSOUjFAw=" - }, - "model": { - "$ref": "AAAAAAF71oBSSOUixQM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 669, - "top": 267, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oBSSOUjFAw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oBSSOUlABY=", - "_parent": { - "$ref": "AAAAAAF71oBSSOUjFAw=" - }, - "model": { - "$ref": "AAAAAAF71oBSSOUixQM=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 684, - "top": 267, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71oBSSOUjFAw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oBSSOUmPgI=", - "_parent": { - "$ref": "AAAAAAF71oBSSOUjFAw=" - }, - "model": { - "$ref": "AAAAAAF71oBSSOUixQM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 640, - "top": 268, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oBSSOUjFAw=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71nyRIuSb+ys=" - }, - "tail": { - "$ref": "AAAAAAF71nwp/eSWGtQ=" - }, - "lineStyle": 1, - "points": "655:244;655:305", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71oBSSOUktL0=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71oBSSOUlABY=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71oBSSOUmPgI=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71oBd6+U0Vbs=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71oBd6+UzfV0=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oBd6+U1Wy8=", - "_parent": { - "$ref": "AAAAAAF71oBd6+U0Vbs=" - }, - "model": { - "$ref": "AAAAAAF71oBd6+UzfV0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 669, - "top": 370, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oBd6+U0Vbs=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oBd6+U2WKQ=", - "_parent": { - "$ref": "AAAAAAF71oBd6+U0Vbs=" - }, - "model": { - "$ref": "AAAAAAF71oBd6+UzfV0=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 684, - "top": 370, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71oBd6+U0Vbs=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oBd6+U3VyI=", - "_parent": { - "$ref": "AAAAAAF71oBd6+U0Vbs=" - }, - "model": { - "$ref": "AAAAAAF71oBd6+UzfV0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 640, - "top": 371, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oBd6+U0Vbs=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71nylYOS1Qjs=" - }, - "tail": { - "$ref": "AAAAAAF71nyRIuSb+ys=" - }, - "lineStyle": 1, - "points": "655:347;655:408", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71oBd6+U1Wy8=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71oBd6+U2WKQ=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71oBd6+U3VyI=" - } - }, - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71oLXAOVGSwA=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71oLW/uVEGCU=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 643, - "top": 718, - "width": 26, - "height": 26 - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71oLj6OVKPb4=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71oLj6OVJoJI=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oLj6eVLq3U=", - "_parent": { - "$ref": "AAAAAAF71oLj6OVKPb4=" - }, - "model": { - "$ref": "AAAAAAF71oLj6OVJoJI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 669, - "top": 473, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oLj6OVKPb4=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oLj6eVMRbw=", - "_parent": { - "$ref": "AAAAAAF71oLj6OVKPb4=" - }, - "model": { - "$ref": "AAAAAAF71oLj6OVJoJI=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 684, - "top": 473, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71oLj6OVKPb4=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oLj6eVNp0w=", - "_parent": { - "$ref": "AAAAAAF71oLj6OVKPb4=" - }, - "model": { - "$ref": "AAAAAAF71oLj6OVJoJI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 640, - "top": 474, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oLj6OVKPb4=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71n4RpeTl8Co=" - }, - "tail": { - "$ref": "AAAAAAF71nylYOS1Qjs=" - }, - "lineStyle": 1, - "points": "655:450;655:511", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71oLj6eVLq3U=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71oLj6eVMRbw=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71oLj6eVNp0w=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71oLvyOVb0cw=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71oLvyOVasMI=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oLvyOVczlQ=", - "_parent": { - "$ref": "AAAAAAF71oLvyOVb0cw=" - }, - "model": { - "$ref": "AAAAAAF71oLvyOVasMI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 669, - "top": 576, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oLvyOVb0cw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oLvyOVd2cc=", - "_parent": { - "$ref": "AAAAAAF71oLvyOVb0cw=" - }, - "model": { - "$ref": "AAAAAAF71oLvyOVasMI=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 684, - "top": 576, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71oLvyOVb0cw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oLvyOVeSu0=", - "_parent": { - "$ref": "AAAAAAF71oLvyOVb0cw=" - }, - "model": { - "$ref": "AAAAAAF71oLvyOVasMI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 640, - "top": 577, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oLvyOVb0cw=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71n+z1eUHcg8=" - }, - "tail": { - "$ref": "AAAAAAF71n4RpeTl8Co=" - }, - "lineStyle": 1, - "points": "655:553;655:614", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71oLvyOVczlQ=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71oLvyOVd2cc=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71oLvyOVeSu0=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71oL6muVsCCE=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71oL6muVrffI=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oL6muVtHDE=", - "_parent": { - "$ref": "AAAAAAF71oL6muVsCCE=" - }, - "model": { - "$ref": "AAAAAAF71oL6muVrffI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 669, - "top": 679, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oL6muVsCCE=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oL6muVuOuo=", - "_parent": { - "$ref": "AAAAAAF71oL6muVsCCE=" - }, - "model": { - "$ref": "AAAAAAF71oL6muVrffI=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 684, - "top": 679, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71oL6muVsCCE=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71oL6muVvoy8=", - "_parent": { - "$ref": "AAAAAAF71oL6muVsCCE=" - }, - "model": { - "$ref": "AAAAAAF71oL6muVrffI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 640, - "top": 680, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71oL6muVsCCE=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71oLXAOVGSwA=" - }, - "tail": { - "$ref": "AAAAAAF71n+z1eUHcg8=" - }, - "lineStyle": 1, - "points": "655:656;655:717", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71oL6muVtHDE=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71oL6muVuOuo=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71oL6muVvoy8=" - } - }, - { - "_type": "UMLFrameView", - "_id": "AAAAAAF71pk5+OXY1Oo=", - "_parent": { - "$ref": "AAAAAAF71nwZDOSR3is=" - }, - "model": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71pk5+OXZruk=", - "_parent": { - "$ref": "AAAAAAF71pk5+OXY1Oo=" - }, - "font": "Arial;13;0", - "left": 473.9873046875, - "top": 189, - "width": 54.08544921875, - "height": 13, - "text": "send SYN" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71pk5+OXa2fg=", - "_parent": { - "$ref": "AAAAAAF71pk5+OXY1Oo=" - }, - "font": "Arial;13;1", - "left": 429, - "top": 189, - "width": 39.9873046875, - "height": 13, - "text": "activity" - } - ], - "font": "Arial;13;0", - "left": 424, - "top": 184, - "width": 465, - "height": 649, - "nameLabel": { - "$ref": "AAAAAAF71pk5+OXZruk=" - }, - "frameTypeLabel": { - "$ref": "AAAAAAF71pk5+OXa2fg=" - } - } - ] - } - ], - "nodes": [ - { - "_type": "UMLInitialNode", - "_id": "AAAAAAF71nwp/eSU2jo=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "name": "InitialNode1" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71nyRIeSZTiQ=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "name": "bind" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71nylX+Szzfc=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "name": "client_isn 🠔 random()" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71n4RpeTjn6w=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "name": "pkt 🠔 make_pkt(SYN, client_isn, NULL)" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71n+z1OUF8/g=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "name": "udp_send(pkt, dest_addr)" - }, - { - "_type": "UMLActivityFinalNode", - "_id": "AAAAAAF71oLW/uVEGCU=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "name": "ActivityFinalNode1" - } - ], - "edges": [ - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71oBSSOUixQM=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "source": { - "$ref": "AAAAAAF71nwp/eSU2jo=" - }, - "target": { - "$ref": "AAAAAAF71nyRIeSZTiQ=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71oBd6+UzfV0=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "source": { - "$ref": "AAAAAAF71nyRIeSZTiQ=" - }, - "target": { - "$ref": "AAAAAAF71nylX+Szzfc=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71oLj6OVJoJI=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "source": { - "$ref": "AAAAAAF71nylX+Szzfc=" - }, - "target": { - "$ref": "AAAAAAF71n4RpeTjn6w=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71oLvyOVasMI=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "source": { - "$ref": "AAAAAAF71n4RpeTjn6w=" - }, - "target": { - "$ref": "AAAAAAF71n+z1OUF8/g=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71oL6muVrffI=", - "_parent": { - "$ref": "AAAAAAF71npkouSGWL4=" - }, - "source": { - "$ref": "AAAAAAF71n+z1OUF8/g=" - }, - "target": { - "$ref": "AAAAAAF71oLW/uVEGCU=" - } - } - ] - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71Z6vLNjyd/I=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "target": { - "$ref": "AAAAAAF71Zy/8NdKhrk=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71Z7BuNkEfAU=", - "_parent": { - "$ref": "AAAAAAF71Z6vLNjyd/I=" - }, - "name": "ACCEPT", - "kind": "call" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71bwXtNyvzN4=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - }, - "target": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "guard": "get_type(recvpkt) = DATA_ACK", - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71bzRy9zB154=", - "_parent": { - "$ref": "AAAAAAF71bwXtNyvzN4=" - }, - "name": "SOCKET_READABLE" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71b01ZdzER2I=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - }, - "target": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71b1RjtzW5Ns=", - "_parent": { - "$ref": "AAAAAAF71b01ZdzER2I=" - }, - "name": "TIMEOUT", - "kind": "time" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71b7TddzamsM=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71ZzlqdeWGqM=" - }, - "target": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "guard": "get_type(recvpkt) = SYN_ACK", - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71b7j7NzsJRk=", - "_parent": { - "$ref": "AAAAAAF71b7TddzamsM=" - }, - "name": "SOCKET_READABLE" - } - ], - "effects": [ - { - "_type": "UMLActivity", - "_id": "AAAAAAF71oyMIOWyC2Y=", - "_parent": { - "$ref": "AAAAAAF71b7TddzamsM=" - }, - "name": "send ACK", - "ownedElements": [ - { - "_type": "UMLActivityDiagram", - "_id": "AAAAAAF71pbZ9+W79fs=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "send ACK", - "ownedViews": [ - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71pcCl+XA8Ls=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71pcCl+W+An4=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 649, - "top": 160, - "width": 20, - "height": 20 - }, - { - "_type": "UMLFrameView", - "_id": "AAAAAAF71pm/euXnvKU=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71pm/euXoIRI=", - "_parent": { - "$ref": "AAAAAAF71pm/euXnvKU=" - }, - "font": "Arial;13;0", - "left": 417.9873046875, - "top": 69, - "width": 55.5263671875, - "height": 13, - "text": "send ACK" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71pm/euXpVok=", - "_parent": { - "$ref": "AAAAAAF71pm/euXnvKU=" - }, - "font": "Arial;13;1", - "left": 373, - "top": 69, - "width": 39.9873046875, - "height": 13, - "text": "activity" - } - ], - "font": "Arial;13;0", - "left": 368, - "top": 64, - "width": 529, - "height": 873, - "nameLabel": { - "$ref": "AAAAAAF71pm/euXoIRI=" - }, - "frameTypeLabel": { - "$ref": "AAAAAAF71pm/euXpVok=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71pnpp+X1LDc=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71pnpp+XznvM=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71pnpqOX2W8o=", - "_parent": { - "$ref": "AAAAAAF71pnpp+X1LDc=" - }, - "model": { - "$ref": "AAAAAAF71pnpp+XznvM=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71pnpqOX3qps=", - "_parent": { - "$ref": "AAAAAAF71pnpqOX2W8o=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 16, - "top": 16, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71pnpqOX4Sts=", - "_parent": { - "$ref": "AAAAAAF71pnpqOX2W8o=" - }, - "font": "Arial;13;1", - "left": 560, - "top": 235, - "width": 197.50732421875, - "height": 13, - "text": "rcvpkt 🠔 udp_recv(&sndr_addr)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71pnpqOX5erk=", - "_parent": { - "$ref": "AAAAAAF71pnpqOX2W8o=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 16, - "top": 16, - "width": 96.8017578125, - "height": 13, - "text": "(from send ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71pnpqOX6qoo=", - "_parent": { - "$ref": "AAAAAAF71pnpqOX2W8o=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 16, - "top": 16, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 555, - "top": 228, - "width": 207.50732421875, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71pnpqOX3qps=" - }, - "nameLabel": { - "$ref": "AAAAAAF71pnpqOX4Sts=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71pnpqOX5erk=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71pnpqOX6qoo=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 555, - "top": 228, - "width": 207.50732421875, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71pnpqOX2W8o=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71poDsOYO3to=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71poDr+YNPEI=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71poDsOYPtIU=", - "_parent": { - "$ref": "AAAAAAF71poDsOYO3to=" - }, - "model": { - "$ref": "AAAAAAF71poDr+YNPEI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 672, - "top": 196, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71poDsOYO3to=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71poDsOYQZqI=", - "_parent": { - "$ref": "AAAAAAF71poDsOYO3to=" - }, - "model": { - "$ref": "AAAAAAF71poDr+YNPEI=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 687, - "top": 196, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71poDsOYO3to=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71poDsOYRlK0=", - "_parent": { - "$ref": "AAAAAAF71poDsOYO3to=" - }, - "model": { - "$ref": "AAAAAAF71poDr+YNPEI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 643, - "top": 197, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71poDsOYO3to=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71pnpp+X1LDc=" - }, - "tail": { - "$ref": "AAAAAAF71pcCl+XA8Ls=" - }, - "lineStyle": 1, - "points": "658:180;658:227", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71poDsOYPtIU=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71poDsOYQZqI=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71poDsOYRlK0=" - } - }, - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71qPFyeYlq3I=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qPFyOYj5Kk=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 639, - "top": 317, - "width": 39, - "height": 41 - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71qQHNeYpwYQ=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qQHNeYoptE=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qQHNeYqiCk=", - "_parent": { - "$ref": "AAAAAAF71qQHNeYpwYQ=" - }, - "model": { - "$ref": "AAAAAAF71qQHNeYoptE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 672, - "top": 285, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71qQHNeYpwYQ=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qQHNuYrDE0=", - "_parent": { - "$ref": "AAAAAAF71qQHNeYpwYQ=" - }, - "model": { - "$ref": "AAAAAAF71qQHNeYoptE=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 687, - "top": 285, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71qQHNeYpwYQ=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qQHNuYs/X0=", - "_parent": { - "$ref": "AAAAAAF71qQHNeYpwYQ=" - }, - "model": { - "$ref": "AAAAAAF71qQHNeYoptE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 643, - "top": 286, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71qQHNeYpwYQ=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71qPFyeYlq3I=" - }, - "tail": { - "$ref": "AAAAAAF71pnpp+X1LDc=" - }, - "lineStyle": 1, - "points": "658:269;658:316", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71qQHNeYqiCk=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71qQHNuYrDE0=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71qQHNuYs/X0=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71qQeCOY7rto=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qQeCOY53rk=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71qQeCOY8g0k=", - "_parent": { - "$ref": "AAAAAAF71qQeCOY7rto=" - }, - "model": { - "$ref": "AAAAAAF71qQeCOY53rk=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71qQeCOY9YV4=", - "_parent": { - "$ref": "AAAAAAF71qQeCOY8g0k=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 126, - "top": 16, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71qQeCOY+Dys=", - "_parent": { - "$ref": "AAAAAAF71qQeCOY8g0k=" - }, - "font": "Arial;13;1", - "left": 602, - "top": 413, - "width": 114.17529296875, - "height": 13, - "text": "allocate resources" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71qQeCOY/GpI=", - "_parent": { - "$ref": "AAAAAAF71qQeCOY8g0k=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 126, - "top": 16, - "width": 96.8017578125, - "height": 13, - "text": "(from send ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71qQeCOZAEOA=", - "_parent": { - "$ref": "AAAAAAF71qQeCOY8g0k=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 126, - "top": 16, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 597, - "top": 406, - "width": 124.17529296875, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71qQeCOY9YV4=" - }, - "nameLabel": { - "$ref": "AAAAAAF71qQeCOY+Dys=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71qQeCOY/GpI=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71qQeCOZAEOA=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 597, - "top": 406, - "width": 124.17529296875, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71qQeCOY8g0k=" - } - }, - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71qX0WuZ35jA=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qX0WuZ1/WI=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 638, - "top": 762, - "width": 41, - "height": 43 - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71qX/c+Z7De0=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qX/c+Z67oc=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qX/c+Z82Vc=", - "_parent": { - "$ref": "AAAAAAF71qX/c+Z7De0=" - }, - "model": { - "$ref": "AAAAAAF71qX/c+Z67oc=" - }, - "font": "Arial;13;0", - "left": 408, - "top": 366, - "width": 223, - "height": 26, - "alpha": -1.5779908644693998, - "distance": 139.00359707575916, - "hostEdge": { - "$ref": "AAAAAAF71qX/c+Z7De0=" - }, - "edgePosition": 1, - "text": " [get_type(recvpkt) = SYN_ACK AND get_seqnum(rcvpkt) = client_isn + 1]", - "wordWrap": true - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qX/c+Z9coc=", - "_parent": { - "$ref": "AAAAAAF71qX/c+Z7De0=" - }, - "model": { - "$ref": "AAAAAAF71qX/c+Z67oc=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 687, - "top": 374, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71qX/c+Z7De0=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qX/c+Z+4yQ=", - "_parent": { - "$ref": "AAAAAAF71qX/c+Z7De0=" - }, - "model": { - "$ref": "AAAAAAF71qX/c+Z67oc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 643, - "top": 375, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71qX/c+Z7De0=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71qQeCOY7rto=" - }, - "tail": { - "$ref": "AAAAAAF71qPFyeYlq3I=" - }, - "lineStyle": 1, - "points": "658:358;658:405", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71qX/c+Z82Vc=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71qX/c+Z9coc=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71qX/c+Z+4yQ=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71qYLSuaMvbo=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qYLSuaLWQo=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qYLS+aNlWc=", - "_parent": { - "$ref": "AAAAAAF71qYLSuaMvbo=" - }, - "model": { - "$ref": "AAAAAAF71qYLSuaLWQo=" - }, - "font": "Arial;13;0", - "left": 831, - "top": 553, - "width": 34.68359375, - "height": 13, - "alpha": 1.6124388420508418, - "distance": 24.020824298928627, - "hostEdge": { - "$ref": "AAAAAAF71qYLSuaMvbo=" - }, - "edgePosition": 1, - "text": " [else]" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qYLS+aOD2g=", - "_parent": { - "$ref": "AAAAAAF71qYLSuaMvbo=" - }, - "model": { - "$ref": "AAAAAAF71qYLSuaLWQo=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 854, - "top": 553, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71qYLSuaMvbo=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qYLS+aP0zk=", - "_parent": { - "$ref": "AAAAAAF71qYLSuaMvbo=" - }, - "model": { - "$ref": "AAAAAAF71qYLSuaLWQo=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 809, - "top": 554, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71qYLSuaMvbo=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71qX0WuZ35jA=" - }, - "tail": { - "$ref": "AAAAAAF71qPFyeYlq3I=" - }, - "points": "677:336;824:336;824:784;678:784", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71qYLS+aNlWc=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71qYLS+aOD2g=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71qYLS+aP0zk=" - } - }, - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71qYuZ+agzq8=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qYuZ+ae1g8=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 646, - "top": 853, - "width": 26, - "height": 26 - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71qY7s+akBoU=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qY7s+ajEgA=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qY7s+alQ9c=", - "_parent": { - "$ref": "AAAAAAF71qY7s+akBoU=" - }, - "model": { - "$ref": "AAAAAAF71qY7s+ajEgA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 672, - "top": 821, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71qY7s+akBoU=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qY7s+amAF8=", - "_parent": { - "$ref": "AAAAAAF71qY7s+akBoU=" - }, - "model": { - "$ref": "AAAAAAF71qY7s+ajEgA=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 687, - "top": 821, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71qY7s+akBoU=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qY7s+anIFU=", - "_parent": { - "$ref": "AAAAAAF71qY7s+akBoU=" - }, - "model": { - "$ref": "AAAAAAF71qY7s+ajEgA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 643, - "top": 822, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71qY7s+akBoU=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71qYuZ+agzq8=" - }, - "tail": { - "$ref": "AAAAAAF71qX0WuZ35jA=" - }, - "lineStyle": 1, - "points": "658:805;658:852", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71qY7s+alQ9c=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71qY7s+amAF8=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71qY7s+anIFU=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71qy/cebZr1I=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71qy/cObXF5U=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71qy/cubaXno=", - "_parent": { - "$ref": "AAAAAAF71qy/cebZr1I=" - }, - "model": { - "$ref": "AAAAAAF71qy/cObXF5U=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71qy/cubbDRw=", - "_parent": { - "$ref": "AAAAAAF71qy/cubaXno=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -336, - "top": 48, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71qy/cubcbt0=", - "_parent": { - "$ref": "AAAAAAF71qy/cubaXno=" - }, - "font": "Arial;13;1", - "left": 555, - "top": 502, - "width": 208.3681640625, - "height": 13, - "text": "server_isn 🠔 get_data(rcvpkt)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71qy/cubdf04=", - "_parent": { - "$ref": "AAAAAAF71qy/cubaXno=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -336, - "top": 48, - "width": 96.8017578125, - "height": 13, - "text": "(from send ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71qy/cubeROc=", - "_parent": { - "$ref": "AAAAAAF71qy/cubaXno=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -336, - "top": 48, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 550, - "top": 495, - "width": 218.3681640625, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71qy/cubbDRw=" - }, - "nameLabel": { - "$ref": "AAAAAAF71qy/cubcbt0=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71qy/cubdf04=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71qy/cubeROc=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 550, - "top": 495, - "width": 218.3681640625, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71qy/cubaXno=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71q1S6Ob18pk=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71q1S6Ob0mW0=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q1S6Ob2J3o=", - "_parent": { - "$ref": "AAAAAAF71q1S6Ob18pk=" - }, - "model": { - "$ref": "AAAAAAF71q1S6Ob0mW0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 672, - "top": 463, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71q1S6Ob18pk=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q1S6Ob33tY=", - "_parent": { - "$ref": "AAAAAAF71q1S6Ob18pk=" - }, - "model": { - "$ref": "AAAAAAF71q1S6Ob0mW0=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 687, - "top": 463, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71q1S6Ob18pk=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q1S6Ob4gCw=", - "_parent": { - "$ref": "AAAAAAF71q1S6Ob18pk=" - }, - "model": { - "$ref": "AAAAAAF71q1S6Ob0mW0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 643, - "top": 464, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71q1S6Ob18pk=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71qy/cebZr1I=" - }, - "tail": { - "$ref": "AAAAAAF71qQeCOY7rto=" - }, - "lineStyle": 1, - "points": "658:447;658:494", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71q1S6Ob2J3o=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71q1S6Ob33tY=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71q1S6Ob4gCw=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71q1buOcHLTg=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71q1buOcFZUo=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71q1buOcIiCE=", - "_parent": { - "$ref": "AAAAAAF71q1buOcHLTg=" - }, - "model": { - "$ref": "AAAAAAF71q1buOcFZUo=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71q1buOcJ6oQ=", - "_parent": { - "$ref": "AAAAAAF71q1buOcIiCE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -400, - "top": 16, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71q1buOcKJMc=", - "_parent": { - "$ref": "AAAAAAF71q1buOcIiCE=" - }, - "font": "Arial;13;1", - "left": 523, - "top": 591, - "width": 271.57177734375, - "height": 13, - "text": "pkt 🠔 make_pkt(ACK, server_isn + 1, NULL)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71q1buOcLXt8=", - "_parent": { - "$ref": "AAAAAAF71q1buOcIiCE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -400, - "top": 16, - "width": 96.8017578125, - "height": 13, - "text": "(from send ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71q1buOcM5Zo=", - "_parent": { - "$ref": "AAAAAAF71q1buOcIiCE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -400, - "top": 16, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 518, - "top": 584, - "width": 281.57177734375, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71q1buOcJ6oQ=" - }, - "nameLabel": { - "$ref": "AAAAAAF71q1buOcKJMc=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71q1buOcLXt8=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71q1buOcM5Zo=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 518, - "top": 584, - "width": 281.57177734375, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71q1buOcIiCE=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71q3z+uckPws=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71q3z+uci6cM=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71q3z+uclwyc=", - "_parent": { - "$ref": "AAAAAAF71q3z+uckPws=" - }, - "model": { - "$ref": "AAAAAAF71q3z+uci6cM=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71q3z+ucmVeE=", - "_parent": { - "$ref": "AAAAAAF71q3z+uclwyc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -272, - "top": -16, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71q3z+ucnJfw=", - "_parent": { - "$ref": "AAAAAAF71q3z+uclwyc=" - }, - "font": "Arial;13;1", - "left": 580, - "top": 680, - "width": 158.9072265625, - "height": 13, - "text": "udp_send(pkt, dest_addr)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71q3z+ucohzU=", - "_parent": { - "$ref": "AAAAAAF71q3z+uclwyc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -272, - "top": -16, - "width": 96.8017578125, - "height": 13, - "text": "(from send ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71q3z+ucpFro=", - "_parent": { - "$ref": "AAAAAAF71q3z+uclwyc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -272, - "top": -16, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 575, - "top": 673, - "width": 168.9072265625, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71q3z+ucmVeE=" - }, - "nameLabel": { - "$ref": "AAAAAAF71q3z+ucnJfw=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71q3z+ucohzU=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71q3z+ucpFro=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 575, - "top": 673, - "width": 168.9072265625, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71q3z+uclwyc=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71q9apedYwhw=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71q9apedXnKQ=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9apedZc34=", - "_parent": { - "$ref": "AAAAAAF71q9apedYwhw=" - }, - "model": { - "$ref": "AAAAAAF71q9apedXnKQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 672, - "top": 552, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71q9apedYwhw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9apedaO/U=", - "_parent": { - "$ref": "AAAAAAF71q9apedYwhw=" - }, - "model": { - "$ref": "AAAAAAF71q9apedXnKQ=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 687, - "top": 552, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71q9apedYwhw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9apedbmKw=", - "_parent": { - "$ref": "AAAAAAF71q9apedYwhw=" - }, - "model": { - "$ref": "AAAAAAF71q9apedXnKQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 643, - "top": 553, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71q9apedYwhw=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71q1buOcHLTg=" - }, - "tail": { - "$ref": "AAAAAAF71qy/cebZr1I=" - }, - "lineStyle": 1, - "points": "658:536;658:583", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71q9apedZc34=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71q9apedaO/U=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71q9apedbmKw=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71q9jXOdpf1g=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71q9jXOdo8aE=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9jXedqQJM=", - "_parent": { - "$ref": "AAAAAAF71q9jXOdpf1g=" - }, - "model": { - "$ref": "AAAAAAF71q9jXOdo8aE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 672, - "top": 641, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71q9jXOdpf1g=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9jXedrTvc=", - "_parent": { - "$ref": "AAAAAAF71q9jXOdpf1g=" - }, - "model": { - "$ref": "AAAAAAF71q9jXOdo8aE=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 687, - "top": 641, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71q9jXOdpf1g=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9jXedscUQ=", - "_parent": { - "$ref": "AAAAAAF71q9jXOdpf1g=" - }, - "model": { - "$ref": "AAAAAAF71q9jXOdo8aE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 643, - "top": 642, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71q9jXOdpf1g=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71q3z+uckPws=" - }, - "tail": { - "$ref": "AAAAAAF71q1buOcHLTg=" - }, - "lineStyle": 1, - "points": "658:625;658:672", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71q9jXedqQJM=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71q9jXedrTvc=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71q9jXedscUQ=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71q9q9Od6xNs=", - "_parent": { - "$ref": "AAAAAAF71pbZ9+W79fs=" - }, - "model": { - "$ref": "AAAAAAF71q9q9Od5HgE=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9q9Od7ZUo=", - "_parent": { - "$ref": "AAAAAAF71q9q9Od6xNs=" - }, - "model": { - "$ref": "AAAAAAF71q9q9Od5HgE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 672, - "top": 730, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71q9q9Od6xNs=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9q9Od89FI=", - "_parent": { - "$ref": "AAAAAAF71q9q9Od6xNs=" - }, - "model": { - "$ref": "AAAAAAF71q9q9Od5HgE=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 687, - "top": 730, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71q9q9Od6xNs=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71q9q9Od9hA0=", - "_parent": { - "$ref": "AAAAAAF71q9q9Od6xNs=" - }, - "model": { - "$ref": "AAAAAAF71q9q9Od5HgE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 643, - "top": 731, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71q9q9Od6xNs=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71qX0WuZ35jA=" - }, - "tail": { - "$ref": "AAAAAAF71q3z+uckPws=" - }, - "lineStyle": 1, - "points": "658:714;658:761", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71q9q9Od7ZUo=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71q9q9Od89FI=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71q9q9Od9hA0=" - } - } - ] - } - ], - "nodes": [ - { - "_type": "UMLInitialNode", - "_id": "AAAAAAF71pcCl+W+An4=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "InitialNode1" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71pnpp+XznvM=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "rcvpkt 🠔 udp_recv(&sndr_addr)" - }, - { - "_type": "UMLDecisionNode", - "_id": "AAAAAAF71qPFyOYj5Kk=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "DecisionNode1" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71qQeCOY53rk=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "allocate resources" - }, - { - "_type": "UMLMergeNode", - "_id": "AAAAAAF71qX0WuZ1/WI=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "MergeNode1" - }, - { - "_type": "UMLActivityFinalNode", - "_id": "AAAAAAF71qYuZ+ae1g8=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "ActivityFinalNode1" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71qy/cObXF5U=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "server_isn 🠔 get_data(rcvpkt)" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71q1buOcFZUo=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "pkt 🠔 make_pkt(ACK, server_isn + 1, NULL)" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71q3z+uci6cM=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "name": "udp_send(pkt, dest_addr)" - } - ], - "edges": [ - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71poDr+YNPEI=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71pcCl+W+An4=" - }, - "target": { - "$ref": "AAAAAAF71pnpp+XznvM=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71qQHNeYoptE=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71pnpp+XznvM=" - }, - "target": { - "$ref": "AAAAAAF71qPFyOYj5Kk=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71qX/c+Z67oc=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71qPFyOYj5Kk=" - }, - "target": { - "$ref": "AAAAAAF71qQeCOY53rk=" - }, - "guard": "get_type(recvpkt) = SYN_ACK AND get_seqnum(rcvpkt) = client_isn + 1" - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71qYLSuaLWQo=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71qPFyOYj5Kk=" - }, - "target": { - "$ref": "AAAAAAF71qX0WuZ1/WI=" - }, - "guard": "else" - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71qY7s+ajEgA=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71qX0WuZ1/WI=" - }, - "target": { - "$ref": "AAAAAAF71qYuZ+ae1g8=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71q1S6Ob0mW0=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71qQeCOY53rk=" - }, - "target": { - "$ref": "AAAAAAF71qy/cObXF5U=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71q9apedXnKQ=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71qy/cObXF5U=" - }, - "target": { - "$ref": "AAAAAAF71q1buOcFZUo=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71q9jXOdo8aE=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71q1buOcFZUo=" - }, - "target": { - "$ref": "AAAAAAF71q3z+uci6cM=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71q9q9Od5HgE=", - "_parent": { - "$ref": "AAAAAAF71oyMIOWyC2Y=" - }, - "source": { - "$ref": "AAAAAAF71q3z+uci6cM=" - }, - "target": { - "$ref": "AAAAAAF71qX0WuZ1/WI=" - } - } - ] - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71d3wYd0o95E=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z1c2dhUU7k=" - }, - "target": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "guard": "get_type(recvpkt) = FIN", - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71gZtGuEYb1s=", - "_parent": { - "$ref": "AAAAAAF71d3wYd0o95E=" - }, - "name": "SOCKET_READABLE" - } - ], - "effects": [ - { - "_type": "UMLActivity", - "_id": "AAAAAAF72PkRp+f0yo0=", - "_parent": { - "$ref": "AAAAAAF71d3wYd0o95E=" - }, - "name": "send FIN_ACK", - "ownedElements": [ - { - "_type": "UMLActivityDiagram", - "_id": "AAAAAAF72PnUXef6Fgo=", - "_parent": { - "$ref": "AAAAAAF72PkRp+f0yo0=" - }, - "name": "send FIN_ACK" - } - ] - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71d35kt05RPQ=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z1LrtguLzI=" - }, - "target": { - "$ref": "AAAAAAF71Z1c2dhUU7k=" - }, - "guard": "get_type(recvpkt) = FIN_ACK", - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71gZnaeEVx0g=", - "_parent": { - "$ref": "AAAAAAF71d35kt05RPQ=" - }, - "name": "SOCKET_READABLE" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71d4DBt1KPU4=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "target": { - "$ref": "AAAAAAF71Z1LrtguLzI=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71enWZ+A06jU=", - "_parent": { - "$ref": "AAAAAAF71d4DBt1KPU4=" - }, - "name": "CLOSE" - } - ], - "effects": [ - { - "_type": "UMLActivity", - "_id": "AAAAAAF72PYToufbdfc=", - "_parent": { - "$ref": "AAAAAAF71d4DBt1KPU4=" - }, - "name": "send FIN", - "ownedElements": [ - { - "_type": "UMLActivityDiagram", - "_id": "AAAAAAF72PaBxOftVjs=", - "_parent": { - "$ref": "AAAAAAF72PYToufbdfc=" - }, - "name": "send FIN" - } - ] - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71d4Mid1bbXo=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "target": { - "$ref": "AAAAAAF71Z2dgNiheI4=" - }, - "guard": "get_type(recvpkt) = FIN", - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71f6P8eEKaUo=", - "_parent": { - "$ref": "AAAAAAF71d4Mid1bbXo=" - }, - "name": "SOCKET_READABLE" - } - ], - "effects": [ - { - "_type": "UMLActivity", - "_id": "AAAAAAF72PLQC+fAyZ8=", - "_parent": { - "$ref": "AAAAAAF71d4Mid1bbXo=" - }, - "name": "send FIN_ACK & FIN", - "ownedElements": [ - { - "_type": "UMLActivityDiagram", - "_id": "AAAAAAF72PO2refG4WQ=", - "_parent": { - "$ref": "AAAAAAF72PLQC+fAyZ8=" - }, - "name": "send FIN_ACK & FIN" - } - ] - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71d4b991sS6A=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z2dgNiheI4=" - }, - "target": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "guard": "get_type(recvpkt) = FIN_ACK", - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71f2bjeD/FA8=", - "_parent": { - "$ref": "AAAAAAF71d4b991sS6A=" - }, - "name": "SOCKET_READABLE" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71d4rTd19w0Q=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "target": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "guard": "get_type(recvpkt) = FIN_ACK", - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71eflieAQiZY=", - "_parent": { - "$ref": "AAAAAAF71d4rTd19w0Q=" - }, - "name": "SOCKET_READABLE" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71d5le92OJ3s=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "target": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71ee3quAM2GU=", - "_parent": { - "$ref": "AAAAAAF71d5le92OJ3s=" - }, - "name": "FINAL_TIMEOUT" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71ekM1OAWpGA=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "target": { - "$ref": "AAAAAAF71Z2HCNh7G7o=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71ek7eOAplF0=", - "_parent": { - "$ref": "AAAAAAF71ekM1OAWpGA=" - }, - "name": "TIMEOUT" - } - ], - "effects": [ - { - "_type": "UMLActivity", - "_id": "AAAAAAF72PuDhOgLfzk=", - "_parent": { - "$ref": "AAAAAAF71ekM1OAWpGA=" - }, - "name": "send FIN_ACK" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71e8a7+BzdQI=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Zy/8NdKhrk=" - }, - "target": { - "$ref": "AAAAAAF71e79/+BifxY=" - }, - "guard": "get_type(recvpkt) = SYN", - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71fCD1+CxbOs=", - "_parent": { - "$ref": "AAAAAAF71e8a7+BzdQI=" - }, - "name": "SOCKET_READABLE" - } - ], - "effects": [ - { - "_type": "UMLActivity", - "_id": "AAAAAAF71mw6YOHu6Ng=", - "_parent": { - "$ref": "AAAAAAF71e8a7+BzdQI=" - }, - "name": "send SYN_ACK", - "ownedElements": [ - { - "_type": "UMLActivityDiagram", - "_id": "AAAAAAF71m6fguKhHgM=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "send SYN_ACK", - "ownedViews": [ - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71m6fguKiHUk=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71ar8Atrfq6E=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 645, - "top": 96, - "width": 20, - "height": 20 - }, - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71m6fguKjYDg=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71asBodrkiIw=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 642, - "top": 834, - "width": 26, - "height": 26 - }, - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71m6fguKkGSU=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71asnidrqhqQ=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 637, - "top": 245, - "width": 36, - "height": 35 - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71m6fguKlPuo=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71aty6dsAuxk=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71m6fguKmJrU=", - "_parent": { - "$ref": "AAAAAAF71m6fguKlPuo=" - }, - "model": { - "$ref": "AAAAAAF71aty6dsAuxk=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fguKnV78=", - "_parent": { - "$ref": "AAAAAAF71m6fguKmJrU=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 496, - "top": -160, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fguKobZU=", - "_parent": { - "$ref": "AAAAAAF71m6fguKmJrU=" - }, - "font": "Arial;13;1", - "left": 598, - "top": 331, - "width": 114.17529296875, - "height": 13, - "text": "allocate resources" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fguKpVYo=", - "_parent": { - "$ref": "AAAAAAF71m6fguKmJrU=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 496, - "top": -160, - "width": 130.76171875, - "height": 13, - "text": "(from send SYN_ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fguKqG6E=", - "_parent": { - "$ref": "AAAAAAF71m6fguKmJrU=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 496, - "top": -160, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 593, - "top": 324, - "width": 124.17529296875, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fguKnV78=" - }, - "nameLabel": { - "$ref": "AAAAAAF71m6fguKobZU=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71m6fguKpVYo=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fguKqG6E=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 593, - "top": 324, - "width": 124.17529296875, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71m6fguKmJrU=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fguKrh/I=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71auAodsZt6A=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguKsyu0=", - "_parent": { - "$ref": "AAAAAAF71m6fguKrh/I=" - }, - "model": { - "$ref": "AAAAAAF71auAodsZt6A=" - }, - "font": "Arial;13;0", - "left": 490, - "top": 287, - "width": 152.8134765625, - "height": 13, - "alpha": -1.6501751048823456, - "distance": 88.2779700718135, - "hostEdge": { - "$ref": "AAAAAAF71m6fguKrh/I=" - }, - "edgePosition": 1, - "text": " [get_type(recvpkt) = SYN]" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguKtJHs=", - "_parent": { - "$ref": "AAAAAAF71m6fguKrh/I=" - }, - "model": { - "$ref": "AAAAAAF71auAodsZt6A=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 294, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fguKrh/I=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguKuF4A=", - "_parent": { - "$ref": "AAAAAAF71m6fguKrh/I=" - }, - "model": { - "$ref": "AAAAAAF71auAodsZt6A=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 295, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fguKrh/I=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71m6fguKlPuo=" - }, - "tail": { - "$ref": "AAAAAAF71m6fguKkGSU=" - }, - "lineStyle": 1, - "points": "654:280;654:323", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fguKsyu0=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fguKtJHs=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fguKuF4A=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fguKvUMo=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71avqxNs+ULI=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguKwY/I=", - "_parent": { - "$ref": "AAAAAAF71m6fguKvUMo=" - }, - "model": { - "$ref": "AAAAAAF71avqxNs+ULI=" - }, - "font": "Arial;13;0", - "left": 886, - "top": 509, - "width": 58.53173828125, - "height": 13, - "alpha": 1.623379268188951, - "distance": 19.026297590440446, - "hostEdge": { - "$ref": "AAAAAAF71m6fguKvUMo=" - }, - "edgePosition": 1, - "text": " [else]" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguKxy60=", - "_parent": { - "$ref": "AAAAAAF71m6fguKvUMo=" - }, - "model": { - "$ref": "AAAAAAF71avqxNs+ULI=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 926, - "top": 509, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fguKvUMo=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguKyN1U=", - "_parent": { - "$ref": "AAAAAAF71m6fguKvUMo=" - }, - "model": { - "$ref": "AAAAAAF71avqxNs+ULI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 881, - "top": 510, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fguKvUMo=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71qSZ5+ZWf5U=" - }, - "tail": { - "$ref": "AAAAAAF71m6fguKkGSU=" - }, - "points": "672:264;896:264;896:768;673:768", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fguKwY/I=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fguKxy60=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fguKyN1U=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71m6fguKzUeY=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71az2qdtg0N8=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71m6fguK0SC0=", - "_parent": { - "$ref": "AAAAAAF71m6fguKzUeY=" - }, - "model": { - "$ref": "AAAAAAF71az2qdtg0N8=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fguK1dRI=", - "_parent": { - "$ref": "AAAAAAF71m6fguK0SC0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 621, - "top": 102, - "width": 96.8017578125, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fguK2V5s=", - "_parent": { - "$ref": "AAAAAAF71m6fguK0SC0=" - }, - "font": "Arial;13;1", - "left": 517, - "top": 167, - "width": 276.3515625, - "height": 13, - "text": "rcvpkt 🠔 udp_recv(&sndr_addr)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fguK3zgA=", - "_parent": { - "$ref": "AAAAAAF71m6fguK0SC0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 621, - "top": 102, - "width": 130.76171875, - "height": 13, - "text": "(from send SYN_ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fguK4M08=", - "_parent": { - "$ref": "AAAAAAF71m6fguK0SC0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 240, - "top": -160, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 512, - "top": 160, - "width": 286.3515625, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fguK1dRI=" - }, - "nameLabel": { - "$ref": "AAAAAAF71m6fguK2V5s=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71m6fguK3zgA=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fguK4M08=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 512, - "top": 160, - "width": 286.3515625, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71m6fguK0SC0=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fguK5sRg=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71bLCZtupG20=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguK6Hfg=", - "_parent": { - "$ref": "AAAAAAF71m6fguK5sRg=" - }, - "model": { - "$ref": "AAAAAAF71bLCZtupG20=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 668, - "top": 130, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fguK5sRg=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguK7i1M=", - "_parent": { - "$ref": "AAAAAAF71m6fguK5sRg=" - }, - "model": { - "$ref": "AAAAAAF71bLCZtupG20=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 130, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fguK5sRg=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguK8aL0=", - "_parent": { - "$ref": "AAAAAAF71m6fguK5sRg=" - }, - "model": { - "$ref": "AAAAAAF71bLCZtupG20=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 131, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fguK5sRg=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71m6fguKzUeY=" - }, - "tail": { - "$ref": "AAAAAAF71m6fguKiHUk=" - }, - "lineStyle": 1, - "points": "654:116;654:159", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fguK6Hfg=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fguK7i1M=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fguK8aL0=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fguK96B0=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71bXEBtu8va8=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguK+BZ8=", - "_parent": { - "$ref": "AAAAAAF71m6fguK96B0=" - }, - "model": { - "$ref": "AAAAAAF71bXEBtu8va8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 668, - "top": 215, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fguK96B0=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fguK/R3o=", - "_parent": { - "$ref": "AAAAAAF71m6fguK96B0=" - }, - "model": { - "$ref": "AAAAAAF71bXEBtu8va8=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 215, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fguK96B0=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LA/iA=", - "_parent": { - "$ref": "AAAAAAF71m6fguK96B0=" - }, - "model": { - "$ref": "AAAAAAF71bXEBtu8va8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 216, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fguK96B0=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71m6fguKkGSU=" - }, - "tail": { - "$ref": "AAAAAAF71m6fguKzUeY=" - }, - "lineStyle": 1, - "points": "654:201;654:244", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fguK+BZ8=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fguK/R3o=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+LA/iA=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71m6fg+LBYg0=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71bYi89vaS2U=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71m6fg+LCAQA=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LBYg0=" - }, - "model": { - "$ref": "AAAAAAF71bYi89vaS2U=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LDZnk=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LCAQA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 592, - "top": -496, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LE4dg=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LCAQA=" - }, - "font": "Arial;13;1", - "left": 585, - "top": 416, - "width": 140.4482421875, - "height": 13, - "text": "server_isn 🠔 random()" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LFaF0=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LCAQA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 592, - "top": -496, - "width": 130.76171875, - "height": 13, - "text": "(from send SYN_ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LGBRE=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LCAQA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 592, - "top": -496, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 580, - "top": 409, - "width": 150.4482421875, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+LDZnk=" - }, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+LE4dg=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71m6fg+LFaF0=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+LGBRE=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 580, - "top": 409, - "width": 150.4482421875, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71m6fg+LCAQA=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71m6fg+LH4iE=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71bZqD9v2vmE=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71m6fg+LIVQo=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LH4iE=" - }, - "model": { - "$ref": "AAAAAAF71bZqD9v2vmE=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LJiqI=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LIVQo=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 448, - "top": -544, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LKe+Y=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LIVQo=" - }, - "font": "Arial;13;1", - "left": 553, - "top": 501, - "width": 203.28369140625, - "height": 13, - "text": "client_isn 🠔 get_seqnum(rcvpkt)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LLVME=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LIVQo=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 448, - "top": -544, - "width": 130.76171875, - "height": 13, - "text": "(from send SYN_ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LMZPw=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LIVQo=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 448, - "top": -544, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 548, - "top": 494, - "width": 213.28369140625, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+LJiqI=" - }, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+LKe+Y=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71m6fg+LLVME=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+LMZPw=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 548, - "top": 494, - "width": 213.28369140625, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71m6fg+LIVQo=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71m6fg+LNuGM=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71bcy/9wWi9E=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71m6fg+LO8+E=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LNuGM=" - }, - "model": { - "$ref": "AAAAAAF71bcy/9wWi9E=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LPgUs=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LO8+E=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 336, - "top": -176, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LQUXs=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LO8+E=" - }, - "font": "Arial;13;1", - "left": 483, - "top": 586, - "width": 343, - "height": 13, - "text": "pkt 🠔 make_pkt(SYN_ACK, client_isn + 1, server_isn)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LR3t8=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LO8+E=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 336, - "top": -176, - "width": 130.76171875, - "height": 13, - "text": "(from send SYN_ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LScx8=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LO8+E=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 336, - "top": -176, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 478, - "top": 579, - "width": 353, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+LPgUs=" - }, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+LQUXs=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71m6fg+LR3t8=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+LScx8=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 478, - "top": 579, - "width": 353, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71m6fg+LO8+E=" - } - }, - { - "_type": "UMLActionView", - "_id": "AAAAAAF71m6fg+LT+zs=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71bgnU9wybGw=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAF71m6fg+LU4/U=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LT+zs=" - }, - "model": { - "$ref": "AAAAAAF71bgnU9wybGw=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LVl7I=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LU4/U=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 400, - "top": -176, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LWam0=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LU4/U=" - }, - "font": "Arial;13;1", - "left": 575, - "top": 671, - "width": 160.3544921875, - "height": 13, - "text": "udp_send(pkt, sndr_addr)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LXBBI=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LU4/U=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 400, - "top": -176, - "width": 130.76171875, - "height": 13, - "text": "(from send SYN_ACK)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71m6fg+LYR3E=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LU4/U=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 400, - "top": -176, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 570, - "top": 664, - "width": 170.3544921875, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+LVl7I=" - }, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+LWam0=" - }, - "namespaceLabel": { - "$ref": "AAAAAAF71m6fg+LXBBI=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+LYR3E=" - } - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 570, - "top": 664, - "width": 170.3544921875, - "height": 41, - "nameCompartment": { - "$ref": "AAAAAAF71m6fg+LU4/U=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fg+LZvFU=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71biAUdxPsCA=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+La5Xc=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LZvFU=" - }, - "model": { - "$ref": "AAAAAAF71biAUdxPsCA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 668, - "top": 719, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LZvFU=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+Lb+5s=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LZvFU=" - }, - "model": { - "$ref": "AAAAAAF71biAUdxPsCA=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 719, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LZvFU=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+Lcf2s=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LZvFU=" - }, - "model": { - "$ref": "AAAAAAF71biAUdxPsCA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 720, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LZvFU=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71qSZ5+ZWf5U=" - }, - "tail": { - "$ref": "AAAAAAF71m6fg+LT+zs=" - }, - "lineStyle": 1, - "points": "654:705;654:748", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+La5Xc=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+Lb+5s=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+Lcf2s=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fg+Ldiew=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71biIKdxgZs0=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LetjA=", - "_parent": { - "$ref": "AAAAAAF71m6fg+Ldiew=" - }, - "model": { - "$ref": "AAAAAAF71biIKdxgZs0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 668, - "top": 634, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+Ldiew=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LfxC8=", - "_parent": { - "$ref": "AAAAAAF71m6fg+Ldiew=" - }, - "model": { - "$ref": "AAAAAAF71biIKdxgZs0=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 634, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+Ldiew=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+Lg7p4=", - "_parent": { - "$ref": "AAAAAAF71m6fg+Ldiew=" - }, - "model": { - "$ref": "AAAAAAF71biIKdxgZs0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 635, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+Ldiew=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71m6fg+LT+zs=" - }, - "tail": { - "$ref": "AAAAAAF71m6fg+LNuGM=" - }, - "lineStyle": 1, - "points": "654:620;654:663", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+LetjA=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+LfxC8=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+Lg7p4=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fg+LhFjU=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71biR3NxxZF0=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+Li5Jw=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LhFjU=" - }, - "model": { - "$ref": "AAAAAAF71biR3NxxZF0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 668, - "top": 549, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LhFjU=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+Ljq1M=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LhFjU=" - }, - "model": { - "$ref": "AAAAAAF71biR3NxxZF0=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 549, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LhFjU=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LkJfw=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LhFjU=" - }, - "model": { - "$ref": "AAAAAAF71biR3NxxZF0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 550, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LhFjU=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71m6fg+LNuGM=" - }, - "tail": { - "$ref": "AAAAAAF71m6fg+LH4iE=" - }, - "lineStyle": 1, - "points": "654:535;654:578", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+Li5Jw=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+Ljq1M=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+LkJfw=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fg+LlnrQ=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71biZUtyC9eQ=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LmkK0=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LlnrQ=" - }, - "model": { - "$ref": "AAAAAAF71biZUtyC9eQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 668, - "top": 464, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LlnrQ=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LnvyM=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LlnrQ=" - }, - "model": { - "$ref": "AAAAAAF71biZUtyC9eQ=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 464, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LlnrQ=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LocMw=", - "_parent": { - "$ref": "AAAAAAF71m6fg+LlnrQ=" - }, - "model": { - "$ref": "AAAAAAF71biZUtyC9eQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 465, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+LlnrQ=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71m6fg+LH4iE=" - }, - "tail": { - "$ref": "AAAAAAF71m6fg+LBYg0=" - }, - "lineStyle": 1, - "points": "654:450;654:493", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+LmkK0=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+LnvyM=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+LocMw=" - } - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71m6fg+Lp+jI=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71biiPdyTg8k=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LqxmE=", - "_parent": { - "$ref": "AAAAAAF71m6fg+Lp+jI=" - }, - "model": { - "$ref": "AAAAAAF71biiPdyTg8k=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 668, - "top": 379, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+Lp+jI=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LrlHI=", - "_parent": { - "$ref": "AAAAAAF71m6fg+Lp+jI=" - }, - "model": { - "$ref": "AAAAAAF71biiPdyTg8k=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 379, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+Lp+jI=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71m6fg+LsXXs=", - "_parent": { - "$ref": "AAAAAAF71m6fg+Lp+jI=" - }, - "model": { - "$ref": "AAAAAAF71biiPdyTg8k=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 380, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71m6fg+Lp+jI=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71m6fg+LBYg0=" - }, - "tail": { - "$ref": "AAAAAAF71m6fguKlPuo=" - }, - "lineStyle": 1, - "points": "654:365;654:408", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71m6fg+LqxmE=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71m6fg+LrlHI=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71m6fg+LsXXs=" - } - }, - { - "_type": "UMLFrameView", - "_id": "AAAAAAF71phX6OXDtG0=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAF71phX6OXE56w=", - "_parent": { - "$ref": "AAAAAAF71phX6OXDtG0=" - }, - "font": "Arial;13;0", - "left": 450.72998046875, - "top": 53, - "width": 93.49169921875, - "height": 13, - "text": "send SYN_ACK" - }, - { - "_type": "LabelView", - "_id": "AAAAAAF71phX6OXFzKU=", - "_parent": { - "$ref": "AAAAAAF71phX6OXDtG0=" - }, - "font": "Arial;13;1", - "left": 405, - "top": 53, - "width": 40.72998046875, - "height": 13, - "text": "activity" - } - ], - "font": "Arial;13;0", - "left": 400, - "top": 48, - "width": 585, - "height": 881, - "nameLabel": { - "$ref": "AAAAAAF71phX6OXE56w=" - }, - "frameTypeLabel": { - "$ref": "AAAAAAF71phX6OXFzKU=" - } - }, - { - "_type": "UMLControlNodeView", - "_id": "AAAAAAF71qSZ5+ZWf5U=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71qSZ5+ZU3zA=" - }, - "font": "Arial;13;0", - "containerChangeable": true, - "left": 635, - "top": 749, - "width": 39, - "height": 41 - }, - { - "_type": "UMLControlFlowView", - "_id": "AAAAAAF71qUGTeZdzIA=", - "_parent": { - "$ref": "AAAAAAF71m6fguKhHgM=" - }, - "model": { - "$ref": "AAAAAAF71qUGTeZc/dA=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qUGTeZeZLA=", - "_parent": { - "$ref": "AAAAAAF71qUGTeZdzIA=" - }, - "model": { - "$ref": "AAAAAAF71qUGTeZc/dA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 668, - "top": 804, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71qUGTeZdzIA=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qUGTeZfZT4=", - "_parent": { - "$ref": "AAAAAAF71qUGTeZdzIA=" - }, - "model": { - "$ref": "AAAAAAF71qUGTeZc/dA=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 683, - "top": 804, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAF71qUGTeZdzIA=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAF71qUGTuZggnM=", - "_parent": { - "$ref": "AAAAAAF71qUGTeZdzIA=" - }, - "model": { - "$ref": "AAAAAAF71qUGTeZc/dA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 639, - "top": 805, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAF71qUGTeZdzIA=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAF71m6fguKjYDg=" - }, - "tail": { - "$ref": "AAAAAAF71qSZ5+ZWf5U=" - }, - "lineStyle": 1, - "points": "654:790;654:833", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAF71qUGTeZeZLA=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAF71qUGTeZfZT4=" - }, - "propertyLabel": { - "$ref": "AAAAAAF71qUGTuZggnM=" - } - } - ] - } - ], - "nodes": [ - { - "_type": "UMLDecisionNode", - "_id": "AAAAAAF71asnidrqhqQ=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "DecisionNode1" - }, - { - "_type": "UMLActivityFinalNode", - "_id": "AAAAAAF71asBodrkiIw=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "ActivityFinalNode1" - }, - { - "_type": "UMLInitialNode", - "_id": "AAAAAAF71ar8Atrfq6E=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "InitialNode1" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71aty6dsAuxk=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "allocate resources" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71az2qdtg0N8=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "rcvpkt 🠔 udp_recv(&sndr_addr)", - "language": "C", - "body": "rcvpkt=udp_recv(&sndr_addr);" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71bYi89vaS2U=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "server_isn 🠔 random()" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71bgnU9wybGw=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "udp_send(pkt, sndr_addr)" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71bcy/9wWi9E=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "pkt 🠔 make_pkt(SYN_ACK, client_isn + 1, server_isn)" - }, - { - "_type": "UMLAction", - "_id": "AAAAAAF71bZqD9v2vmE=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "client_isn 🠔 get_seqnum(rcvpkt)" - }, - { - "_type": "UMLMergeNode", - "_id": "AAAAAAF71qSZ5+ZU3zA=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "name": "MergeNode1" - } - ], - "edges": [ - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71biiPdyTg8k=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71aty6dsAuxk=" - }, - "target": { - "$ref": "AAAAAAF71bYi89vaS2U=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71biZUtyC9eQ=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71bYi89vaS2U=" - }, - "target": { - "$ref": "AAAAAAF71bZqD9v2vmE=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71biR3NxxZF0=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71bZqD9v2vmE=" - }, - "target": { - "$ref": "AAAAAAF71bcy/9wWi9E=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71biIKdxgZs0=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71bcy/9wWi9E=" - }, - "target": { - "$ref": "AAAAAAF71bgnU9wybGw=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71biAUdxPsCA=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71bgnU9wybGw=" - }, - "target": { - "$ref": "AAAAAAF71qSZ5+ZU3zA=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71bXEBtu8va8=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71az2qdtg0N8=" - }, - "target": { - "$ref": "AAAAAAF71asnidrqhqQ=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71bLCZtupG20=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71ar8Atrfq6E=" - }, - "target": { - "$ref": "AAAAAAF71az2qdtg0N8=" - } - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71avqxNs+ULI=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71asnidrqhqQ=" - }, - "target": { - "$ref": "AAAAAAF71qSZ5+ZU3zA=" - }, - "guard": "else" - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71auAodsZt6A=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71asnidrqhqQ=" - }, - "target": { - "$ref": "AAAAAAF71aty6dsAuxk=" - }, - "guard": "get_type(recvpkt) = SYN" - }, - { - "_type": "UMLControlFlow", - "_id": "AAAAAAF71qUGTeZc/dA=", - "_parent": { - "$ref": "AAAAAAF71mw6YOHu6Ng=" - }, - "source": { - "$ref": "AAAAAAF71qSZ5+ZU3zA=" - }, - "target": { - "$ref": "AAAAAAF71asBodrkiIw=" - } - } - ] - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71e88EOCEujQ=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71e79/+BifxY=" - }, - "target": { - "$ref": "AAAAAAF71ZzQTddw1Ps=" - } - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71fO7nODezH4=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71e79/+BifxY=" - }, - "target": { - "$ref": "AAAAAAF71ZyLv9cG+m8=" - } - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71kqnU+Gh48Y=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "target": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71k0dfuHiAJ8=", - "_parent": { - "$ref": "AAAAAAF71kqnU+Gh48Y=" - }, - "name": "SEND" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71kriK+G0xEA=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "target": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71k02gOHmVxU=", - "_parent": { - "$ref": "AAAAAAF71kriK+G0xEA=" - }, - "name": "RECV" - } - ] - }, - { - "_type": "UMLTransition", - "_id": "AAAAAAF71ksKH+HHLH4=", - "_parent": { - "$ref": "AAAAAAF71Zx6oNbvFXQ=" - }, - "source": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "target": { - "$ref": "AAAAAAF71Z0EUNe8u34=" - }, - "triggers": [ - { - "_type": "UMLEvent", - "_id": "AAAAAAF71k2GmOHqSHs=", - "_parent": { - "$ref": "AAAAAAF71ksKH+HHLH4=" - }, - "name": "TIMEOUT" - } - ] - } - ] - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/docs/rfcs-compliance-metadata.csv b/docs/RFC/rfcs-compliance-metadata.csv similarity index 69% rename from docs/rfcs-compliance-metadata.csv rename to docs/RFC/rfcs-compliance-metadata.csv index ae6d043..c38fca8 100644 --- a/docs/rfcs-compliance-metadata.csv +++ b/docs/RFC/rfcs-compliance-metadata.csv @@ -26,11 +26,11 @@ RFC 1350 - TFTP (Trivial File Transfer Protocol);; ;The source and destination port fields of the Datagram header are used by TFTP and the length field reflects the size of the TFTP packet.;test_false ;The transfer identifiers (TID's) used by TFTP are passed to the Datagram layer to be used as ports.;test_false ;The transfer identifiers (TID's) used by TFTP must be between 0 and 65,535.;test_false -;The TFTP header consists of a 2 byte opcode field which indicates the packet's type (e.g., DATA, ERROR, etc.).;test_false +;The TFTP header consists of a 2 byte opcode field which indicates the packet's type (e.g., TFTP_OPCODE_DATA, TFTP_OPCODE_ERROR, etc.).;test_false 4. Initial Connection Protocol;; ;A transfer is established by sending a request.;test_false -;A WRQ request is used to write onto a foreign file system.;test_false -;A RRQ request is used to read from a foreign file system.;test_false +;A TFTP_OPCODE_WRQ request is used to write onto a foreign file system.;test_false +;A TFTP_OPCODE_RRQ request is used to read from a foreign file system.;test_false ;If the reply is an error packet then the request has been denied.;test_false ;The positive response to a read request is the first data packet for read.;test_false ;The positive response to a write request is an acknowledgment packet with block number zero.;test_false @@ -47,35 +47,35 @@ RFC 1350 - TFTP (Trivial File Transfer Protocol);; ;During the transfer the hosts should make sure that the source TID of a received packet matches the previously agreed value.;test_false ;If a source TID does not match, the packet should be discarded as erroneously sent from somewhere else and an error packet should be sent to the source of the incorrect packet, while not disturbing the transfer.;test_false 5. TFTP Packets;; -;RRQ (read request, opcode = 1) packet type support.;test_false -;WRQ (write request, opcode = 2) packet type support.;test_false -;DATA (data, opcode = 3) packet type support.;test_false -;ACK (acknowledgment, opcode = 4) packet type support.;test_false -;ERROR (errore, opcode = 5) packet type support.;test_false +;TFTP_OPCODE_RRQ (read request, opcode = 1) packet type support.;test_false +;TFTP_OPCODE_WRQ (write request, opcode = 2) packet type support.;test_false +;TFTP_OPCODE_DATA (data, opcode = 3) packet type support.;test_false +;TFTP_OPCODE_ACK (acknowledgment, opcode = 4) packet type support.;test_false +;TFTP_OPCODE_ERROR (errore, opcode = 5) packet type support.;test_false ;The TFTP header of a packet contains the associated opcode.;test_false -;RRQ packets (opcode 1) have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte);test_false -;WRQ packets (opcode 2) have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte);test_false +;TFTP_OPCODE_RRQ packets (opcode 1) have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte);test_false +;TFTP_OPCODE_WRQ packets (opcode 2) have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte);test_false ;Filename is a netascii sequence terminated by a zero byte.;test_false ;Mode field specifies "netascii" or "octet" modes (case insensitive) terminated by a zero byte.;test_false ;A host which receives netascii mode data must translate the data to its own format.;test_false ;Octet mode is used to transfer a file that is in the 8-bit format of the machine from which the file is being transferred.;test_false ;If a host receives a octet file and then returns it, the returned file must be identical to the original.;test_false ;Sender and recipient may operate in different modes.;test_false -;DATA packets (opcode 3) have the following format:
  • Opcode (2 bytes)
  • Block number (2 bytes)
  • Data (n bytes);test_false -;DATA packets have a block number and data field.;test_false +;TFTP_OPCODE_DATA packets (opcode 3) have the following format:
  • Opcode (2 bytes)
  • Block number (2 bytes)
  • Data (n bytes);test_false +;TFTP_OPCODE_DATA packets have a block number and data field.;test_false ;The block numbers on data packets begin with one and increase by one for each new block of data.;test_false ;An implementation should detect duplicated packets via the block number.;test_false ;The data field is from zero to 512 bytes long.;test_false ;If the data field is 512 bytes long, the block is not the last block of data.;test_false ;If the data field is from zero to 511 bytes long, it signals the end of the transfer.;test_false -;All packets other than duplicate ACK's and those used for termination are acknowledged unless a timeout occurs.;test_false -;Sending a DATA packet is an acknowledgment for the first ACK packet of the previous DATA packet.;test_false -;The WRQ and DATA packets are acknowledged by ACK or ERROR packets, while RRQ and ACK packets are acknowledged by DATA or ERROR packets.;test_false -;ACK packets (opcode 4) have the following format:
  • Opcode (2 bytes)
  • Block number (2 bytes);test_false -;The block number in an ACK echoes the block number of the DATA packet being acknowledged.;test_false -;A WRQ is acknowledged with an ACK packet having a block number of zero.;test_false -;ERROR packets (opcode 5) have the following format:
  • Opcode (2 bytes)
  • ErrorCode (2 bytes)
  • ErrMsg (variable-length string)
  • 0x0 (1 byte);test_false -;An ERROR packet can be the acknowledgment of any other type of packet.;test_false +;All packets other than duplicate TFTP_OPCODE_ACK's and those used for termination are acknowledged unless a timeout occurs.;test_false +;Sending a TFTP_OPCODE_DATA packet is an acknowledgment for the first TFTP_OPCODE_ACK packet of the previous TFTP_OPCODE_DATA packet.;test_false +;The TFTP_OPCODE_WRQ and TFTP_OPCODE_DATA packets are acknowledged by TFTP_OPCODE_ACK or TFTP_OPCODE_ERROR packets, while TFTP_OPCODE_RRQ and TFTP_OPCODE_ACK packets are acknowledged by TFTP_OPCODE_DATA or TFTP_OPCODE_ERROR packets.;test_false +;TFTP_OPCODE_ACK packets (opcode 4) have the following format:
  • Opcode (2 bytes)
  • Block number (2 bytes);test_false +;The block number in an TFTP_OPCODE_ACK echoes the block number of the TFTP_OPCODE_DATA packet being acknowledged.;test_false +;A TFTP_OPCODE_WRQ is acknowledged with an TFTP_OPCODE_ACK packet having a block number of zero.;test_false +;TFTP_OPCODE_ERROR packets (opcode 5) have the following format:
  • Opcode (2 bytes)
  • ErrorCode (2 bytes)
  • ErrMsg (variable-length string)
  • 0x0 (1 byte);test_false +;An TFTP_OPCODE_ERROR packet can be the acknowledgment of any other type of packet.;test_false ;The error code is an integer indicating the nature of the error.;test_false ;The error message is intended for human consumption, and should be a string in netascii terminated with a zero byte.;test_false ;Error handling for codes 0 (Not defined, see error message (if any).) are implemented;test_false @@ -87,14 +87,14 @@ RFC 1350 - TFTP (Trivial File Transfer Protocol);; ;Error handling for codes 6 (File already exists.) are implemented;test_false ;Error handling for codes 7 (No such user.) are implemented;test_false 6. Normal Termination;; -;Transfer ends with a DATA packet containing 0 to 511 bytes.;test_false -;Final DATA packet is acknowledged like other DATA packets.;test_false -;The host sending the final ACK wait for a long timeout before terminating in oreder to retransmit the final ACK if lost.;test_false -;Sender of the final DATA packet should retransmit until acknowledged or timeout.;test_false -;Successful completion is indicated by reception of the final ACK.;test_false -;Timeout or DATA sender unwillingness to retransmit may indicate an incomplete transfer.;test_false +;Transfer ends with a TFTP_OPCODE_DATA packet containing 0 to 511 bytes.;test_false +;Final TFTP_OPCODE_DATA packet is acknowledged like other TFTP_OPCODE_DATA packets.;test_false +;The host sending the final TFTP_OPCODE_ACK wait for a long timeout before terminating in order to retransmit the final TFTP_OPCODE_ACK if lost.;test_false +;Sender of the final TFTP_OPCODE_DATA packet should retransmit until acknowledged or timeout.;test_false +;Successful completion is indicated by reception of the final TFTP_OPCODE_ACK.;test_false +;Timeout or TFTP_OPCODE_DATA sender unwillingness to retransmit may indicate an incomplete transfer.;test_false 7. Premature Termination;; -;Errors during transfer result in an ERROR packet.;test_false +;Errors during transfer result in an TFTP_OPCODE_ERROR packet.;test_false ;Error packets are not retransmitted.;test_false ;Error packets are not acknowledged.;test_false ;Timeouts are used to detect errors.;test_false @@ -105,8 +105,8 @@ Packet Formats;; ;TFTP options are appended to the Read Request and Write Request packets.;test_false ;OACK (Option Acknowledgment, opcode = 6) packet type support.;test_false ;Error handling for codes 8 (Invalid options.) are implemented.;test_false -;Sending an ERROR packet with error code 8 terminate the transfer.;test_false -;RRQ and WRQ packets containig options have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte)
  • Opt1
  • 0x0 (1 byte)
  • Value1
  • 0x0 (1 byte)
  • ...
  • OptN (variable-length string)
  • 0x0 (1 byte)
  • ValueN
  • 0x0 (1 byte);test_false +;Sending an TFTP_OPCODE_ERROR packet with error code 8 terminate the transfer.;test_false +;TFTP_OPCODE_RRQ and TFTP_OPCODE_WRQ packets containig options have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte)
  • Opt1
  • 0x0 (1 byte)
  • Value1
  • 0x0 (1 byte)
  • ...
  • OptN (variable-length string)
  • 0x0 (1 byte)
  • ValueN
  • 0x0 (1 byte);test_false ;OptK is a case-insensitive ASCII sequence terminated by a zero byte.;test_false ;ValueK is a case-insensitive ASCII sequence terminated by a zero byte.;test_false ;If multiple options are to be negotiated, they are appended to each other.;test_false @@ -123,20 +123,20 @@ Negotiation Protocol;; ;Each option the server recognizes, and accepts the value for, is included in the OACK.;test_false ;The server must not include in the OACK any option which had not been specifically requested by the client.;test_false ;Options which the server does not support should be omitted from the OACK.;test_false -;Options which the server does not support should not cause an ERROR packet to be generated.;test_false +;Options which the server does not support should not cause an TFTP_OPCODE_ERROR packet to be generated.;test_false ;An option not acknowledged by the server is ignored by the hosts as if it were never requested.;test_false -;An OACK packet response to a RRQ packet which carries options acknowledges the request and the options it specifies.;test_false -;A DATA packet response to a RRQ packet which carries options acknowledges the request but not the options.;test_false -;An ERROR packet response to a RRQ packet which carries options rejects the request.;test_false -;An OACK packet response to a WRQ packet which carries options acknowledges the request and the options it specifies.;test_false -;An ACK packet response to a WRQ packet which carries options acknowledges the request but not the options.;test_false -;An ERROR packet response to a WRQ packet which carries options rejects the request.;test_false +;An OACK packet response to a TFTP_OPCODE_RRQ packet which carries options acknowledges the request and the options it specifies.;test_false +;A TFTP_OPCODE_DATA packet response to a TFTP_OPCODE_RRQ packet which carries options acknowledges the request but not the options.;test_false +;An TFTP_OPCODE_ERROR packet response to a TFTP_OPCODE_RRQ packet which carries options rejects the request.;test_false +;An OACK packet response to a TFTP_OPCODE_WRQ packet which carries options acknowledges the request and the options it specifies.;test_false +;An TFTP_OPCODE_ACK packet response to a TFTP_OPCODE_WRQ packet which carries options acknowledges the request but not the options.;test_false +;An TFTP_OPCODE_ERROR packet response to a TFTP_OPCODE_WRQ packet which carries options rejects the request.;test_false ;A client attempt to repeat the request without options if it receive an Error packet to a request which carries options;test_false -;A client send an ACK (with the data block number set to 0) to confirm the values in the OACK packet that acknowledged a RRQ packet.;test_false -;A client begins the transfer with the first DATA packet, using the negotiated values in the OACK packet that acknowledged a WRQ packet if it accepts them.;test_false -;A client sends an ERROR packet, with error code 8, and terminate the connection in response to the OACK packet if it rejects the values negotiated.;test_false +;A client send an TFTP_OPCODE_ACK (with the data block number set to 0) to confirm the values in the OACK packet that acknowledged a TFTP_OPCODE_RRQ packet.;test_false +;A client begins the transfer with the first TFTP_OPCODE_DATA packet, using the negotiated values in the OACK packet that acknowledged a TFTP_OPCODE_WRQ packet if it accepts them.;test_false +;A client sends an TFTP_OPCODE_ERROR packet, with error code 8, and terminate the connection in response to the OACK packet if it rejects the values negotiated.;test_false ;A client acknowledging an OACK, with an appropriate non-error response, use only the options and values returned by the server.;test_false -;A client receiving an OACK containing an unrequested option respond with an ERROR packet, with error code 8, and terminate the transfer.;test_false +;A client receiving an OACK containing an unrequested option respond with an TFTP_OPCODE_ERROR packet, with error code 8, and terminate the transfer.;test_false RFC 2348 - TFTP Blocksize Option;; Abstract;; ;Blocksize negotiation support.;test_false @@ -171,14 +171,14 @@ Abstract;; ;The data sender, after a successful Windowsize negotiation, will transmit the accorded number of consecutive blocks before stopping and waiting for the reception of the acknowledgment of the last block transmitted.;test_false ;A server willing to accept the windowsize option sends an OACK to the client.;test_false ;A server windowsize specified value MUST be less than or equal to the value requested by the client.;test_false -;A client use the size specified in the OACK or send an ERROR packet, with error code 8, to terminate the transfer.;test_false +;A client use the size specified in the OACK or send an TFTP_OPCODE_ERROR packet, with error code 8, to terminate the transfer.;test_false ;The reception of a data window with a number of blocks less than the negotiated windowsize is the final window.;test_false 4. Traffic Flow and Error Handling;; -;The data sender cyclically send to the receiver the agreed windowsize consecutive data blocks before normally stopping and waiting for the ACK of the transferred window.;test_false -;The data receiver send to the data sender the ACK of the last data block of the window in order to confirm a successful data block window reception.;test_false -;In the case of an expected ACK not timely reaching the data sender (timeout), the last received ACK SHALL set the beginning of the next windowsize data block window to be sent.;test_false -;In the case of a data block sequence error, the data receiver SHOULD notify the data sender by sending an ACK corresponding to the last data block correctly received.;test_false -;The notified data sender SHOULD send a new data block window whose beginning MUST be set based on the ACK received out of sequence.;test_false +;The data sender cyclically send to the receiver the agreed windowsize consecutive data blocks before normally stopping and waiting for the TFTP_OPCODE_ACK of the transferred window.;test_false +;The data receiver send to the data sender the TFTP_OPCODE_ACK of the last data block of the window in order to confirm a successful data block window reception.;test_false +;In the case of an expected TFTP_OPCODE_ACK not timely reaching the data sender (timeout), the last received TFTP_OPCODE_ACK SHALL set the beginning of the next windowsize data block window to be sent.;test_false +;In the case of a data block sequence error, the data receiver SHOULD notify the data sender by sending an TFTP_OPCODE_ACK corresponding to the last data block correctly received.;test_false +;The notified data sender SHOULD send a new data block window whose beginning MUST be set based on the TFTP_OPCODE_ACK received out of sequence.;test_false ;Traffic with windowsize = 1 MUST be equivalent to traffic specified with request not containing a windowsize option.;test_false 6. Congestion and Error Control;; ;The rate at which TFTP UDP datagrams are sent SHOULD follow the CC guidelines in Section 3.1 of [RFC5405].;test_false diff --git a/docs/rfcs-compliance-results.md b/docs/RFC/rfcs-compliance-results.md similarity index 76% rename from docs/rfcs-compliance-results.md rename to docs/RFC/rfcs-compliance-results.md index fcecc64..acc577e 100644 --- a/docs/rfcs-compliance-results.md +++ b/docs/RFC/rfcs-compliance-results.md @@ -133,7 +133,7 @@ - The TFTP header consists of a 2 byte opcode field which indicates the packet's type (e.g., DATA, ERROR, etc.). + The TFTP header consists of a 2 byte opcode field which indicates the packet's type (e.g., TFTP_OPCODE_DATA, TFTP_OPCODE_ERROR, etc.). ❌ @@ -146,12 +146,12 @@ - A WRQ request is used to write onto a foreign file system. + A TFTP_OPCODE_WRQ request is used to write onto a foreign file system. ❌ - A RRQ request is used to read from a foreign file system. + A TFTP_OPCODE_RRQ request is used to read from a foreign file system. ❌ @@ -234,27 +234,27 @@ - RRQ (read request, opcode = 1) packet type support. + TFTP_OPCODE_RRQ (read request, opcode = 1) packet type support. ❌ - WRQ (write request, opcode = 2) packet type support. + TFTP_OPCODE_WRQ (write request, opcode = 2) packet type support. ❌ - DATA (data, opcode = 3) packet type support. + TFTP_OPCODE_DATA (data, opcode = 3) packet type support. ❌ - ACK (acknowledgment, opcode = 4) packet type support. + TFTP_OPCODE_ACK (acknowledgment, opcode = 4) packet type support. ❌ - ERROR (errore, opcode = 5) packet type support. + TFTP_OPCODE_ERROR (errore, opcode = 5) packet type support. ❌ @@ -264,12 +264,12 @@ - RRQ packets (opcode 1) have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte) + TFTP_OPCODE_RRQ packets (opcode 1) have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte) ❌ - WRQ packets (opcode 2) have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte) + TFTP_OPCODE_WRQ packets (opcode 2) have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte) ❌ @@ -304,12 +304,12 @@ - DATA packets (opcode 3) have the following format:
  • Opcode (2 bytes)
  • Block number (2 bytes)
  • Data (n bytes) + TFTP_OPCODE_DATA packets (opcode 3) have the following format:
  • Opcode (2 bytes)
  • Block number (2 bytes)
  • Data (n bytes) ❌ - DATA packets have a block number and data field. + TFTP_OPCODE_DATA packets have a block number and data field. ❌ @@ -339,42 +339,42 @@ - All packets other than duplicate ACK's and those used for termination are acknowledged unless a timeout occurs. + All packets other than duplicate TFTP_OPCODE_ACK's and those used for termination are acknowledged unless a timeout occurs. ❌ - Sending a DATA packet is an acknowledgment for the first ACK packet of the previous DATA packet. + Sending a TFTP_OPCODE_DATA packet is an acknowledgment for the first TFTP_OPCODE_ACK packet of the previous TFTP_OPCODE_DATA packet. ❌ - The WRQ and DATA packets are acknowledged by ACK or ERROR packets, while RRQ and ACK packets are acknowledged by DATA or ERROR packets. + The TFTP_OPCODE_WRQ and TFTP_OPCODE_DATA packets are acknowledged by TFTP_OPCODE_ACK or TFTP_OPCODE_ERROR packets, while TFTP_OPCODE_RRQ and TFTP_OPCODE_ACK packets are acknowledged by TFTP_OPCODE_DATA or TFTP_OPCODE_ERROR packets. ❌ - ACK packets (opcode 4) have the following format:
  • Opcode (2 bytes)
  • Block number (2 bytes) + TFTP_OPCODE_ACK packets (opcode 4) have the following format:
  • Opcode (2 bytes)
  • Block number (2 bytes) ❌ - The block number in an ACK echoes the block number of the DATA packet being acknowledged. + The block number in an TFTP_OPCODE_ACK echoes the block number of the TFTP_OPCODE_DATA packet being acknowledged. ❌ - A WRQ is acknowledged with an ACK packet having a block number of zero. + A TFTP_OPCODE_WRQ is acknowledged with an TFTP_OPCODE_ACK packet having a block number of zero. ❌ - ERROR packets (opcode 5) have the following format:
  • Opcode (2 bytes)
  • ErrorCode (2 bytes)
  • ErrMsg (variable-length string)
  • 0x0 (1 byte) + TFTP_OPCODE_ERROR packets (opcode 5) have the following format:
  • Opcode (2 bytes)
  • ErrorCode (2 bytes)
  • ErrMsg (variable-length string)
  • 0x0 (1 byte) ❌ - An ERROR packet can be the acknowledgment of any other type of packet. + An TFTP_OPCODE_ERROR packet can be the acknowledgment of any other type of packet. ❌ @@ -432,32 +432,32 @@ - Transfer ends with a DATA packet containing 0 to 511 bytes. + Transfer ends with a TFTP_OPCODE_DATA packet containing 0 to 511 bytes. ❌ - Final DATA packet is acknowledged like other DATA packets. + Final TFTP_OPCODE_DATA packet is acknowledged like other TFTP_OPCODE_DATA packets. ❌ - The host sending the final ACK wait for a long timeout before terminating in oreder to retransmit the final ACK if lost. + The host sending the final TFTP_OPCODE_ACK wait for a long timeout before terminating in oreder to retransmit the final TFTP_OPCODE_ACK if lost. ❌ - Sender of the final DATA packet should retransmit until acknowledged or timeout. + Sender of the final TFTP_OPCODE_DATA packet should retransmit until acknowledged or timeout. ❌ - Successful completion is indicated by reception of the final ACK. + Successful completion is indicated by reception of the final TFTP_OPCODE_ACK. ❌ - Timeout or DATA sender unwillingness to retransmit may indicate an incomplete transfer. + Timeout or TFTP_OPCODE_DATA sender unwillingness to retransmit may indicate an incomplete transfer. ❌ @@ -465,7 +465,7 @@ - Errors during transfer result in an ERROR packet. + Errors during transfer result in an TFTP_OPCODE_ERROR packet. ❌ @@ -512,12 +512,12 @@ - Sending an ERROR packet with error code 8 terminate the transfer. + Sending an TFTP_OPCODE_ERROR packet with error code 8 terminate the transfer. ❌ - RRQ and WRQ packets containig options have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte)
  • Opt1
  • 0x0 (1 byte)
  • Value1
  • 0x0 (1 byte)
  • ...
  • OptN (variable-length string)
  • 0x0 (1 byte)
  • ValueN
  • 0x0 (1 byte) + TFTP_OPCODE_RRQ and TFTP_OPCODE_WRQ packets containig options have the following format:
  • Opcode (2 bytes)
  • Filename (variable-length string)
  • 0x0 (1 byte)
  • Mode (variable-length string)
  • 0x0 (1 byte)
  • Opt1
  • 0x0 (1 byte)
  • Value1
  • 0x0 (1 byte)
  • ...
  • OptN (variable-length string)
  • 0x0 (1 byte)
  • ValueN
  • 0x0 (1 byte) ❌ @@ -600,7 +600,7 @@ - Options which the server does not support should not cause an ERROR packet to be generated. + Options which the server does not support should not cause an TFTP_OPCODE_ERROR packet to be generated. ❌ @@ -610,32 +610,32 @@ - An OACK packet response to a RRQ packet which carries options acknowledges the request and the options it specifies. + An OACK packet response to a TFTP_OPCODE_RRQ packet which carries options acknowledges the request and the options it specifies. ❌ - A DATA packet response to a RRQ packet which carries options acknowledges the request but not the options. + A TFTP_OPCODE_DATA packet response to a TFTP_OPCODE_RRQ packet which carries options acknowledges the request but not the options. ❌ - An ERROR packet response to a RRQ packet which carries options rejects the request. + An TFTP_OPCODE_ERROR packet response to a TFTP_OPCODE_RRQ packet which carries options rejects the request. ❌ - An OACK packet response to a WRQ packet which carries options acknowledges the request and the options it specifies. + An OACK packet response to a TFTP_OPCODE_WRQ packet which carries options acknowledges the request and the options it specifies. ❌ - An ACK packet response to a WRQ packet which carries options acknowledges the request but not the options. + An TFTP_OPCODE_ACK packet response to a TFTP_OPCODE_WRQ packet which carries options acknowledges the request but not the options. ❌ - An ERROR packet response to a WRQ packet which carries options rejects the request. + An TFTP_OPCODE_ERROR packet response to a TFTP_OPCODE_WRQ packet which carries options rejects the request. ❌ @@ -645,17 +645,17 @@ - A client send an ACK (with the data block number set to 0) to confirm the values in the OACK packet that acknowledged a RRQ packet. + A client send an TFTP_OPCODE_ACK (with the data block number set to 0) to confirm the values in the OACK packet that acknowledged a TFTP_OPCODE_RRQ packet. ❌ - A client begins the transfer with the first DATA packet, using the negotiated values in the OACK packet that acknowledged a WRQ packet if it accepts them. + A client begins the transfer with the first TFTP_OPCODE_DATA packet, using the negotiated values in the OACK packet that acknowledged a TFTP_OPCODE_WRQ packet if it accepts them. ❌ - A client sends an ERROR packet, with error code 8, and terminate the connection in response to the OACK packet if it rejects the values negotiated. + A client sends an TFTP_OPCODE_ERROR packet, with error code 8, and terminate the connection in response to the OACK packet if it rejects the values negotiated. ❌ @@ -665,7 +665,7 @@ - A client receiving an OACK containing an unrequested option respond with an ERROR packet, with error code 8, and terminate the transfer. + A client receiving an OACK containing an unrequested option respond with an TFTP_OPCODE_ERROR packet, with error code 8, and terminate the transfer. ❌ RFC 2348 - TFTP Blocksize Option @@ -814,7 +814,7 @@ - A client use the size specified in the OACK or send an ERROR packet, with error code 8, to terminate the transfer. + A client use the size specified in the OACK or send an TFTP_OPCODE_ERROR packet, with error code 8, to terminate the transfer. ❌ @@ -827,27 +827,27 @@ - The data sender cyclically send to the receiver the agreed windowsize consecutive data blocks before normally stopping and waiting for the ACK of the transferred window. + The data sender cyclically send to the receiver the agreed windowsize consecutive data blocks before normally stopping and waiting for the TFTP_OPCODE_ACK of the transferred window. ❌ - The data receiver send to the data sender the ACK of the last data block of the window in order to confirm a successful data block window reception. + The data receiver send to the data sender the TFTP_OPCODE_ACK of the last data block of the window in order to confirm a successful data block window reception. ❌ - In the case of an expected ACK not timely reaching the data sender (timeout), the last received ACK SHALL set the beginning of the next windowsize data block window to be sent. + In the case of an expected TFTP_OPCODE_ACK not timely reaching the data sender (timeout), the last received TFTP_OPCODE_ACK SHALL set the beginning of the next windowsize data block window to be sent. ❌ - In the case of a data block sequence error, the data receiver SHOULD notify the data sender by sending an ACK corresponding to the last data block correctly received. + In the case of a data block sequence error, the data receiver SHOULD notify the data sender by sending an TFTP_OPCODE_ACK corresponding to the last data block correctly received. ❌ - The notified data sender SHOULD send a new data block window whose beginning MUST be set based on the ACK received out of sequence. + The notified data sender SHOULD send a new data block window whose beginning MUST be set based on the TFTP_OPCODE_ACK received out of sequence. ❌ diff --git a/docs/rfcs_compliance-test.py b/docs/RFC/rfcs_compliance-test.py similarity index 100% rename from docs/rfcs_compliance-test.py rename to docs/RFC/rfcs_compliance-test.py diff --git a/docs/chapters/ambiente_sviluppo.tex b/docs/chapters/ambiente_sviluppo.tex new file mode 100644 index 0000000..89c3dec --- /dev/null +++ b/docs/chapters/ambiente_sviluppo.tex @@ -0,0 +1,4 @@ +\section{Ambiente di sviluppo e di testing} + +% l'indicazione della piattaforma software usata per lo sviluppo ed il +% testing del sistema diff --git a/docs/chapters/architettura.tex b/docs/chapters/architettura.tex new file mode 100644 index 0000000..93ce16e --- /dev/null +++ b/docs/chapters/architettura.tex @@ -0,0 +1,10 @@ +\section{Architettura} + +% descrizione dettagliata dell'architettura del sistema e delle scelte +% progettuali effettuate + +\input{chapters/architettura/tftp.tex} +\input{chapters/architettura/timeout.tex} +\input{chapters/architettura/perdita_pacchetti.tex} +\input{chapters/architettura/client.tex} +\input{chapters/architettura/server.tex} diff --git a/docs/chapters/architettura/client.tex b/docs/chapters/architettura/client.tex new file mode 100644 index 0000000..5a3c5f7 --- /dev/null +++ b/docs/chapters/architettura/client.tex @@ -0,0 +1 @@ +\subsection{Client} diff --git a/docs/chapters/architettura/perdita_pacchetti.tex b/docs/chapters/architettura/perdita_pacchetti.tex new file mode 100644 index 0000000..2f0832a --- /dev/null +++ b/docs/chapters/architettura/perdita_pacchetti.tex @@ -0,0 +1 @@ +\subsection{Simulazione della perdita dei pacchetti} diff --git a/docs/chapters/architettura/server.tex b/docs/chapters/architettura/server.tex new file mode 100644 index 0000000..588a3a6 --- /dev/null +++ b/docs/chapters/architettura/server.tex @@ -0,0 +1 @@ +\subsection{Server} diff --git a/docs/chapters/architettura/tftp.tex b/docs/chapters/architettura/tftp.tex new file mode 100644 index 0000000..53cb0c0 --- /dev/null +++ b/docs/chapters/architettura/tftp.tex @@ -0,0 +1 @@ +\subsection{Tftp} diff --git a/docs/chapters/architettura/timeout.tex b/docs/chapters/architettura/timeout.tex new file mode 100644 index 0000000..31ac76b --- /dev/null +++ b/docs/chapters/architettura/timeout.tex @@ -0,0 +1 @@ +\subsection{Timeout adattivo} diff --git a/docs/chapters/esempi_funzionamento.tex b/docs/chapters/esempi_funzionamento.tex new file mode 100644 index 0000000..6abfb76 --- /dev/null +++ b/docs/chapters/esempi_funzionamento.tex @@ -0,0 +1 @@ +\section{Esempi di funzionamento} diff --git a/docs/chapters/implementazione.tex b/docs/chapters/implementazione.tex new file mode 100644 index 0000000..1bb61a9 --- /dev/null +++ b/docs/chapters/implementazione.tex @@ -0,0 +1,3 @@ +\section{Implementazione} + +% descrizione dell'implementazione diff --git a/docs/chapters/implementazione/client.tex b/docs/chapters/implementazione/client.tex new file mode 100644 index 0000000..e69de29 diff --git a/docs/chapters/implementazione/server.tex b/docs/chapters/implementazione/server.tex new file mode 100644 index 0000000..e69de29 diff --git a/docs/chapters/limitazioni.tex b/docs/chapters/limitazioni.tex new file mode 100644 index 0000000..ff094ed --- /dev/null +++ b/docs/chapters/limitazioni.tex @@ -0,0 +1,3 @@ +\section{Limitazioni} + +% descrizione delle eventuali limitazioni riscontrate diff --git a/docs/chapters/manuale.tex b/docs/chapters/manuale.tex new file mode 100644 index 0000000..16c5013 --- /dev/null +++ b/docs/chapters/manuale.tex @@ -0,0 +1,3 @@ +\section{Manuale d'uso} + +% manuale per l'installazione, la configurazione e l'esecuzione del sistema. diff --git a/docs/chapters/prestazioni.tex b/docs/chapters/prestazioni.tex new file mode 100644 index 0000000..7856e93 --- /dev/null +++ b/docs/chapters/prestazioni.tex @@ -0,0 +1,5 @@ +\section{Valutazione delle prestazioni} + +% valutazione delle prestazioni del protocollo al variare della dimensione +% della finestra di spedizione N, della probabilità di perdita dei messaggi p, +% e della durata del timeout T (incluso il caso di T adattativo) diff --git a/docs/chapters/traccia.tex b/docs/chapters/traccia.tex new file mode 100644 index 0000000..8d14870 --- /dev/null +++ b/docs/chapters/traccia.tex @@ -0,0 +1,81 @@ +\section{Requisiti} + +Lo scopo del progetto è quello di progettare ed implementare in linguaggi C +usando l'API del socket di Berkeley un'applicazione client-server per il +trasferimento di file che impieghi il servizio di rete senza connessione +(socket tipo SOCK\_DGRAM, ovvero UDP come protocollo di strato di trasporto.) +Il software deve permettere: + +\begin{itemize} + \item Connessione client-server senza autenticazione; + \item La visualizzazione sul client dei file disponibili sul server + (comando list); + \item Il download di un file dal server (comando get); + \item L'upload di un file sul server (comando put); + \item Il trasferimento file in modo affidabile. +\end{itemize} + +La comunicazione tra client e server deve avvenire tramite un opportuno +protocollo. Il protocollo di comunicazione deve prevedere lo scambio di due +tipi di messaggi: + +\begin{enumerate} + \item messaggi di comando: vengono inviati dal client al server per + richiedere l'esecuzione delle diverse operazioni + \item messaggi di risposta: vengono inviati dal server al client in risposta + ad un comando con l'esito dell'operazione. +\end{enumerate} + +\subsection{Funzionalità del server} + +Il server, di tipo concorrente, deve fornire le seguenti funzionalità: + +\begin{itemize} + \item L’invio del messaggio di risposta al comando list al client + richiedente; il messaggio di risposta contiene la filelist, ovvero la + lista dei nomi dei file disponibili per la condivisione; + \item L’invio del messaggio di risposta al comando get contenente il file + richiesto, se presente, od un opportuno messaggio di errore; + \item La ricezione di un messaggio put contenente il file da caricare sul + server e l’invio di un messaggio di risposta con l’esito + dell’operazione. +\end{itemize} + +\subsection{Funzionalità del client} + +I client, di tipo concorrente, devono fornire le seguenti funzionalità: + +\begin{itemize} + \item L’invio del messaggio list per richiedere la lista dei nomi dei file + disponibili; + \item L’invio del messaggio get per ottenere un file + \item La ricezione di un file richiesta tramite il messaggio di get o la + gestione dell’eventuale errore + \item L’invio del messaggio put per effettuare l’upload di un file sul + server e la ricezione del messaggio di risposta con l’esito + dell’operazione. +\end{itemize} + +\pagebreak +\subsection{Trasmissione affidabile} + +Lo scambio di messaggi avviene usando un servizio di comunicazione non +affidabile. Al fine di garantire la corretta spedizione/ricezione dei messaggi +e dei file sia i client che il server implementano a livello applicativo il +protocollo Go-Back N +(cfr. Kurose \& Ross “Reti di Calcolatori e Internet”, 7° Edizione). + +Per simulare la perdita dei messaggi in rete (evento alquanto improbabile in +una rete locale per non parlare di quando client e server sono eseguiti sullo +stesso host), si assume che ogni messaggio sia scartato dal mittente con +probabilità $p$. + +La dimensione della finestra di spedizione $N$, la probabilità di perdita dei +messaggi $p$, e la durata del timeout $T$, sono tre costanti configurabili +ed uguali per tutti i processi. Oltre all'uso di un timeout fisso, deve essere +possibile scegliere l'uso di un valore per il timeout adattivo calcolato +dinamicamente in base alla evoluzione dei ritardi di rete osservati. + +I client ed il server devono essere eseguiti nello spazio utente senza +richiedere privilegi di root. Il server deve essere in ascolto su una porta +di default (configurabile). diff --git a/docs/documentazione.tex b/docs/documentazione.tex new file mode 100644 index 0000000..f26750b --- /dev/null +++ b/docs/documentazione.tex @@ -0,0 +1,49 @@ +\documentclass[12pt]{article} +\usepackage[a4paper, left=1.9cm, right=1.9cm, top=2.5cm, bottom=2.5cm]{geometry} +\usepackage{hyperref} +\usepackage{colortbl} +\usepackage[export]{adjustbox} +\usepackage{graphicx} +\usepackage{listings} +\usepackage[T1]{fontenc} +\usepackage{lmodern} +\graphicspath{ {./media/} } +%\def\darktheme{0} % Comment this line to toggle dark theme +\input{style.tex} +\input{title.tex} +\input{header.tex} +\usepackage{tikz} +\usetikzlibrary{arrows.meta, positioning, automata} + +% La relazione deve contenere: +% - la descrizione dettagliata dell'architettura del sistema e delle scelte +% progettuali effettuate; +% - la descrizione dell'implementazione; +% - la descrizione delle eventuali limitazioni riscontrate; +% - l'indicazione della piattaforma software usata per lo sviluppo ed il +% testing del sistema; +% - alcuni esempi di funzionamento; +% - valutazione delle prestazioni del protocollo al variare della dimensione +% della finestra di spedizione N, della probabilità di perdita dei messaggi p, +% e della durata del timeout T (incluso il caso di T adattativo) +% - un manuale per l'installazione, la configurazione e l'esecuzione del +% sistema. +\title{\uppercase{Trasferimento file su UDP}} +\authorid{0253822} +\authorname{Fabio} +\authorsurname{Buracchi} +\begin{document} +\includegraphics[width=2.1cm, valign=t]{image1} +\hfill +\includegraphics[width=3.55cm, valign=t]{image2} +{\let\newpage\relax\maketitle} +\tableofcontents +\include{chapters/traccia.tex} +\include{chapters/architettura.tex} +\include{chapters/implementazione.tex} +\include{chapters/limitazioni.tex} +\include{chapters/ambiente_sviluppo.tex} +\include{chapters/esempi_funzionamento.tex} +\include{chapters/prestazioni.tex} +\include{chapters/manuale.tex} +\end{document} diff --git a/docs/header.tex b/docs/header.tex new file mode 100644 index 0000000..a6372e4 --- /dev/null +++ b/docs/header.tex @@ -0,0 +1,13 @@ +\usepackage{fancyhdr} + +\makeatletter +\fancypagestyle{plain}{% + \renewcommand{\headrulewidth}{0pt} + \fancyhf{} + \fancyhead[L]{\textit{\@authorid}} + \fancyhead[C]{\@authorsurname \ \@authorname} + \fancyhead[R]{Ingegneria di Internet e del Web} + \fancyfoot[C]{\thepage} +} +\makeatother +\pagestyle{plain} diff --git a/docs/media/image1.png b/docs/media/image1.png new file mode 100644 index 0000000000000000000000000000000000000000..2670ed7cbf0b4e2e04a63b6b39bedfcfd1ca8323 GIT binary patch literal 10000 zcmaKS1ymd1_a#!iK!X-N}*_h;uMOzQ`{|R(W1dE zKnb?#@BiQ3v%BYP&Y8@aIWyn)zWd(2@6Mcwe5;{IN=#3Tg@r|`tOU}+?CUVw2q8Y^ zpPD-A-|m6OYh@im%rAh@8jd+8a#b?$z`}Yu{cpp5@U12T3+oA%GU%0#&$ojN-vFJ> z*83GZfdpx53su6W``CEz=5Yu=d=J*Aet;*xk>s!kxh`4Wb2h6rY;`ayIj@_EGR30; z7*M_So(H{jsv=~6L}>KHU+MKs>ebzym|f?y)SuP@sRL(QUuR#Ap)&-td`B;@WX8k^ zs35OEevwLLNbZXoX=gIP32nSLkit;2@onz6`}e46MUX;d|7t+^P6x{!A|GOoYSCFg8V{V`fi}d~S6NC5jf>wMrj{W0 zSaW07)tz(m0r7|KVFceMS!m<$0r4T{Q$Z_5*FPAX7OZP-NeXMESP#lcovczD*iAs* zQT@^*&QR6WrFNAu0a_nsq@HG@U2twkAaJs0Nx=j!IYh8`h3yIcW1;|Fpo{-xMZx%F zCyWY?1#$WVD-Rb$iYQ{KG8h>kYJld1kBF>=%fY;niuLE0u|MlyBWirq5o;m#tN7-) z-?8JO`^UG05v?id)%*au2VwyO2WbTv(9v0;(g)AM1#M^?GBas{xg4;eJmmsC4vK#{ z^8uNCnl9OQa2iwK14pNH7_!eYI9`W%pFl%&qStkm=E=f}#}ZqtR6`y8GS#~VPLe*xn+tIfQXg_3^r+FZ*w}0;V)+DfYGzQL z?|`Wz+`o|s&-c;93j!@EDx%cG>)DSr(0X^$f z+G7JU6rZ!@C_Xv-p?lx*?w%dsD+hvk9%_OhH1XQJzXv_)OhCMJ=Vg?E?zSGqR-((2 zcR=|Ken91nr%?|tuo;wRXVUk($IYj=;#aCF@C*26Wxj9Oc88( zPxXi$C~3^t91V1b)>diKiIf83X+e3zKLR4%T5ja6U)5d^gRE;v{7Wu56|V0o;^i99 z0HZfhI~n|&3B_g-IZaQN19`xF9@y}UHj~|@9l(|2r2(i@SiM9>V3dEbd_suF3}hLS zA!mo0I8sGTpfpeu`;>rzNg-!{;cHVoh&x0d5$efw-cPwG=It9@+|p!I5;s1vfe|W4H%q^navcy5w86o8 z#8ik{c?|vpSTHLaUSlbnF{?O&2p;0f>rbpgHiCL)5ZGpO*&MKc;pX2q|j5&cU^g9E<$pOx#u^oRUN>(9mpr&LXIbWpG z=lEpKgoGXUM$GCE@FDPxczQYkxLW?J6=9Z<_@ z<9=AlgYa6FcC%gNRK`6XI@o_5had2XA+{6&xyGGrtMML36@Y_0R6H8Yy*=xTh|>x-qx#7^2vGyK zySp~Ue^eyNCL07^{RE)^{=|M_RkY*OOZnKh*W{3yA|>r{A?J2D<7-)S!Qghd5|t}g z)7xzZd8^o2vDGD8B-&&Z8)e_`65GxV5GQOQ=bjcC*(@)V0c{wXGoi7eTa0MqZmrxe zj*R@Li3M{LN*dVB*s`k-Ps!Q$l)}bW%)Ancg2jK}8bbbyrk=^?JWm=7XTaYx*400T zI>*QWdKWskURKaYkB8j`!&|V`6z@+(9n!psJ8Y}LFc=K$M+eDyP4YXlh_nJ5xDxP~ z`z2jq)g&Or{Duvu9kNdVSPJrlqn{nqACtOjA%p<&rO52+@bQfU67^Lhb;K4Hygw*M zv%xO1-$ET>>hYqm_PhG~M@yTxwCFe&l4Ev1LIGUq!?v+^GcmAZY&0C6pQP{l2>pW! zg?Qsp{m6&TWbtEgL=0+-5%1>ahWGJ3=>r;3FCnj9c)Sd`6d(E-lHHSk<$)ACFvy)+ zu{2@;nZt|Ss}r{=`h{g8mx8Cs@(1|$o=CcL@PKt1X$v=yKv><#(OR-C!XB{m9c)+^ z&CY13>|%?s&5rgXcP%LZizq61%}#YwTU=*H4^r2Q-O83W{`55eHaSD*k|lqc;qFEu z@;UTywkIcIByAyFO&y{8J&Q+}Za67?D^Eb06N{`o|s|#XO_Tbc@H*!rp0JPDJK!IP!@q6;1naF-)+KF)HUPn5wataCK zs$ED}*O3#4##31Ue{??y((N?7s@tbk9t43pa4CT@<+4(m@YE5+6SD-T6CBYc^3Nvq z9bG}SrqqGd;}*#*dN(U-=7ECgn_^oD`3^`yGM|CQ@AGgmDez-AR@c{{HU)A|#oV#| zk0HR-9b1v9RK9WYa8_Q475P-NY`$?6Hd$mpI|`{lh%TB*ZnB#n1!XI~Hl>J0>U8I0)W)eHu6mfYTzIacuMp(1||g&+4s{z)KuAu>SD5+le}{jS_2f!Wx^vQ<4c}L$*t>b-HV^!LRp=QkSp3=0{L4r;Z19ETrn+i z#v9@?D?wuUxAn#bHSWg=Zca>|)5XGh*?r6z^_I-?&{qy>Gu@A}O7bmoc}O2AnoNl9 zLa}+pdrJ0xYBS`x`!Ml|DA0NdQ|j{$oA@~F>IjDh@to*e z36%B)c7z!Z&;scZ^42rUv$_C`=e|i`bY$nb+vQX<9nZxO!Q14Z3Tn!^zOvgDUl(Kf z=oiiXg_qZ^Qo?p>@wVlqX^PjCVg|!m+wS^R)MdFP15u#Pu=T>__eufSMtaC0iU(JwVGp-mBST`kAMJ3acM6$k z_SXBd>;Hp~B&hoBP=9}sOu6}%tepz@axyJ&vol?^6&w{8S03-RKgSP)Za}^DBxU`q zQRB-g*Wt4yl3*8#@(lkG4Y^wzBWjLEXDR$vId)^22W>JM-{AVq-lan=p`j1j7hKwx zHmllJwA#65*Tk~%z%dM>pO~fnrutQfG!Xs2HXLF|l}OD09{&-ZJn)v>dT*N?7*Dp| z8*4TGJ#9~yNy0N&OYN(plLjbM;iZ>DY^;lEL{<+unPsst%?VAEbP)g1d%kEWE zBL>k)xfUYLB9i@#AZwMpAs^5KfG{N~@C;aavtb#HVyn3bmj1Ghpu^EOdi-3OhSVs4hn zW5Mc68e@( z<+oQieL=8rHpQ2DT0`fYmh`*sg^+hhZ+d;HU&)e8sD$2&|AOa#PexJK$pQXR1edLq z1q}#`?84Nv92KN(#@8E6=H7%qoJis}SX<+rY*RJQlB*S4w^-!ePf2-v*nA`dh33B1 zc}2?g|3W{o{=3#Onw(LrqP_Bblq!qvY4+q_g25UbG<1JV@GSG>Ke5flWn46`+(JBa zxkgWyn@c}Q^C#K8?Yww4Nd4~r2Kiq~{}b~f^I`S86U}$KTrL_*VDr@h7@#8U9NdWk zo8L+y>rYoiVN-L~3vS#ebOmnGsmFL=~S$j+YI8!8lWX;pb3HR72}xbl{dlz;{+vIAV`@t_S*)kn(oVI?AwO-q}2^#x84^qI7%XDHWe|B6NkAsz+=kXn zG+!hRSUXHj26}_NHC@)*_amlho_(^cg7&inar`CO%?5j|mJ+q_=y zi}U|YBhMuVn)C6?orS>M&2cIWOCv51778t4#8uVR^V&6im{Ej3f&C5K-A{Sr40G(M zf38$zo+tbK?iu=gFWInk z_xExg9A065k3)D`!#47U+QMbA;iS5tft2`Y8?;MQ_tojFhHgZ`D+oOoPRiiz-%Syw zn0nt6urD3By{s(E9>o%fn(+7ag|t4@nizb}&Arh@$>O)8vm`7Wnvs-raaCnAG_`5z z443DkiDx=$)f;N)d~aWFeX`bbl=#*6lKruKS5Z;X>PVhwpzS2G!)DlTZ>IF#xY0e| z(9#8$+n_O&lE1Cqv9a#2yRwCa1roJ`=+oym4WJ9l(HI;WYP?^$OrT6y2WzJ#qa+T8 z>o6UsJ@8`QF1C4B{?B`E{ifiJ6l=}&KQz0B0B&jDQ|pt~%VkM6F|mf8hgvlc@$m2# zuYa{tdAsc~9ZJ3NTk2#N6g-o2bv577=}q>9P0AYg{Ybe z(haFU8{Z>$t66xLW%&8W>%cPjM@&^65fT$OS)q^<6N79g5SnuR;XU#4k9?Zx^lMRK z75dz`dZmW7{*!siZ2?LFX(*H@#ODfM;2P~yQS!75j@!JH>3h8TE4qH!Z!hCup}fq47Td`s(Q&!HU-_MxiNcx6i4mzE5~~N)d^i+Tr*Q9yAPbhcWzJA-_+qS6#Vi z%=btZ(w)P?bnSFvVqyxH=7Ty@7_99v*QxZE3wD10E~lRp&Ktll1%wP02lLf->gMLF zOrlGk+8n~EPD|D{HvCty1J>sQr!|~Bij-lbZ_y*7D?ei+BU43uj#pjT4ri2oeZ>)G zDWh$f*8wZ#-dit1Nk6y4Zqc!UXjH1Wc}MUQ%tc`srTH`=l1&YGFH*%iZBRvG}e-IpEfJQxKrFgXsH<* zOy4LS4L_ckjPpSw5lV$3Xs7J0M7qx>FU>oI*dSw1f!rpwiCITz@wz#Y zfa;Jf3XzV@p=5-J^Y5DLG{>^k-}NqL)Ef{BQ0SOm(CD`Vk0Q9^dVMHF!{Q_FAt{5% z{{B+)9+8``$NYtei-xVRjjFzo#(Dr;RNY*lOMd%-t$YB>|_49!6j zLx~oixSVZ`WNmG2kueK9=)`q&$O(IjvR++X4F_1Tli<+EH58VDZ5j;bu-KH9l~q46 zic@%Zk%~S%nybvEfC{>zknh;rMB+0{pdSpSi_0bQoFax<22d{mE-Q&dJBOy^?j4zQdwvr#D3Yn5bAJy`2tn z|SDlXW@LhOZO_)htA*V%;K9oK$?h7=Ow%^ zT&njLamexPS@&LM1~Muq2Pk8D$A7dR7|{iYcp5cxqwd2}YC6|Ni2a@FcZPl6zG-!; z#}H#U8Bk20a++%l6r3IlYt&86fah7B@{wy0p={dB%>)>KMs&VaK=w ztHUVx+qZ8!>JAp>5?dm@zoE7C1EBRua9Nd$Goy<`J49a!+XfBj=%rH zz}w}VFcuX>JUKZz=kLnruW@A2*T}@kEukb|PfX~cnj96rs@>_W5G*h*Ak-P%xx+A# z9NPcDFYGeMNJq5EdtasSQ-=LYn{;+|wmx?f{aU?Ysg91$TOA#ch6Y)xLF%_5slJ^S z#J3jDFB>h^U(OOZ33=osq7V7%mviUqjtwkkmEhEnD=+RjNk#WAybL09C{!Ha-6Ye%C5;*J5WvQV$z&m6}qzkJ-AG9P~U8 zvcc>reqwSWoV9Gu*u z3cb%}NkREPZ43N;pnegT>{3q{OO^&xI|UhsC6pRlTdIsD@4Z0}Kst3R{6$tarjfOI zRrknMd$htuzK5%wT3Y1(7bh}nrYH46^)ps_VAWPOs;{0zJDoQ~XQ_{60kVj%x11Pf zE!DhkK*MaLpE|>lLmt5_1@e0*K}16FciO9cEtMfNTUCjcS^rWlBX|FIO)XU-?IXHJ z1ECN~eGdoqSOG!wC{}UPQGitCVG&ugYvwB#hVy?^@uJzv0aM3opAH zVlo{I{t*7m215KU8K<4=yZGo>HSgWCIHj057GA96_= zbYPL)NYIwCUc28n4)A&)E*qs8(Y`Wv5(J)p* zZ+g~cEZ_%c4Uk6HNSW;0 zGo&m+GDnne-!CM&E$BzpKJ5=PSE23aB<#GaJ>9f)%}!2VVFjL9ot;)BSF_ncZc_I? zn@aa_Zuj3!6om@SxU(_5RZrFQc0Mmfo}Rl?%mLWRb16vL3m$A*Fn6 ze1&h^^a4CC7zCmO;lwePuj`54pW32+?1qEz6x_^Kt{YshaLSJYTNRA5Snj{wR0U92 zQ4n30(-ZkwT$0=btdGqnR7PM0GmLT6}xomQLL?H5ISnNCbu>j%1f zR)75TF(xgt$lIJP#$p_?Td)}{R#cEKt-ghPn*m0}4aBp_oo%&Abc8#t)dsjBbj6~aex^P)~odj5c#xL4I zy2> zu3Y}9_gsZOyJvbwaY9KM_h6oJWVo0@S;mf}@7==oo|B-kZ0$0PDJ!3!b)PHO^v}er zxGzR&L($D<*3$#;^UO2~7j;L!-FfK0bbIx2fu2tAOBf}s<)m5*^sn@?#>?gp`?@>+_amnH__p=p>sHKLkntsq6LC4+&UR$RF~4Ym+N6EtXzP7c@!22nv31YpQ>G?Gf96>LbA6zApzk z${!1(R~Q6}ZyGLa5A!=se|3=I?oco96<@10rG&y=F-o;VF`2K>)^0YPo*9acn3SoI z26i5jG&4*^tp8kfDfY#s?jzs5j$RR+L8<)9cUWtV0M1E7x2!q8LGi{4rki3|!O6|H z5k1A1R(m8(=P%~F^UNaYWhM(+3aHgXioHa8hn|}KdwnH{(f!|7vn?Eko~}YJF}}Is z2W4W3f$+f8YeFG{(}6rqCAbo~4cm4@$Ql zfWzgaNtLVO%Yo5dNg(S#(@n;P5TcrQl^+UoCHwxt;~9m9j}#YH5rw!*-?h zDFr05Im~Dcg1wsY@#uIaq~3>Zhx*PD8N@GW!7fN?3k2SzoUtb8YFHjDonIf9UhYam zP0sWQLSP}?q21<`40gn04cu0fY7h6MVxDG1JhCQR!@T|z$(lEAoEQk{v3@EdM1!$b zn3k$%lN_#Ktk1|k0@6^{Xr-0x4(sL5J9{L72hEu>9E@v}b$`D1_G_$C#s^7&6B#=O z-687ay#tma#%vbkCDy;x{7doto~>Te8R8~o+!+UVfjqBUw==2QNG#1i@sTPa(?P6@PUFUrz-5@-ZVR=Am$Zk``8!)6g%Q+ev9!zK2QwEfinguuu#y`RTl@WTDa zKE=e-iQ#5%W(-ze+{ANxpVbLxhnBi3V?@<}s5<5A%aY!*Zj4zxyhgXZ@UJvtD4nHN zewL8A8_AWKgRzpf=jlD?{IiH!4?S)g5(9CAeU*it4=l8wO}-p}DIGaTukQ&M=RT%b VpYID`K54^Jme&B4y*B^wKLEh%)u#Xe literal 0 HcmV?d00001 diff --git a/docs/media/image2.png b/docs/media/image2.png new file mode 100644 index 0000000000000000000000000000000000000000..fdf4317808ac5ff984bd52ab95bce33a70d2df28 GIT binary patch literal 6835 zcmV;k8cgMhP)Px#32;bRa{vGf6951U69E94oEQKA8dyn0K~#7F?VSmH6;+nTUtU5&FacQt0c5Ay zK^bsIjf@MfXe-JNGKz>guG7xgqBeutPSb9U8?7|PAR;QZh)9bV+(pF}wGl)%WeWxp zNFWPI=KsZ)_?D`dynUH8xu&CTIqlgEY_* zRD*sX8MFu4pvrNsYi_F*rAr6h97kPClE6-|3rL4VVY@*ANCp1_hro8Q9~9LcNm#;p zT#JlhiDiMfw*X8bV(AQy0qsCXkOkU0(3+zNMnrNn2&EiBc^OD?WTImQCopK*XWzy8b3_r_|}1(^?VnwF2^rlvV_(bEWSB)ZsKJqE0Phk$0jY z(&s?l$p-Kf*a|8^HJwUi6f~i}>Zhu+$W`?_1Dpa>Z+Ua_>JEdi!B^lzun}l@x09O{ z7jv%yKg*{joY z`x1zd{t4EC&6HnWTExX7pNKyK{|hj7?ASXXhClkR!KXV#aO#kn6x|CEeNNt=HEGgh zd2Vj5yn%p)&ZPDzkEIvL0mlN7jD{cWKr;|3Ie=ny&Q;)3Ag^XO1yX&ZUa*~P(-m9{ zMg!T-S>(BijD7_9;4QF*`jvBxi0ENk5~CIvJ$m#lkibI`EIw_%cA|78<#@Tex;p>G z7hl}k*z!0!jEJQZ7y?cJrved;9fVTky{RL91pWcmfI>Qi>J-yL1vEU9S3DiarkY1G zl5GUO4&DUosQXT%M4<|^$R|{m4TIh&ARqTyYVKnRM3nc!3oocMy9k{^^A;L1X>LZ7 z!F|9Wp!u6{h`gEAK=U>4g7tKOn3xEftHcADrboo3t%J7bIpNFR7j*lr_F z@q8+A54aUHYea?gBc!>btGV}Cjw1d|=<>NiSmcA{;#6>2k~Goiv(Sn(TsjeG^V-R{ zO()x`syh0tDEK#MmFi`%h{nD(`05i+BX*P*G=pcpj-%#w+BDJ%8Kk=jOa}A0|4DRd zw|Aq(BA-T^D!voac$!i}xz%IANuWDu0U|9L&uQi5nZ-kfCB6}1tO&?xTzE5|3$RnVB5E%IrMmEtfB4TUAD+Lsiy{CyD^0ra{8Z6_+^ zXG-xd_&*Btxk_}-;K4&i_Um^w*Y>pP>KcZB3T}#rw~*oG3SGprI9{i@j5<2cYo#V9 zC;vV#FR#G~c6P*fVJLnJ_v;mmD$ez+lS@)lm%WjjyM&9+Xcw0dwh;6NzDNfW-vcJ2 zV~ z=XvsK5c);C6!ZgGAlhUP1$r20Irm2ztn;sXZCTSRE30Z);&&H1QLPK)9v+9+9yal@ zj=JANM|Dk&wqw)_w*Q_sqv7{rptj?WoXJi4k<)PQ59r+DuEZiAcZ)t)5K+=c8)|N3Bkl*XfGN23c1)>*(DAhnh60Nk5{+3Rlr<4sA+HTeU4M zz0NfzdaeqaHM70bv#0I7ZryC#Ted7+I)A=*;)$N3Crn8oijN_U+o2ROW4_QjiY-RkS3^ETAEi zo{J``1ARW)(xLX7W04P5hy~$^_oAG`fqp$s1+81~S_9u19(Yr<-!fKD*BQOpFsAo#u3YDML(-$h)obK9Y2-6Rm zsqn1yvfl4Z6Zjcnk&mB-3&(9Vkqq*=7-;2TFvte=9ZlW@?_Kd*6D{&31-Y9N zzA-90yG4_#suL-^+3<>0^s33E{~s@f|B#MyJxj*gyI9PikE(ChRco2xi9|kR>{tY}x;kR;h5s2qy01`*C6XGUM*1X!pCa`qq$y?PFL{Xl<^(#2Dlv@3u2LY-lyDWQSj@Gh=(}a z!i5X>aL}Z(nH4^Ec)$=%I@t^Z=L4-)^#-vn6;vP(+z07w zZfs?k(^->}lA`ywI-1+<@#e*N)K!zoyj}eP!>cPjB7X_!&Z>-vDO$)UY3kIeDaFObJ$ZY)FDq?rSuN6! zN9|j?oR?FV4WNMY^_iKOKg^vw_XzPkeoMSrz#<=S*6|~b7N_@MG>jStG=Y2+h!@d> z@)KYtq*LT?rRb<%aIYEpuzMh>i;-?fOYf~biUthk&o{-GrWVAcM=gJfreIEt%^5UO@%bX zdOHXet9`e}Ob_H@HY`=gd)%D>xV{rzK_MSHXf~uXiG0R^+U?`$H!G@hxDu&9bqCW0 zI{McT(d*PFchRCn8wm?kE}%^R>#Rg&11kw@kbLmmx|8-%bhvonluxfuSmfhu*l0h8 zaGF!bOTYv$1hfS4Ch~S>f!Xw~vS3t>#=eVRYt0#XesJiQ3_5U7Xa2>l_$5q#4TpGi zsCrM}*QCCk<>cg~WM^lKG;XGIcJu9S)6^obfz)*pUfb-{)YPZv&!6wN4XLMwbAgCH zAm*WiFg@e2i?!P2qsi$FY2bM95V!sSo&#rrcqATqC>Oy3vw+{71|2R!Cn^EET>_mh zh|7WujNh1bN*AnCUhC-DzJ2>Q$oEkYEaGu4-Z6CWN6O2~SKtY0?#~h|V3AKFn*n6g z5!^uXB_JQ%0@{Fh6cNpzftJsMG0bwY5fjP>=!`pD0$neT$;!$ye2>dDS>RWAB`FHN?;2T3gg&+8m~1rCFTi9s^n` zcNsHt9Z#}}%3KQm3*spbMa7s?!K9vF)N{%!;9-=LF4I#H4umh|&a^C2kg= zXK2ph6mSg~4idpm>hO!ee?mGxa}1eOR#sLs8Cw;y3>u-B&YvUVA!$pKa=31LO6GxTI^0zmj3p*$~ewfj(u3Yc0Ai zM~{BcjHDd|^$4Dl>6Z(PZi<(%gd`uxCK>bqH}W9A084@9a*hUxB3g2t1I~tYOotVr zFb3~#wLov@Q0Q(*C&N9>-Tc-h-AhG9MHjL$Flb&Gx(-8Rcai5TlRQskh<24pTwDhy z9Qi;vXY)k=0zL(rK<*0?O}0?sagff9pd<<_PqBh8EOWO(>#atbal}5vOSxhB^5r4f z^TIG}hGVGbkv)0ep?M;Yh&T^O2=ak&GQf3U70>f^p!to~asF)0s`n8Q7tp(3GD{HZ zaQ3@s9xdH0A39@cNl8i2hk$hck>qK#`+Pqe0dETCs}M$Y!|S?>@O1`pEg`;X2H|lX z6u0C9(P#p>54f8r_zloINoRtlAQ45s==!>#=65uq8Zlv>L&L64W(9yGw&HjqF71%}9=4DGy>LreWU#^4D&!0BYJwW<{r!VssMw}RlIZ1WcHXPnxG$54;F zo@TGj9zS*Zd>cc%3N|-BPQKl_`7p;?lEov+$1GHB4uJ65fV zHsVn!Pw?`vO4)}~=q29gC^~fy2(2UDh5Nm083sjS{;MWGKi}o;a<1LW7WdaNMLv*@ z-gF)TZUQHQ=D-p+3#g0n&-p4>ckUeYNn5w9e7&dA=ptjy!5KGIdftC6Q_x~juU z*t%fP!9X%cLp+)jbP@9U(IvpT2&)C6ntUJ~efwlI(D15PSS@2^77`0U1i$sBn0RfA z2(}omtl-$OppFGN|SPL^^WK(|{#jERb5oS1H`)F}x&?cobgEC<>t=w3%!S z_J`ojGP1%WZ-I!D52T|FXL7~IfZ-sa_vcXm#>(DJZQ6Waos@Jklgm*zx8q(3eX6vf z7bqboZ43>%;Ng^`+ek0$ZAqjo5Ha$BbTX;*R4@*B-S-hG<3w@3+Od4q#;mODnxrIs z^epNH0=SJrdh>b;SoYR?cR?5Cb~K#RyJ9ZGEC{Jv&@;0XHrfI%52Mj{1&|QZ=?QK{ zg(enf1B-Zkv*$LFkKlKF+2&TQn%54!e7isFsrg{mg>?RF8p!xDmc92ikjAfQNJg(~ zh_KzIlY4-59X<<$jeH=Sf#8o+Z8i8a=p4Rk#!XHiQU#|I^wU=j4Z^LUFjyeuTXT1)IU>*Uf~_4mJE5Gp@coWl@ib7sl2?rP2SQ35{E~%9nb3r104f+ znX|7!c%*`Oyon5gG&XZ0NSfqmy0u&L?BuM6PfO^7qJDs^BAI{1p!{H5Ns`s1jaQY7r z4J7wJn({2+gI#x_p{1Lav3O^X684!}ct|d%Oqr5-=+L1DA)Aw&Gk}b2ltaVngpr^O zyC5Aq(5jVwUG%Mzk-#Dzj1ZuxQ(O~E#G_wzI{FIA-4OAxMX6>MZ+Nc^&_<2AUH?&n zgzp$6h6j@FHV6+%5X5u7JMlO&(!7@lJ7FXs@_}&l;jYWD)*6rph5>7HAVvs+JOJt3 z42pxGE95Hhc3MF^Ga!^@WTw|*yb2v~QE_px79heVl z-xww|4t$@^HkQ?`UB=O&3)Q4DBru1G-E+xB|H;BxT2iigctHARfgd|Z1bU9W(#i8# zd00lecux1>^%NMz#dBDr$cGl(79(hQbvy82?szs(nCo^wq!TQUCy53h(2v7;=be|9 zm6esjL~RDTvam%DI?7NwsR*YJ@YQpEg${TPoqK;)MczeUi#KhZu|D_N3cP1CjWKRXn&BwZLUF6r%r7`9#=V5 z*gCn!)?a-|8E)gsFQe9LiP00oMu{cr-n+V*di4eY{es4*ZNgdXS!8xMMh<2pOh>0y zR{W|*9m)86zf-0g)WO)lqKW9Jkhyc`Rx%Wt&OD2}xw8!lrG7CHFYipepjYtvo`i%p z8l>|GnmBP{Yu-`2^x(mR(})}l%)G;&(R&qocX}vEkN_9*q2+6Mbve-Rs#}8CAkx*m z4|m+hEMWF&jrEy&nOk>`UGe<_uCx(7Cmq3>i7M!lXSQ%pw9B_ z!Y(Pn=mcRewUuG51!zpAG$wtg=17Q4qL#h`lmJbP?j+AG?D5&mD%d)_hV6ufQ}lkD zyasiax*tmYr_V#dD1>8W>k7P}9PBy@omw96?x$|qY&m)sGY|B4Y$Xy3NFd)5^l8U? zWt}3fn}MpjYi~!MTO~U7I?_vrNV^I4i^vM3JReJ9)B^0-eTiQ!?H2a`=dgjgGof0< zXD7<(KnLhRjcVi#sPsFU&hrmC&*{zzsJor;J RRQ - 2 C1 <- |6|multicast|224.100.100.100,1758,1| OACK - 3 C1 |4|0| -> ACK - 4 M <- |3|1|1| 512 octets of data| DATA - 5 C1 |4|1| -> ACK - 6 M <- |3|2|1| 512 octets of data| DATA - 7 C2 |1|afile|0|octet|0|multicast|0|0| -> RRQ - 8 C2 <- |6|multicast|224.100.100.100,1758,0| OACK - 9 C2 |4|0| -> ACK - 10 C1 |4|2| -> ACK - 11 M <- |3|3|1| 512 octets of data| DATA - 12 C3 |1|afile|0|octet|0|multicast|0|0| -> RRQ - 13 C3 <- |6|multicast|224.100.100.100,1758,0| OACK - 14 C1 |4|3| -> ACK - 15 C2 |4|0| -> ACK - 16 M (except C2) <- |3|4|1| 512 octets of data| DATA - 17 C1 |4|4| -> ACK - 18 M <- |3|5|1| 512 octets of data| DATA - 19 C1 |4|5| -> ACK - 20 M <- |3|6|1| 100 octets of data| DATA - 21 C1 |4|6| -> ACK - 22 C2 <- |6|multicast|,,1| OACK - 23 C2 |4|0| -> ACK - 24 M <- |3|1|1| 512 octets of data| DATA - 25 C2 |4|1| -> ACK - 26 M <- |3|2|1| 512 octets of data| DATA - 27 C2 |4|3| -> ACK - 28 M <- |3|4|1| 512 octets of data| DATA - 29 C2 |4|6| -> ACK - 30 C3 <- |6|multicast|,,1| OACK - 31 C3 |4|2| -> ACK - 32 M <- |3|3|1| 512 octets of data| DATA - 33 C3 |4|6| -> ACK - - Comments: - 1 request from client 1 - 2 option acknowledgment - 3 acknowledgment for option acknowledgment, - or request for first block of data - 4 first data packet sent to the multicast address - 7 request from client 2 - 8 option acknowledgment to client 2, - send no acknowledgments - 9 OACK acknowledgment from client 2 - 15 OACK acknowledgment from client 3 - 16 client 2 fails to receive a packet - 21 client 1 acknowledges receipt of the last block, - telling the server it is done - 23 option acknowledgment to client 2, - now the master client - 25 client 2 acknowledging with request for first block - 27 client 2 acknowledges with request for missed block - 29 client 2 signals it is finished - 31 client 3 is master client and asks for missing blocks - 33 client 3 signals it is finished diff --git a/docs/rfc2347_req.txt b/docs/rfc2347_req.txt deleted file mode 100644 index 74c91d5..0000000 --- a/docs/rfc2347_req.txt +++ /dev/null @@ -1,123 +0,0 @@ - TFTP Option Extension - -Introduction - - File transfer options can be negotiated prior to the transfer using a request-respond-acknowledge sequence mechanism. - -Packet Formats - - TFTP options are appended to the Read Request and Write Request packets. - A new type of TFTP packet, the Option Acknowledgment (OACK), is used to acknowledge a client's option negotiation request. - A new error code (8) indicates that a transfer should be terminated due to option negotiation. - - Options are appended to a TFTP Read Request or Write Request packet as follows: - - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> >-------+---+---~~---+---+ - | opc |filename| 0 | mode | 0 | opt1 | 0 | value1 | 0 | < < optN | 0 | valueN | 0 | - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> >-------+---+---~~---+---+ - - opc - The opcode field contains either a 1, for Read Requests, or 2, for Write Requests. - - filename - The name of the file to be read or written. This is a zero terminated field. - - mode - The mode of the file transfer: "netascii" or "octet". This is a zero terminated field. - - opt1 - The first option, in case-insensitive ASCII (e.g., blksize). This is a zero terminated field. - - value1 - The value associated with the first option, in case-insensitive ASCII. This is a zero terminated field. - - optN, valueN - The final option/value pair. Each zero terminated field is specified in case-insensitive ASCII. - - The options and values are all zero terminated, in keeping with the original request format. - If multiple options are to be negotiated, they are appended to each other. - The order in which options are specified is not significant. - The maximum size of a request packet is 512 octets. - - The OACK packet has the following format: - - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ - | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ - - opc - The opcode field contains a 6, for Option Acknowledgment. - - opt1 - The first option acknowledgment, copied from the original request. - - value1 - The acknowledged value associated with the first option. - - optN, valueN - The final option/value acknowledgment pair. - -Negotiation Protocol - - The client appends options at the end of the Read Request or Writecrequest packet. - Any number of options may be specified. - An option may only be specified once. - The order of the options is not significant. - - If the server supports option negotiation, and it recognizes one or more of the options specified in the request packet, the server may respond with an Options Acknowledgment (OACK). - Each option the server recognizes, and accepts the value for, is included in the OACK. - The server must not include in the OACK any option which had not been specifically requested by the client. - Options which the server does not support should be omitted from the OACK. - Options which the server does not support should not cause an ERROR packet to be generated. - If the value of a supported option is invalid, the specification for that option will indicate whether the server should simply omit the option from the OACK, respond with an alternate value, or send an ERROR packet, with error code 8, to terminate the transfer. - - An option not acknowledged by the server must be ignored by the client and server as if it were never requested. - If multiple options were requested, the client must use those options which were acknowledged by the server and must not use those options which were not acknowledged by the server. - - When the client appends options to the end of a Read Request packet, three possible responses may be returned by the server: - - OACK (acknowledge of Read Request and the options) - - DATA (acknowledge of Read Request, but not the options) - - ERROR (the request has been denied) - - When the client appends options to the end of a Write Request packet, three possible responses may be returned by the server: - - OACK (acknowledge of Write Request and the options) - - ACK (acknowledge of Write Request, but not the options) - - ERROR (the request has been denied) - - In the event that a server returns an error for a request which carries an option, the client may attempt to repeat the request without appending any options. - - If the transfer was initiated with a Read Request, then an ACK (with the data block number set to 0) is sent by the client to confirm the values in the server's OACK packet. - If the transfer was initiated with a Write Request, then the client begins the transfer with the first DATA packet, using the negotiated values. - If the client rejects the OACK, then it sends an ERROR packet, with error code 8, to the server and the transfer is terminated. - - A client acknowledging an OACK, with an appropriate non-error response, will agreed to use only the options and values returned by the server. - If the client receives an OACK containing an unrequested option, it should respond with an ERROR packet, with error code 8, and terminate the transfer. - -Examples - - Read Request - - client server - ------------------------------------------------------- - |1|foofile|0|octet|0|blksize|0|1432|0| --> RRQ - <-- |6|blksize|0|1432|0| OACK - |4|0| --> ACK - <-- |3|1| 1432 octets of data | DATA - |4|1| --> ACK - <-- |3|2| 1432 octets of data | DATA - |4|2| --> ACK - <-- |3|3|<1432 octets of data | DATA - |4|3| --> ACK - - Write Request - - client server - ------------------------------------------------------- - |2|barfile|0|octet|0|blksize|0|2048|0| --> RRQ - <-- |6|blksize|0|2048|0| OACK - |3|1| 2048 octets of data | --> DATA - <-- |4|1| ACK - |3|2| 2048 octets of data | --> DATA - <-- |4|2| ACK - |3|3|<2048 octets of data | --> DATA - <-- |4|3| ACK diff --git a/docs/rfc2348_req.txt b/docs/rfc2348_req.txt deleted file mode 100644 index 9ee7f89..0000000 --- a/docs/rfc2348_req.txt +++ /dev/null @@ -1,44 +0,0 @@ - TFTP Blocksize Option -Abstract - - This document describes a TFTP option which allows the client and server to negotiate a blocksize more applicable to the network medium. - -Blocksize Option Specification - - The TFTP Read Request or Write Request packet is modified to include the blocksize option as follows. - Note that all fields except "opc" are zero terminated. - - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ - | opc |filename| 0 | mode | 0 | blksize| 0 | #octets| 0 | - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ - - opc - The opcode field contains either a 1, for Read Requests, or 2, for Write Requests. - - filename - The name of the file to be read or written. - - mode - The mode of the file transfer: "netascii", "octet", or "mail". - - blksize - The Blocksize option, "blksize" (case in-sensitive). - - #octets - The number of octets in a block, specified in ASCII. - Valid values range between "8" and "65464" octets, inclusive. - The blocksize refers to the number of data octets (it does not include the four octets of TFTP header). - - For example: - - +-------+--------+---+--------+---+--------+---+--------+---+ - | 1 | foobar | 0 | octet | 0 | blksize| 0 | 1428 | 0 | - +-------+--------+---+--------+---+--------+---+--------+---+ - - is a Read Request, for the file named "foobar", in octet (binary) transfer mode, with a block size of 1428 octets (Ethernet MTU, less the TFTP, UDP and IP header lengths). - - If the server is willing to accept the blocksize option, it sends an Option Acknowledgment (OACK) to the client. - The specified value must be less than or equal to the value specified by the client. - The client must then either use the size specified in the OACK, or send an ERROR packet, with error code 8, to terminate the transfer. - - The reception of a data packet with a data length less than the negotiated blocksize is the final packet. diff --git a/docs/rfc2349_req.txt b/docs/rfc2349_req.txt deleted file mode 100644 index 51f8148..0000000 --- a/docs/rfc2349_req.txt +++ /dev/null @@ -1,96 +0,0 @@ - TFTP Timeout Interval and Transfer Size Options - -Abstract - - This document describes two TFTP options. - The first allows the client and server to negotiate the Timeout Interval. - The second allows the side receiving the file to determine the ultimate size of the transfer before it begins. - -Timeout Interval Option Specification - - The TFTP Read Request or Write Request packet is modified to include - the timeout option as follows: - - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ - | opc |filename| 0 | mode | 0 | timeout| 0 | #secs | 0 | - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ - - opc - The opcode field contains either a 1, for Read Requests, or 2, - for Write Requests, as defined in [1]. - - filename - The name of the file to be read or written, as defined in [1]. - This is a NULL-terminated field. - - mode - The mode of the file transfer: "netascii", "octet", or "mail", - as defined in [1]. This is a NULL-terminated field. - - timeout - The Timeout Interval option, "timeout" (case in-sensitive). - This is a NULL-terminated field. - - #secs - The number of seconds to wait before retransmitting, specified - in ASCII. Valid values range between "1" and "255" seconds, - inclusive. This is a NULL-terminated field. - - For example: - - +-------+--------+---+--------+---+--------+---+-------+---+ - | 1 | foobar | 0 | octet | 0 | timeout| 0 | 1 | 0 | - +-------+--------+---+--------+---+--------+---+-------+---+ - - is a Read Request, for the file named "foobar", in octet (binary) - transfer mode, with a timeout interval of 1 second. - - If the server is willing to accept the timeout option, it sends an - Option Acknowledgment (OACK) to the client. The specified timeout - value must match the value specified by the client. - -Transfer Size Option Specification - - The TFTP Read Request or Write Request packet is modified to include - the tsize option as follows: - - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ - | opc |filename| 0 | mode | 0 | tsize | 0 | size | 0 | - +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ - - opc - The opcode field contains either a 1, for Read Requests, or 2, - for Write Requests, as defined in [1]. - - filename - The name of the file to be read or written, as defined in [1]. - This is a NULL-terminated field. - - mode - The mode of the file transfer: "netascii", "octet", or "mail", - as defined in [1]. This is a NULL-terminated field. - - tsize - The Transfer Size option, "tsize" (case in-sensitive). This is - a NULL-terminated field. - - size - The size of the file to be transfered. This is a NULL- - terminated field. - - For example: - - +-------+--------+---+--------+---+--------+---+--------+---+ - | 2 | foobar | 0 | octet | 0 | tsize | 0 | 673312 | 0 | - +-------+--------+---+--------+---+--------+---+--------+---+ - - is a Write Request, with the 673312-octet file named "foobar", in - octet (binary) transfer mode. - - In Read Request packets, a size of "0" is specified in the request - and the size of the file, in octets, is returned in the OACK. If the - file is too large for the client to handle, it may abort the transfer - with an Error packet (error code 3). In Write Request packets, the - size of the file, in octets, is specified in the request and echoed - back in the OACK. If the file is too large for the server to handle, - it may abort the transfer with an Error packet (error code 3). diff --git a/docs/rfc7440_req.txt b/docs/rfc7440_req.txt deleted file mode 100644 index 080809f..0000000 --- a/docs/rfc7440_req.txt +++ /dev/null @@ -1,101 +0,0 @@ - TFTP Windowsize Option - -Abstract - - This document describes a TFTP option to negotiate a window size of consecutive blocks to send. - -3. Windowsize Option Specification - - The TFTP Read Request or Write Request packet is modified to include the windowsize option as follows. - Note that all fields except "opc" MUST be ASCII strings followed by a zero byte. - - 2B string 1B string 1B string 1B string 1B - +-------+---~~---+----+---~~---+----+-----~~-----+----+---~~---+----+ - | opc |filename| 0 | mode | 0 | windowsize | 0 | #blocks| 0 | - +-------+---~~---+----+---~~---+----+-----~~-----+----+---~~---+----+ - - opc - The opcode field contains either a 1 for Read Requests or a 2 for Write Requests. - - filename - The name of the file to be read or written. - - mode - The mode of the file transfer: "netascii" or "octet". - - windowsize - The windowsize option, "windowsize" (case insensitive). - - #blocks - The base-10 ASCII string representation of the number of blocks in a window. - The valid values range MUST be between 1 and 65535 blocks, inclusive. - The windowsize refers to the number of consecutive blocks transmitted before stopping and waiting for the reception of the acknowledgment of the last block transmitted. - - For example: - - +------+--------+----+-------+----+------------+----+----+----+ - |0x0001| foobar |0x00| octet |0x00| windowsize |0x00| 16 |0x00| - +------+--------+----+-------+----+------------+----+----+----+ - - is a Read Request for the file named "foobar" in octet transfer mode with a windowsize of 16 blocks (option blocksize is not negotiated in this example, the default of 512 Bytes per block applies). - - If the server is willing to accept the windowsize option, it sends an Option Acknowledgment (OACK) to the client. - The specified value MUST be less than or equal to the value specified by the client. - The client MUST then either use the size specified in the OACK or send an ERROR packet, with error code 8, to terminate the transfer. - - The reception of a data window with a number of blocks less than the negotiated windowsize is the final window. - -4. Traffic Flow and Error Handling - - The next diagram depicts a section of the traffic flow between the Data Sender (DSND) and the Data Receiver (DRCV) parties on a generic windowsize TFTP file transfer. - - The DSND MUST cyclically send to the DRCV the agreed windowsize consecutive data blocks before normally stopping and waiting for the - ACK of the transferred window. - The DRCV MUST send to the DSND the ACK of the last data block of the window in order to confirm a successful data block window reception. - - In the case of an expected ACK not timely reaching the DSND (timeout), the last received ACK SHALL set the beginning of the next windowsize data block window to be sent. - - In the case of a data block sequence error, the DRCV SHOULD notify the DSND by sending an ACK corresponding to the last data block - correctly received. - The notified DSND SHOULD send a new data block window whose beginning MUST be set based on the ACK received out of sequence. - - Traffic with windowsize = 1 MUST be equivalent to traffic specified by [RFC1350]. - - [ DRCV ] <---traffic---> [ DSND ] - ACK# -> <- Data Block# window block# - ... - <- |DB n+01| 1 - <- |DB n+02| 2 - <- |DB n+03| 3 - <- |DB n+04| 4 - |ACK n+04| -> - <- |DB n+05| 1 - Error |<- |DB n+06| 2 - <- |DB n+07| 3 - |ACK n+05| -> - <- |DB n+06| 1 - <- |DB n+07| 2 - <- |DB n+08| 3 - <- |DB n+09| 4 - |ACK n+09| -> - <- |DB n+10| 1 - Error |<- |DB n+11| 2 - <- |DB n+12| 3 - |ACK n+10| ->| Error - <- |DB n+13| 4 - - timeout - - <- |DB n+10| 1 - <- |DB n+11| 2 - <- |DB n+12| 3 - <- |DB n+13| 4 - |ACK n+13| -> - ... - - Section of a Windowsize = 4 TFTP Transfer - Including Errors and Error Recovery - -6. Congestion and Error Control - - The rate at which TFTP UDP datagrams are sent SHOULD follow the CC guidelines in Section 3.1 of [RFC5405]. - - Implementations SHOULD always set a maximum number of retries for datagram retransmissions, imposing an appropriate threshold on error recovery attempts, after which a transfer SHOULD always be aborted to prevent pathological retransmission conditions. diff --git a/docs/style.tex b/docs/style.tex new file mode 100644 index 0000000..3431412 --- /dev/null +++ b/docs/style.tex @@ -0,0 +1,152 @@ +\usepackage[italian]{babel} +\usepackage{tocloft} +\usepackage[table]{xcolor} +\usepackage{pagecolor} +\usepackage{parskip} +\usepackage{environ} +\usepackage[most]{tcolorbox} +\usepackage{titlesec} +\usepackage[dotinlabels]{titletoc} + +\titleformat{\section}[hang]{\bfseries\Large}{\thesection.}{0.5em}{} + +\appto\appendix{% + \setcounter{secnumdepth}{-2} + \addtocontents{toc}{% + \unexpanded{\unexpanded{% + \cftpagenumbersoff{section}% + \setlength\cftsecindent{\cftsecnumwidth}% + }}% + }% +} + +\renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}} +\addto\captionsitalian{ + \renewcommand{\contentsname}% + {Indice}% +} + +\ifx\darktheme\undefined + \definecolor{bcolor}{RGB}{255,255,255} + \definecolor{textcolor}{RGB}{0,0,0} + \definecolor{linkcolor}{RGB}{0, 0, 255} + \definecolor{tblhdrcolor}{gray}{0.8} + \definecolor{tmpltbcolot}{gray}{0.9} +\else + \definecolor{bcolor}{RGB}{41,41,41} + \definecolor{textcolor}{RGB}{255,255,255} + \definecolor{linkcolor}{RGB}{0, 167, 255} + \definecolor{tblhdrcolor}{RGB}{69, 69, 69} + \definecolor{tmpltbcolot}{RGB}{69, 69, 69} +\fi + +\pagecolor{bcolor} +\color{textcolor} + +\newtcolorbox{templateblock}{ + colupper=textcolor, + colback=tmpltbcolot, + boxrule=0pt, + boxsep=0pt, + breakable} + +\hypersetup{ + colorlinks=true, + linkcolor=textcolor, + urlcolor=linkcolor} + +\urlstyle{same} + +\lstdefinestyle{customc}{ + basicstyle=\footnotesize\ttfamily, + belowcaptionskip=1\baselineskip, + breaklines=true, + language=C, + commentstyle=\color{green!50!black}, + keywordstyle=\color{blue}, + stringstyle=\color{red!55!black!92!white}, + directivestyle=\bfseries\color{gray}, + numbers=left, + numbersep=5pt, + numberstyle=\tiny\color{black}, + showstringspaces=false, + stepnumber=1, + tabsize=2, + title=\lstname, +} + +\lstdefinestyle{customcxx}{ + basicstyle=\footnotesize\ttfamily, + belowcaptionskip=1\baselineskip, + breaklines=true, + language=C++, + commentstyle=\color{green!50!black}, + keywordstyle=\color{blue}, + stringstyle=\color{red!55!black!92!white}, + directivestyle=\bfseries\color{gray}, + numbers=left, + numbersep=5pt, + numberstyle=\tiny\color{black}, + showstringspaces=false, + stepnumber=1, + tabsize=2, + title=\lstname, +} + +\lstdefinestyle{customsql}{ + basicstyle=\footnotesize\ttfamily, + belowcaptionskip=1\baselineskip, + breaklines=true, + language=SQL, + commentstyle=\color{green!50!black}, + keywordstyle=\color{blue}, + stringstyle=\color{red!55!black!92!white}, + numbers=left, + numbersep=5pt, + numberstyle=\tiny\color{black}, + showstringspaces=false, + stepnumber=1, + tabsize=2, + title=\lstname, +} +\newcommand{\inputcfile}[1]{{ + \lstset{mathescape=false, escapechar=, style=customc} + \lstinputlisting[language=C]{#1} + \pagebreak}} + +\newcommand{\inputcxxfile}[1]{{ + \lstset{mathescape=false, escapechar=, style=customcxx} + \lstinputlisting[language=C++]{#1} + \pagebreak}} + +\newcommand{\inputsqlfile}[1]{{ + \lstset{mathescape=false, escapechar=, style=customsql} + \lstinputlisting[language=SQL]{#1}}} + +\counterwithin*{figure}{subsection} +\counterwithin*{table}{subsection} +% * means don't change the representation, because we'll do it now +\renewcommand{\thefigure}{% + \ifnum\value{subsubsection}>0 + \thesubsubsection.% + \else + \ifnum\value{subsection}>0 + \thesubsection.% + \else + \thesection.% + \fi + \fi + \arabic{figure}% +} +\renewcommand{\thetable}{% + \ifnum\value{subsubsection}>0 + \thesubsubsection.% + \else + \ifnum\value{subsection}>0 + \thesubsection.% + \else + \thesection.% + \fi + \fi + \arabic{table}% +} \ No newline at end of file diff --git a/docs/title.tex b/docs/title.tex new file mode 100644 index 0000000..3ce3368 --- /dev/null +++ b/docs/title.tex @@ -0,0 +1,40 @@ +\usepackage{anyfontsize} + +\makeatletter +\def\authorid#1{\gdef\@authorid{#1}} +\def\@authorid{\@latex@warning@no@line{No \noexpand\authorid given}} +\makeatother + +\makeatletter +\def\authorname#1{\gdef\@authorname{#1}} +\def\@authorname{\@latex@warning@no@line{No \noexpand\authorname given}} +\makeatother + +\makeatletter +\def\authorsurname#1{\gdef\@authorsurname{#1}} +\def\@authorsurname{\@latex@warning@no@line{No \noexpand\authorsurname given}} +\makeatother + +\makeatletter +\def\@maketitle{% + \null + \begin{center}% + \let \footnote \thanks + {\fontsize{16}{18}\selectfont + \vskip 1.5em% + Ingegneria di Internet e del Web \\ + \vskip 1em% + Progetto B1 A.A. 2019/2020 \\} + \vskip 5em% + {\fontsize{20}{22}\selectfont \@title \par}% + {\vskip 5em% + \lineskip .5em% + \fontsize{20}{22}\selectfont + \@authorid \\ + \vskip 1em% + \@authorname \ \@authorsurname \par}% + \vskip 1em% + \end{center}% + \par + \vskip 3em} +\makeatother diff --git a/logger/CMakeLists.txt b/logger/CMakeLists.txt new file mode 100644 index 0000000..b7fd6e6 --- /dev/null +++ b/logger/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(logger STATIC "src/logger.c") +target_include_directories(logger PUBLIC + "$" + "$") +target_link_libraries(logger INTERFACE $) + +if (BUILD_TESTS) + add_subdirectory("test") +endif () diff --git a/logger/include/logger.h b/logger/include/logger.h new file mode 100644 index 0000000..1d21fc6 --- /dev/null +++ b/logger/include/logger.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +enum logger_log_level : uint8_t { + LOGGER_LOG_LEVEL_OFF = 0, // No events will be logged. + LOGGER_LOG_LEVEL_FATAL = 1, // A fatal event that will prevent the application from continuing. + LOGGER_LOG_LEVEL_ERROR = 2, // An error in the application, possibly recoverable. + LOGGER_LOG_LEVEL_WARN = 3, // An event that might possible lead to an error. + LOGGER_LOG_LEVEL_INFO = 4, // An event for informational purposes. + LOGGER_LOG_LEVEL_DEBUG = 5, // A general debugging event. + LOGGER_LOG_LEVEL_TRACE = 6, // A fine-grained debug message, typically capturing the flow through the application. + LOGGER_LOG_LEVEL_ALL = UINT8_MAX // All events should be logged. +}; + +struct logger_config { + enum logger_log_level default_level; + FILE *file; + bool show_date_time; + bool show_source_file; + bool show_process_id; + bool show_thread_id; + bool show_level_name; +}; + +struct logger { + struct logger_config config; + mtx_t mutex; +}; + +static const struct logger_config logger_default_config = { + .default_level = LOGGER_LOG_LEVEL_INFO, + .file = nullptr, + .show_date_time = true, + .show_source_file = false, + .show_process_id = false, + .show_thread_id = false, + .show_level_name = true +}; + +#define logger_log_trace(logger, ...) logger_log(logger, LOGGER_LOG_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define logger_log_debug(logger, ...) logger_log(logger, LOGGER_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define logger_log_info(logger, ...) logger_log(logger, LOGGER_LOG_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define logger_log_warn(logger, ...) logger_log(logger, LOGGER_LOG_LEVEL_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define logger_log_error(logger, ...) logger_log(logger, LOGGER_LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define logger_log_fatal(logger, ...) logger_log(logger, LOGGER_LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +/** + * If config.file is nullptr, the logger will be initialized to log to stderr. + */ +extern bool logger_init(struct logger logger[static 1], struct logger_config config); + +extern void logger_destroy(struct logger logger[static 1]); + +extern void logger_log(struct logger logger[static 1], enum logger_log_level level, const char *file, int line, const char *fmt, ...); diff --git a/logger/src/logger.c b/logger/src/logger.c new file mode 100644 index 0000000..f288e54 --- /dev/null +++ b/logger/src/logger.c @@ -0,0 +1,106 @@ +#include "logger.h" + +#include +#include +#include +#include + +#include +#include + +static const char* logger_levels_str[] = { + [LOGGER_LOG_LEVEL_FATAL] = "FATAL", + [LOGGER_LOG_LEVEL_ERROR] = "ERROR", + [LOGGER_LOG_LEVEL_WARN] = "WARN", + [LOGGER_LOG_LEVEL_INFO] = "INFO", + [LOGGER_LOG_LEVEL_DEBUG] = "DEBUG", + [LOGGER_LOG_LEVEL_TRACE] = "TRACE" +}; + +#define LOGGER_USE_COLOR + +#ifdef LOGGER_USE_COLOR + +#define RESET_STYLE "\x1b[0m" + +#define COLOR_BLACK "\x1b[30m" +#define COLOR_RED "\x1b[31m" +#define COLOR_GREEN "\x1b[32m" +#define COLOR_YELLOW "\x1b[33m" +#define COLOR_BLUE "\x1b[34m" +#define COLOR_MAGENTA "\x1b[35m" +#define COLOR_CYAN "\x1b[36m" +#define COLOR_WHITE "\x1b[37m" + +#define COLOR_LIGHT_BLUE "\x1b[94m" + +static const char* logger_level_colors[] = { + [LOGGER_LOG_LEVEL_FATAL] = COLOR_MAGENTA, + [LOGGER_LOG_LEVEL_ERROR] = COLOR_RED, + [LOGGER_LOG_LEVEL_WARN] = COLOR_YELLOW, + [LOGGER_LOG_LEVEL_INFO] = COLOR_GREEN, + [LOGGER_LOG_LEVEL_DEBUG] = COLOR_CYAN, + [LOGGER_LOG_LEVEL_TRACE] = COLOR_LIGHT_BLUE +}; +#endif + +static inline char* get_date_time(char(*date_time)[20]); + +extern bool logger_init(struct logger logger[static 1], struct logger_config config) { + logger->config = config; + if (logger->config.file == nullptr) { + logger->config.file = stderr; + } + return mtx_init(&logger->mutex, mtx_plain) == thrd_success; +} + +extern void logger_destroy(struct logger logger[static 1]) { + mtx_destroy(&logger->mutex); +} + +extern void logger_log(struct logger logger[static 1], + enum logger_log_level level, + const char *file, + int line, + const char *fmt, + ...) { + va_list ap; + if (logger->config.default_level >= level) { + if (mtx_lock(&logger->mutex) == thrd_error) { + return; + } + va_start(ap, fmt); + if (logger->config.show_date_time) { + fprintf(logger->config.file, "%s ", get_date_time(&(char[20]) {})); + } + if (logger->config.show_level_name) { +#ifdef LOGGER_USE_COLOR + fprintf(logger->config.file, "%s%-5s" RESET_STYLE " ", logger_level_colors[level], logger_levels_str[level]); +#else + fprintf(logger->config.file, "%-5s ", logger_levels_str[level]); +#endif + } + if (logger->config.show_process_id) { + fprintf(logger->config.file, "%d ", getpid()); + } + if (logger->config.show_thread_id) { + fprintf(logger->config.file, "%lu ", thrd_current()); + } + if (logger->config.show_source_file) { + fprintf(logger->config.file, "%s: %d ", file, line); + } + fprintf(logger->config.file, "- "); + vfprintf(logger->config.file, fmt, ap); + fprintf(logger->config.file, "\n"); + while (mtx_unlock(&logger->mutex) != thrd_success); + fflush(logger->config.file); + va_end(ap); + } +} + +static inline char* get_date_time(char(*date_time)[20]) { + time_t t = time(NULL); + struct tm* tm = localtime(&t); + strftime(*date_time, sizeof * date_time, "%F %T", tm); + return *date_time; +} diff --git a/logger/test/CMakeLists.txt b/logger/test/CMakeLists.txt new file mode 100644 index 0000000..79c1f29 --- /dev/null +++ b/logger/test/CMakeLists.txt @@ -0,0 +1,7 @@ +find_package(buracchi-cutest CONFIG REQUIRED) + +add_executable(test_logger "test_logger.c") +target_link_libraries(test_logger PRIVATE logger) +target_link_libraries(test_logger PRIVATE buracchi::cutest::cutest buracchi::cutest::cutest_main) +target_link_libraries(test_logger INTERFACE coverage_config) +cutest_discover_tests(test_logger) diff --git a/logger/test/test_logger.c b/logger/test/test_logger.c new file mode 100644 index 0000000..ddfc8cc --- /dev/null +++ b/logger/test/test_logger.c @@ -0,0 +1,17 @@ +#include + +#include "logger.h" + +TEST(logger, dummy) { + struct logger logger; + logger_init(&logger, logger_default_config); + logger.config.default_level = LOGGER_LOG_LEVEL_ALL; + logger_log_fatal(&logger, "Message"); + logger_log_error(&logger, "Message"); + logger_log_warn(&logger, "Message"); + logger_log_info(&logger, "Message"); + logger_log_debug(&logger, "Message"); + logger_log_trace(&logger, "Message"); + logger_destroy(&logger); + ASSERT_EQ(0, 0); +} diff --git a/network_utils/CMakeLists.txt b/network_utils/CMakeLists.txt index b81b723..5b4ec33 100644 --- a/network_utils/CMakeLists.txt +++ b/network_utils/CMakeLists.txt @@ -1,13 +1,7 @@ -find_package(buracchi-common CONFIG REQUIRED) -find_package(libevent CONFIG REQUIRED) - add_library(network_utils STATIC "src/inet_utils.c") target_include_directories(network_utils PUBLIC "$" "$") -target_link_libraries(network_utils PRIVATE buracchi::common::logger) -target_link_libraries(network_utils PRIVATE buracchi::common::utilities) -target_link_libraries(network_utils PUBLIC libevent::core) target_link_libraries(network_utils INTERFACE $) if (BUILD_TESTS) diff --git a/network_utils/include/inet_utils.h b/network_utils/include/inet_utils.h index c9c931a..afbe0eb 100644 --- a/network_utils/include/inet_utils.h +++ b/network_utils/include/inet_utils.h @@ -1,6 +1,6 @@ #pragma once -#include +#include /** * Converts the provided socket address to a string representation. @@ -23,6 +23,8 @@ const char *inet_ntop_address(const struct sockaddr address[static 1], char addr */ int inet_addr_in_to_in6(struct sockaddr_in const src[restrict static 1], struct sockaddr_in6 dest[restrict static 1]); +int inet_add_in6_to_in(struct sockaddr_in6 const src[restrict static 1], struct sockaddr_in dest[restrict static 1]); + /** * Prints detailed information about the provided socket descriptor. * diff --git a/network_utils/src/inet_utils.c b/network_utils/src/inet_utils.c index 8b95676..77be60b 100644 --- a/network_utils/src/inet_utils.c +++ b/network_utils/src/inet_utils.c @@ -1,7 +1,8 @@ #include "inet_utils.h" -#include -#include +#include +#include +#include const char *inet_ntop_address(const struct sockaddr address[static 1], char address_str[static INET6_ADDRSTRLEN], uint16_t port[static 1]) { void *client_addr; @@ -24,14 +25,14 @@ const char *inet_ntop_address(const struct sockaddr address[static 1], char addr // SET ERRNO return nullptr; } - return evutil_inet_ntop(address->sa_family, client_addr, address_str, INET6_ADDRSTRLEN); + return inet_ntop(address->sa_family, client_addr, address_str, INET6_ADDRSTRLEN); } int inet_addr_in_to_in6(struct sockaddr_in const src[restrict static 1], struct sockaddr_in6 dest[restrict static 1]) { char host[INET_ADDRSTRLEN] = { 0 }; char serv[6] = { 0 }; // 0 to 65536 plus '\0' struct addrinfo *addrinfo; - struct addrinfo hints = { + static const struct addrinfo hints = { .ai_flags = AI_V4MAPPED | AI_ADDRCONFIG, .ai_family = AF_INET6, .ai_socktype = SOCK_DGRAM, @@ -44,19 +45,59 @@ int inet_addr_in_to_in6(struct sockaddr_in const src[restrict static 1], struct int gai_ret; gai_ret = getnameinfo((const struct sockaddr *)src, sizeof *src, host, sizeof host, serv, sizeof serv, NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST); if (gai_ret != 0) { - cmn_logger_log_error("getnameinfo: %s", gai_strerror(gai_ret)); - return -1; + return gai_ret; } gai_ret = getaddrinfo(host, serv, &hints, &addrinfo); if (gai_ret != 0) { - cmn_logger_log_error("getaddrinfo: %s", gai_strerror(gai_ret)); - return -1; + return gai_ret; } memcpy(dest, addrinfo->ai_addr, sizeof *dest); freeaddrinfo(addrinfo); return 0; } +int inet_add_in6_to_in(struct sockaddr_in6 const src[restrict static 1], struct sockaddr_in dest[restrict static 1]) { + char host[INET6_ADDRSTRLEN] = { 0 }; + char serv[6] = { 0 }; // 0 to 65536 plus '\0' + struct addrinfo *addrinfo; + static const struct addrinfo hints = { + .ai_flags = AI_ADDRCONFIG, + .ai_family = AF_INET, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = 0, + .ai_addrlen = 0, + .ai_addr = nullptr, + .ai_canonname = nullptr, + .ai_next = nullptr + }; + int gai_ret; + gai_ret = getnameinfo((const struct sockaddr *)src, sizeof *src, host, sizeof host, serv, sizeof serv, NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST); + if (gai_ret != 0) { + return gai_ret; + } + if (IN6_IS_ADDR_UNSPECIFIED(&src->sin6_addr)) { + dest->sin_family = AF_INET; + dest->sin_addr.s_addr = htonl(INADDR_ANY); + dest->sin_port = src->sin6_port; + return 0; + } + if (IN6_IS_ADDR_V4MAPPED(&src->sin6_addr)) { + struct in_addr addr4; + memcpy(&addr4, &src->sin6_addr.s6_addr[12], sizeof(addr4)); + dest->sin_family = AF_INET; + dest->sin_addr = addr4; + dest->sin_port = src->sin6_port; + return 0; + } + gai_ret = getaddrinfo(host, serv, &hints, &addrinfo); + if (gai_ret != 0) { + return gai_ret; + } + memcpy(dest, addrinfo->ai_addr, sizeof *dest); + freeaddrinfo(addrinfo); + return 0; +} + void print_inet_address_detail(int fd) { struct sockaddr_storage sockaddr; struct sockaddr_storage peeraddr; @@ -70,5 +111,4 @@ void print_inet_address_detail(int fd) { getpeername(fd, (struct sockaddr *)&peeraddr, &sockpeer_len); inet_ntop_address((struct sockaddr *)&sockaddr, sockaddr_str, &sockaddr_port); inet_ntop_address((struct sockaddr *)&peeraddr, peeraddr_str, &peeraddr_port); - cmn_logger_log_trace("IP Header contains: = %s = %s = %hu = %hu", sockaddr_str, peeraddr_str, sockaddr_port, peeraddr_port); } diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index dc2f2c3..303f233 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,29 +1,27 @@ -find_package(buracchi-argparser CONFIG REQUIRED) -find_package(buracchi-common CONFIG REQUIRED) -find_package(libevent CONFIG REQUIRED) +find_package(PkgConfig) +pkg_check_modules(liburing REQUIRED IMPORTED_TARGET GLOBAL liburing>=2.0) -add_executable(server "src/main.c") +add_executable(server src/main.c) +target_link_libraries(server INTERFACE $) target_link_libraries(server PRIVATE $) -target_link_libraries(server PRIVATE buracchi::argparser::argparser) -target_link_libraries(server PRIVATE buracchi::common::logger) -target_link_libraries(server PRIVATE buracchi::common::utilities) -target_link_libraries(server PRIVATE libevent::core libevent::pthreads) +target_link_libraries(server PRIVATE logger) target_link_libraries(server PRIVATE network_utils) -target_link_libraries(server INTERFACE $) +target_link_libraries(server PRIVATE tftp) +target_link_libraries(server PRIVATE PkgConfig::liburing) add_library(server_obj OBJECT - "src/cmdline.c" - "src/acceptor.c" - "src/dispatcher.c" - "src/service_handler.c" - ) + src/tftp_server.c + src/tftp_handler.c + src/server_stats.c + src/session_stats.c + src/uring_thread_pool.c +) target_include_directories(server_obj PUBLIC "$") -target_link_libraries(server_obj PRIVATE buracchi::argparser::argparser) -target_link_libraries(server_obj PRIVATE buracchi::common::logger) -target_link_libraries(server_obj PRIVATE buracchi::common::utilities) -target_link_libraries(server_obj PUBLIC libevent::core libevent::pthreads) -target_link_libraries(server_obj PRIVATE network_utils) target_link_libraries(server_obj INTERFACE $) +target_link_libraries(server_obj PRIVATE logger) +target_link_libraries(server_obj PRIVATE network_utils) +target_link_libraries(server_obj PRIVATE tftp) +target_link_libraries(server_obj PRIVATE PkgConfig::liburing) if (BUILD_TESTS) add_subdirectory("test") diff --git a/server/src/acceptor.c b/server/src/acceptor.c deleted file mode 100644 index e86a6dd..0000000 --- a/server/src/acceptor.c +++ /dev/null @@ -1,285 +0,0 @@ -#include "acceptor.h" - -#include -#include - -#include "inet_utils.h" - -static ssize_t recv_packet(evutil_socket_t sockfd, struct addrinfo receiver_addrinfo[static 1], struct acceptor_packet packet[static 1]); - -static struct evutil_addrinfo *get_accepting_addrinfo_list(char const *service); - -static int attempt_binding(evutil_socket_t *sockfd, struct addrinfo *addrinfo); - -static evutil_socket_t create_client_transport_endpoint(struct acceptor_packet *packet); - -int acceptor_init(struct acceptor acceptor[static 1], char const *port) { - *acceptor = (struct acceptor) { - .peer_acceptor = EVUTIL_INVALID_SOCKET, - }; - struct evutil_addrinfo *accepting_addrinfo_list; - try(accepting_addrinfo_list = get_accepting_addrinfo_list(port), nullptr, get_accepting_addrinfo_list_fail); - for (struct addrinfo *addrinfo = accepting_addrinfo_list; addrinfo != nullptr; addrinfo = addrinfo->ai_next) { - if (attempt_binding(&acceptor->peer_acceptor, addrinfo)) { - continue; - } - memcpy(&acceptor->addr_storage, addrinfo->ai_addr, addrinfo->ai_addrlen); - memcpy(&acceptor->addrinfo, addrinfo, sizeof acceptor->addrinfo); - acceptor->addrinfo.ai_addr = (struct sockaddr *)&acceptor->addr_storage; - break; - } - evutil_freeaddrinfo(accepting_addrinfo_list); - if (acceptor->peer_acceptor == EVUTIL_INVALID_SOCKET) { - cmn_logger_log_error("Address already in use."); - goto fail; - } - try(evutil_make_socket_nonblocking(acceptor->peer_acceptor), -1, evutil_make_socket_nonblocking_fail); - return 0; -evutil_make_socket_nonblocking_fail: - cmn_logger_log_error("evutil_make_socket_nonblocking_fail: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - try(evutil_closesocket(acceptor->peer_acceptor), -1, close_fail); - acceptor->peer_acceptor = EVUTIL_INVALID_SOCKET; - goto fail; -close_fail: - cmn_logger_log_error("close: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - acceptor->peer_acceptor = EVUTIL_INVALID_SOCKET; - goto fail; -get_accepting_addrinfo_list_fail: - goto fail; -fail: - cmn_logger_log_error("Can't create a passive mode transport endpoint."); - return 1; -} - -int acceptor_destroy(struct acceptor acceptor[static 1]) { - if (evutil_closesocket(acceptor->peer_acceptor) == -1) { - cmn_logger_log_error("close: %s", evutil_socket_error_to_string(evutil_socket_geterror(acceptor->peer_acceptor))); - return 1; - } - return 0; -} - -bool acceptor_accept(struct acceptor acceptor[static 1], evutil_socket_t socket[static 1], struct acceptor_packet packet[static 1]) { - char client_address_str[INET6_ADDRSTRLEN] = {}; - uint16_t client_address_port; - char rbuff_hex[sizeof packet->data] = {}; - if (recv_packet(acceptor->peer_acceptor, &acceptor->addrinfo, packet) == -1) { - cmn_logger_log_debug("Acceptor: Unable to receive the request packet correctly, denying connection."); - return false; - } - if (inet_ntop_address(packet->src_addrinfo.ai_addr, client_address_str, &client_address_port) == nullptr) { - cmn_logger_log_error("inet_ntop_address failed"); - return false; - } - for (ssize_t i = 0; i < packet->payload_size; i++) { - sprintf(rbuff_hex + 2 * i, "%02x", packet->data[i]); - } - cmn_logger_log_debug("Acceptor: Got packet from host %s on port %hu of %zu bytes, packet content is \"%s\"", - client_address_str, client_address_port, packet->payload_size, rbuff_hex); - *socket = create_client_transport_endpoint(packet); - if (*socket == EVUTIL_INVALID_SOCKET) { - cmn_logger_log_error("Acceptor: Unable to create an endpoint to the client, denying connection."); - return false; - } - return true; -} - -// Resolves the specified service and returns a list of addrinfo -// structures containing information about the resulting addresses suitable for -// binding a socket that will accept connections. -// On error, returns nullptr. -static struct evutil_addrinfo *get_accepting_addrinfo_list(char const *service) { - struct evutil_addrinfo *result = nullptr; - struct evutil_addrinfo hints = { - .ai_flags = EVUTIL_AI_PASSIVE | EVUTIL_AI_NUMERICSERV | EVUTIL_AI_ADDRCONFIG, - .ai_family = AF_INET6, - .ai_socktype = SOCK_DGRAM, - .ai_protocol = 0, - .ai_addrlen = 0, - .ai_addr = nullptr, - .ai_canonname = nullptr, - .ai_next = nullptr - }; - int gai_ret; - if ((gai_ret = evutil_getaddrinfo(nullptr, service, &hints, &result))) { - cmn_logger_log_error("getaddrinfo: %s", gai_strerror(gai_ret)); - if (result) { - // According to the POSIX 2008 standard, if getaddrinfo returns - // a non-zero error value, the content of the result parameter - // is undefined. - // Therefore, it is not guaranteed that the result parameter will - // not be assigned in the event of a getaddrinfo failure. - freeaddrinfo(result); - } - result = nullptr; - } - return result; -} - -// Attempts to bind a socket that will accept connections using the given -// address info structure -// Returns 0 on success, 1 on failure -static int attempt_binding(evutil_socket_t *sockfd, struct addrinfo *addrinfo) { - char bind_addr_str[INET6_ADDRSTRLEN] = { 0 }; - uint16_t bind_addr_port; - try(*sockfd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol), EVUTIL_INVALID_SOCKET, socket_fail); - try(evutil_make_listen_socket_reuseable(*sockfd), -1, evutil_make_listen_socket_reuseable_fail); - try(setsockopt(*sockfd, IPPROTO_IP, IP_RECVORIGDSTADDR, &(int){ true }, sizeof(int)) == -1, true, setsockopt_fail); - try(setsockopt(*sockfd, IPPROTO_IPV6, IPV6_RECVORIGDSTADDR, &(int){ true }, sizeof(int)) == -1, true, setsockopt_fail); - try(setsockopt(*sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ false }, sizeof(int)) == -1, true, setsockopt_fail); - try(inet_ntop_address(addrinfo->ai_addr, bind_addr_str, &bind_addr_port), nullptr, inet_ntop_address_fail); - cmn_logger_log_debug("Binding to host %s on port %hu", bind_addr_str, bind_addr_port); - if (bind(*sockfd, addrinfo->ai_addr, addrinfo->ai_addrlen) == -1) { - cmn_logger_log_debug("bind: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - try(evutil_closesocket(*sockfd), -1, close_fail); - return 1; - } - cmn_logger_log_info("Acceptor bound to host %s on port %hu", bind_addr_str, bind_addr_port); - return 0; -socket_fail: - cmn_logger_log_debug("socket: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - return 1; -evutil_make_listen_socket_reuseable_fail: - cmn_logger_log_error("evutil_make_listen_socket_reuseable: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - try(evutil_closesocket(*sockfd), -1, close_fail); - *sockfd = EVUTIL_INVALID_SOCKET; - return 1; -setsockopt_fail: - cmn_logger_log_error("setsockopt: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - try(evutil_closesocket(*sockfd), -1, close_fail); - *sockfd = EVUTIL_INVALID_SOCKET; - return 1; -inet_ntop_address_fail: - cmn_logger_log_error("inet_ntop_address failed"); - try(evutil_closesocket(*sockfd), -1, close_fail); - *sockfd = EVUTIL_INVALID_SOCKET; - return 1; -close_fail: - cmn_logger_log_error("close: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - return 1; -} - -static evutil_socket_t create_client_transport_endpoint(struct acceptor_packet *packet) { - evutil_socket_t sockfd; - char src_addr_str[INET6_ADDRSTRLEN] = { 0 }; - char dest_addr_str[INET6_ADDRSTRLEN] = { 0 }; - uint16_t client_address_port; - uint16_t request_destination_address_port; - try(inet_ntop_address(packet->src_addrinfo.ai_addr, src_addr_str, &client_address_port), nullptr, inet_ntop_address_fail); - try(inet_ntop_address(packet->dest_addrinfo.ai_addr, dest_addr_str, &request_destination_address_port), nullptr, inet_ntop_address_fail); - cmn_logger_log_debug("Acceptor: Creating connection from %s on port %hu to host %s on port %hu", dest_addr_str, request_destination_address_port, src_addr_str, client_address_port); - try(sockfd = socket(packet->dest_addrinfo.ai_family, SOCK_DGRAM, 0), EVUTIL_INVALID_SOCKET, socket_fail); - try(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){ true }, sizeof(int)) == -1, true, setsockopt_fail); - try(bind(sockfd, packet->dest_addrinfo.ai_addr, packet->dest_addrinfo.ai_addrlen), -1, bind_fail); - try(connect(sockfd, packet->src_addrinfo.ai_addr, packet->src_addrinfo.ai_addrlen), -1, connect_fail); - if (packet->dest_addrinfo.ai_protocol == IPPROTO_IP) { - cmn_logger_log_debug("Acceptor: Setting connection network protocol to IPV4 to support IPV6 unaware clients"); - try(setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADDRFORM, &(int){ AF_INET }, sizeof(int)) == -1, true, setsockopt_fail); - } - try(evutil_make_socket_nonblocking(sockfd), -1, evutil_make_socket_nonblocking_fail); - print_inet_address_detail(sockfd); - return sockfd; -inet_ntop_address_fail: - cmn_logger_log_error("inet_ntop_address failed"); - return EVUTIL_INVALID_SOCKET; -socket_fail: - cmn_logger_log_error("socket: %s", evutil_socket_error_to_string(evutil_socket_geterror(client_socket))); - return EVUTIL_INVALID_SOCKET; -setsockopt_fail: - cmn_logger_log_error("setsockopt: %s", evutil_socket_error_to_string(evutil_socket_geterror(client_socket))); - return EVUTIL_INVALID_SOCKET; -bind_fail: - cmn_logger_log_error("bind: %s", evutil_socket_error_to_string(evutil_socket_geterror(client_socket))); - return EVUTIL_INVALID_SOCKET; -connect_fail: - cmn_logger_log_error("connect: %s", evutil_socket_error_to_string(evutil_socket_geterror(client_socket))); - return EVUTIL_INVALID_SOCKET; -evutil_make_socket_nonblocking_fail: - cmn_logger_log_error("evutil_make_socket_nonblocking_fail: %s", evutil_socket_error_to_string(evutil_socket_geterror(peer_acceptor))); - return EVUTIL_INVALID_SOCKET; -} - -static ssize_t recv_packet(evutil_socket_t sockfd, struct addrinfo receiver_addrinfo[static 1], struct acceptor_packet packet[static 1]) { - ssize_t bytes_recv; - char *msg_control = nullptr; - size_t msg_control_size = 128; - try(msg_control = calloc(1, msg_control_size), nullptr, msg_control_calloc_fail); - struct iovec iovec[1] = { - {.iov_base = packet->data, .iov_len = sizeof packet->data} - }; - *packet = (struct acceptor_packet) { - .src_addrinfo = { .ai_addr = (struct sockaddr *)&packet->src_addr_storage, .ai_addrlen = sizeof packet->src_addr_storage}, - .dest_addrinfo = {.ai_addr = (struct sockaddr *)&packet->dest_addr_storage, .ai_addrlen = sizeof packet->dest_addr_storage} - }; - struct msghdr msghdr = { - .msg_name = packet->src_addrinfo.ai_addr, - .msg_namelen = packet->src_addrinfo.ai_addrlen, - .msg_iov = iovec, - .msg_iovlen = (sizeof iovec) / (sizeof *iovec), - .msg_control = msg_control, - .msg_controllen = sizeof msg_control, - .msg_flags = MSG_CTRUNC - }; - (void)recvmsg(sockfd, &msghdr, MSG_PEEK | MSG_TRUNC); - if (msghdr.msg_flags & MSG_TRUNC) { - cmn_logger_log_debug("Acceptor: request packet payload exceeded maximum allowed size, denying connection."); - return -1; - } - while (msghdr.msg_flags & MSG_CTRUNC) { - if (msg_control_size == 4096) { - cmn_logger_log_debug("Acceptor: msg_control buffer exceed 4096 bytes, denying connection."); - return -1; - } - char *msg_control_reallocd = nullptr; - msg_control_size <<= 1; - try(msg_control_reallocd = realloc(msg_control, msg_control_size), nullptr, msg_control_realloc_fail); - msg_control = msg_control_reallocd; - memset(msg_control, 0, msg_control_size); - msghdr.msg_control = msg_control; - msghdr.msg_controllen = msg_control_size; - (void)recvmsg(sockfd, &msghdr, MSG_PEEK | MSG_TRUNC); - } - bytes_recv = recvmsg(sockfd, &msghdr, 0); - if (msghdr.msg_flags & MSG_CTRUNC || msghdr.msg_flags & MSG_TRUNC) { - cmn_logger_log_debug("Acceptor: request packet read was truncated, denying connection."); - return -1; - } - packet->src_addrinfo.ai_family = packet->src_addrinfo.ai_addr->sa_family; - for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) { - if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_ORIGDSTADDR) { - struct sockaddr_in6 addr; - try(inet_addr_in_to_in6((struct sockaddr_in *)CMSG_DATA(cmsg), &addr), -1, inet_addr_in_to_in6_fail); - memcpy(packet->dest_addrinfo.ai_addr, &addr, sizeof addr); - packet->dest_addrinfo.ai_addrlen = sizeof addr; - packet->dest_addrinfo.ai_family = addr.sin6_family; - packet->dest_addrinfo.ai_protocol = IPPROTO_IP; - break; - } - if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_ORIGDSTADDR) { - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)CMSG_DATA(cmsg); - memcpy(packet->dest_addrinfo.ai_addr, addr, sizeof *addr); - packet->dest_addrinfo.ai_addrlen = sizeof *addr; - packet->dest_addrinfo.ai_family = addr->sin6_family; - packet->dest_addrinfo.ai_protocol = IPPROTO_IPV6; - break; - } - } - free(msg_control); - if (!memcmp(&packet->dest_addr_storage, &(struct sockaddr_storage){ 0 }, sizeof packet->dest_addr_storage)) { - cmn_logger_log_debug("Acceptor: no IP control messages were included in the packet header. " - "Using the acceptor address as the packet destination address."); - memcpy(&packet->dest_addrinfo, receiver_addrinfo, sizeof packet->dest_addrinfo); - } - packet->payload_size = bytes_recv; - return bytes_recv; -inet_addr_in_to_in6_fail: - cmn_logger_log_error("inet_addr_in_to_in6 failed"); - return -1; -msg_control_calloc_fail: - cmn_logger_log_error("calloc: %s", strerror(errno)); - return -1; -msg_control_realloc_fail: - cmn_logger_log_error("realloc: %s", strerror(errno)); - free(msg_control); - return -1; -} diff --git a/server/src/acceptor.h b/server/src/acceptor.h deleted file mode 100644 index 34ffe4a..0000000 --- a/server/src/acceptor.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -struct acceptor { - struct sockaddr_storage addr_storage; // should not be directly accessed - struct evutil_addrinfo addrinfo; - evutil_socket_t peer_acceptor; -}; - -struct acceptor_packet { - struct sockaddr_storage src_addr_storage; // should not be directly accessed - struct sockaddr_storage dest_addr_storage; // should not be directly accessed - - struct evutil_addrinfo src_addrinfo; - struct evutil_addrinfo dest_addrinfo; - char data[512]; - ssize_t payload_size; -}; - -/** - * Initializes an acceptor object listening to incoming connections on the specified port. - * - * @param acceptor pointer to the acceptor struct to be initialized - * @param port the port number on which to listen for connections - * @return 0 on success, non-zero on failure - */ -int acceptor_init(struct acceptor acceptor[static 1], char const *port); - -/** - * Releases resources associated with an acceptor struct - * - * @param acceptor pointer to the acceptor struct to be destroyed - * @return 0 on success, non-zero on failure - */ -int acceptor_destroy(struct acceptor acceptor[static 1]); - -/** - * Accept an incoming request providing the socket for the connection and the received packet. - * - * @param acceptor - * @param socket - * @param packet - */ -bool acceptor_accept(struct acceptor acceptor[static 1], evutil_socket_t socket[static 1], struct acceptor_packet packet[static 1]); diff --git a/server/src/cmdline.c b/server/src/cmdline.c deleted file mode 100644 index 20b25d4..0000000 --- a/server/src/cmdline.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "cmdline.h" - -#include - -#include -#include - -int parse_cmdline_args(int argc, const char *argv[static const argc + 1], cmdline_args_t args[static 1]) { - const char *cwd = getcwd(NULL, 0); - argparser_t argparser; - try(argparser = argparser_init(argc, argv), NULL, fail); - argparser_set_description(argparser, "File transfer server."); - try(argparser_add_argument(argparser, &(args->listen_port), { .flag = "p", .long_flag = "port", .default_value = "1234", .help = "specify the listening port number (the default value is \"1234\")" }), 1, fail); - try(argparser_add_argument(argparser, &(args->directory), { .flag = "d", .long_flag = "directory", .default_value = cwd, .help = "specify the shared directory (the default value is the current working directory)" }), 1, fail); - try(argparser_parse_args(argparser), 1, fail); - argparser_destroy(argparser); - return 0; -fail: - return 1; -} diff --git a/server/src/cmdline.h b/server/src/cmdline.h deleted file mode 100644 index a5e2fe2..0000000 --- a/server/src/cmdline.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -typedef struct cmdline_args { - char *listen_port; // port number to listen on - char *directory; // file root directory - unsigned int thread_count; // number of worker threads to use - uint32_t window_size; // size of the dispatch window to use for the Go-Back N protocol - long timeout_duration; // duration of the timeout to use for the Go-Back N protocol, in milliseconds - bool adaptive_timeout; // flag to use an adaptive timeout calculated dynamically based on network delays - double loss_probability; // probability of packet loss to simulate - int verbose_level; // verbose level to output additional information -} cmdline_args_t; - -/** - * @brief Parse the command line arguments. - * @param argc The number of command line arguments. - * @param argv The command line arguments. - * @param args The structure to fill with the parsed arguments. - * @return 0 on success, 1 on failure - */ -int parse_cmdline_args(int argc, const char *argv[static const argc + 1], cmdline_args_t args[static 1]); diff --git a/server/src/dispatcher.c b/server/src/dispatcher.c deleted file mode 100644 index fd67a59..0000000 --- a/server/src/dispatcher.c +++ /dev/null @@ -1,80 +0,0 @@ -#include "dispatcher.h" - -#include -#include - -#include -#include - -static void stop_on_signal_handler(int signal, short events, void *arg); - -extern int dispatcher_init(dispatcher_t *dispatcher, int thread_count) { - enum event_method_feature evmthd_feature; -#ifdef EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED - try(evthread_use_windows_threads(), -1, evthread_setup_fail); -#endif -#ifdef EVTHREAD_USE_PTHREADS_IMPLEMENTED - try(evthread_use_pthreads(), -1, evthread_setup_fail); -#endif - cmn_logger_log_info("Dispatcher: Using Libevent version: %s", event_get_version()); - try(dispatcher->event_base = event_base_new(), NULL, event_base_new_fail); - evmthd_feature = event_base_get_features(dispatcher->event_base); - cmn_logger_log_info("Dispatcher: Using Libevent with backend method %s. %s %s %s", - event_base_get_method(dispatcher->event_base), - (evmthd_feature & EV_FEATURE_ET) ? "Edge-triggered events are supported." : "", - (evmthd_feature & EV_FEATURE_O1) ? "O(1) event notification is supported." : "", - (evmthd_feature & EV_FEATURE_FDS) ? "All FD types are supported." : ""); - // TODO: Initialize the thread pool - return 0; -event_base_new_fail: - cmn_logger_log_error("Dispatcher: Couldn't initialize an event base."); - return -1; -evthread_setup_fail: - cmn_logger_log_error("Dispatcher: Couldn't setup Libevent threads handling support."); - return -1; -} - -extern int dispatcher_dispatch(dispatcher_t *dispatcher) { - return event_base_dispatch(dispatcher->event_base); -} - -extern int dispatcher_stop(dispatcher_t *dispatcher) { - return event_base_loopexit(dispatcher->event_base, NULL); -} - -extern dispatcher_event_t dispatcher_signal_event_add(dispatcher_t *dispatcher, int signal, event_callback_fn callback, void *arg) { - dispatcher_event_t signal_event; - try(signal_event = evsignal_new(dispatcher->event_base, signal, callback, arg), NULL, event_new_fail); - try(evsignal_add(signal_event, NULL), -1, evsignal_add_fail); - return signal_event; -evsignal_add_fail: - cmn_logger_log_error("Dispatcher: Couldn't set %s event status to pending.", strsignal(signal)); - return NULL; -event_new_fail: - cmn_logger_log_error("Dispatcher: Couldn't create the %s event.", strsignal(signal)); - return NULL; -} - -extern dispatcher_event_t dispatcher_stop_on_signal_event_add(dispatcher_t *dispatcher, int signal) { - return dispatcher_signal_event_add(dispatcher, signal, stop_on_signal_handler, dispatcher); -} - -extern int dispatcher_event_remove(dispatcher_event_t event) { - int ret; - try(ret = event_del(event), -1, fail); - event_free(event); - return ret; -fail: - return ret; -} - -extern void dispatcher_destroy(dispatcher_t *dispatcher) { - // TODO: Destroy thread_pool - event_base_free(dispatcher->event_base); - libevent_global_shutdown(); -} - -static void stop_on_signal_handler(int signal, short events, void *arg) { - cmn_logger_log_info("Dispatcher: Received %s, shutting down...", strsignal(signal)); - dispatcher_stop((dispatcher_t *)arg); -} diff --git a/server/src/dispatcher.h b/server/src/dispatcher.h deleted file mode 100644 index ef1fe26..0000000 --- a/server/src/dispatcher.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -typedef struct event* dispatcher_event_t; - -typedef struct dispatcher { - struct event_base *event_base; - // TODO: thread_pool_t thread_pool; -} dispatcher_t; - -extern int dispatcher_init(dispatcher_t *dispatcher, int thread_count); - -extern int dispatcher_dispatch(dispatcher_t *dispatcher); - -extern int dispatcher_stop(dispatcher_t *dispatcher); - -extern dispatcher_event_t dispatcher_signal_event_add(dispatcher_t *dispatcher, int signal, event_callback_fn callback, void* arg); - -extern dispatcher_event_t dispatcher_stop_on_signal_event_add(dispatcher_t *dispatcher, int signal); - -extern int dispatcher_event_remove(dispatcher_event_t event); - -extern void dispatcher_destroy(dispatcher_t *dispatcher); diff --git a/server/src/main.c b/server/src/main.c index 06f4034..a856984 100644 --- a/server/src/main.c +++ b/server/src/main.c @@ -1,98 +1,156 @@ +#include #include +#include #include +#include +#include -#include -#include +#include "tftp_server.h" +#include "logger.h" -#include "acceptor.h" -#include "cmdline.h" -#include "dispatcher.h" -#include "service_handler_factory.h" +struct arguments { + char *host; // host to listen on + char *port; // port to listen on + char *root; // file root directory + uint32_t workers; // number of worker threads to use + uint16_t max_worker_sessions; // maximum number of sessions a worker can handle + uint8_t retries; // number of retries to attempt before giving up + uint8_t timeout_s; // duration of the timeout in seconds + bool adaptive_timeout; // flag to use an adaptive timeout calculated dynamically based on network delays + double loss_probability; // probability of packet loss to simulate + enum logger_log_level verbose_level; // verbose level to output additional information +}; -static void event_accept(evutil_socket_t peer_acceptor, short event, void *arg); -static inline void set_log_level(int verbose_level); +static void sigint_handler(int signal); +static inline struct arguments get_arguments(); +static void print_session_stats(struct tftp_session_stats stats[static 1]); +static void print_server_stats(struct server_stats stats[static 1]); -extern int main(int argc, char *argv[argc + 1]) { - cmdline_args_t args; - dispatcher_t dispatcher = { 0 }; - struct acceptor acceptor = { 0 }; - service_handler_factory_t factory; - dispatcher_event_t dispatcher_sigterm_event; - dispatcher_event_t dispatcher_sigint_event; - dispatcher_event_t accept_event; - try(parse_cmdline_args(argc, (const char**)argv, &args), 1, argparser_fail); - set_log_level(args.verbose_level); - service_handler_factory_init(&factory, (struct service_parameters){ .directory = args.directory, .window_size = args.window_size, .timeout_duration = args.timeout_duration, .loss_probability = args.loss_probability }); - try(dispatcher_init(&dispatcher, args.thread_count), -1, dispatcher_init_fail); - try(acceptor_init(&acceptor, args.listen_port), 1, acceptor_init_fail); - try((accept_event = event_new(dispatcher.event_base, acceptor.peer_acceptor, EV_READ | EV_PERSIST, event_accept, &acceptor)), nullptr, event_new_fail); - try(event_add(accept_event, nullptr), -1, event_add_fail); - try(dispatcher_sigterm_event = dispatcher_stop_on_signal_event_add(&dispatcher, SIGTERM), nullptr, dispatcher_register_signal_handler_fail); - try(dispatcher_sigint_event = dispatcher_stop_on_signal_event_add(&dispatcher, SIGINT), nullptr, dispatcher_register_signal_handler_fail); - try(dispatcher_dispatch(&dispatcher), -1, dispatcher_dispatch_fail); - cmn_logger_log_info("All events have been terminated."); - try(dispatcher_event_remove(accept_event), -1, remove_event_fail); - try(acceptor_destroy(&acceptor), 1, acceptor_destroy_fail); - try(dispatcher_event_remove(dispatcher_sigterm_event), -1, remove_event_fail); - try(dispatcher_event_remove(dispatcher_sigint_event), -1, remove_event_fail); - dispatcher_destroy(&dispatcher); - return EXIT_SUCCESS; -argparser_fail: - cmn_logger_log_error("Can't parse CLI arguments."); - return EXIT_FAILURE; -dispatcher_init_fail: - cmn_logger_log_error("Can't initialize dispatcher."); - return EXIT_FAILURE; -dispatcher_register_signal_handler_fail: - return EXIT_FAILURE; -event_new_fail: - cmn_logger_log_error("Can't create an accept event."); - return EXIT_FAILURE; -event_add_fail: - cmn_logger_log_error("Couldn't set accept event status to pending."); - return EXIT_FAILURE; -acceptor_init_fail: - cmn_logger_log_error("Can't initialize acceptor."); - return EXIT_FAILURE; -dispatcher_dispatch_fail: - cmn_logger_log_error("Event loop aborted for an unhandled backend error."); - return EXIT_FAILURE; -remove_event_fail: - cmn_logger_log_error("Error encountered when removing a dispatcher event."); - return EXIT_FAILURE; -acceptor_destroy_fail: - cmn_logger_log_error("Error encountered when destroying acceptor."); - return EXIT_FAILURE; +static struct tftp_server server; + +/* +typedef struct arguments { + char *listen_port; // port number to listen on + char *directory; // file root directory + unsigned int thread_count; // number of worker threads to use + uint32_t window_size; // size of the dispatch window to use for the Go-Back N protocol + long timeout_duration; // duration of the timeout to use for the Go-Back N protocol, in milliseconds + bool adaptive_timeout; // flag to use an adaptive timeout calculated dynamically based on network delays + double loss_probability; // probability of packet loss to simulate + int verbose_level; // verbose level to output additional information +} cmdline_args_t; + +int parse_cmdline_args(int argc, const char *argv[static const argc + 1], cmdline_args_t args[static 1], const char* cwd) { + argparser_t argparser; + try(argparser = argparser_init(argc, argv), NULL, fail); + argparser_set_description(argparser, "File transfer server."); + try(argparser_add_argument(argparser, &(args->listen_port), { .flag = "p", .long_flag = "port", .default_value = "1234", .help = "specify the listening port number (the default value is \"1234\")" }), 1, fail); + try(argparser_add_argument(argparser, &(args->directory), { .flag = "d", .long_flag = "directory", .default_value = cwd, .help = "specify the shared directory (the default value is the current working directory)" }), 1, fail); + try(argparser_add_argument(argparser, &(args->thread_count), { .flag = "t", .long_flag = "threads", .default_value = "4", .help = "specify the number of worker threads to use (the default value is 4)" }), 1, fail); + try(argparser_add_argument(argparser, &(args->window_size), { .flag = "w", .long_flag = "window-size", .default_value = "4", .help = "specify the size of the dispatch window to use for the Go-Back N protocol (the default value is 4)" }), 1, fail); + try(argparser_add_argument(argparser, &(args->timeout_duration), { .flag = "T", .long_flag = "timeout", .default_value = "1000", .help = "specify the duration of the timeout to use for the Go-Back N protocol, in milliseconds (the default value is 1000)" }), 1, fail); + try(argparser_add_argument(argparser, &(args->verbose_level), { .flag = "v", .long_flag = "verbose", .default_value = "0", .help = "specify the verbose level to output additional information (the default value is 0)" }), 1, fail); + try(argparser_parse_args(argparser), 1, fail); + argparser_destroy(argparser); + return 0; +fail: + return 1; +} +*/ + +int main(int argc, char *argv[argc + 1]) { + /* + char cwd[PATH_MAX + 1]; + cmdline_args_t args; + if (getcwd(cwd, sizeof cwd) == nullptr) { + cmn_logger_log_error("Couldn't get the current working directory."); + try(parse_cmdline_args(argc, (const char**)argv, &args, nullptr), 1, argparser_fail); + } + try(parse_cmdline_args(argc, (const char**)argv, &args, cwd), 1, argparser_fail); + */ + enum { OLD, NEW }; + struct sigaction action[2] = {}; + struct arguments args = get_arguments(); + struct logger logger; + if (!logger_init(&logger, logger_default_config)) { + return EXIT_FAILURE; + } + logger.config.default_level = args.verbose_level; + if (sigaction(SIGINT, nullptr, &action[OLD]) == -1) { + logger_log_error(&logger, "Could not get the current SIGINT handler. %s", strerror(errno)); + return EXIT_FAILURE; + } + memcpy(&action[NEW], &action[OLD], sizeof *action); + action[NEW].sa_handler = sigint_handler; + bool server_initialized = tftp_server_init(&server, + (struct tftp_server_arguments) { + .ip = args.host, + .port = args.port, + .root = args.root, + .retries = args.retries, + .timeout_s = args.timeout_s, + .workers = args.workers, + .max_worker_sessions = args.max_worker_sessions, + .server_stats_callback = print_server_stats, + .handler_stats_callback = print_session_stats, + .stats_interval_seconds = datapoints_interval_seconds, + }, + &logger); + if (!server_initialized) { + logger_log_error(&logger, "Could not initialize server."); + return EXIT_FAILURE; + } + if (sigaction(SIGINT, &action[NEW], nullptr) == -1) { + logger_log_error(&logger, "Could not set the new SIGINT handler. %s", strerror(errno)); + return EXIT_FAILURE; + } + tftp_server_start(&server); + if (sigaction(SIGINT, &action[OLD], nullptr) == -1) { + logger_log_error(&logger, "Could not restore the old SIGINT handler. %s", strerror(errno)); + return EXIT_FAILURE; + } + tftp_server_destroy(&server); + return EXIT_SUCCESS; } -static void event_accept(evutil_socket_t peer_acceptor, short event, void *arg) { - struct acceptor *acceptor = arg; - evutil_socket_t socket; - struct acceptor_packet packet; - acceptor_accept(acceptor, &socket, &packet); - struct connection_details connection_details = { - .peer_stream = socket, - .request = (uint8_t *)packet.data, - .request_size = packet.payload_size +static void sigint_handler([[maybe_unused]] int signal) { + tftp_server_stop(&server); +} + +static inline struct arguments get_arguments() { + return (struct arguments) { + .host = "::", + .port = "6969", + .retries = 5, + .timeout_s = 2, + .root = "", + .workers = 7, + .max_worker_sessions = 64, + .verbose_level = LOGGER_LOG_LEVEL_DEBUG, }; - //service_handler_factory_create(factory, dispatcher, connection_details); - try(evutil_closesocket(socket), -1, fail); -fail: +} +static void print_session_stats(struct tftp_session_stats stats[static 1]) { + logger_log_info(stats->logger, "Stats: for %s requesting %s", stats->peer_addr, stats->file_path); + if (stats->error.error_occurred) { + logger_log_warn(stats->logger, "\tError: %s", stats->error.error_message); + } + logger_log_info(stats->logger, "\tTime spent: %dms", (int) (difftime(time(nullptr), stats->start_time) * 1000)); + logger_log_info(stats->logger, "\tPackets sent: %d", stats->packets_sent); + logger_log_info(stats->logger, "\tPackets ACKed: %d", stats->packets_acked); + logger_log_info(stats->logger, "\tBytes sent: %zu", stats->bytes_sent); + logger_log_info(stats->logger, "\tOptions: %s", stats->options_acked); + logger_log_info(stats->logger, "\tBlock size: %zu", stats->blksize); + logger_log_info(stats->logger, "\tWindow size: %d", stats->window_size); + logger_log_info(stats->logger, "\tRetransmits: %d", stats->retransmits); + logger_log_info(stats->logger, "\tServer port: %d", stats->server_port); + logger_log_info(stats->logger, "\tClient port: %d", stats->peer_port); } -static inline void set_log_level(int verbose_level) { - switch (verbose_level) { - case 1: - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_WARN); - break; - case 2: - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_INFO); - break; - case 3: - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_DEBUG); - break; - default: - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_ALL); - } +static void print_server_stats(struct server_stats stats[static 1]) { + uint64_t counters[TFTP_SERVER_STATS_TOTAL_COUNTERS]; + tftp_server_stats_get_and_reset_all_counters(stats, counters); + uint64_t active_sessions = counters[TFTP_SERVER_STATS_SESSIONS_COUNT]; + logger_log_info(stats->logger, "Server stats - every %d seconds", stats->interval); + logger_log_info(stats->logger, "Number of spawned TFTP sessions in stats time frame : %lu", active_sessions); } diff --git a/server/src/server_stats.c b/server/src/server_stats.c new file mode 100644 index 0000000..874cab3 --- /dev/null +++ b/server/src/server_stats.c @@ -0,0 +1,40 @@ +#include "server_stats.h" + +#include +#include + +struct server_stats tftp_server_stats_init(const struct sockaddr server_address[static 1], + int interval, + struct logger logger[static 1]) { + struct server_stats stats = (struct server_stats) { + .logger = logger, + .server_addr = server_address, + .interval = interval, + .start_time = time(nullptr), + .counters_len = TFTP_SERVER_STATS_TOTAL_COUNTERS, + .counters = { + [TFTP_SERVER_STATS_SESSIONS_COUNT] = 0, + } + }; + mtx_init(&stats.counters_mtx, mtx_plain); + return stats; +} + +void tftp_server_stats_get_all_counters(struct server_stats stats[static 1], + uint64_t counters[static TFTP_SERVER_STATS_TOTAL_COUNTERS]) { + mtx_lock(&stats->counters_mtx); + memcpy(counters, stats->counters, sizeof stats->counters); + mtx_unlock(&stats->counters_mtx); +} + +void tftp_server_stats_get_and_reset_all_counters(struct server_stats stats[static 1], + uint64_t counters[static TFTP_SERVER_STATS_TOTAL_COUNTERS]) { + mtx_lock(&stats->counters_mtx); + memcpy(counters, stats->counters, sizeof stats->counters); + memset(stats->counters, 0, sizeof stats->counters); + mtx_unlock(&stats->counters_mtx); +} + +void tftp_server_stats_destroy(struct server_stats stats[static 1]) { + mtx_destroy(&stats->counters_mtx); +} diff --git a/server/src/server_stats.h b/server/src/server_stats.h new file mode 100644 index 0000000..637cb53 --- /dev/null +++ b/server/src/server_stats.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +#include "logger.h" + +enum tftp_server_stats_counters { + TFTP_SERVER_STATS_SESSIONS_COUNT, + TFTP_SERVER_STATS_TOTAL_COUNTERS +}; + +struct server_stats { + struct logger *logger; + [[maybe_unused]] const struct sockaddr *server_addr; + [[maybe_unused]] int interval; + time_t start_time; + mtx_t counters_mtx; + size_t counters_len; + uint64_t counters[TFTP_SERVER_STATS_TOTAL_COUNTERS]; +}; + +struct server_stats tftp_server_stats_init(const struct sockaddr server_address[static 1], + int interval, + struct logger logger[static 1]); + +void tftp_server_stats_get_all_counters(struct server_stats stats[static 1], + uint64_t counters[static TFTP_SERVER_STATS_TOTAL_COUNTERS]); + +void tftp_server_stats_get_and_reset_all_counters(struct server_stats stats[static 1], + uint64_t counters[static TFTP_SERVER_STATS_TOTAL_COUNTERS]); + +void tftp_server_stats_destroy(struct server_stats stats[static 1]); diff --git a/server/src/service_handler.c b/server/src/service_handler.c deleted file mode 100644 index c80ea09..0000000 --- a/server/src/service_handler.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "service_handler.h" - -#include - -#include -#include - -#include - -#include "inet_utils.h" - -static void do_read(evutil_socket_t peer_stream, short events, void *arg); -static void do_write(evutil_socket_t peer_stream, short events, void *arg); - -extern service_handler_t *service_handler_init(dispatcher_t *dispatcher, struct service_parameters parameters, struct connection_details connection_details) { - service_handler_t *service_handler; - struct event *read_event; - struct event *write_event; - try(service_handler = malloc(sizeof *service_handler), NULL, fail); - service_handler->directory = parameters.directory; - service_handler->window_size = parameters.window_size; - service_handler->timeout_duration = parameters.timeout_duration; - service_handler->loss_probability = parameters.loss_probability; - try(read_event = event_new(dispatcher->event_base, connection_details.peer_stream, EV_READ | EV_PERSIST, do_read, service_handler), NULL, read_event_init_fail); - try(write_event = event_new(dispatcher->event_base, connection_details.peer_stream, EV_WRITE | EV_PERSIST, do_write, service_handler), NULL, write_event_init_fail); - memcpy(&(service_handler->state), &(struct service_handler_state){ .buffer = "MSG", .buffer_used = 0, .n_written = 0, .write_upto = sizeof "MSG", .read_event = read_event, .write_event = write_event }, sizeof service_handler->state); - try(event_add(service_handler->state.write_event, NULL), -1, write_event_add_fail); - return service_handler; -write_event_add_fail: -write_event_init_fail: - event_free(read_event); -read_event_init_fail: - free(service_handler); -fail: - cmn_logger_log_error("Can't initialize service handler"); - return NULL; -} - -extern void service_handler_destroy(service_handler_t *service_handler) { - event_free(service_handler->state.read_event); - event_free(service_handler->state.write_event); - free(service_handler); -} - -extern char *service_handler_error_to_string(enum service_handler_error error) { - switch (error) { - case SERVICE_HANDLER_SUCCESS: - return "Success"; - case SERVICE_HANDLER_ENOMEM: - return "Out of memory."; - default: - return "Unknown error code."; - } -} - -static void do_read(evutil_socket_t peer_stream, short events, void *arg) { - service_handler_t *service_handler = arg; - ssize_t result; - char rbuff[1024]; - cmn_logger_log_debug("Waiting for client response."); - while (true) { - try((result = recv(peer_stream, rbuff, sizeof rbuff - 1, 0)) == -1, true, recv_fail); - cmn_logger_log_debug("Received %zd bytes, packet content is \"%s\"", result, rbuff); - cmn_logger_log_debug("Closing connection"); - evutil_closesocket(peer_stream); - service_handler_destroy(service_handler); - return; - } -recv_fail: - if (evutil_socket_geterror(peer_stream) == EAGAIN) { - return; - } - cmn_logger_log_error("recv: %s\n", evutil_socket_error_to_string(evutil_socket_geterror(peer_stream))); - service_handler_destroy(service_handler); -} - -static void do_write(evutil_socket_t peer_stream, short events, void *arg) { - service_handler_t *service_handler = arg; - ssize_t result; - cmn_logger_log_debug("Replying to client."); - while (service_handler->state.n_written < service_handler->state.write_upto) { - result = send(peer_stream, service_handler->state.buffer + service_handler->state.n_written, service_handler->state.write_upto - service_handler->state.n_written, 0); - if (result < 0) { - if (evutil_socket_geterror(peer_stream) == EAGAIN) { - return; - } - service_handler_destroy(service_handler); - return; - } - service_handler->state.n_written += result; - } - cmn_logger_log_debug("Message sent."); - if (service_handler->state.n_written == service_handler->state.buffer_used) { - service_handler->state.n_written = 1; - service_handler->state.write_upto = 1; - service_handler->state.buffer_used = 1; - } - event_del(service_handler->state.write_event); - event_add(service_handler->state.read_event, NULL); -} diff --git a/server/src/service_handler.h b/server/src/service_handler.h deleted file mode 100644 index e4c7d16..0000000 --- a/server/src/service_handler.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include - -#include "dispatcher.h" - -enum service_handler_error { - SERVICE_HANDLER_SUCCESS = 0, - SERVICE_HANDLER_ENOMEM = 1 -}; - -struct service_handler_state { - char buffer[512]; - size_t buffer_used; - size_t n_written; - size_t write_upto; - struct event *read_event; - struct event *write_event; -}; - -typedef struct service_handler { - char *directory; // file root directory - uint32_t window_size; // size of the dispatch window - long timeout_duration; // duration of the timeout in milliseconds - double loss_probability; // probability of packet loss - struct service_handler_state state; -} service_handler_t; - -struct service_parameters { - char *directory; // file root directory - uint32_t window_size; // size of the dispatch window - long timeout_duration; // duration of the timeout in milliseconds - double loss_probability; // probability of packet loss -}; - -struct connection_details { - evutil_socket_t peer_stream; - uint8_t *request; - size_t request_size; -}; - -extern service_handler_t *service_handler_init(dispatcher_t *dispatcher, struct service_parameters parameters, struct connection_details connection_details); - -extern void service_handler_destroy(service_handler_t *service_handler); - -extern char *service_handler_error_to_string(enum service_handler_error error); diff --git a/server/src/service_handler_factory.h b/server/src/service_handler_factory.h deleted file mode 100644 index 4b73e9c..0000000 --- a/server/src/service_handler_factory.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "service_handler.h" - -typedef struct service_handler_factory { - struct service_parameters service_parameters; -} service_handler_factory_t; - -// Initialize a service handler factory -static inline void service_handler_factory_init(struct service_handler_factory *factory, struct service_parameters service_parameters) { - factory->service_parameters = service_parameters; -} - -// Create a new service handler -static inline void service_handler_factory_create(struct service_handler_factory *factory, dispatcher_t *dispatcher, struct connection_details connection_details) { - service_handler_t *service_handler = service_handler_init(dispatcher, factory->service_parameters, connection_details); -} diff --git a/server/src/session_stats.c b/server/src/session_stats.c new file mode 100644 index 0000000..c73ae1b --- /dev/null +++ b/server/src/session_stats.c @@ -0,0 +1,26 @@ +#include "session_stats.h" +#include "inet_utils.h" + +#include "tftp.h" + +struct tftp_session_stats tftp_session_stat_init(const struct sockaddr server_address[static 1], + const struct sockaddr peer_address[static 1], + const char file_path[static 1], + struct logger logger[static 1]) { + struct tftp_session_stats stats = { + .logger = logger, + .file_path = file_path, + .error = {.error_occurred = false, .error_number = 0, .error_message = nullptr}, + .options_acked = "[]", + .start_time = time(nullptr), + .packets_sent = 0, + .packets_acked = 0, + .bytes_sent = 0, + .retransmits = 0, + .blksize = tftp_default_blksize, + .window_size = tftp_default_window_size, + }; + inet_ntop_address(server_address, stats.server_addr, &stats.server_port); + inet_ntop_address(peer_address, stats.peer_addr, &stats.peer_port); + return stats; +} diff --git a/server/src/session_stats.h b/server/src/session_stats.h new file mode 100644 index 0000000..9bfe665 --- /dev/null +++ b/server/src/session_stats.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#include "logger.h" +#include "tftp.h" + +struct tftp_session_stats { + struct logger *logger; + char peer_addr[INET6_ADDRSTRLEN]; + uint16_t peer_port; + char server_addr[INET6_ADDRSTRLEN]; + uint16_t server_port; + const char *file_path; + struct tftp_session_stats_error { + bool error_occurred; + uint16_t error_number; + const char *error_message; + } error; + char options_in[tftp_option_formatted_string_max_size]; + char options_acked[tftp_option_formatted_string_max_size]; + time_t start_time; + int packets_sent; + int packets_acked; + size_t bytes_sent; + int retransmits; + uint16_t blksize; + uint16_t window_size; +}; + +struct tftp_session_stats tftp_session_stat_init(const struct sockaddr server_address[static 1], + const struct sockaddr peer_address[static 1], + const char file_path[static 1], + struct logger logger[static 1]); diff --git a/server/src/tftp_handler.c b/server/src/tftp_handler.c new file mode 100644 index 0000000..dd0b25f --- /dev/null +++ b/server/src/tftp_handler.c @@ -0,0 +1,716 @@ +#include "tftp_handler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "inet_utils.h" +#include "tftp.h" + +static size_t file_size(int fd); + +static void resume_execution(struct tftp_handler handler[1]); + +static void submit_transmit_oack_request(struct tftp_handler handler[static 1]); +static void on_transmit_oack(int32_t ret, void *args); + +static void submit_on_next_block_request(struct tftp_handler handler[static 1]); +static void on_next_block(int32_t ret, void *args); + +static void submit_on_transmit_data_request(struct tftp_handler handler[1]); +static void on_transmit_data(int32_t ret, void *args); + +static void submit_on_transmit_error_request(struct tftp_handler handler[static 1]); +static void on_transmit_error(int32_t ret, void *args); + +static void submit_on_new_data_request(struct tftp_handler handler[static 1]); +static void on_new_data(int32_t ret, void *args); + +static void on_timeout(int32_t ret, void *args); + +static void close_handler(struct tftp_handler handler[static 1]); + +static void handle_new_data(struct tftp_handler handler[static 1], size_t n, uint8_t data[static n]); +static bool listener_init(struct tftp_handler handler[static 1], bool peer_demand_ipv4); +static bool required_file_setup(struct tftp_handler handler[static 1], const char *root); +static void parse_options(struct tftp_handler handler[static 1]); + +/** if n > tftp_request_packet_max_size - sizeof(enum tftp_opcode) behaviour is undefined **/ +// TODO: Reduce cognitive complexity +void tftp_handler_init(struct tftp_handler handler[static 1], + struct uring_worker *worker, + const struct addrinfo server_addr[static 1], + const struct sockaddr_storage peer, + socklen_t peer_addrlen, + bool peer_demand_ipv4, + const char *root, + size_t n, + const char options[static n], + uint8_t retries, + uint8_t timeout, + void (*stats_callback)(struct tftp_session_stats *), + struct logger logger[static 1]) { + *handler = (struct tftp_handler) { + .logger = logger, + .worker = worker, + .timeout_job = { + .routine = on_timeout, + .args = handler, + }, + .on_new_data_job = { + .routine = on_new_data, + .args = handler, + }, + .transmit_oack_job = { + .routine = on_transmit_oack, + .args = handler, + }, + .next_block_job = { + .routine = on_next_block, + .args = handler, + }, + .transmit_data_job = { + .routine = on_transmit_data, + .args = handler, + }, + .transmit_error_job = { + .routine = on_transmit_error, + .args = handler, + }, + .state = TFTP_HANDLER_STATE_MAKE_DATA_PACKET, + .listener = { + .file_descriptor = -1, + .peer_addr_storage = peer, + .peer_addrlen = peer_addrlen, + }, + .retries = retries, + .timeout = timeout, + .block_size = tftp_default_blksize, + .window_size = 1, + .stats_callback = stats_callback, + .file_descriptor = -1, + .next_data_packet_to_send = 1, + .next_data_packet_to_make = 1, + }; + + { // TODO: Remove block statement when io_uring_prep_recvfrom will be available. + handler->listener.msghdr.msg_iov = handler->listener.iovec; + handler->listener.msghdr.msg_iovlen = 1; + handler->listener.msghdr.msg_control = nullptr; + handler->listener.msghdr.msg_controllen = 0; + handler->listener.msghdr.msg_flags = 0; + } + + memcpy(&handler->listener.addr_storage, server_addr->ai_addr, server_addr->ai_addrlen); + memcpy(&handler->listener.addrinfo, server_addr, sizeof *server_addr); + handler->listener.addrinfo.ai_addr = (struct sockaddr *) &handler->listener.addr_storage; + + memcpy(handler->options_storage, options, n); + handler->path = &handler->options_storage[0]; + const char *mode = &handler->options_storage[strlen(handler->path) + 1]; + handler->options_str_size = n - (strlen(handler->path) + 1 + strlen(mode) + 1); + handler->options_str = handler->options_str_size == 0 ? nullptr : &handler->options_storage[n - handler->options_str_size]; + + if (!listener_init(handler, peer_demand_ipv4)) { + logger_log_error(logger, "Could not initialize session listener. Ignoring request."); + return; + } + + handler->stats = tftp_session_stat_init(handler->listener.addrinfo.ai_addr, (const struct sockaddr *) &peer, handler->path, logger); + + if (strcasecmp(mode, "octet") == 0) { + handler->mode = TFTP_MODE_OCTET; + } + else if (strcasecmp(mode, "netascii") == 0) { + handler->mode = TFTP_MODE_NETASCII; + //TODO set Netascii mode + } + else { + handler->mode = TFTP_MODE_INVALID; + const char format[] = "Unknown mode: '%s'."; + char *message = malloc(sizeof format + strlen(mode)); + sprintf(message, format, mode); + handler->stats.error = (struct tftp_session_stats_error) { + .error_occurred = true, + .error_number = TFTP_ERROR_ILLEGAL_OPERATION, + .error_message = message, + }; + handler->state = TFTP_HANDLER_STATE_SEND_ERROR_PACKET; + return; + } + if (!required_file_setup(handler, root)) { + handler->state = TFTP_HANDLER_STATE_SEND_ERROR_PACKET; + return; + } + parse_options(handler); + if (handler->valid_options_required) { + handler->state = TFTP_HANDLER_STATE_SEND_OACK_PACKET; + } + else { + handler->window_begin = 1; + } + handler->data_packets = malloc(handler->window_size * (sizeof *handler->data_packets + handler->block_size)); + if (handler->data_packets == nullptr) { + logger_log_error(logger, "Not enough memory: %s", strerror(errno)); + exit(1); + } +} + +void tftp_handler_start(struct tftp_handler handler[static 1]) { + resume_execution(handler); +} + +static void resume_execution(struct tftp_handler handler[1]) { + switch (handler->state) { + case TFTP_HANDLER_STATE_SEND_ERROR_PACKET: + submit_on_transmit_error_request(handler); + break; + case TFTP_HANDLER_STATE_SEND_OACK_PACKET: + submit_transmit_oack_request(handler); + break; + case TFTP_HANDLER_STATE_MAKE_DATA_PACKET: + submit_on_next_block_request(handler); + break; + case TFTP_HANDLER_STATE_SEND_DATA_PACKET: + submit_on_transmit_data_request(handler); + break; + case TFTP_HANDLER_STATE_WAIT_ACK_PACKET: + submit_on_new_data_request(handler); + break; + case TFTP_HANDLER_STATE_CLOSE: + close_handler(handler); + default: + break; + } +} + +// TODO: Refactory when io_uring_prep_recvfrom will be available. +static void submit_on_new_data_request(struct tftp_handler handler[static 1]) { + struct io_uring_sqe *sqe = io_uring_get_sqe(&handler->worker->ring); + if (sqe == nullptr) { + logger_log_error(handler->logger, "Could not get a submission queue entry."); + exit(1); + } + handler->listener.recv_peer_addrlen = sizeof handler->listener.recv_peer_addr_storage; + handler->listener.iovec[0].iov_base = handler->listener.recv_buffer; + handler->listener.iovec[0].iov_len = sizeof handler->listener.recv_buffer; + handler->listener.msghdr.msg_name = &handler->listener.recv_peer_addr_storage; + handler->listener.msghdr.msg_namelen = handler->listener.recv_peer_addrlen; + io_uring_prep_recvmsg(sqe, handler->listener.file_descriptor, &handler->listener.msghdr, 0); + io_uring_sqe_set_data(sqe, &handler->on_new_data_job); + io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK); + sqe = io_uring_get_sqe(&handler->worker->ring); + if (sqe == nullptr) { + logger_log_error(handler->logger, "Could not get a submission queue entry."); + exit(1); + } + io_uring_prep_link_timeout(sqe, &(struct __kernel_timespec) {.tv_sec = handler->timeout}, 0); + io_uring_sqe_set_data(sqe, &handler->timeout_job); + io_uring_submit(&handler->worker->ring); +} + +static void on_new_data(int32_t ret, void *args) { + struct tftp_handler *handler = args; + if (ret == -ECANCELED) { + return; + } + if (ret < 0) { + logger_log_error(handler->logger, "Error while receiving data: %s", strerror(-ret)); + exit(1); + } + // TODO: ret must be at least 4 bytes + switch (handler->listener.recv_peer_addr_storage.ss_family) { + case AF_INET: + handler->listener.recv_peer_addrlen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + handler->listener.recv_peer_addrlen = sizeof(struct sockaddr_in6); + break; + default: + logger_log_warn(handler->logger, "Unknown address family: %d. Ignoring packet.", handler->listener.recv_peer_addr_storage.ss_family); + resume_execution(handler); + return; + } + handle_new_data(handler, ret, handler->listener.recv_buffer); + resume_execution(handler); +} + +static void handle_new_data(struct tftp_handler handler[static 1], size_t n, uint8_t data[static n]) { + struct sockaddr *recv_peer_addr = (struct sockaddr *) &handler->listener.recv_peer_addr_storage; + socklen_t recv_peer_addrlen = handler->listener.recv_peer_addrlen; + if (recv_peer_addrlen != handler->listener.peer_addrlen || + memcmp(&handler->listener.peer_addr_storage, recv_peer_addr, recv_peer_addrlen) != 0) { + char peer_addr_str[INET6_ADDRSTRLEN]; + uint16_t peer_port; + inet_ntop_address(recv_peer_addr, peer_addr_str, &peer_port); + logger_log_error(handler->logger, "Unexpected peer: '%s:%d', expected peer: '%s:%d'.", peer_addr_str, peer_port, handler->stats.peer_addr, handler->stats.peer_port); + handler->state = TFTP_HANDLER_STATE_CLOSE; + return; + } + enum tftp_opcode opcode = ntohs(*(uint16_t *) data); + uint16_t block_number = ntohs(*(uint16_t *) &data[2]); + switch (opcode) { + case TFTP_OPCODE_ERROR: + struct tftp_error_packet *error_packet = (struct tftp_error_packet *) data; + uint16_t error_code = ntohs(error_packet->error_code); + char *error_message; + const char *end_ptr = memchr(error_packet->error_message, '\0', n - sizeof *error_packet); + if (end_ptr == nullptr) { + error_message = "Error message received was invalid (not null terminated)."; + } + else { + size_t error_message_len = end_ptr - (char *) error_packet->error_message; + error_message = malloc(error_message_len + 1); + strcpy(error_message, (const char *) error_packet->error_message); + } + handler->stats.error = (struct tftp_session_stats_error) { + .error_occurred = true, + .error_number = error_code, + .error_message = error_message, + }; + logger_log_warn(handler->logger, "Error reported from client: %s", handler->stats.error.error_message); + handler->state = TFTP_HANDLER_STATE_SEND_ERROR_PACKET; + return; + case TFTP_OPCODE_ACK: + size_t payload_size = 0; + uint16_t last_packet_sent = handler->next_data_packet_to_send - 1; + if (block_number < handler->window_begin || block_number > last_packet_sent) { + handler->state = TFTP_HANDLER_STATE_WAIT_ACK_PACKET; + return; // ignore unexpected ACK + } + if (handler->valid_options_required && block_number == 0) { + handler->options_acknowledged = true; + } + else { + payload_size = handler->block_size * (block_number - handler->window_begin) + + (block_number == last_packet_sent ? handler->last_block_size : handler->block_size); + } + handler->window_begin = block_number + 1; + handler->stats.packets_acked += 1; + handler->stats.bytes_sent += payload_size; + handler->retransmits = 0; + if (handler->waiting_last_ack) { + handler->state = (block_number == last_packet_sent) ? + TFTP_HANDLER_STATE_CLOSE : + TFTP_HANDLER_STATE_WAIT_ACK_PACKET; + } + else { + handler->state = TFTP_HANDLER_STATE_MAKE_DATA_PACKET; + } + return; + default: + logger_log_error(handler->logger, "Expected an ACK opcode from %s, got: %hu.", handler->stats.peer_addr, opcode); + handler->stats.error = (struct tftp_session_stats_error) { + .error_occurred = true, + .error_number = TFTP_ERROR_ILLEGAL_OPERATION, + .error_message = "I only do reads for now...", + }; + handler->state = TFTP_HANDLER_STATE_SEND_ERROR_PACKET; + return; + } +} + +static void on_timeout(int32_t ret, void *args) { + struct tftp_handler *handler = args; + switch (-ret) { + case ETIME: + break; + case ECANCELED: + return; + default: + // for small timeout race conditions may occur resulting in ENOENT + logger_log_error(handler->logger, "Error while handling timeout: %s", strerror(-ret)); + exit(1); + return; + } + if (handler->retransmits < handler->retries) { + logger_log_debug(handler->logger, "ACK timeout for peer %s:%d. Retransmission no %d.", handler->stats.peer_addr, handler->stats.peer_port, handler->retransmits + 1); + handler->retransmits += 1; + handler->global_retransmits += 1; + if (handler->valid_options_required && !handler->options_acknowledged) { + submit_transmit_oack_request(handler); + } + else { + handler->next_data_packet_to_send = handler->window_begin; + submit_on_transmit_data_request(handler); + } + return; + } + static const char format[] = "timeout after %d retransmits."; + static const char format_miss_last_ack[] = "timeout after %d retransmits. Missed last ack."; + char *error_msg = malloc(sizeof(format_miss_last_ack) + 3 * sizeof(handler->retransmits) + 2); + sprintf(error_msg, handler->waiting_last_ack ? format : format_miss_last_ack, handler->retransmits); + handler->stats.error.error_occurred = true; + handler->stats.error.error_number = TFTP_ERROR_NOT_DEFINED; + handler->stats.error.error_message = error_msg; + handler->state = TFTP_HANDLER_STATE_SEND_ERROR_PACKET; + resume_execution(handler); +} + +static void submit_on_next_block_request(struct tftp_handler handler[static 1]) { + size_t next_data_packet_index = (handler->next_data_packet_to_make - 1) % handler->window_size; + size_t packet_offset = next_data_packet_index * (sizeof(struct tftp_data_packet) + handler->block_size); + struct tftp_data_packet *packet = (struct tftp_data_packet *) ((uint8_t *) handler->data_packets + packet_offset); + uint8_t *block = packet->data; + size_t size = handler->next_data_block_info.current_size; + struct io_uring_sqe *sqe = io_uring_get_sqe(&handler->worker->ring); + if (sqe == nullptr) { + logger_log_error(handler->logger, "Could not get a submission queue entry."); + exit(1); + } + io_uring_prep_read(sqe, + handler->file_descriptor, + &block[size], + handler->block_size - size, + -1); + io_uring_sqe_set_data(sqe, &handler->next_block_job); + io_uring_submit(&handler->worker->ring); +} + +static void on_next_block(int32_t ret, void *args) { + struct tftp_handler *handler = args; + if (ret < 0) { + logger_log_warn(handler->logger, "Error while reading from source: %s", strerror(-ret)); + handler->stats.error = (struct tftp_session_stats_error) { + .error_occurred = true, + .error_number = TFTP_ERROR_NOT_DEFINED, + .error_message = "Error while reading from source" + }; + handler->state = TFTP_HANDLER_STATE_SEND_ERROR_PACKET; + } + else { + size_t next_data_packet_index = (handler->next_data_packet_to_make - 1) % handler->window_size; + size_t packet_offset = next_data_packet_index * (sizeof(struct tftp_data_packet) + handler->block_size); + struct tftp_data_packet *packet = (struct tftp_data_packet *) ((uint8_t *) handler->data_packets + packet_offset); + size_t *current_size = &handler->next_data_block_info.current_size; + size_t *previous_size = &handler->next_data_block_info.previous_size; + *current_size += ret; + if (*current_size != handler->block_size && *current_size != *previous_size) { + *previous_size = *current_size; + submit_on_next_block_request(handler); + return; + } + handler->last_block_size = *current_size; + memcpy(packet, + &(struct tftp_data_packet) { + .opcode = htons(TFTP_OPCODE_DATA), + .block_number = htons(handler->next_data_packet_to_make), + }, + sizeof *handler->data_packets); + handler->next_data_packet_to_make += 1; + bool is_packet_last_in_window = (handler->next_data_packet_to_make - handler->window_size == handler->window_begin); + if (is_packet_last_in_window || handler->last_block_size < handler->block_size) { + handler->state = TFTP_HANDLER_STATE_SEND_DATA_PACKET; + } + else { + handler->state = TFTP_HANDLER_STATE_MAKE_DATA_PACKET; + } + } + handler->next_data_block_info = (struct block_info) {}; + resume_execution(handler); +} + +static void submit_on_transmit_data_request(struct tftp_handler handler[1]) { + size_t next_data_packet_index = (handler->next_data_packet_to_send - 1) % handler->window_size; + size_t packet_offset = next_data_packet_index * (sizeof(struct tftp_data_packet) + handler->block_size); + struct tftp_data_packet *packet = (struct tftp_data_packet *) ((uint8_t *) handler->data_packets + packet_offset); + bool is_last_packet = handler->next_data_packet_to_send == handler->next_data_packet_to_make - 1; + size_t packet_size = sizeof *handler->data_packets + (is_last_packet ? handler->last_block_size : handler->block_size); + struct io_uring_sqe *sqe = io_uring_get_sqe(&handler->worker->ring); + if (sqe == nullptr) { + logger_log_error(handler->logger, "Could not get a submission queue entry."); + exit(1); + } + io_uring_prep_sendto(sqe, + handler->listener.file_descriptor, + packet, + packet_size, + 0, + (struct sockaddr *) &handler->listener.peer_addr_storage, + handler->listener.peer_addrlen); + io_uring_sqe_set_data(sqe, &handler->transmit_data_job); + io_uring_submit(&handler->worker->ring); +} + +static void on_transmit_data(int32_t ret, void *args) { + struct tftp_handler *handler = args; + if (ret < 0) { + logger_log_error(handler->logger, "Error while sending data: %s", strerror(-ret)); + exit(1); + } + handler->stats.packets_sent += 1; + handler->next_data_packet_to_send += 1; + bool is_last_packet = (handler->next_data_packet_to_send == handler->next_data_packet_to_make) && + (handler->last_block_size < handler->block_size); + if (is_last_packet) { + handler->waiting_last_ack = true; + handler->state = TFTP_HANDLER_STATE_WAIT_ACK_PACKET; + } + else { + bool is_packet_last_in_window = handler->next_data_packet_to_send - handler->window_size == handler->window_begin; + handler->state = is_packet_last_in_window ? TFTP_HANDLER_STATE_WAIT_ACK_PACKET + : TFTP_HANDLER_STATE_SEND_DATA_PACKET; + } + resume_execution(handler); +} + +static void submit_on_transmit_error_request(struct tftp_handler handler[static 1]) { + struct io_uring_sqe *sqe = io_uring_get_sqe(&handler->worker->ring); + if (sqe == nullptr) { + logger_log_error(handler->logger, "Could not get a submission queue entry."); + exit(1); + } + size_t packet_len = sizeof(struct tftp_error_packet) + strlen(handler->stats.error.error_message) + 1; + struct tftp_error_packet *packet = malloc(packet_len); + memcpy(packet, + &(struct tftp_error_packet) { + .opcode = htons(TFTP_OPCODE_ERROR), + .error_code = htons(handler->stats.error.error_number) + }, + sizeof *packet); + strcpy((char *) packet->error_message, handler->stats.error.error_message); + io_uring_prep_sendto(sqe, + handler->listener.file_descriptor, + packet, + packet_len, + 0, + (struct sockaddr *) &handler->listener.peer_addr_storage, + handler->listener.peer_addrlen); + io_uring_sqe_set_data(sqe, &handler->transmit_error_job); + io_uring_submit(&handler->worker->ring); +} + +static void on_transmit_error(int32_t ret, void *args) { + struct tftp_handler *handler = args; + // free(packet); + if (ret < 0) { + logger_log_error(handler->logger, "Error while sending error: %s", strerror(-ret)); + exit(1); + } + handler->state = TFTP_HANDLER_STATE_CLOSE; + resume_execution(handler); +} + +static void submit_transmit_oack_request(struct tftp_handler handler[static 1]) { + size_t options_values_length = 0; + for (enum tftp_option_recognized option = 0; option < TFTP_OPTION_TOTAL_OPTIONS; option++) { + if (handler->options[option].is_active) { + options_values_length += strlen(tftp_option_recognized_string[option]) + 1; + options_values_length += strlen(handler->options[option].value) + 1; + } + } + size_t packet_len = sizeof(struct tftp_oack_packet) + options_values_length; + struct tftp_oack_packet *packet = malloc(packet_len); + packet->opcode = htons(TFTP_OPCODE_OACK); + unsigned char *ptr = packet->options_values; + for (enum tftp_option_recognized option = 0; option < TFTP_OPTION_TOTAL_OPTIONS; option++) { + if (handler->options[option].is_active) { + size_t len = strlen(tftp_option_recognized_string[option]) + 1; + ptr = (unsigned char *) memcpy(ptr, tftp_option_recognized_string[option], len) + len; + len = strlen(handler->options[option].value) + 1; + ptr = (unsigned char *) memcpy(ptr, handler->options[option].value, len) + len; + } + } + struct io_uring_sqe *sqe = io_uring_get_sqe(&handler->worker->ring); + if (sqe == nullptr) { + logger_log_error(handler->logger, "Could not get a submission queue entry."); + exit(1); + } + io_uring_prep_sendto(sqe, + handler->listener.file_descriptor, + packet, + packet_len, + 0, + (struct sockaddr *) &handler->listener.peer_addr_storage, + handler->listener.peer_addrlen); + io_uring_sqe_set_data(sqe, &handler->transmit_oack_job); + io_uring_submit(&handler->worker->ring); +} + +static void on_transmit_oack(int32_t ret, void *args) { + struct tftp_handler *handler = args; + if (ret < 0) { + logger_log_error(handler->logger, "Error while sending OACK: %s", strerror(-ret)); + exit(1); + } + // free(packet); + handler->stats.packets_sent += 1; + handler->state = TFTP_HANDLER_STATE_WAIT_ACK_PACKET; + resume_execution(handler); +} + +static void close_handler(struct tftp_handler handler[static 1]) { + handler->stats.retransmits = handler->global_retransmits; + handler->stats_callback(&handler->stats); + logger_log_debug(handler->logger, "Closing response data object."); + close(handler->file_descriptor); + logger_log_debug(handler->logger, "Closing socket."); + close(handler->listener.file_descriptor); + logger_log_debug(handler->logger, "Session closed."); + sem_post(&handler->worker->available_slots); +} + +static void parse_options(struct tftp_handler handler[static 1]) { + if (handler->options_str == nullptr) { + logger_log_info(handler->logger, "No options requested from peer %s:%d.", handler->stats.peer_addr, handler->stats.peer_port); + return; + } + tftp_format_option_strings(handler->options_str_size, handler->options_str, handler->stats.options_in); + logger_log_info(handler->logger, "Options requested from peer %s:%d are [%s]", handler->stats.peer_addr, handler->stats.peer_port, handler->stats.options_in); + tftp_parse_options(handler->options, handler->options_str_size, handler->options_str); + for (enum tftp_option_recognized o = 0; o < TFTP_OPTION_TOTAL_OPTIONS; o++) { + if (handler->options[o].is_active) { + handler->valid_options_required = true; + switch (o) { + case TFTP_OPTION_BLKSIZE: + handler->block_size = strtoul(handler->options[o].value, nullptr, 10); + break; + case TFTP_OPTION_TIMEOUT: + handler->timeout = strtoul(handler->options[o].value, nullptr, 10); + break; + case TFTP_OPTION_TSIZE: + sprintf((char *) handler->options[o].value, "%zu", handler->file_size); + break; + case TFTP_OPTION_WINDOWSIZE: + handler->window_size = strtoul(handler->options[o].value, nullptr, 10); + break; + default: // unreachable + break; + } + } + } + tftp_format_options(handler->options, handler->stats.options_acked); + logger_log_info(handler->logger, "Options to ack for peer %s:%d are %s", handler->stats.peer_addr, handler->stats.peer_port, handler->stats.options_acked); + handler->stats.blksize = handler->block_size; + handler->stats.window_size = handler->window_size; +} + +static bool listener_init(struct tftp_handler handler[static 1], bool peer_demand_ipv4) { + char peer_addr[INET6_ADDRSTRLEN]; + uint16_t peer_port; + if (peer_demand_ipv4) { + logger_log_debug(handler->logger, "Peer request an IPV4 response, using an IPV4 connection to support an IPV6 unaware client."); + struct sockaddr_storage ipv4_serv_addr = {}; + struct sockaddr_storage ipv4_peer_addr = {}; + if (inet_add_in6_to_in((const struct sockaddr_in6 *) &handler->listener.addr_storage, (struct sockaddr_in *) &ipv4_serv_addr) < 0) { + logger_log_error(handler->logger, "Could not translate server address to an IPV4 address."); + return false; + } + memcpy(&handler->listener.addr_storage, &ipv4_serv_addr, sizeof ipv4_serv_addr); + handler->listener.addrinfo.ai_addrlen = sizeof(struct sockaddr_in); + handler->listener.addrinfo.ai_family = AF_INET; + if (inet_add_in6_to_in((const struct sockaddr_in6 *) &handler->listener.peer_addr_storage, (struct sockaddr_in *) &ipv4_peer_addr) < 0) { + logger_log_error(handler->logger, "Could not translate peer address to an IPV4 address."); + return false; + } + memcpy(&handler->listener.peer_addr_storage, &ipv4_peer_addr, sizeof ipv4_peer_addr); + handler->listener.peer_addrlen = sizeof(struct sockaddr_in); + } + else { + handler->listener.peer_addrlen = sizeof(struct sockaddr_in6); + } + if (inet_ntop_address((const struct sockaddr *) &handler->listener.peer_addr_storage, peer_addr, &peer_port) == nullptr) { + logger_log_error(handler->logger, "Could not translate peer address to a string representation."); + } + else { + logger_log_info(handler->logger, "New incoming connection from peer '%s:%d' asking for path '%s'", peer_addr, peer_port, handler->path); + } + handler->listener.file_descriptor = socket(handler->listener.addrinfo.ai_family, SOCK_DGRAM, 0); + if (handler->listener.file_descriptor == -1) { + logger_log_error(handler->logger, "Could not create socket: %s", strerror(errno)); + return false; + } + // use an available ephemeral port for binding + if (peer_demand_ipv4) { + ((struct sockaddr_in *) handler->listener.addrinfo.ai_addr)->sin_port = 0; + } + else { + ((struct sockaddr_in6 *) handler->listener.addrinfo.ai_addr)->sin6_port = 0; + } + if (bind(handler->listener.file_descriptor, handler->listener.addrinfo.ai_addr, handler->listener.addrinfo.ai_addrlen) == -1) { + logger_log_error(handler->logger, "Could not bind socket: %s", strerror(errno)); + close(handler->listener.file_descriptor); + return false; + } + if (getsockname(handler->listener.file_descriptor, handler->listener.addrinfo.ai_addr, &handler->listener.addrinfo.ai_addrlen) == -1) { + logger_log_error(handler->logger, "Could not get socket name: %s", strerror(errno)); + close(handler->listener.file_descriptor); + return false; + } + return true; +} + +static bool required_file_setup(struct tftp_handler handler[static 1], const char *root) { + const char *path; + if (root == nullptr) { + path = handler->path; + } + else { + char *full_path = malloc(strlen(root) + strlen(handler->path) + 1); + strcpy(full_path, root); + strcat(full_path, handler->path); + path = full_path; + } + errno = 0; + do { + handler->file_descriptor = open(path, O_RDONLY); + } while (handler->file_descriptor == -1 && errno == EINTR); + if (root != nullptr) { + free((void *) path); + } + if (handler->file_descriptor == -1) { + enum tftp_error_code error_code; + char *message; + switch (errno) { + case EACCES: + error_code = TFTP_ERROR_ACCESS_VIOLATION; + message = "Permission denied."; + break; + case ENOENT: + error_code = TFTP_ERROR_FILE_NOT_FOUND; + message = "No such file or directory."; + break; + default: + error_code = TFTP_ERROR_NOT_DEFINED; + message = strerror(errno); + } + handler->stats.error = (struct tftp_session_stats_error) { + .error_occurred = true, + .error_number = error_code, + .error_message = message, + }; + return false; + } + handler->file_size = file_size(handler->file_descriptor); + return true; +} + +static size_t file_size(int fd) { + struct stat file_stat; + if (fd < 0) { + return -1; + } + if (fstat(fd, &file_stat) < 0) { + return -1; + } + switch (file_stat.st_mode & __S_IFMT) { + case __S_IFBLK: + uint64_t bytes; + if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { + return -1; + } + return bytes; + case __S_IFREG: + return file_stat.st_size; + default: + return -1; + } +} diff --git a/server/src/tftp_handler.h b/server/src/tftp_handler.h new file mode 100644 index 0000000..e2c4636 --- /dev/null +++ b/server/src/tftp_handler.h @@ -0,0 +1,98 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "session_stats.h" +#include "uring_thread_pool.h" + +enum tftp_handler_state: uint8_t { + TFTP_HANDLER_STATE_MAKE_DATA_PACKET, + TFTP_HANDLER_STATE_WAIT_ACK_PACKET, + TFTP_HANDLER_STATE_SEND_DATA_PACKET, + TFTP_HANDLER_STATE_SEND_OACK_PACKET, + TFTP_HANDLER_STATE_SEND_ERROR_PACKET, + TFTP_HANDLER_STATE_CLOSE, +}; + +struct tftp_handler_listener { + int file_descriptor; + struct sockaddr_storage addr_storage; + struct addrinfo addrinfo; + struct sockaddr_storage peer_addr_storage; + socklen_t peer_addrlen; + struct sockaddr_storage recv_peer_addr_storage; + socklen_t recv_peer_addrlen; + uint8_t recv_buffer[tftp_default_blksize]; + // TODO: As of Linux kernel version 6.10.4 IO_URING_OP_RECVFROM is not implemented, this is a workaround. + // Remove this fields and use io_uring_prep_recvfrom when available. + struct msghdr msghdr; + struct iovec iovec[1]; +}; + +struct tftp_handler { + struct logger *logger; + struct uring_worker* worker; + struct uring_job timeout_job; + struct uring_job on_new_data_job; + struct uring_job transmit_oack_job; + struct uring_job next_block_job; + struct uring_job transmit_data_job; + struct uring_job transmit_error_job; + enum tftp_handler_state state; + struct tftp_handler_listener listener; + uint8_t retries; + uint8_t timeout; + uint16_t block_size; + uint16_t window_size; + uint8_t retransmits; + int global_retransmits; + struct tftp_data_packet *data_packets; // array to window size elements of sizeof((struct tftp_data_packet)) + block_size bytes + uint16_t window_begin; + uint16_t next_data_packet_to_make; + uint16_t next_data_packet_to_send; + size_t last_block_size; // last data packet may have less than sizeof((struct tftp_data_packet)) + block_size used bytes + bool waiting_last_ack; + const char *path; + enum tftp_mode mode; + int file_descriptor; + size_t file_size; + struct tftp_session_stats stats; + void (*stats_callback)(struct tftp_session_stats *); + bool valid_options_required; + bool options_acknowledged; + struct tftp_option options[TFTP_OPTION_TOTAL_OPTIONS]; + char options_storage[tftp_request_packet_max_size - sizeof(enum tftp_opcode)]; + char *options_str; + size_t options_str_size; + + struct block_info { + size_t current_size; + size_t previous_size; + } next_data_block_info; +}; + +void tftp_handler_init(struct tftp_handler handler[static 1], + struct uring_worker* worker, + const struct addrinfo server_addr[static 1], + const struct sockaddr_storage peer, + socklen_t peer_addrlen, + bool peer_demand_ipv4, + const char* root, + size_t n, + const char options[static n], + uint8_t retries, + uint8_t timeout, + void (*stats_callback)(struct tftp_session_stats *), + struct logger logger[static 1]); + +void tftp_handler_start(struct tftp_handler handler[static 1]); + +void tftp_handler_on_new_data(struct tftp_handler handler[static 1]); diff --git a/server/src/tftp_server.c b/server/src/tftp_server.c new file mode 100644 index 0000000..78bf644 --- /dev/null +++ b/server/src/tftp_server.c @@ -0,0 +1,484 @@ +#include "tftp_server.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tftp.h" + +#include "server_stats.h" +#include "tftp_handler.h" +#include "inet_utils.h" + +constexpr size_t msg_controllen = 4096; + +struct new_tftp_request_args { + struct tftp_server *server; + struct uring_worker *worker; + struct sockaddr_storage peer_addr; + socklen_t peer_addrlen; + ssize_t bytes_recvd; + bool is_orig_dest_addr_ipv4; + uint8_t buffer[tftp_request_packet_max_size]; +}; + +static bool listener_init(struct tftp_server server[static 1], const char *host, const char *service); + +static bool submit_metrics_timeout_request(struct tftp_server server[static 1]); +static void remove_metrics_timeout_request(struct tftp_server server[static 1]); +static void on_metrics_timeout(int32_t ret, void *args); +static void on_metrics_timeout_request_removed(int32_t ret, void *args); + +static void submit_on_data_available_request(struct tftp_server server[static 1]); +static void remove_on_data_available_request(struct tftp_server server[static 1]); +static void on_data_available(int32_t ret, void *args); +static void on_data_available_request_removed(int32_t ret, void *args); + +static void submit_on_new_tftp_request_to_worker(struct uring_worker worker[static 1], struct new_tftp_request_args new_tftp_request_args[static 1], struct logger logger[static 1]); +static void on_new_tftp_request(int32_t ret, void *args); + +bool tftp_server_init(struct tftp_server server[static 1], struct tftp_server_arguments args, struct logger logger[static 1]) { + *server = (struct tftp_server) { + .logger = logger, + .should_stop = false, + .thread_pool = nullptr, + .is_timeout_job_pending = false, + .timeout_job = { + .routine = on_metrics_timeout, + .args = server, + }, + .remove_timeout_job = { + .routine = on_metrics_timeout_request_removed, + .args = server, + }, + .is_on_data_available_job_pending = false, + .on_data_available_args = { + .server = server, + .peer_addrlen = sizeof server->on_data_available_args.peer_addr, + .iovec = { + { + .iov_base = server->on_data_available_args.buffer, + .iov_len = sizeof server->on_data_available_args.buffer + } + }, + }, + .data_available_job = { + .routine = on_data_available, + .args = &server->on_data_available_args, + }, + .remove_data_available_job = { + .routine = on_data_available_request_removed, + .args = server, + }, + .server_stats_callback = args.server_stats_callback, + .handler_stats_callback = args.handler_stats_callback, + .listener = -1, + .root = args.root, + .retries = args.retries, + .timeout = args.timeout_s, + .stats = tftp_server_stats_init((const struct sockaddr *) &server->addr_storage, + args.stats_interval_seconds, logger), + }; + server->on_data_available_args.msghdr = (struct msghdr) { + .msg_name = &server->on_data_available_args.peer_addr, + .msg_namelen = server->on_data_available_args.peer_addrlen, + .msg_iov = server->on_data_available_args.iovec, + .msg_iovlen = 1, + .msg_control = malloc(msg_controllen), + .msg_controllen = msg_controllen, + .msg_flags = MSG_CTRUNC + }; + if (server->on_data_available_args.msghdr.msg_control == nullptr) { + logger_log_error(logger, "Could not allocate memory for the server msghdr control buffer."); + return false; + } + server->thread_pool = malloc(sizeof *server->thread_pool + args.workers * sizeof *server->thread_pool->workers); + if (server->thread_pool == nullptr) { + logger_log_error(logger, "Not enough memory to start the server. Try closing other applications to free up memory or decrease the server workers."); + return false; + } + uring_thread_pool_init(server->thread_pool, args.workers, args.max_worker_sessions * 2, logger); + if (!listener_init(server, args.ip, args.port)) { + return false; + } + int ret = io_uring_queue_init(4, &server->ring, 0); + if (ret < 0) { + logger_log_error(logger, "Could not initialize the server io_uring %s.", -ret); + return false; + } + return true; +} + +void tftp_server_start(struct tftp_server server[static 1]) { + bool pending_requests_removed = false; + logger_log_info(server->logger, "Server starting."); + if (!submit_metrics_timeout_request(server)) { + exit(1); + } + submit_on_data_available_request(server); + while (!(server->should_stop && + io_uring_cq_ready(&server->ring) == 0 && + io_uring_sq_ready(&server->ring) == 0 && + !server->is_on_data_available_job_pending && + !server->is_timeout_job_pending)) { + struct io_uring_cqe *cqe; + errno = 0; + int ret = io_uring_wait_cqe(&server->ring, &cqe); + if (ret != -EINTR) { + if (ret < 0) { + logger_log_error(server->logger, "io_uring_wait_cqe failed: %s.", strerror(-ret)); + exit(1); + } + struct uring_job *job = io_uring_cqe_get_data(cqe); + if (job != nullptr) { + job->routine(cqe->res, job->args); + } + io_uring_cqe_seen(&server->ring, cqe); + } + if (server->should_stop && !pending_requests_removed) { + remove_metrics_timeout_request(server); + remove_on_data_available_request(server); + pending_requests_removed = true; + } + } + logger_log_info(server->logger, "Server stopped."); +} + +void tftp_server_destroy(struct tftp_server server[static 1]) { + logger_log_info(server->logger, "Server shutting down."); + uring_thread_pool_destroy(server->thread_pool); + free(server->thread_pool); + io_uring_queue_exit(&server->ring); + close(server->listener); + tftp_server_stats_destroy(&server->stats); +} + +static bool listener_init(struct tftp_server server[static 1], const char *host, const char *service) { + struct addrinfo *addrinfo_list = nullptr; + static const struct addrinfo hints = { + .ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_ADDRCONFIG, + .ai_family = AF_INET6, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = 0, + .ai_addrlen = 0, + .ai_addr = nullptr, + .ai_canonname = nullptr, + .ai_next = nullptr + }; + int gai_ret = getaddrinfo(host, service, &hints, &addrinfo_list); + if (gai_ret) { + logger_log_error(server->logger, "Could not translate the host and service required to a valid internet address. %s", gai_strerror(gai_ret)); + if (addrinfo_list != nullptr) { + freeaddrinfo(addrinfo_list); + } + return false; + } + for (struct addrinfo *addrinfo = addrinfo_list; addrinfo != nullptr; addrinfo = addrinfo->ai_next) { + char bind_addr_str[INET6_ADDRSTRLEN] = {}; + uint16_t bind_addr_port; + server->listener = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol); + if (server->listener == -1) { + logger_log_error(server->logger, "Could not create a socket for the server. %s", strerror(errno)); + goto fail; + } + if (setsockopt(server->listener, SOL_SOCKET, SO_REUSEADDR, &(int) {true}, sizeof(int)) == -1 || + setsockopt(server->listener, IPPROTO_IP, IP_RECVORIGDSTADDR, &(int) {true}, sizeof(int)) == -1 || + setsockopt(server->listener, IPPROTO_IPV6, IPV6_RECVORIGDSTADDR, &(int) {true}, sizeof(int)) == -1 || + setsockopt(server->listener, IPPROTO_IPV6, IPV6_V6ONLY, &(int) {false}, sizeof(int)) == -1) { + logger_log_error(server->logger, "Could not set listener socket options for the server. %s", strerror(errno)); + goto fail2; + } + if (inet_ntop_address(addrinfo->ai_addr, bind_addr_str, &bind_addr_port) == nullptr) { + logger_log_error(server->logger, "Could not translate binding address to a string representation. %s", strerror(errno)); + goto fail2; + } + logger_log_debug(server->logger, "Trying to bind to host %s on port %hu", bind_addr_str, bind_addr_port); + if (bind(server->listener, addrinfo->ai_addr, addrinfo->ai_addrlen)) { + logger_log_debug(server->logger, "Can't bind to this address. %s", strerror(errno)); + close(server->listener); + continue; + } + logger_log_info(server->logger, "Server is listening on %s port %hu", bind_addr_str, bind_addr_port); + memcpy(&server->addr_storage, addrinfo->ai_addr, addrinfo->ai_addrlen); + server->addrinfo = (struct addrinfo) { + .ai_addr = (struct sockaddr *) &server->addr_storage, + .ai_addrlen = addrinfo->ai_addrlen, + .ai_family = addrinfo->ai_family, + .ai_socktype = addrinfo->ai_socktype, + .ai_flags = addrinfo->ai_flags, + .ai_protocol = addrinfo->ai_protocol, + .ai_next = nullptr, + .ai_canonname = nullptr, + }; + freeaddrinfo(addrinfo_list); + return true; + } + logger_log_error(server->logger, "Failed to bind server to any address"); + if (server->listener == -1) { + goto fail; + } +fail2: + close(server->listener); +fail: + freeaddrinfo(addrinfo_list); + return false; +} + +static bool submit_metrics_timeout_request(struct tftp_server server[static 1]) { + if (server->server_stats_callback == nullptr) { + logger_log_warn(server->logger, "No callback specified for server statistics logging, will continue without."); + return true; + } + struct io_uring_sqe *sqe = io_uring_get_sqe(&server->ring); + if (sqe == nullptr) { + logger_log_error(server->logger, "Could not get a submission queue entry."); + return false; + } + io_uring_prep_timeout(sqe, &(struct __kernel_timespec){.tv_sec = server->stats.interval}, 0, 0); + io_uring_sqe_set_data(sqe, &server->timeout_job); + int ret = io_uring_submit(&server->ring); + if (ret < 0) { + logger_log_error(server->logger, "Could not submit the timeout request. %s", strerror(-ret)); + return false; + } + server->is_timeout_job_pending = true; + logger_log_debug(server->logger, "Starting the metrics callback in %ds.", server->stats.interval); + return true; +} + +static void remove_metrics_timeout_request(struct tftp_server server[static 1]) { + if (server->server_stats_callback == nullptr) { + return; + } + struct io_uring_sqe *sqe = io_uring_get_sqe(&server->ring); + if (sqe == nullptr) { + logger_log_error(server->logger, "Could not get a submission queue entry."); + exit(1); + } + io_uring_prep_timeout_remove(sqe, (size_t) &server->timeout_job, 0); + io_uring_sqe_set_data(sqe, &server->remove_timeout_job); + int ret = io_uring_submit(&server->ring); + if (ret < 0) { + logger_log_error(server->logger, "Could not remove the metrics callback request. %s", strerror(-ret)); + exit(1); + } + logger_log_debug(server->logger, "Removing the metrics callback request."); +} + +static void on_metrics_timeout(int32_t ret, void *args) { + struct tftp_server *server = args; + server->is_timeout_job_pending = false; + if (ret < 0) { + switch (-ret) { + case ECANCELED: + logger_log_debug(server->logger, "Server metrics callback request canceled."); + return; + case ETIME: + break; + default: + logger_log_error(server->logger, "Timeout operation failed. %s.", strerror(-ret)); + exit(1); + } + } + logger_log_debug(server->logger, "Running the metrics callback."); + server->server_stats_callback(&server->stats); + submit_metrics_timeout_request(server); +} + +static void on_metrics_timeout_request_removed(int32_t ret, void *args) { + struct tftp_server *server = args; + server->is_timeout_job_pending = false; +} + +static void submit_on_data_available_request(struct tftp_server server[static 1]) { + server->on_data_available_args.msghdr.msg_controllen = msg_controllen; + server->on_data_available_args.msghdr.msg_flags = MSG_CTRUNC; + for (size_t i = 0; i < msg_controllen; i++) { + ((char *)server->on_data_available_args.msghdr.msg_control)[i] = 0; + } + struct io_uring_sqe *sqe = io_uring_get_sqe(&server->ring); + if (sqe == nullptr) { + logger_log_error(server->logger, "Could not get a submission queue entry."); + exit(1); + } + io_uring_prep_recvmsg(sqe, server->listener, &server->on_data_available_args.msghdr, MSG_PEEK | MSG_TRUNC); + io_uring_sqe_set_data(sqe, &server->data_available_job); + int ret = io_uring_submit(&server->ring); + if (ret < 0) { + logger_log_error(server->logger, "Could not submit the on data available request. %s", strerror(-ret)); + exit(1); + } + server->is_on_data_available_job_pending = true; +} + +static void remove_on_data_available_request(struct tftp_server server[static 1]) { + if (server->is_on_data_available_job_pending == false) { + return; + } + struct io_uring_sqe *sqe = io_uring_get_sqe(&server->ring); + if (sqe == nullptr) { + logger_log_error(server->logger, "Could not get a submission queue entry."); + exit(1); + } + io_uring_prep_cancel(sqe, &server->data_available_job, 0); + io_uring_sqe_set_data(sqe, nullptr); + io_uring_submit(&server->ring); + logger_log_debug(server->logger, "Removing the server on data available request."); +} + +static void on_data_available(int32_t ret, void *args) { + struct tftp_server_on_new_data_available_args* on_data_args = args; + struct tftp_server *server = on_data_args->server; + bool is_orig_dest_addr_ipv4 = false; + ssize_t bytes_recvd; + server->is_on_data_available_job_pending = false; + if (ret < 0) { + if (-ret == ECANCELED) { + logger_log_debug(server->logger, "Server on data available request canceled."); + return; + } + logger_log_error(server->logger, "Async recvmsg operation failed. %s", strerror(-ret)); + exit(1); + } + if (on_data_args->msghdr.msg_flags & MSG_TRUNC) { + logger_log_warn(server->logger, "Received size greater than maximum request size, ignoring request."); + submit_on_data_available_request(server); + return; + } + if (on_data_args->msghdr.msg_flags & MSG_CTRUNC) { + logger_log_warn(server->logger, "Received size greater than maximum ancillary buffer size allowed, ignoring request."); + submit_on_data_available_request(server); + return; + } + switch (on_data_args->peer_addr.ss_family) { + case AF_INET: + on_data_args->peer_addrlen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + on_data_args->peer_addrlen = sizeof(struct sockaddr_in6); + break; + default: + logger_log_warn(server->logger, "Received request from unsupported address family, ignoring request."); + submit_on_data_available_request(server); + return; + } + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&on_data_args->msghdr); cmsg != nullptr; cmsg = CMSG_NXTHDR(&on_data_args->msghdr, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_ORIGDSTADDR) { + is_orig_dest_addr_ipv4 = true; + break; + } + } + bytes_recvd = recvmsg(server->listener, &on_data_args->msghdr, 0); + if (bytes_recvd == -1) { + logger_log_error(server->logger, "%s", strerror(errno)); + exit(1); + } + struct uring_worker *worker = uring_thread_pool_get_least_busy_worker(server->thread_pool); + sem_wait(&worker->available_slots); + submit_on_data_available_request(server); + struct new_tftp_request_args *new_tftp_request_args = malloc(sizeof *new_tftp_request_args); + if (new_tftp_request_args == nullptr) { + logger_log_error(server->logger, "Could not allocate memory to handle the request, ignoring request."); + return; + } + *new_tftp_request_args = (struct new_tftp_request_args) { + .server = server, + .peer_addr = on_data_args->peer_addr, + .peer_addrlen = on_data_args->peer_addrlen, + .bytes_recvd = bytes_recvd, + .is_orig_dest_addr_ipv4 = is_orig_dest_addr_ipv4, + }; + memcpy(new_tftp_request_args->buffer, on_data_args->buffer, bytes_recvd); + submit_on_new_tftp_request_to_worker(worker, new_tftp_request_args, server->logger); +} + +static void on_data_available_request_removed(int32_t ret, void *args) { + struct tftp_server *server = args; + server->is_on_data_available_job_pending = false; +} + +static void submit_on_new_tftp_request_to_worker(struct uring_worker worker[static 1], struct new_tftp_request_args new_tftp_request_args[static 1], struct logger logger[static 1]) { + struct io_uring_sqe *sqe = io_uring_get_sqe(&worker->ring); + if (sqe == nullptr) { + logger_log_error(logger, "Could not get a submission queue entry."); + exit(1); + } + struct uring_job *job = malloc(sizeof *job); + if (job == nullptr) { + logger_log_error(logger, "Not enough memory: %s", strerror(errno)); + exit(1); + } + new_tftp_request_args->worker = worker; + *job = (struct uring_job) { + .routine = on_new_tftp_request, + .args = new_tftp_request_args, + }; + io_uring_prep_nop(sqe); + io_uring_sqe_set_data(sqe, job); + io_uring_submit(&worker->ring); +} + +static void on_new_tftp_request(int32_t ret, void *args) { + struct new_tftp_request_args *request_args = args; + struct tftp_server *server = request_args->server; + uint16_t code; + // We use memcpy since loading after casting to (uint16_t *) without 2 byte alignment is UB + memcpy(&code, request_args->buffer, sizeof code); + code = ntohs(code); + if (code != TFTP_OPCODE_RRQ) { + logger_log_warn(server->logger, "Unexpected TFTP opcode %d, expected %d.", code, TFTP_OPCODE_RRQ); + return; + } + size_t token_length = 0; + size_t option_size = request_args->bytes_recvd - 2; + const char *option_ptr = (const char *) &request_args->buffer[2]; + const char *end_ptr = (const char *) (&option_ptr[option_size - 1]); + while (option_ptr != nullptr && option_ptr < end_ptr) { + option_ptr = memchr(option_ptr, '\0', end_ptr - option_ptr + 1); + if (option_ptr != nullptr) { + token_length++; + option_ptr++; + } + } + option_ptr = (const char *) &request_args->buffer[2]; + if (token_length < 2 || token_length % 2 != 0) { + logger_log_warn(server->logger, "Received ill formed packet, ignoring (tokens length: %zu).", token_length); + return; + } + char *options = malloc(option_size); + if (options == nullptr) { + logger_log_error(server->logger, "Could not allocate memory to handle the request, ignoring request."); + return; + } + memcpy(options, option_ptr, option_size); + struct tftp_handler *handler = malloc(sizeof(struct tftp_handler)); + if (handler == nullptr) { + logger_log_error(server->logger, "Could not allocate memory to handle the request, ignoring request."); + return; + } + tftp_handler_init(handler, + request_args->worker, + &server->addrinfo, + request_args->peer_addr, + request_args->peer_addrlen, + request_args->is_orig_dest_addr_ipv4, + server->root, + option_size, + options, + server->retries, + server->timeout, + server->handler_stats_callback, + server->logger); + free(options); + free(request_args); + mtx_lock(&server->stats.counters_mtx); + server->stats.counters[TFTP_SERVER_STATS_SESSIONS_COUNT]++; + mtx_unlock(&server->stats.counters_mtx); + tftp_handler_start(handler); +} diff --git a/server/src/tftp_server.h b/server/src/tftp_server.h new file mode 100644 index 0000000..ffc62a9 --- /dev/null +++ b/server/src/tftp_server.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "logger.h" +#include "server_stats.h" +#include "session_stats.h" +#include "uring_thread_pool.h" + +//How many seconds to aggregate before sampling datapoints +constexpr int datapoints_interval_seconds = 60; + +struct tftp_server_on_new_data_available_args { + struct tftp_server *server; + struct sockaddr_storage peer_addr; + socklen_t peer_addrlen; + struct iovec iovec[1]; + struct msghdr msghdr; + uint8_t buffer[tftp_request_packet_max_size]; +}; + +struct tftp_server { + struct logger *logger; + volatile sig_atomic_t should_stop; // volatile sig_atomic_t is used instead of atomic_bool for N3220 5.1.2.4/5 since it's implementation-defined whether the type is lock-free + struct uring_thread_pool *thread_pool; + struct io_uring ring; + bool is_timeout_job_pending; + struct uring_job timeout_job; + struct uring_job remove_timeout_job; + bool is_on_data_available_job_pending; + struct tftp_server_on_new_data_available_args on_data_available_args; + struct uring_job data_available_job; + struct uring_job remove_data_available_job; + struct sockaddr_storage addr_storage; + struct addrinfo addrinfo; + int listener; + const char *root; + uint8_t retries; + uint8_t timeout; + struct server_stats stats; + void (*server_stats_callback)(struct server_stats *); + void (*handler_stats_callback)(struct tftp_session_stats *); +}; + +struct tftp_server_arguments { + char *ip; + char *port; + const char *root; + uint8_t retries; + uint8_t timeout_s; + uint32_t workers; + uint16_t max_worker_sessions; + void (*server_stats_callback)(struct server_stats *); + void (*handler_stats_callback)(struct tftp_session_stats *); + int stats_interval_seconds; +}; + +bool tftp_server_init(struct tftp_server server[static 1], struct tftp_server_arguments args, struct logger logger[static 1]); + +void tftp_server_start(struct tftp_server server[static 1]); + +static inline void tftp_server_stop(struct tftp_server server[static 1]) { + server->should_stop = true; +} + +void tftp_server_destroy(struct tftp_server server[static 1]); diff --git a/server/src/uring_thread_pool.c b/server/src/uring_thread_pool.c new file mode 100644 index 0000000..49fdbda --- /dev/null +++ b/server/src/uring_thread_pool.c @@ -0,0 +1,106 @@ +#include "uring_thread_pool.h" + +#include +#include + +#include +#include + +static int worker_routine(struct uring_worker *worker); + +void uring_thread_pool_init(struct uring_thread_pool *pool, size_t workers_number, size_t worker_sq_size, struct logger logger[static 1]) { + *pool = (struct uring_thread_pool) { + .logger = logger, + .shutdown = false, + .workers_number = workers_number, + .worker_sq_size = worker_sq_size, + }; + for (size_t i = 0; i < workers_number; i++) { + int ret = sem_init(&pool->workers[i].available_slots, 0, pool->worker_sq_size / 2); + if (ret == -1) { + logger_log_error(pool->logger, "Could not initialize the handler semaphore. %s", strerror(errno)); + exit(EXIT_FAILURE); + } + ret = io_uring_queue_init(worker_sq_size, &pool->workers[i].ring, 0); + if (ret < 0) { + exit(EXIT_FAILURE); + } + struct io_uring_sqe *sqe = io_uring_get_sqe(&pool->workers[i].ring); + if (sqe == nullptr) { + exit(EXIT_FAILURE); + } + io_uring_prep_nop(sqe); + io_uring_sqe_set_data(sqe, pool); + io_uring_submit(&pool->workers[i].ring); + thrd_create(&pool->workers[i].worker, (thrd_start_t) worker_routine, &pool->workers[i]); + } +} + +// TODO: add an option to empty the queue (i.e. bool cancel_futures) +int uring_thread_pool_destroy(struct uring_thread_pool *pool) { + pool->shutdown = true; + for (size_t i = 0; i < pool->workers_number; i++) { + // wake up workers + struct io_uring_sqe *sqe = io_uring_get_sqe(&pool->workers[i].ring); + io_uring_prep_nop(sqe); + io_uring_sqe_set_data(sqe, nullptr); + io_uring_submit(&pool->workers[i].ring); + } + for (size_t i = 0; i < pool->workers_number; i++) { + thrd_join(pool->workers[i].worker, nullptr); + io_uring_queue_exit(&pool->workers[i].ring); + } + return 0; +} + +// TODO implement a reasonable load balancing strategy +struct uring_worker *uring_thread_pool_get_least_busy_worker(struct uring_thread_pool *pool) { + size_t least_busy_worker = 0; + size_t least_busy_worker_queue_size = SIZE_MAX; + for (size_t i = 0; i < pool->workers_number; i++) { + int sem_value; + sem_getvalue(&pool->workers[i].available_slots, &sem_value); + size_t busy_queue_size = pool->worker_sq_size - sem_value * 2; + if (busy_queue_size == 0) { + least_busy_worker = i; + break; + } + if (busy_queue_size < least_busy_worker_queue_size) { + least_busy_worker = i; + least_busy_worker_queue_size = busy_queue_size; + } + } + return &pool->workers[least_busy_worker]; +} + +// TODO: io_uring_wait_cqe ret < 0 +static int worker_routine(struct uring_worker *worker) { + struct uring_thread_pool *pool = nullptr; + struct io_uring_cqe *cqe = nullptr; + int ret = io_uring_wait_cqe(&worker->ring, &cqe); + if (ret < 0) { + logger_log_error(pool->logger, "io_uring_wait_cqe failed: %s", strerror(-ret)); + exit(1); + } + pool = (struct uring_thread_pool *) io_uring_cqe_get_data(cqe); + io_uring_cqe_seen(&worker->ring, cqe); + // size_t worker_id = worker - pool->workers; + while (true) { + if (pool->shutdown) { + break; + } + do { + ret = io_uring_wait_cqe(&worker->ring, &cqe); + } while (ret == -EINTR); + if (ret < 0) { + logger_log_error(pool->logger, "io_uring_wait_cqe failed: %s", strerror(-ret)); + exit(1); + } + struct uring_job *job = io_uring_cqe_get_data(cqe); + if (job != nullptr) { + job->routine(cqe->res, job->args); + } + io_uring_cqe_seen(&worker->ring, cqe); + } + return 0; +} diff --git a/server/src/uring_thread_pool.h b/server/src/uring_thread_pool.h new file mode 100644 index 0000000..fee9cb8 --- /dev/null +++ b/server/src/uring_thread_pool.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +#include "logger.h" +#include + +// TODO remove logger from uring_thread_pool + +struct uring_job { + void (*routine)(int32_t aio_result, void *); + void* args; +}; + +struct uring_worker { + thrd_t worker; + struct io_uring ring; + sem_t available_slots; +}; + +struct uring_thread_pool { + struct logger *logger; + atomic_bool shutdown; + size_t workers_number; + size_t worker_sq_size; + struct uring_worker workers[]; +}; + +void uring_thread_pool_init(struct uring_thread_pool *pool, size_t workers_number, size_t worker_sq_size, struct logger logger[static 1]); + +int uring_thread_pool_destroy(struct uring_thread_pool *pool); + +struct uring_worker *uring_thread_pool_get_least_busy_worker(struct uring_thread_pool *pool); diff --git a/server/test/CMakeLists.txt b/server/test/CMakeLists.txt index 8ba66b4..ca757c9 100644 --- a/server/test/CMakeLists.txt +++ b/server/test/CMakeLists.txt @@ -1,7 +1,11 @@ find_package(buracchi-cutest CONFIG REQUIRED) -add_executable(test_acceptor "test_acceptor.c") -target_link_libraries(test_acceptor PRIVATE server_obj) -target_link_libraries(test_acceptor PRIVATE buracchi::cutest::cutest buracchi::cutest::cutest_main) -target_link_libraries(test_acceptor INTERFACE coverage_config) -cutest_discover_tests(test_acceptor) +add_executable(test_server "test.c") +target_link_libraries(test_server INTERFACE coverage_config) +target_link_libraries(test_server PRIVATE buracchi::cutest::cutest buracchi::cutest::cutest_main) +target_link_libraries(test_server PRIVATE $) +target_link_libraries(test_server PRIVATE logger) +target_link_libraries(test_server PRIVATE network_utils) +target_link_libraries(test_server PRIVATE tftp) +target_link_libraries(test_server PRIVATE PkgConfig::liburing) +cutest_discover_tests(test_server) diff --git a/server/test/test.c b/server/test/test.c new file mode 100644 index 0000000..1c50df2 --- /dev/null +++ b/server/test/test.c @@ -0,0 +1,31 @@ +#include + +TEST(test, dummy) { + ASSERT_EQ(0, 0); +} + +// testRRQ +// Send an RRQ packet with a filename, mode, and some options to the server. +// Asserts that the server correctly parses the RRQ and that the handler's attributes (addr, peer, path, and options) are set as expected. +// Example [path: ("some_file"); options: ("mode": "binascii", "opt1_key": "opt1_val", "default_timeout": 100, "retries": 200)]. + +// testTimer +// Verifies that the server's statistics callback is invoked after a timer is started. +// Assert that the statistics callback is triggered and executed within the wait period. + +// testTimerNoCallBack +// Runs the server with no callback provided for statistics. +// Assert that no callback is executed. + +// testUnexpectedOpsCode +// Send a packet with an unexpected or unknown opcode and checks how the server responds to it. +// Assert that the server does not create a handler for this invalid opcode. +// Assert that the server does not crash and correctly ignores the invalid opcode. + +//TEST(acceptor, can_bound_to_host) + +//TEST(acceptor, receive_test_packet) + +//TEST(acceptor, support_ipv4_requests) + +//TEST(acceptor, support_ipv6_requests) diff --git a/server/test/test_acceptor.c b/server/test/test_acceptor.c deleted file mode 100644 index b9d57f4..0000000 --- a/server/test/test_acceptor.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include - -#include "acceptor.h" - -#define STR_(s) #s -#define STR(s) STR_(s) -#define DEFAULT_PORT 1234 -#define DATA_PKT_SIZE 512 - -TEST(acceptor, bound_to_host) { - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_OFF); - int result = 0; - struct acceptor acceptor = {}; - result |= acceptor_init(&acceptor, STR(DEFAULT_PORT)); - result |= acceptor_destroy(&acceptor); - ASSERT_EQ(result, 0); -} - -TEST(acceptor, receive_packet) { - cmn_logger_set_default_level(CMN_LOGGER_LOG_LEVEL_OFF); - struct acceptor acceptor = {}; - if (acceptor_init(&acceptor, STR(DEFAULT_PORT))) { - cutest_test_fail_(__func__, __FILE__, __LINE__, "Acceptor initialization failed."); - return; - } - - // Send a message to the acceptor - const char* message_sent = "Hello, acceptor!"; - evutil_socket_t s = socket(AF_INET, SOCK_DGRAM, 0); - if (s == EVUTIL_INVALID_SOCKET) { - cutest_test_fail_(__func__, __FILE__, __LINE__, "Socket initialization failed.."); - return; - } - struct sockaddr_in server_addr = { - .sin_family = AF_INET, - .sin_addr = { - .s_addr = htonl(INADDR_LOOPBACK), - }, - .sin_port = htons((DEFAULT_PORT)), - }; - if (sendto(s, message_sent, strlen(message_sent), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { - cutest_test_fail_(__func__, __FILE__, __LINE__, "Failed to send message."); - return; - } - - // receive message - char message_recv[DATA_PKT_SIZE + 1] = {0}; - evutil_socket_t client_socket = EVUTIL_INVALID_SOCKET; - struct acceptor_packet packet = {}; - if (!acceptor_accept(&acceptor, &client_socket, &packet)) { - cutest_test_fail_(__func__, __FILE__, __LINE__, "Failed to accept message."); - return; - } - - acceptor_destroy(&acceptor); - memcpy(message_recv, packet.data, packet.payload_size); - ASSERT_STREQ(message_sent, message_recv); -} diff --git a/tftp/CMakeLists.txt b/tftp/CMakeLists.txt new file mode 100644 index 0000000..a44c0ce --- /dev/null +++ b/tftp/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(tftp STATIC "src/tftp.c") +target_include_directories(tftp PUBLIC + "$" + "$") +target_link_libraries(tftp INTERFACE $) +target_link_libraries(tftp PRIVATE network_utils) + +if (BUILD_TESTS) + add_subdirectory("test") +endif () diff --git a/tftp/include/tftp.h b/tftp/include/tftp.h new file mode 100644 index 0000000..0b2ab71 --- /dev/null +++ b/tftp/include/tftp.h @@ -0,0 +1,174 @@ +#pragma once + +#include +#include +#include + +constexpr size_t tftp_request_packet_max_size = 512; +constexpr uint16_t tftp_default_blksize = 512; +constexpr uint16_t tftp_default_window_size = 1; + +enum tftp_mode { + TFTP_MODE_OCTET, + TFTP_MODE_NETASCII, + TFTP_MODE_INVALID +}; + +enum tftp_opcode : uint16_t { + TFTP_OPCODE_RRQ = 0x01, + TFTP_OPCODE_WRQ = 0x02, + TFTP_OPCODE_DATA = 0x03, + TFTP_OPCODE_ACK = 0x04, + TFTP_OPCODE_ERROR = 0x05, + TFTP_OPCODE_OACK = 0x06, +}; + +enum tftp_error_code : uint16_t { + TFTP_ERROR_NOT_DEFINED = 0x00, + TFTP_ERROR_FILE_NOT_FOUND = 0x01, + TFTP_ERROR_ACCESS_VIOLATION = 0x02, + TFTP_ERROR_DISK_FULL = 0x03, + TFTP_ERROR_ILLEGAL_OPERATION = 0x04, + TFTP_ERROR_UNKNOWN_TRANSFER_ID = 0x05, + TFTP_ERROR_FILE_ALREADY_EXISTS = 0x06, + TFTP_ERROR_NO_SUCH_USER = 0x07, + TFTP_ERROR_INVALID_OPTIONS = 0x08, +}; + +struct [[gnu::packed]] tftp_packet { +enum tftp_opcode opcode; +union { + uint8_t rrq[510]; + uint8_t wrq[510]; + struct [[gnu::packed]] { + uint16_t block_number; + uint8_t data[512]; + } data; + struct [[gnu::packed]] { + uint16_t block_number; + } ack; + struct [[gnu::packed]] { + uint16_t error_code; + uint8_t error_message[512]; + } error; +}; +}; + +struct [[gnu::packed]] tftp_rrq_packet { + enum tftp_opcode opcode; + uint8_t padding[510]; +}; + +struct [[gnu::packed]] tftp_ack_packet { + enum tftp_opcode opcode; + uint16_t block_number; +}; + +struct [[gnu::packed]] tftp_data_packet { + enum tftp_opcode opcode; + uint16_t block_number; + uint8_t data[]; +}; + +struct [[gnu::packed]] tftp_oack_packet { + enum tftp_opcode opcode; + unsigned char options_values[]; +}; + +struct [[gnu::packed]] tftp_error_packet { + const enum tftp_opcode opcode; + const enum tftp_error_code error_code; + const uint8_t error_message[]; +}; + +struct tftp_error_packet_info { + const struct tftp_error_packet *packet; + size_t size; +}; + +/** + * The error packet information for each default error packets. + * The index of the array is the error code. + * The error packet information for TFTP_ERROR_NOT_DEFINED is not defined. + * Use @see tftp_encode_error to send TFTP_ERROR_NOT_DEFINED code + * packets or to send error packets with custom error messages. + */ +extern const struct tftp_error_packet_info tftp_error_packet_info[]; + +enum tftp_request_type { + TFTP_REQUEST_READ, + TFTP_REQUEST_WRITE, + TFT_REQUEST_INVALID +}; + +enum tftp_create_rqp_error { + TFTP_CREATE_RQP_SUCCESS, + TFTP_CREATE_RQP_INVALID_MODE, + TFTP_CREATE_RQP_TRUNCATED_FILENAME, +}; + +enum tftp_request_error { + TFTP_REQUEST_ERROR_MAX_SIZE_EXCEEDED, + TFTP_REQUEST_ERROR_NOT_A_REQUEST, + TFTP_REQUEST_ERROR_INVALID_FILENAME, + TFTP_REQUEST_ERROR_INVALID_MODE, +}; + +struct tftp_request { + enum tftp_request_type type; + union { + struct { + const char *filename; + enum tftp_mode mode; + }; + enum tftp_request_error error; + }; +}; + +constexpr size_t tftp_option_string_max_size = tftp_request_packet_max_size - sizeof(enum tftp_opcode) - 4; +constexpr size_t tftp_option_formatted_string_max_size = tftp_option_string_max_size * 2 - 1; + +enum tftp_option_recognized { + TFTP_OPTION_BLKSIZE, + TFTP_OPTION_TIMEOUT, + TFTP_OPTION_TSIZE, + TFTP_OPTION_WINDOWSIZE, + TFTP_OPTION_TOTAL_OPTIONS +}; + +extern const char *tftp_option_recognized_string[TFTP_OPTION_TOTAL_OPTIONS]; + +struct tftp_option { + bool is_active; + const char *value; +}; + +extern enum tftp_create_rqp_error tftp_encode_request(struct tftp_packet packet[1], + size_t psize[1], enum tftp_request_type type, + size_t n, const char filename[static n], + enum tftp_mode mode); + +extern bool tftp_parse_packet(struct tftp_packet packet[static 1], size_t n, const uint8_t data[static n]); + +extern struct tftp_request tftp_decode_request(size_t n, const uint8_t data[n]); + +extern struct tftp_packet tftp_encode_error(enum tftp_error_code error_code, size_t n, char error_message[static n]); + +extern struct tftp_packet tftp_encode_data(uint16_t block_number, size_t n, const uint8_t data[n], + size_t *packet_size); + +extern struct tftp_packet tftp_encode_ack(uint16_t block_number); + +void tftp_format_option_strings(size_t n, const char options[static n], char formatted_options[static tftp_option_formatted_string_max_size]); + +void tftp_format_options(struct tftp_option options[4], char formatted_options[static tftp_option_formatted_string_max_size]); + +void tftp_parse_options(struct tftp_option options[static TFTP_OPTION_TOTAL_OPTIONS], size_t n, const char str[static n]); + +extern const char *tftp_mode_to_string(enum tftp_mode mode); + +ssize_t tftp_rrq_packet_init(struct tftp_rrq_packet packet[static 1], + size_t n, + const char filename[static n], + enum tftp_mode mode, + struct tftp_option options[static TFTP_OPTION_TOTAL_OPTIONS]); diff --git a/tftp/src/tftp.c b/tftp/src/tftp.c new file mode 100644 index 0000000..679640f --- /dev/null +++ b/tftp/src/tftp.c @@ -0,0 +1,351 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * While being approved for C23, as of the time of writing, N3022 is still unsupported by any compiler I know. + * The following code is a workaround for the lack of support of the header. + */ +#include + +#ifndef TFTP_H16TOBE16 +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define TFTP_H16TOBE16(n) ((((n) >> 8) & 0xFFu) | (((n) << 8) & 0xFF00u)) +# elif __BYTE_ORDER == __BIG_ENDIAN +# define define TFTP_H16TOBE16(n) (n) +# else +# error "Unsupported architecture. Define a TFTP_H16TOBE16 macro for your architecture in order to use this library." +# endif +#endif + +#define TFTP_ERROR_NOT_DEFINED_MESSAGE "Not defined, see error message (if any)." +#define TFTP_ERROR_FILE_NOT_FOUND_MESSAGE "File not found." +#define TFTP_ERROR_ACCESS_VIOLATION_MESSAGE "Access violation." +#define TFTP_ERROR_DISK_FULL_MESSAGE "Disk full or allocation exceeded." +#define TFTP_ERROR_ILLEGAL_OPERATION_MESSAGE "Illegal TFTP operation." +#define TFTP_ERROR_UNKNOWN_TRANSFER_ID_MESSAGE "Unknown transfer ID." +#define TFTP_ERROR_FILE_ALREADY_EXISTS_MESSAGE "File already exists." +#define TFTP_ERROR_NO_SUCH_USER_MESSAGE "No such user." +#define TFTP_ERROR_INVALID_OPTIONS_MESSAGE "Invalid options." + +#define TFTP_ERROR_PACKET_INFO_INIT(err_code) { \ + .packet = (struct tftp_error_packet*)&(struct { \ + const enum tftp_opcode opcode; \ + const enum tftp_error_code error_code; \ + const uint8_t error_message[sizeof(err_code##_MESSAGE)]; \ + }) { \ + .opcode = TFTP_H16TOBE16(TFTP_OPCODE_ERROR), \ + .error_code = TFTP_H16TOBE16(err_code), \ + .error_message = err_code##_MESSAGE \ + }, \ + .size = sizeof(struct tftp_error_packet) + sizeof(err_code##_MESSAGE), \ +} + +const struct tftp_error_packet_info tftp_error_packet_info[] = { + [TFTP_ERROR_NOT_DEFINED] = {.packet = nullptr, .size = 0}, + [TFTP_ERROR_FILE_NOT_FOUND] = TFTP_ERROR_PACKET_INFO_INIT(TFTP_ERROR_FILE_NOT_FOUND), + [TFTP_ERROR_ACCESS_VIOLATION] = TFTP_ERROR_PACKET_INFO_INIT(TFTP_ERROR_ACCESS_VIOLATION), + [TFTP_ERROR_DISK_FULL] = TFTP_ERROR_PACKET_INFO_INIT(TFTP_ERROR_DISK_FULL), + [TFTP_ERROR_ILLEGAL_OPERATION] = TFTP_ERROR_PACKET_INFO_INIT(TFTP_ERROR_ILLEGAL_OPERATION), + [TFTP_ERROR_UNKNOWN_TRANSFER_ID] = TFTP_ERROR_PACKET_INFO_INIT(TFTP_ERROR_UNKNOWN_TRANSFER_ID), + [TFTP_ERROR_FILE_ALREADY_EXISTS] = TFTP_ERROR_PACKET_INFO_INIT(TFTP_ERROR_FILE_ALREADY_EXISTS), + [TFTP_ERROR_NO_SUCH_USER] = TFTP_ERROR_PACKET_INFO_INIT(TFTP_ERROR_NO_SUCH_USER), + [TFTP_ERROR_INVALID_OPTIONS] = TFTP_ERROR_PACKET_INFO_INIT(TFTP_ERROR_INVALID_OPTIONS), +}; + +static const char *tftp_mode_str[] = { + [TFTP_MODE_OCTET] = "octet", + [TFTP_MODE_NETASCII] = "netascii", + [TFTP_MODE_INVALID] = "invalid" +}; + +extern const char *tftp_mode_to_string(enum tftp_mode mode) { + return tftp_mode_str[mode]; +} + +static enum tftp_mode tftp_get_mode(size_t n, const char mode[static n]); + +const char *tftp_option_recognized_string[] = { + [TFTP_OPTION_BLKSIZE] = "blksize", + [TFTP_OPTION_TIMEOUT] = "timeout", + [TFTP_OPTION_TSIZE] = "tsize", + [TFTP_OPTION_WINDOWSIZE] = "windowsize" +}; + +ssize_t tftp_rrq_packet_init(struct tftp_rrq_packet packet[static 1], size_t n, const char filename[static n], enum tftp_mode mode, struct tftp_option options[static TFTP_OPTION_TOTAL_OPTIONS]) { + packet->opcode = htons(TFTP_OPCODE_RRQ); + size_t filename_len = strnlen(filename, n); + size_t mode_len = strlen(tftp_mode_str[mode]); + if (filename_len == n || (filename_len + 1 + mode_len + 1) > sizeof packet->padding) { + return -1; + } + uint8_t *mode_ptr = memccpy(packet->padding, filename, '\0', n); + uint8_t *end_ptr = memccpy(mode_ptr, tftp_mode_str[mode], '\0', mode_len + 1); + for (enum tftp_option_recognized o = 0; o < TFTP_OPTION_TOTAL_OPTIONS; o++) { + if (options[o].is_active) { + size_t option_len = strlen(tftp_option_recognized_string[o]); + size_t value_len = strlen(options[o].value); + if ((option_len + 1 + value_len + 1) > (sizeof packet->padding - (end_ptr - packet->padding))) { + return -1; + } + end_ptr = memccpy(end_ptr, tftp_option_recognized_string[o], '\0', strlen(tftp_option_recognized_string[o]) + 1); + end_ptr = memccpy(end_ptr, options[o].value, '\0', strlen(options[o].value) + 1); + } + } + return (ssize_t) sizeof packet->opcode + (end_ptr - packet->padding); +} + +extern enum tftp_create_rqp_error tftp_encode_request(struct tftp_packet packet[1], size_t psize[1], enum tftp_request_type type, size_t n, const char filename[static n], enum tftp_mode mode) { + if (mode != TFTP_MODE_OCTET && mode != TFTP_MODE_NETASCII) { + return TFTP_CREATE_RQP_INVALID_MODE; + } + size_t filename_len = strnlen(filename, n); + size_t mode_len = strlen(tftp_mode_str[mode]); + if (filename_len == n || (filename_len + 1 + mode_len + 1) > 510) { + return TFTP_CREATE_RQP_TRUNCATED_FILENAME; + } + packet->opcode = htons(type == TFTP_REQUEST_READ ? TFTP_OPCODE_RRQ : TFTP_OPCODE_WRQ); + uint8_t *mode_ptr = memccpy(type == TFTP_REQUEST_READ ? packet->rrq : packet->wrq, filename, '\0', n); + uint8_t *end_ptr = memccpy(mode_ptr, tftp_mode_str[mode], '\0', mode_len + 1); + *psize = end_ptr - (uint8_t *) packet; + return TFTP_CREATE_RQP_SUCCESS; +} + +extern struct tftp_packet tftp_encode_error(enum tftp_error_code error_code, size_t n, char error_message[static n]) { + struct tftp_packet packet = { + .opcode = htons(TFTP_OPCODE_ERROR), + .error = { + .error_code = htons(error_code), + .error_message = {0} + } + }; + strncpy((char *) packet.error.error_message, error_message, n); + return packet; +} + +extern struct tftp_packet tftp_encode_data(uint16_t block_number, size_t n, const uint8_t data[n], + size_t *packet_size) { + struct tftp_packet packet = { + .opcode = htons(TFTP_OPCODE_DATA), + .data = { + .block_number = htons(block_number) + } + }; + memcpy(packet.data.data, data, n); + *packet_size = sizeof packet.opcode + sizeof packet.data.block_number + n; + return packet; +} + +extern struct tftp_packet tftp_encode_ack(uint16_t block_number) { + return (struct tftp_packet) { + .opcode = htons(TFTP_OPCODE_ACK), + .ack = { + .block_number = htons(block_number) + } + }; +} + +extern bool tftp_parse_packet(struct tftp_packet packet[static 1], size_t n, const uint8_t data[static n]) { + if (n < 4) { + // Packet is too small to contain an opcode and any meaningful field + return false; + } + enum tftp_opcode opcode = ntohs(*(uint16_t *) data); + const uint8_t *data_to_parse = data + sizeof packet->opcode; + size_t data_to_parse_size = n - sizeof packet->opcode; + switch (opcode) { + case TFTP_OPCODE_RRQ: + packet->opcode = opcode; + memcpy(packet->rrq, data_to_parse, data_to_parse_size); + break; + case TFTP_OPCODE_WRQ: + packet->opcode = opcode; + memcpy(packet->wrq, data_to_parse, data_to_parse_size); + break; + case TFTP_OPCODE_DATA: + packet->opcode = opcode; + packet->data.block_number = ntohs(*(uint16_t *) data_to_parse); + data_to_parse += sizeof packet->data.block_number; + data_to_parse_size -= sizeof packet->data.block_number; + memcpy(packet->data.data, data_to_parse, data_to_parse_size); + break; + case TFTP_OPCODE_ACK: + packet->opcode = opcode; + packet->ack.block_number = ntohs(*(uint16_t *) data_to_parse); + break; + case TFTP_OPCODE_ERROR: + if (n < 5) { + return false; + } + packet->opcode = opcode; + packet->error.error_code = ntohs(*(uint16_t *) data_to_parse); + data_to_parse += sizeof packet->error.error_code; + data_to_parse_size -= sizeof packet->error.error_code; + memcpy(packet->error.error_message, data_to_parse, data_to_parse_size); + break; + } + return true; +} + +extern struct tftp_request tftp_decode_request(size_t n, const uint8_t data[n]) { + if (n > tftp_request_packet_max_size) { + return (struct tftp_request) {.type = TFT_REQUEST_INVALID, .error = TFTP_REQUEST_ERROR_MAX_SIZE_EXCEEDED}; + } + enum tftp_opcode opcode = ntohs(*(uint16_t *) data); + if (opcode != TFTP_OPCODE_RRQ && opcode != TFTP_OPCODE_WRQ) { + return (struct tftp_request) {.type = TFT_REQUEST_INVALID, .error = TFTP_REQUEST_ERROR_NOT_A_REQUEST}; + } + const uint8_t *data_to_parse = data + sizeof opcode; + size_t data_to_parse_size = n - sizeof opcode; + const char *filename_ptr = (const char *) data_to_parse; + size_t filename_len = strnlen(filename_ptr, data_to_parse_size); + if (filename_len == 0 || filename_len > (data_to_parse_size - 3)) { // 2 zero bytes and at least 1 byte for mode + return (struct tftp_request) {.type = TFT_REQUEST_INVALID, .error = TFTP_REQUEST_ERROR_INVALID_FILENAME}; + } + data_to_parse += filename_len + 1; + data_to_parse_size -= filename_len + 1; + const char *mode_ptr = (const char *) data_to_parse; + size_t mode_len = strnlen(mode_ptr, data_to_parse_size); + if (mode_ptr[mode_len] != '\0') { + return (struct tftp_request) {.type = TFT_REQUEST_INVALID, .error = TFTP_REQUEST_ERROR_INVALID_MODE}; + } + enum tftp_mode mode = tftp_get_mode(mode_len + 1, mode_ptr); + if (mode == TFTP_MODE_INVALID) { + return (struct tftp_request) {.type = TFT_REQUEST_INVALID, .error = TFTP_REQUEST_ERROR_INVALID_MODE}; + } + return (struct tftp_request) { + .type = opcode == TFTP_OPCODE_RRQ ? TFTP_REQUEST_READ : TFTP_REQUEST_WRITE, + .filename = filename_ptr, + .mode = mode + }; +} + +// TODO: This function is locale dependent. Find out if it's a problem. +static enum tftp_mode tftp_get_mode(size_t n, const char mode[static n]) { + if (strncasecmp(mode, tftp_mode_str[TFTP_MODE_OCTET], n) == 0) { + return TFTP_MODE_OCTET; + } + if (strncasecmp(mode, tftp_mode_str[TFTP_MODE_NETASCII], n) == 0) { + return TFTP_MODE_NETASCII; + } + return TFTP_MODE_INVALID; +} + +void tftp_format_option_strings(size_t n, const char options[static n], char formatted_options[static tftp_option_formatted_string_max_size]) { + const char *option = options; + const char *value = option + strlen(option) + 1; + while (option < options + n) { + formatted_options += sprintf(formatted_options, "%s:%s", option, value); + option = value + strlen(value) + 1; + value = option + strlen(option) + 1; + if (option < options + n) { + formatted_options += sprintf(formatted_options, ", "); + } + } +} + +void tftp_format_options(struct tftp_option options[static TFTP_OPTION_TOTAL_OPTIONS], char formatted_options[static tftp_option_formatted_string_max_size]) { + formatted_options += sprintf(formatted_options, "["); + for (enum tftp_option_recognized o = 0; o < TFTP_OPTION_TOTAL_OPTIONS; o++) { + if (options[o].is_active) { + formatted_options += sprintf(formatted_options, "%s:%s", tftp_option_recognized_string[o], options[o].value); + for (enum tftp_option_recognized o_peek = o + 1; o_peek < TFTP_OPTION_TOTAL_OPTIONS; o_peek++) { + if (options[o_peek].is_active) { + formatted_options += sprintf(formatted_options, ", "); + break; + } + } + } + } + sprintf(formatted_options, "]"); +} + +void tftp_parse_options(struct tftp_option options[static TFTP_OPTION_TOTAL_OPTIONS], size_t n, const char str[static n]) { + const char *option = str; + const char *end_ptr = &str[n - 1]; + while (option < end_ptr) { + const char *val = &option[strlen(option) + 1]; + for (enum tftp_option_recognized o = 0; o < TFTP_OPTION_TOTAL_OPTIONS; o++) { + if (strcasecmp(option, tftp_option_recognized_string[o]) == 0) { + bool is_val_valid = true; + switch (o) { + case TFTP_OPTION_BLKSIZE: { + char *not_parsed; + errno = 0; + size_t blksize = strtoul(val, ¬_parsed, 10); + if (*val == '\0' || *not_parsed != '\0' || blksize < 8 || blksize > 65464) { + is_val_valid = false; + break; + } + while (isspace(*val) || *val == '+') { + val++; + } + break; + } + case TFTP_OPTION_TIMEOUT: { + char *not_parsed; + errno = 0; + unsigned long timeout = strtoul(val, ¬_parsed, 10); + if (*val == '\0' || *not_parsed != '\0' || timeout < 1 || timeout > 255) { + is_val_valid = false; + break; + } + while (isspace(*val) || *val == '+') { + val++; + } + break; + } + case TFTP_OPTION_TSIZE: { + if (strcmp(val, "0") == 0) { + val = calloc(1, 3 * sizeof(size_t) + 1); // TODO remove malloc + break; + } + char *not_parsed; + errno = 0; + unsigned long long size = strtoull(val, ¬_parsed, 10); + if (*val == '\0' || *not_parsed != '\0' || (size == ULLONG_MAX && errno == ERANGE)) { + is_val_valid = false; + break; + } + while (isspace(*val) || *val == '+') { + val++; + } + break; + } + case TFTP_OPTION_WINDOWSIZE: { + char *not_parsed; + errno = 0; + unsigned long window_size = strtoul(val, ¬_parsed, 10); + if (*val == '\0' || *not_parsed != '\0' || window_size < 1 || window_size > 65535) { + is_val_valid = false; + break; + } + while (isspace(*val) || *val == '+') { + val++; + } + break; + } + default: // unreachable + break; + } + if (!is_val_valid) { + break; + } + options[o] = (struct tftp_option) { + .is_active = true, + .value = val, + }; + break; + } + } + val = &option[strlen(option) + 1]; // TODO: remove after removing the malloc + option = &val[strlen(val) + 1]; + } +} diff --git a/tftp/test/CMakeLists.txt b/tftp/test/CMakeLists.txt new file mode 100644 index 0000000..1de901a --- /dev/null +++ b/tftp/test/CMakeLists.txt @@ -0,0 +1,7 @@ +find_package(buracchi-cutest CONFIG REQUIRED) + +add_executable(test_tftp "test.c") +target_link_libraries(test_tftp PRIVATE tftp) +target_link_libraries(test_tftp PRIVATE buracchi::cutest::cutest buracchi::cutest::cutest_main) +target_link_libraries(test_tftp INTERFACE coverage_config) +cutest_discover_tests(test_tftp) diff --git a/tftp/test/test.c b/tftp/test/test.c new file mode 100644 index 0000000..76fa72d --- /dev/null +++ b/tftp/test/test.c @@ -0,0 +1,5 @@ +#include + +TEST(tftp, dummy) { + ASSERT_EQ(0, 0); +} diff --git a/vcpkg.json b/vcpkg.json index 117aaba..3adaf16 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -5,7 +5,7 @@ "dependencies": [ "argparser", "libcommon", - "libevent" + "liburing" ], "overrides": [ { "name": "libevent", "version": "2.1.12#7" }