diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..980989d --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +Program/src/*.o +main +settings.json +SpaceProjects.odt +Program/src/incomplete_requests +Program/src/obj +Program/test/incomplete_requests +Program/test/temp + +*.o +*.a +TODO +Program/FreeRTOSv10.2.1 +Program/libcsp +.vscode* +*.jpg +*.jpeg +.gitignore + +give_packet_loss.sh +Program/libcsp +Program/__pycache__ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e507c8e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Program/libcsp"] + path = Program/libcsp + url = git@github.com:EvanGee/libcsp.git diff --git a/Documents/Design.pdf b/Documents/Design.pdf new file mode 100755 index 0000000..8bc8513 Binary files /dev/null and b/Documents/Design.pdf differ diff --git a/Documents/ImplementorsGuide.pdf b/Documents/ImplementorsGuide.pdf new file mode 100755 index 0000000..212c5a6 Binary files /dev/null and b/Documents/ImplementorsGuide.pdf differ diff --git a/Documents/MostRecentRatifications.pdf b/Documents/MostRecentRatifications.pdf new file mode 100755 index 0000000..ea3fb75 Binary files /dev/null and b/Documents/MostRecentRatifications.pdf differ diff --git a/Documents/SpaceProjects.odt b/Documents/SpaceProjects.odt new file mode 100755 index 0000000..3497308 Binary files /dev/null and b/Documents/SpaceProjects.odt differ diff --git a/Documents/mib.txt b/Documents/mib.txt new file mode 100755 index 0000000..41e8e1c --- /dev/null +++ b/Documents/mib.txt @@ -0,0 +1,80 @@ +The MIB is known as the management information base. +This base holds the configuration information for users (peers or applications). +This data is used for determining how and who we can send file or commands +to. Files will not be sent to users that are not included in the MIB. + +The MIB is currently configured as key value pairs. These pairs are formatted +in the JSON format. + +Here is an example of a MIB entry: +{ + "cfdp_id": 1, + "UT_address" : 2130706433, + "UT_port" : 1111, + "type_of_network" : 1, + "default_transmission_mode" : 1, + "MTU" : 1500, + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} + + +Below are the meanings of the fields for the MIB + +- cfdp_id + This is the unique identifier of a peer on the network. We can start it at 1 + and increment it from there. This is an unsigned integer (32 bit) value; + +- UT_address + This is an Underlying Transmission address. For example, in an IP stack, this + would be an IP4 Ip address. This value is a decimal representation of an IP + address. This particular one is 127.0.0.1. + +- UT_port + This os an Underlying Transmission port. For example, in an IP stack, this + would be a 16 bit value -- like port 8080. combined with the UT_address, + together they form a complete UT address. The one above would be equal to + 127.0.0.1:1111. This is an unsigned integer (16 bit) value + +- type_of_network + This number represents what type of network the underlying UT address takes. + currently, the only acceptable values are 0 and 1. + - 0: UDP + - 1: TCP + - 2: csp + +- default_transmission_mode: + not implemented + +- MTU + This number represents the 'maximum transmissible unit' -- this will also + take the form of a buffer in the program. This value is the maximum size + packet that the application will receive. + +- one_way_light_time : not implemented +- total_round_trip_allowance : not implemented +- async_NAK_interval : not implemented +- async_keep_alive_interval : not implemented +- async_report_interval : not implemented +- immediate_nak_mode_enabled : not implemented +- prompt_transmission_interval : not implemented +- disposition_of_incomplete : not implemented +- CRC_required : not implemented +- keep_alive_discrepancy_limit : not implemented +- positive_ack_timer_expiration_limit : not implemented +- nak_timer_expiration_limit : not implemented +- transaction_inactivity_limit : not implemented + +if you want to get in contact with me +email me at evangiese77@gmail.com diff --git a/Program/__init__.py b/Program/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Program/ftp_python.py b/Program/ftp_python.py new file mode 100644 index 0000000..646384d --- /dev/null +++ b/Program/ftp_python.py @@ -0,0 +1,59 @@ +import subprocess +from pprint import pprint +import os +import datetime + +def change_working_dir(): + path = os.path.abspath(__file__) + split = path.split("/")[0:-1] + split.append("src") + new_path = "/".join(split) + os.chdir(new_path) + + +class logger(): + def __init__(self, path="ftp_log/log.txt"): + self.log_file = self.create_ftp_log(path) + + def create_ftp_log(self, path): + if not os.path.exists("ftp_log"): + os.makedirs("ftp_log") + + file = open(path, "a") + return file + + def close(self): + self.log_file.close() + + def log(self, stuff): + self.log_file.writelines(str(datetime.datetime.today()) + ":" + stuff) + +def get_request(sat_path, file_path, block=True): + change_working_dir() + + print("need sudo password for opening /dev/ttyUSB0") + cmd = ["sudo", "./main", "-i", "10", "-c", "1", "-k", "/dev/ttyUSB0", "-f", 'GET {0}|{1}'.format(sat_path, file_path)] + + if block: + output = subprocess.run(cmd) + print(output) + return + + #subprocess.Popen(cmd) + +def put_request(file_path, sat_path, block=True): + change_working_dir() + + print("need sudo password for opening /dev/ttyUSB0") + cmd = ["sudo", "./main", "-i", "10", "-c", "1", "-k", "/dev/ttyUSB0", "-f", 'PUT {0}|{1}'.format(file_path, sat_path)] + + if block: + output = subprocess.run(cmd) + print(output) + return + + #subprocess.Popen(cmd) + + +if __name__ == "__main__": + print("thats not how you use this, please import either 'get_request' or 'put_request' into your program") diff --git a/Program/include/app_control.h b/Program/include/app_control.h new file mode 100755 index 0000000..95e0705 --- /dev/null +++ b/Program/include/app_control.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef SSP_TASKS_H +#define SSP_TASKS_H + +#include "types.h" + +void ssp_cleanup_client(Client *client); +void ssp_cleanup_req(void *request); +void ssp_cleanup_ftp(FTP *app); +void *ssp_connectionless_server_task(void *params); +void *ssp_connectionless_client_task(void* params); + +void *ssp_connection_client_task(void *params); +void *ssp_connection_server_task(void *params); +void *ssp_csp_connectionless_client_task(void *params); +void *ssp_csp_connectionless_server_task(void *params); +void *ssp_csp_connection_client_task(void *params); +void *ssp_csp_connection_server_task(void *params); +void ssp_client_join(Client *client); +void *ssp_generic_client_task(void *params); +void *ssp_generic_server_task(void *params); + +void reset_timeout(int *prevtime); +void remove_request_check(Node *node, void *request, void *args); + +#endif \ No newline at end of file diff --git a/Program/include/changeNotes b/Program/include/changeNotes new file mode 100755 index 0000000..d5350b9 --- /dev/null +++ b/Program/include/changeNotes @@ -0,0 +1 @@ +added PDU_NAK_METADATA directive, due to spec not containing room for missing metadata packet \ No newline at end of file diff --git a/Program/include/csp_server_provider.h b/Program/include/csp_server_provider.h new file mode 100755 index 0000000..d73b512 --- /dev/null +++ b/Program/include/csp_server_provider.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef CSP_SERVER_H +#define CSP_SERVER_H +#include "stdint.h" + +void csp_connectionless_client(uint8_t dest_id, uint8_t dest_port, uint8_t src_port, uint32_t packet_len, + int (*onSend)(int sfd, void *addr, uint32_t size_of_addr, void *onSendParams), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *onRecvParams) , + int (*checkExit)(void *checkExitParams), + void (*onExit)(void *params), + void *params); + +void csp_connectionless_server(uint8_t my_port, uint32_t packet_len, uint32_t time_out, + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other), + int (*onTimeOut)(void *other), + int (*checkExit)(void *other), + void (*onExit)(void *other), + void *other); + +void csp_connection_server(uint8_t my_port, uint32_t packet_len, uint32_t time_out, + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other), + int (*onTimeOut)(void *other), + int (*checkExit)(void *other), + void (*onExit)(void *other), + void *other); + +void csp_connection_client(uint8_t dest_id, uint8_t dest_port, uint8_t my_port, uint32_t packet_len, uint32_t time_out, void*lock, + int (*onSend)(int sfd, void *addr, uint32_t size_of_addr, void *onSendParams), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *onRecvParams) , + int (*checkExit)(void *checkExitParams), + void (*onExit)(void *params), + void *params); +int csp_custom_ftp_ping(uint32_t dest_id, uint32_t port); +#endif diff --git a/Program/include/file_delivery_app.h b/Program/include/file_delivery_app.h new file mode 100755 index 0000000..042420c --- /dev/null +++ b/Program/include/file_delivery_app.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef FTP_APP_H +#define FTP_APP_H +#include "types.h" + +int init_ftp(uint32_t my_cfdp_address, FTP *app); +Client *ssp_client(uint32_t cfdp_id, FTP *app); +void create_ssp_server(FTP *app); +int create_ssp_server_drivers(FTP *app); +void *create_ftp_task(uint32_t cfdp_id, FTP *app); +Client *init_client(uint32_t dest_cfdp_id, uint32_t my_cfdp_id); + +#endif diff --git a/Program/include/filesystem_funcs.h b/Program/include/filesystem_funcs.h new file mode 100755 index 0000000..c432e73 --- /dev/null +++ b/Program/include/filesystem_funcs.h @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef FILESYSTEM_FUNCS_H +#define FILESYSTEM_FUNCS_H +#include "types.h" + +#define TEMP_FILESIZE 1000 + +struct params { + int error; + int fd; +}; + + +int get_file_size(char *source_file_name); + +//allocates space for a file pointer +File *create_file(char *source_file_name, int clear_file_contents); +void ssp_free_file(void *file); +int add_first_offset(File *file, uint32_t file_size); + +int does_file_exist(char *source_file_name); +int get_offset(File *file, void *buff, uint32_t buf_size, int offset); +int write_offset(File *file, void *buff, uint32_t size, uint32_t offset); +uint32_t calc_check_sum(char *data, uint32_t length); +uint32_t check_sum_file(File *file, uint16_t stack_buffer); +int receive_offset(File *file, uint32_t offset_start, uint32_t offset_end); +File *create_temp_file(char *file_name, uint32_t size); +int change_tempfile_to_actual(char *temp, char *destination_file_name, uint32_t file_size, File *file); +int read_json(char *file_name, int (*callback)(char *key, char *value, void *params), void *params); + +int write_lv(int fd, LV lv); +int read_lv(int fd, LV *lv); + +int read_id(int fd, uint64_t *id); +int write_id(int fd, uint64_t id); + +int save_req_to_file(Request *req); +int save_file_to_file(int fd, File *file); +int get_file_from_file(int fd, File *file); +int get_req_from_file(uint32_t dest_cfdp_id, uint64_t transaction_seq_num, uint32_t my_cfdp_id, Request *req); +int delete_saved_request(Request *req); +int read_request_from_file(int fd, Request *req); + + +//new json stuff: +int write_request_json (Request *req, char *file_name); +int get_request_from_json (Request *req, char *file_name); + +#endif diff --git a/Program/include/generic_server_provider.h b/Program/include/generic_server_provider.h new file mode 100755 index 0000000..306f950 --- /dev/null +++ b/Program/include/generic_server_provider.h @@ -0,0 +1,19 @@ +#ifndef SSP_GENERIC_PROVIDER_H +#define SSP_GENERIC_PROVIDER_H +#include "port.h" + +void csp_generic_server( + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *app), + int (*onTimeOut)(void *app), + int (*checkExit)(void *app), + void (*onExit)(void *app), + void *app); + +void csp_generic_client(uint8_t dest_id, uint8_t dest_port, uint8_t my_port, uint32_t packet_len, + int (*onSend)(int sfd, void *addr, uint32_t size_of_addr, void *onSendParams), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *onRecvParams) , + int (*checkExit)(void *checkExitParams), + void (*onExit)(void *params), + void *params); + +#endif diff --git a/Program/include/jsmn.h b/Program/include/jsmn.h new file mode 100755 index 0000000..6d9524f --- /dev/null +++ b/Program/include/jsmn.h @@ -0,0 +1,476 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +//#include removed this, added just _SIZE_T + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/Program/include/list.h b/Program/include/list.h new file mode 100755 index 0000000..58eb843 --- /dev/null +++ b/Program/include/list.h @@ -0,0 +1,55 @@ + +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef MY_LIST_H +#define MY_LIST_H +#include "stdint.h" + +/*------------------------------------------------------------------------------ + Purpose: This is a node for the link list bellow +------------------------------------------------------------------------------*/ +typedef struct Node +{ + uint32_t id; + void *element; + struct Node *next; + struct Node *prev; +} Node; + +/*------------------------------------------------------------------------------ + Purpose: This is a LIST struct that contains functions useful for + doing linked list functionality. +------------------------------------------------------------------------------*/ +typedef struct List +{ + struct Node *head; + struct Node *tail; + uint32_t count; + int (*push)(struct List *list, void *element, int id); + void *(*remove)(struct List *list, int id, int (*f)(void *element, void *args), void *args); + void (*iterate)(struct List *list, void (*f)(Node *node, void *element, void *args), void *args); + void (*free)(struct List *list, void (*f)(void *element)); + void *(*pop) (struct List *list); + int (*insert) (struct List *list, void *element, int id); + int (*insertAt)(struct List *list, void *element, int id, int (*f)(void *element, void *args), void *args); + //returns a void pointer that should be cast to the type + void *(*find)(struct List *list, int id, int (*f)(void *element, void *args), void *args); + struct Node *(*findNode)(struct List *list, int id, int (*f)(void *element, void *args), void *args); + void (*freeOnlyList)(struct List *list); + void (*freeNode)(Node *node); + void *(*removeNode)(struct List *list, Node *node); + +} List; + +Node *createNode(void *element, int id); +/*------------------------------------------------------------------------------ + Purpose: This function initializes a linked list LIST *. + Perameters: empty is just for the compiler errors, TODO use it for something + Return: returns a pointer to an initilized LIST * +------------------------------------------------------------------------------*/ +List *linked_list(void); +#endif diff --git a/Program/include/mib.h b/Program/include/mib.h new file mode 100755 index 0000000..e3eaf8c --- /dev/null +++ b/Program/include/mib.h @@ -0,0 +1,16 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef MIB_H +#define MIB_H + +#include "types.h" + +int get_header_from_mib(Pdu_header *pdu_header, Remote_entity remote, uint32_t my_cfdp_id); +void ssp_cleanup_pdu_header(Pdu_header *pdu_header); +int get_remote_entity_from_json (Remote_entity *remote, uint32_t cfdp_id); + +#endif \ No newline at end of file diff --git a/Program/include/packet.h b/Program/include/packet.h new file mode 100755 index 0000000..580d007 --- /dev/null +++ b/Program/include/packet.h @@ -0,0 +1,53 @@ + +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef PACKET_H +#define PACKET_H + +//dont change this +#define PACKET_STATIC_HEADER_LEN 4 //in bytes + +#include "types.h" +#include "list.h" + +int build_pdu_header(char *packet, uint64_t transaction_sequence_number, uint32_t transmission_mode, uint16_t data_len, Pdu_header *pdu_header); +uint8_t build_finished_pdu(char *packet, uint32_t start); +uint8_t build_put_packet_metadata(char *packet, uint32_t start, Request *req); +uint8_t build_nak_response(char *packet, uint32_t start, uint32_t offset, Request *req, Client* client); +void get_finished_pdu(char *packet, Pdu_finished *pdu_finished); +int get_nak_packet(char *packet, Pdu_nak *nak); + +int copy_id_to_packet(char *bytes, uint64_t id); +int copy_id_lv_to_packet(char *bytes, uint64_t id); +int copy_id_lv_from_packet(char *bytes, uint64_t *id); +uint64_t copy_id_from_packet(char *bytes, uint32_t length_of_ids); +void set_packet_directive(char *packet, uint32_t location, uint8_t directive); +void set_bits_to_protocol_byte(char *byte, uint8_t from_position, uint8_t to_position, uint8_t value); + + +uint32_t get_data_offset_from_packet(char *packet); + +int build_data_packet(char *packet, uint32_t start, uint32_t end, uint32_t offset, File *file); +void build_eof_packet(char *packet, uint32_t start, uint32_t file_size, uint32_t checksum); +void get_eof_from_packet(char *packet, Pdu_eof *eof); + +void fill_nak_array_callback(Node *node, void *element, void *args); +uint32_t build_nak_packet(char *packet, uint32_t start, Request *req); +uint8_t build_ack(char*packet, uint32_t start, uint8_t type); +uint8_t build_nak_directive(char *packet, uint32_t start, uint8_t directive) ; +void set_data_length(char*packet, uint16_t data_len); +uint16_t get_data_length(char*packet); +int get_pdu_header_from_packet(char *packet, Pdu_header *pdu_header); +uint8_t get_bits_from_protocol_byte(char byte, uint8_t from_position, uint8_t to_position); +void ssp_print_header(Pdu_header *pdu_header); +uint32_t get_message_from_packet(char *packet, uint32_t start, Request *req); +uint32_t get_messages_from_packet(char *packet, uint32_t start, uint32_t data_length, Request *req); +uint32_t add_messages_to_packet(char *packet, uint32_t start, List *messages_to_user); +void get_ack_from_packet(char *packet, Pdu_ack *ack); + + +#endif diff --git a/Program/include/port.h b/Program/include/port.h new file mode 100755 index 0000000..c5a39bb --- /dev/null +++ b/Program/include/port.h @@ -0,0 +1,161 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef PORT_H +#define PORT_H + + +#define FREE_RTOS_PORT +#define RED_FS + +#define CSP_NETWORK +//#define POSIX_FILESYSTEM +//#define POSIX_PORT + +#include "types.h" +#define STACK_ALLOCATION 2000 + +#ifdef CSP_NETWORK + #include "csp/csp.h" + typedef struct csp_packet_wrapper { + uint8_t dest_id; + uint8_t dest_port; + uint8_t src_id; + uint8_t src_port; + csp_packet_t *packet; + } csp_packet_wrapper; + + #ifndef POSIX_PORT + #include + #define SSP_INET_ADDRSTRLEN 16 + #define SSP_AF_INET 2 + #define ssp_htonl csp_hton32 + #define ssp_ntohl csp_ntoh32 + #define ssp_ntohs csp_ntoh16 + #define ssp_htons csp_hton16 + #define ssp_inet_ntop inet_ntop + #endif +#endif + +#ifdef POSIX_FILESYSTEM + #include + #include + #include + #include + #include + + #define SSP_O_RDWR O_RDWR + #define SSP_O_CREAT O_CREAT + #define SSP_O_TRUNC O_TRUNC + #define SSP_SEEK_SET SEEK_SET + #define SSP_SEEK_END SEEK_END + #define SSP_EEXIST EEXIST + #define ssp_open open + #define ssp_rename rename + #define ssp_close close + #define ssp_read read + #define ssp_write write + #define ssp_closedir closedir + #define ssp_lseek lseek + #define ssp_remove remove + + +#endif + +#ifdef RED_FS + + #include + #include + #include + #include + #include + #include + #define SSP_O_RDWR RED_O_RDWR + #define SSP_O_CREAT RED_O_CREAT + #define SSP_O_TRUNC RED_O_TRUNC + #define SSP_EEXIST RED_EEXIST + #define SSP_SEEK_SET RED_SEEK_SET + #define SSP_SEEK_END SEEK_END + #define ssp_open red_open + #define ssp_rename red_rename + #define ssp_close red_close + #define ssp_read red_read + #define ssp_write red_write + #define ssp_closedir red_closedir + #define ssp_lseek red_lseek + #define ssp_remove red_unlink + + +#endif + +#ifdef POSIX_PORT + #include + #include + #include + + #define SSP_INET_ADDRSTRLEN INET_ADDRSTRLEN + + #define SSP_AF_INET AF_INET + #define ssp_htonl htonl + #define ssp_ntohl ntohl + #define ssp_htons htons + #define ssp_ntohs ntohs + #define ssp_inet_ntop inet_ntop + + #include + #define ssp_memcpy memcpy + + #include + #define ssp_snprintf snprintf + + #include "stdlib.h" + #define ssp_atol atol + #define ssp_atoll atoll +#endif + +#ifdef FREE_RTOS_PORT + + #include + #define ssp_memcpy memcpy + + #include + #define ssp_snprintf snprintf + + #include "stdlib.h" + #define ssp_atol atol + #define ssp_atoll atoll +#endif + + +//https://stackoverflow.com/questions/3022552/is-there-any-standard-htonl-like-function-for-64-bits-integers-in-c +#define ssp_htonll(x) ((1==ssp_htonl(1)) ? (x) : ((uint64_t)ssp_htonl((x) & 0xFFFFFFFF) << 32) | ssp_htonl((x) >> 32)) +#define ssp_ntohll(x) ((1==ssp_ntohl(1)) ? (x) : ((uint64_t)ssp_ntohl((x) & 0xFFFFFFFF) << 32) | ssp_ntohl((x) >> 32)) + +//don't change these in the header file here, if you need to change them +//change them in the .c file +void ssp_error(char *msg); +void ssp_printf(char *stuff, ...); +void *ssp_alloc(uint32_t u_memb, size_t size); +void ssp_sendto(Response res); +void *ssp_thread_create(int stack_size, void * (thread_func)(void *params), void *params); +int ssp_time_count(void); +void ssp_error(char *error); +void ssp_free(void *pointer); +void ssp_thread_join(void *thread_handle); +int ssp_mkdir(char *dir_name); +void *ssp_opendir(char *dir_name); +int ssp_readdir(void *dir, char *file); +int get_exit(); +void set_exit(); +int ssp_lock_give(void *lock); +int ssp_lock_take(void *lock); +void *ssp_lock_create(); +int ssp_lock_destory(void *lock); + +void reset_request(Request *req); + + +#endif diff --git a/Program/include/posix_server_provider.h b/Program/include/posix_server_provider.h new file mode 100755 index 0000000..8d315bb --- /dev/null +++ b/Program/include/posix_server_provider.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef POSIX_SERVER_H +#define POSIX_SERVER_H + +#include "stdint.h" +int prepareSignalHandler(void); +/*------------------------------------------------------------------------------ + Purpose: This function creates a udp select server on the socket sfd. + Perameters: int sfd: The socket descriptor created by prepareUdpHost + void *other: this is the void* that we want to pass into + onRecv, and onTimeout + Return: None +------------------------------------------------------------------------------*/ +/*-----------------------------CALLBACK onRecv---------------------------------- + Purpose: This function handles what happends when a user receives a + packet + Perameters: int sfd: Is the socket descriptor that the msg was received on + this is useful for resending messges. + char *msg: Is the packet that was sent + struct sockaddr_storage client: Is the address of the sender + void *other: Is the void * we passed into selectUdp + Return: None +------------------------------------------------------------------------------*/ +/*-----------------------------CALLBACK onTimeOut------------------------------- + Purpose: This function + Perameters: void *other: Is the void * we passed into selectUdp + Return: None +------------------------------------------------------------------------------*/ + +/*-----------------------------CALLBACK onTimeOut------------------------------- + Purpose: This is a simple udp client + Perameters: hostname is the name of an address eg, 127.0.0.1, port is the por + channel_size is the size of the channel in bytes + eg, 1111 + Return: None +------------------------------------------------------------------------------*/ + + +void connectionless_server(char *host_name, char* port, int initial_buff_size, + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other), + int (*onTimeOut)(void *other), + int (*checkExit)(void *other), + void (*onExit)(void *other), + void *other); + +void connection_server(char *host_name, char* port, int initial_buff_size, int connection_limit, + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other), + int (*onTimeOut)(void *other), + int (*checkExit)(void *other), + void (*onExit)(void *other), + void *other); + +void connection_client(char *hostname, char*port, int packet_len, void *params, + int (*onSend)(int sfd, void *addr, size_t size_of_addr, void *params), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *params) , + int (*checkExit)(void *params), + void (*onExit)(void *params)); + +void connectionless_client(char *hostname, char*port, int packet_len, void *params, + int (*onSend)(int sfd, void *addr, size_t size_of_addr, void *params), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *params) , + int (*checkExit)(void *params), + void (*onExit)(void *params)); + +#endif \ No newline at end of file diff --git a/Program/include/protocol_handler.h b/Program/include/protocol_handler.h new file mode 100755 index 0000000..e011191 --- /dev/null +++ b/Program/include/protocol_handler.h @@ -0,0 +1,46 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef PROTOCOL_H +#define PROTOCOL_H +#include "types.h" + +#define RESEND_EOF_TIMES 1 +#define RESEND_FINISHED_TIMES 1 +#define RESEND_META_TIMES 1 + +//#define TIMEOUT_BEFORE_CANCEL_REQUEST 600 +//#define TIMEOUT_BEFORE_SAVE_REQUEST 30 + +int parse_packet_server(char *packet, uint32_t packet_index, Response res, Request *req, Pdu_header incoming_header, FTP *app); +void user_request_handler(Response res, Request *req, Client *client); +void parse_packet_client(char* buff, uint32_t packet_index, Response res, Request *req, Client *client); +void on_server_time_out(Response res, Request *current_request); +int process_pdu_header(char*packet, uint8_t is_server, Pdu_header *incoming_pdu_header, Response res, Request **req, List *request_list, FTP *app); + +void process_pdu_eof(char *packet, Request *req, Response res); + + Request *new_incomming_request(uint32_t source_id, + uint32_t transmission_mode, + uint32_t transaction_sequence_number, + Response res, + FTP *app); + +uint32_t parse_metadata_packet(char *meta_data_packet, uint32_t start, Request *req_to_fill); +void process_messages(Request *req, FTP *app); +void process_data_packet(char *packet, uint32_t data_len, File *file); +int create_data_burst_packets(char *packet, uint32_t start, File *file, uint32_t length); + +int process_nak_pdu(char *packet, Request *req, Response res, Client *client); +uint8_t build_ack (char *packet, uint32_t start, uint8_t type); +int process_file_request_metadata(Request *req); +void set_data_length(char*packet, uint16_t data_len); +uint32_t build_nak_packet(char *packet, uint32_t start, Request *req); +uint16_t get_data_length(char*packet); + + + +#endif diff --git a/Program/include/requests.h b/Program/include/requests.h new file mode 100755 index 0000000..f041e75 --- /dev/null +++ b/Program/include/requests.h @@ -0,0 +1,99 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef REQUESTS_H +#define REQUESTS_H +#include "types.h" + + +//--------------------------------------------User functions-------------------------------- +/* +params: + id of destination, + source file name, + name of the file as it will arrive at destination, + ACKNOWLEDGED_MODE/UN_ACKNOWLEDGED_MODE (ACKNOWLEDGED_MODE will allow for acks/naks to be sent.), + app from create_ftp_task +*/ + +Request *put_request( + uint32_t dest_id, + char *source_file_name, + char *destination_file_name, + uint8_t transmission_mode, + FTP *app + ); + +/* +params: + id of destination, + source file name, + name of the file as it will arrive at destination, + ACKNOWLEDGED_MODE/UN_ACKNOWLEDGED_MODE (ACKNOWLEDGED_MODE will allow for acks/naks to be sent.), + app from create_ftp_task +*/ +Request *get_request( + uint32_t dest_id, + char *source_file_name, + char *destination_file_name, + uint8_t transmission_mode, + FTP *app); + +//to start sending packets +void start_request(Request *req); + +/* +params: + id of destination, + source file name, + name of the file as it will arrive at destination, + ACKNOWLEDGED_MODE/UN_ACKNOWLEDGED_MODE (ACKNOWLEDGED_MODE will allow for acks/naks to be sent.), + app from create_ftp_task +*/ +int add_proxy_message_to_request(uint32_t beneficial_cfid, uint8_t length_of_id, char *source_name, char *dest_name, Request *req); + +//doesn't really work yet (have to fix byte order for storate) +int add_cont_partial_message_to_request(uint32_t beneficial_cfid, + uint32_t originator_id, + uint32_t transaction_id, + Request *req); + + +//----------------------------------------------------------------------------------------- + +void ssp_cleanup_req(void *request); +Request *init_request(char *buff, uint32_t buff_len); +Message_put_proxy *create_message_put_proxy(uint32_t beneficial_cfid, uint8_t length_of_id, char *source_name, char *dest_name); +Message_cont_part_request *create_message_cont_partial_request(uint32_t beneficial_cfid, + uint32_t originator_id, + uint32_t transaction_id); + +int init_cont_partial_request(Message_cont_part_request *p_cont, char *buff, uint32_t buff_len); +void print_request_state(Request *req); +void ssp_free_message(void *params); +uint16_t copy_lv_to_buffer(char *buffer, LV lv); +void copy_lv_from_buffer(LV *lv, char *packet, uint32_t start); +Message *create_message(uint8_t type); +void create_lv(LV *lv, int len, void *value); +void free_lv(LV lv); +void print_request_procedure(Request *req); +int start_scheduled_requests(uint32_t dest_id, FTP *app); +int schedule_put_request(uint32_t dest_id,char *source_file_name,char *destination_file_name,uint8_t transmission_mode, FTP *app); +int schedule_request(Request *req, uint32_t dest_id, FTP *app); + +Request *init_request_no_client(); + +int put_request_no_client( + Request *req, + char *source_file_name, + char *destination_file_name, + uint8_t transmission_mode, + FTP *app); + +void print_res(Response res); +void add_request_to_client(Request *req, Client *client); +Client *start_client(FTP *app, uint8_t dest_id); +#endif diff --git a/Program/include/types.h b/Program/include/types.h new file mode 100755 index 0000000..fdf20d1 --- /dev/null +++ b/Program/include/types.h @@ -0,0 +1,717 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef SSP_TYPES_H +#define SSP_TYPES_H + +#include "list.h" +#include + +#define MAX_PATH 255 +#define ACKNOWLEDGED_MODE 0 +#define UN_ACKNOWLEDGED_MODE 1 + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + + +/*----------------------------------------------------------------------------- + + Packet structs for building packets + +------------------------------------------------------------------------------*/ + +typedef struct pdu_header{ + unsigned int version: 3; + + //0 File directive, 1 File Data, + unsigned int PDU_type: 1; + + //0 toward file receiver, 1 toward file sender + unsigned int direction: 1; + + //0 acknowledged, 1 unacknowledged + unsigned int transmission_mode: 1; + + //0 crc not present, 1 crc present + unsigned int CRC_flag: 1; + + //set to 0 + unsigned int reserved_bit_0: 1; + + //data field length in octets + unsigned int PDU_data_field_len: 16; + + //set to 0 + unsigned int reserved_bit_1: 1; + + //length of entity ids in octets + unsigned int length_of_entity_IDs: 3; + + //set to 0 + unsigned int reserved_bit_2: 1; + + //number of octets in sequence number, 0 is one octet + unsigned int transaction_seq_num_len: 3; + + //variable in size (for actual packet), and will get memory allocated based on the length variables above + uint32_t source_id; + + //variable in size (for actual packet), and will get memory allocated based on the length variables above + uint64_t transaction_sequence_number; + + //variable in size (for actual packet), and will get memory allocated based on the length variables above + uint32_t destination_id; + + //this is how much space is reserved for the entire header. Since the IDs are variable length, we need + //to calculate how much space we need based on the size of IDs. + uint32_t reserved_space_for_header; + +} Pdu_header; + +typedef struct originating_tranaction_id_message { + + unsigned int reserved_bit_0 : 1; + unsigned int length_of_entity_id : 3; + unsigned int reserved_bit_1 : 1; + unsigned int length_of_sequence_number: 3; + + //variable length of bits + void *source_id; + void *transaction_sequence_number; + +} Originating_tranaction_id_message; + + +/*------------------------------------------------------------------------------ + The following are the different TLV parameters and types associated + filestore_request: type 0x00 + value: struct filestore_request + filestore_response: type 0x01 + value: struct filestore_response + + //TODO + Messages to user. + Fault Handler overrides. + Flow Label. + +------------------------------------------------------------------------------*/ + + +typedef struct lv { + uint8_t length; + // size is 8 * length + void *value; +} LV; + +typedef struct tlv { + uint8_t type; + uint8_t length; + // size is 8 * length + void *value; +} TLV; + +//TLV Types +#define MESSAGE_TO_USER 0x02 +#define FILESTORE_RESPONSE 0x01 +#define FILESTORE_REQUEST 0x00 + +//-------------------------------Messages--------------------------------------- +// The type for this TVL is 0x02 hex + +//message types +#define PROXY_PUT_REQUEST 00 +#define PROXY_MESSAGE_TO_USER 01 +#define PROXY_FILESTORE_REQUEST 02 +#define PROXY_FAULT_HANDLER_OVERRIDE 03 +#define PROXY_TRANSMISSION_MODE 04 +#define PROXY_FLOW_LABLE 05 +#define PROXY_SEGMENTATION_CONTROL 06 +#define PROXY_PUT_RESPONSE 07 +#define PROXY_FILESTORE_RESPONSE 08 +#define PROXY_PUT_CANCEL 09 + +//custom +#define CONTINUE_PARTIAL 10 + +typedef struct message_header { + + char message_id_cfdp[5]; + uint8_t message_type; + +} Message_header; + + +//type PROXY_PUT_REQUEST +typedef struct message_put_proxy { + + uint64_t destination_id; + LV source_file_name; + LV destination_file_name; + +} Message_put_proxy; + +typedef struct message_cont_part_request { + + uint64_t destination_id; + uint64_t originator_id; + uint64_t transaction_id; + +} Message_cont_part_request; + +//can cast the message_value based on the message_type in the header; +typedef struct message { + + Message_header header; + //will take various kinds of Message_xx types + void *value; + +} Message; + + + +//------------------------------------------------------------------------------ + +//---------------------------filestore_request---------------------------------- +//action codes +#define CREATE_FILE 0x00 +#define DELETE_FILE 0x01 +#define RENAME_FILE 0x02 //second filename present +#define APPEND_FILE 0x03 //second filename present +#define REPLACE_FILE 0x04 //second filename present +#define CREATE_DIRECTORY 0x05 +#define REMOVE_DIRECTORY 0x06 +#define DENY_FILE 0x07 //delete if present +#define DENY_DIRECTORY 0x08 //delete if present + +// The type for this TLV is 0x00 +typedef struct filestore_request { + unsigned int action_code: 4; + unsigned int spare : 4; + LV first_file_name; + LV second_file_name; //only present in #RENAME_FILE, APPEND_FILE, REPLACE_FILE + + +} Filestore_request; + + +//---------------------------filestore_response--------------------------------- +#define SUCCESS 0x00 +#define CREATE_NOT_ALLOWED 0x01 +#define FILE_DOES_NOT_EXIST 0x01 +#define OLD_FILE_NAME_DOES_NOT_EXIST 0x01 +#define DELETE_NOT_ALLOWED 0x02 +#define NEW_FILE_NAME_ALREADY_EXISTS 0x02 +#define RENAME_NOT_ALLOWED 0x04 +#define FILE_NAME_1_DOES_NOT_EXIST 0x01 +#define FILE_NAME_2_DOES_NOT_EXIST 0x02 +#define APPEND_NOT_ALLOWED 0x04 +#define REPLACE_NOT_ALLOWED 0x04 +#define DIRECTORY_CANNOT_BE_CREATED 0x01 +#define DIRECTORY_DOES_NOT_EXIST 0x01 +#define NOT_PERFORMED 0x0F + + +// The type for this TLV is 0x01 +typedef struct filestore_response { + unsigned int action_code: 4; + unsigned int status_code : 4; + LV first_file_name; + LV second_file_name; //only present in #RENAME_FILE, APPEND_FILE, REPLACE_FILE + + LV filestore_message; + /*LV length field indicates zero length and LV value field is + omitted when there is no Filestore Message parameter present. */ + +} Filestore_response; + + + +/*------------------------------------------------------------------------------ + The following are the different PDUs, PDUs are what is included in the + data portion of the PDU_header these include: + + File directive PDUs: + File directive PDUs all start with a 1 byte directive code, + followed by one of the following various pdu types: + + //not implemented + -END-OF-FILE PDU + -FINISHED PDU + -ACK PDU + -NAK PDU + -PROMPT PDU + -KEEP ALIVE PDU + + //implemented + -METADATA PDU + + Non directive PDUs (when the PDU type in the header is set to 1): + -FILE DATA PDU + + + + + +------------------------------------------------------------------------------*/ + + +#define DIRECTIVE 0 +#define DATA 1 + +//directive_codes 00 - 03, and 0D-FF are reserved +#define EOF_PDU 0x04 +#define FINISHED_PDU 0x05 +#define ACK_PDU 0x06 +#define META_DATA_PDU 0x07 +#define NAK_PDU 0x08 +#define PROMPT_PDU 0x09 +#define KEEP_ALIVE_PDU 0x0C +#define SIZE_OF_DIRECTIVE_CODE 1 + +//custom, for missing metadata and eof +#define NAK_DIRECTIVE 0x0d + +//condition codes: + +#define COND_NO_ERROR 0x00 +/* +‘0000’ No error +‘0001’ Positive ACK limit reached +‘0010’ Keep alive limit reached +‘0011’ Invalid transmission mode +‘0100’ Filestore rejection +‘0101’ File checksum failure +‘0110’ File size error +‘0111’ NAK limit reached +‘1000’ Inactivity detected +‘1001’ Invalid file structure +‘1010’ Check limit reached +‘1011’ – ‘1101’ +CCSDS 727.0-B-4 +Condition +(reserved) +‘1110’ Suspend.request received +‘1111’ Cancel.request received +*/ + + +typedef struct pdu_directive { + uint8_t directive_code; +} Pdu_directive; + +/* +The segmentation control parameter + - shall indicate whether the file being delivered is to be segmented as an array of octets + or as an array of variable-length records; + - shall be omitted when local and remote file names are omitted. +*/ + +typedef struct pdu_meta_data { + //0 Record boundaries respeced (read as array of octets), 1 not respected (variable length) + unsigned int segmentation_control : 1; + + unsigned int reserved_bits: 7; + + //length of the file in octets, set all 0 for unbounded size + uint32_t file_size; + LV source_file_name; + LV destination_file_name; + + /* + Options include: + Filestore requests, + Messages to user. + Fault Handler overrides. + Flow Label. + */ + + TLV *options; + +} Pdu_meta_data; + + +typedef struct file_data_pdu_contents { + //in octets + uint32_t offset; + //variable sized + unsigned char *data; +} File_data_pdu_contents; + + +typedef struct pdu_eof { + unsigned int condition_code : 4; + unsigned int spare : 4; + uint32_t checksum; + /* In octets. This value shall be the total number of file data octets + transmitted by the sender, regardless of the condition code + (i.e., it shall be supplied even if the condition code is other than + ‘No error’). */ + uint32_t file_size; + + /* + Omitted if condition code is ‘No error’. Otherwise, entity ID in the + TLV is the ID of the entity at which transaction cancellation was + initiated.*/ + TLV fault_location; + +} Pdu_eof; + + +//custom, nak missing other directives, like metadata and eof +typedef struct pdu_nak_directive{ + uint8_t directive; + +} pdu_nak_directive; + + /* + start_scope is the begining of the nak requests + end_scope is the end + segments are + */ +typedef struct pdu_nak { + uint32_t start_scope; + uint32_t end_scope; + + //number of Nak_segments + uint64_t segment_requests; + void *segments; +} Pdu_nak; + + +#define ACK_FINISHED_WAYPOINT 0 +#define ACK_FINISHED_END 1 +#define ACK_OTHER 0 + +typedef struct pdu_ack { + //Directive code of the acknowledged PDU. + unsigned int directive_code : 4; + + /* + Values depend on directive code. For ACK of Finished PDU: binary ‘0000’ + if Finished PDU is generated by waypoint, binary ‘0001’ if Finished + PDU is generated by end system. (NOTE: this discrimination is + meaningful if the Extended Procedures are implemented.) + Binary ‘0000’ for ACKs of all other file directives. + */ + + unsigned int directive_subtype_code : 4; + + //Condition code of the acknowledged PDU. + unsigned int condition_code : 4; + unsigned int spare : 2; + + //Status of the transaction in the context of the entity that is issuing the acknowledgment. + unsigned int transaction_status : 2; + +} Pdu_ack; + + + +//file_status codes +#define FILE_DISCARDED_DELIBERATELY 1 +#define FILE_DISCARDED_DUE_TO_REJECTION 2 +#define FILE_RETAINED_SUCCESSFULLY 3 +#define FILE_STATUS_UNREPORTED 4 + + +typedef struct pdu_finished { + unsigned int condition_code : 4; + + // 0 generated by waypoint 1 generated by end system. + unsigned int end_system_status : 1; + + //0 data complete, 1 data incomplete + unsigned int delivery_code : 1; + + //see above + unsigned int file_status : 2; + TLV file_store_responses; + TLV fault_location; + +}Pdu_finished; + + + + +/*----------------------------------------------------------------------------- + + Functions and structs for the app + +------------------------------------------------------------------------------*/ + + +typedef struct offset { + uint32_t start; + uint32_t end; +} Offset; + + +typedef struct file { + int fd; + uint8_t is_temp; + uint32_t next_offset_to_send; + uint32_t total_size; + uint32_t partial_checksum; + uint32_t eof_checksum; + List *missing_offsets; + +} File; + + +typedef struct local_entity { + unsigned int EOF_sent_indication : 1; + + unsigned int EOF_recv_indication : 1; + + //required + unsigned int file_segment_recv_indication : 1; + + //required when acting as receiving entity + unsigned int transaction_finished_indication : 1; + + //required when acting as receiving entity + unsigned int suspended_indication : 1; + + //required when acting as receiving entity + unsigned int resumed_indication : 1; + + unsigned int Metadata_recv_indication: 1; + + unsigned int Metadata_sent_indication: 1; + + //function pointer to default handler? + void *default_fault_handler; + + //TODO routing information, extended procedures only, list maybe? + +} Local_entity; + + + +typedef enum Network_type { + posix_connectionless, + posix_connection, + csp_connectionless, + csp_connection, + generic, + test + +} Network_type; + +typedef struct remote_entity { + + //TODO probably add new UT layer specific stuff + uint32_t UT_address; + uint16_t UT_port; + + //csp, ip, rtos/ip + Network_type type_of_network; + + uint32_t cfdp_id; + + unsigned int one_way_light_time; + unsigned int total_round_trip_allowance; + unsigned int async_NAK_interval; + unsigned int async_keep_alive_interval; + unsigned int async_report_interval; + unsigned int immediate_nak_mode_enabled : 1; + unsigned int prompt_transmission_interval; + + //0 acknowledged or 1 unacknowledged + unsigned int default_transmission_mode: 1; + + //discard or retain (no idea what this is for yet) + unsigned int disposition_of_incomplete : 1; + + unsigned int CRC_required : 1; + //in octets + unsigned int mtu; + //in octets + unsigned int keep_alive_discrepancy_limit; + + //Number of expirations + unsigned int positive_ack_timer_expiration_limit; + + //Number of expirations + unsigned int nak_timer_expiration_limit; + + //time limit + unsigned int transaction_inactivity_limit; + + + /* + Start of transmission opportunity A signal produced by the operating environment. + End of transmission opportunity A signal produced by the operating environment. + Start of reception opportunity A signal produced by the operating environment. + End of reception opportunity A signal produced by the operating environment. + */ + + +} Remote_entity; + +typedef struct mib { + List *remote_entities; + +} MIB; + + +typedef struct response { + int sfd; + //this is a pointer to buff in a request + char *msg; + //this is type (struct sockaddr_in) in posix + void *addr; + size_t size_of_addr; + + enum Network_type type_of_network; + int transmission_mode; + + uint32_t packet_len; + +} Response; + + +typedef enum procedure { + none, + sending_nak_data, + clean_up, + sending_start, + +} Request_procedure; + +//incoming requests spin up requests +typedef struct request { + Request_procedure procedure; + + uint64_t transaction_sequence_number; + uint32_t dest_cfdp_id; + uint32_t my_cfdp_id; + + File *file; + uint32_t file_size; + + char source_file_name[MAX_PATH]; + char destination_file_name[MAX_PATH]; + + //previous timeout time in seconds + int timeout_before_cancel; + int timeout_before_journal; + + uint8_t segmentation_control; + uint8_t fault_handler_overides; + uint8_t flow_lable; + uint8_t transmission_mode; + bool paused; + + //counter for resending eof packets == 3 + uint8_t resent_eof; + //counter for resending finished packets == 3 + uint8_t resent_finished; + + //bool for sending first blast of data packets + uint8_t sent_first_data_round; + + Remote_entity remote_entity; + Local_entity local_entity; + + List *messages_to_user; + + //sets the buffer length, for making packets + uint32_t buff_len; + + //buffer for making packets + char* buff; + + //header for building response packets + Pdu_header pdu_header; + + //handler for sending responses back + Response res; +} Request; + + +typedef struct custom_queue { + + //used for generic drivers (aka no drivers) cast to a QueueHandle_t in our implementation + void* queue; + //used for csp connection struct + void* connection; +} Custom_queue; + + +//add "client" in here to represent local entity +typedef struct ftp { + + //the maximum size of the packet + uint32_t packet_len; + + //buffer for making packets, is length of packet_len + char* buff; + + Custom_queue custom_queue; + + void *server_handle; + uint32_t my_cfdp_id; + + List* request_list; + + Request *current_request; + + //underlying connection information + Remote_entity remote_entity; + + List *active_clients; + + //lock this + uint32_t transaction_sequence_number; + + uint8_t verbose_level; + + //is ok now to start client threads... probably. There is a chance that the server will not be ready yet to receive 'get requests' but that is a rare timing/scheduling. + bool initialized; + + //bool for exiting the server thread + uint8_t close; + +} FTP; + + +//outgoing requests spin up client threads +typedef struct client { + + void *client_handle; + //the maximum size of the packet + uint32_t packet_len; + + //buffer for making packets, is length of packet_len + char* buff; + + uint32_t cfdp_id; + + Request *current_request; + List *request_list; + + //information about the remote_entity + Remote_entity remote_entity; + + //packet header, useful for copying into outgoing packets + Pdu_header pdu_header; + + FTP *app; + + //bool for exiting the client thread + bool close; + + //for contiuously running clients + void *lock; + +} Client; + +#endif + diff --git a/Program/include/utils.h b/Program/include/utils.h new file mode 100755 index 0000000..0be3ceb --- /dev/null +++ b/Program/include/utils.h @@ -0,0 +1,17 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#ifndef UTILS_H +#define UTILS_H + +#include "stdint.h" + +void ssp_print_hex(char *stuff, int size); +void ssp_print_bits(char *stuff, int size); +char *safe_strncpy(char *to, char*from, int len); +void log_ftp(char* info, char *stuff); + +#endif //UTILS_H diff --git a/Program/libcsp b/Program/libcsp new file mode 160000 index 0000000..35952db --- /dev/null +++ b/Program/libcsp @@ -0,0 +1 @@ +Subproject commit 35952dbdf086a94055d0adf996e36c7241750f60 diff --git a/Program/src/app_control.c b/Program/src/app_control.c new file mode 100755 index 0000000..5d63fd6 --- /dev/null +++ b/Program/src/app_control.c @@ -0,0 +1,614 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "protocol_handler.h" +#include "app_control.h" +#include "port.h" +#include "mib.h" +#include "filesystem_funcs.h" +#include "types.h" + +#ifdef POSIX_PORT +#include "posix_server_provider.h" +#endif + +#ifdef CSP_NETWORK +#include "csp_server_provider.h" +#endif + +//for print_request_state +#include "requests.h" + + +/*------------------------------------------------------------------------------ + + Generic functions + +------------------------------------------------------------------------------*/ + +/* +//usefull for cpu clocks +static int is_negative(int number) { + int negative_mask = 0x80000000; + int is_negative = number & negative_mask; + return is_negative; +} +*/ + +void reset_timeout(int *prevtime) { + *prevtime = ssp_time_count(); +} + +static int check_timeout(int *prevtime, uint32_t timeout) { + + int prev = *prevtime; + int current_time = ssp_time_count(); + int time_out = prev + timeout; + + if (current_time >= time_out) { + *prevtime = current_time; + return 1; + } + //wrap around the overflow condition + else if (current_time < prev) { + *prevtime = current_time; + } + return 0; +} + +//sets request procedure as clean_up if ttl has passed +static void timeout(Request *req, uint32_t time_out_before_cancel) { + + bool is_timeout = check_timeout(&req->timeout_before_cancel, time_out_before_cancel); + if (is_timeout) { + if (req->local_entity.transaction_finished_indication){ + ssp_printf("ACKNOWLEDGED request successfully sent without issue transaction: %llu\n", req->transaction_sequence_number); + } else if (req->transmission_mode == UN_ACKNOWLEDGED_MODE){ + ssp_printf("UN_ACKNOWLEDGED request successfully sent without issue transaction: %llu\n", req->transaction_sequence_number); + } + else { + ssp_printf("stopped early, timed out without finishing request, saving req to be reopened later: %llu\n", req->transaction_sequence_number); + print_request_state(req); + save_req_to_file(req); + } + req->procedure = clean_up; + } + + is_timeout = check_timeout(&req->timeout_before_journal, time_out_before_cancel/2); + if (is_timeout) { + int error = save_req_to_file(req); + if (error < 0) { + ssp_printf("couldn't journal file\n"); + } + } +} + +void remove_request_check(Node *node, void *request, void *args) { + Request *req = (Request *) request; + List *req_list = (List *) args; + + if (req->procedure == clean_up) { + ssp_printf("removing request\n"); + Request *remove_this = req_list->removeNode(req_list, node); + + if (req->local_entity.transaction_finished_indication || req->transmission_mode == UN_ACKNOWLEDGED_MODE) { + int error = delete_saved_request(req); + if (error < 0) { + //TODO check if file exists, errno should be present + //ssp_error("couldn't delete finished request, the request may have finished before journaling it\n"); + } + + } + + ssp_cleanup_req(remove_this); + } +} + +/*------------------------------------------------------------------------------ + + client callbacks + +------------------------------------------------------------------------------*/ + + +static int on_recv_client_callback(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other) { + + Client *client = (Client *) other; + if (packet_len > client->app->packet_len) { + ssp_printf("packet received is too big for app\n"); + return -1; + } + + Response res; + res.addr = addr; + res.sfd = sfd; + res.type_of_network = client->remote_entity.type_of_network; + res.size_of_addr = size_of_addr; + res.transmission_mode = client->remote_entity.default_transmission_mode; + res.msg = client->buff; + res.packet_len = packet_len; + + Request **request_container = &client->current_request; + Pdu_header incoming_pdu_header; + + int packet_index = process_pdu_header(packet, false, &incoming_pdu_header, res, request_container, client->request_list, client->app); + if (packet_index < 0) { + ssp_printf("error parsing header\n"); + return -1; + } + + + Request *current_request = (*request_container); + + parse_packet_client(packet, packet_index, res, current_request, client); + + reset_timeout(¤t_request->timeout_before_cancel); + + memset(packet, 0, packet_len); + + return 0; + +} + +struct user_request_check_params { + Response res; + Client *client; +}; + +static void user_request_check(Node *node, void *request, void *args) { + Request *req = (Request *) request; + struct user_request_check_params* params = (struct user_request_check_params *) args; + + memset(params->res.msg, 0, params->client->packet_len); + + user_request_handler(params->res, req, params->client); + timeout(req, params->client->remote_entity.transaction_inactivity_limit); + + remove_request_check(node, request, params->client->request_list); +} + +static int on_send_client_callback(int sfd, void *addr, size_t size_of_addr, void *other) { + + Response res; + Client *client = (Client *) other; + + if (client->request_list->count == 0 && client->lock == NULL){ + client->close = true; + return 0; + } + + res.sfd = sfd; + res.packet_len = client->packet_len; + res.addr = addr; + res.size_of_addr = size_of_addr; + res.type_of_network = client->remote_entity.type_of_network; + res.transmission_mode = client->remote_entity.default_transmission_mode; + res.msg = client->buff; + + struct user_request_check_params params = { + res, + client + }; + client->request_list->iterate(client->request_list, user_request_check, ¶ms); + return 0; +} + +/*------------------------------------------------------------------------------ + + Server callbacks + +------------------------------------------------------------------------------*/ + +static void client_check_callback(Node *node, void *client, void *args) { + Client *c = (Client *) client; + List *list = (List *) args; + if (c->close) { + Client *remove_this = (Client *) list->removeNode(list, node); + ssp_printf("removing client, from server \n"); + ssp_client_join(remove_this); + } +} + + +static void timeout_check_callback_server(Node *node, void *request, void *args) { + Request *req = (Request *) request; + FTP *app = (FTP *) args; + + on_server_time_out(req->res, req); + timeout(req, app->remote_entity.transaction_inactivity_limit); + remove_request_check(node, request, app->request_list); +} + +//return 1 if there are active requests, 0 if not +static int on_time_out_callback_server(void *other) { + + FTP *app = (FTP*) other; + + if (app->active_clients->count) { + app->active_clients->iterate(app->active_clients, client_check_callback, app->active_clients); + } + if (app->request_list->count) { + app->request_list->iterate(app->request_list, timeout_check_callback_server, app); + + } else { + return 0; + } + + return 1; +} + +static int on_recv_server_callback(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other) { + + FTP *app = (FTP *) other; + if (packet_len > app->packet_len) { + ssp_printf("packet received is too big for app\n"); + return -1; + } + + Response res; + res.addr = addr; + res.sfd = sfd; + res.size_of_addr = size_of_addr; + + Pdu_header incoming_pdu_header; + Request **request_container = &app->current_request; + + int packet_index = process_pdu_header(packet, true, &incoming_pdu_header, res, request_container, app->request_list, app); + if (packet_index < 0) { + ssp_printf("error parsing header\n"); + return -1; + } + + Request *current_request = (*request_container); + app->current_request = current_request; + + int count = parse_packet_server(packet, packet_index, app->current_request->res, current_request, incoming_pdu_header, app); + + reset_timeout(¤t_request->timeout_before_cancel); + + memset(packet, 0, count); + + return count; + +} +/*------------------------------------------------------------------------------ + + check exit callbacks, will exit the app if returns 1 + +------------------------------------------------------------------------------*/ + +static int check_exit_server_callback(void *params) { + FTP *app = (FTP*) params; + if (app->close) + return 1; + return 0; +} + +static int check_exit_client_callback(void *params) { + Client *client = (Client*) params; + if (client->close) + return 1; + return 0; +} + +/*------------------------------------------------------------------------------ + + on Exit callbacks, used for cleaning up memory when exiting app + +------------------------------------------------------------------------------*/ + +static void on_exit_client_callback (void *params) { + Client *client = (Client *) params; + if (client == NULL) + return; + + client->close = true; +} + +static void on_exit_server_callback (void *params) { + FTP *app = (FTP*) params; + ssp_cleanup_ftp(app); +} + + +/*------------------------------------------------------------------------------ + + different server/client drivers + +------------------------------------------------------------------------------*/ + +static int get_ip_port(Remote_entity remote_entity, char *host_name, char *port){ + //convert int to char * + int error = ssp_snprintf(port, 10, "%d", remote_entity.UT_port); + if (error < 0) { + ssp_error("ssp_snprintf"); + return -1; + } + + uint32_t ut_addr = ssp_htonl(remote_entity.UT_address); + + //convert uint id to char * + const char *ret = ssp_inet_ntop(SSP_AF_INET, &ut_addr, host_name, SSP_INET_ADDRSTRLEN); + if (ret == NULL) { + ssp_error("inet_ntop"); + return -1; + } + return 0; +} + +void *ssp_connectionless_server_task(void *params) { + #ifdef POSIX_PORT + ssp_printf("starting posix connectionless server task\n"); + FTP* app = (FTP*) params; + app->transaction_sequence_number = 1; + + char port[10]; + char host_name[SSP_INET_ADDRSTRLEN]; + + int error = get_ip_port(app->remote_entity, host_name, port); + if (error < 0) { + ssp_cleanup_ftp(app); + return NULL; + } + + connectionless_server(host_name, port, + app->packet_len, + on_recv_server_callback, + on_time_out_callback_server, + check_exit_server_callback, + on_exit_server_callback, + app); + #endif + #ifndef POSIX_PORT + ssp_printf("can't start posix connectionless server, no drivers\n"); + #endif + return NULL; +} + + +void *ssp_connectionless_client_task(void* params){ + #ifdef POSIX_PORT + ssp_printf("starting posix connectionless client task \n"); + Client *client = (Client *) params; + + char port[10]; + char host_name[SSP_INET_ADDRSTRLEN]; + + int error = get_ip_port(client->remote_entity, host_name, port); + if (error < 0) { + ssp_cleanup_client(client); + return NULL; + } + + connectionless_client(host_name, + port, + client->packet_len, + client, + on_send_client_callback, + on_recv_client_callback, + check_exit_client_callback, + on_exit_client_callback); + + return NULL; + #endif + #ifndef POSIX_PORT + ssp_printf("can't start posix connectionless client, no drivers\n"); + #endif +} + +void *ssp_connection_server_task(void *params) { + #ifdef POSIX_PORT + ssp_printf("starting posix connection server\n"); + FTP* app = (FTP*) params; + app->transaction_sequence_number = 1; + + char port[10]; + char host_name[SSP_INET_ADDRSTRLEN]; + + int error = get_ip_port(app->remote_entity, host_name, port); + if (error < 0) { + ssp_cleanup_ftp(app); + return NULL; + } + + //1024 is the connection max limit + connection_server(host_name, + port, + app->packet_len, + 10, + on_recv_server_callback, + on_time_out_callback_server, + check_exit_server_callback, + on_exit_server_callback, + app); + #endif + #ifndef POSIX_PORT + ssp_printf("can't start posix connection server, no drivers\n"); + #endif + return NULL; +} + +void *ssp_connection_client_task(void *params) { + #ifdef POSIX_PORT + ssp_printf("starting posix connection client\n"); + Client *client = (Client *) params; + + char port[10]; + char host_name[SSP_INET_ADDRSTRLEN]; + + int error = get_ip_port(client->remote_entity, host_name, port); + if (error < 0) { + ssp_cleanup_client(client); + return NULL; + } + + + connection_client(host_name, + port, + client->packet_len, + client, + on_send_client_callback, + on_recv_client_callback, + check_exit_client_callback, + on_exit_client_callback); + #endif + #ifndef POSIX_PORT + ssp_printf("can't start posix connection client, no drivers\n"); + #endif + return NULL; +} + +void *ssp_csp_connectionless_server_task(void *params) { + #ifdef CSP_NETWORK + ssp_printf("starting csp connectionless server\n"); + FTP *app = (FTP *) params; + + + csp_connectionless_server( + app->remote_entity.UT_port, + app->remote_entity.mtu, + app->remote_entity.async_NAK_interval, + on_recv_server_callback, + on_time_out_callback_server, + check_exit_server_callback, + on_exit_server_callback, + app); + #endif + #ifndef CSP_NETWORK + ssp_printf("can't start csp connectionless server, no drivers\n"); + #endif + return NULL; +} + +void *ssp_csp_connectionless_client_task(void *params) { + #ifdef CSP_NETWORK + ssp_printf("starting csp connectionless client\n"); + Client *client = (Client *) params; + + csp_connectionless_client(client->remote_entity.UT_address, + client->remote_entity.UT_port, + CSP_ANY, + client->remote_entity.mtu, + on_send_client_callback, + on_recv_client_callback, + check_exit_client_callback, + on_exit_client_callback, + client); + #endif + #ifndef CSP_NETWORK + ssp_printf("can't start csp connectionless client, no drivers\n"); + #endif + return NULL; +} + + +void *ssp_csp_connection_server_task(void *params) { + #ifdef CSP_NETWORK + ssp_printf("starting csp connection server\n"); + FTP *app = (FTP *) params; + + csp_connection_server(app->remote_entity.UT_port, + app->remote_entity.mtu, + app->remote_entity.async_NAK_interval, + on_recv_server_callback, + on_time_out_callback_server, + check_exit_server_callback, + on_exit_server_callback, + params); + #endif + #ifndef CSP_NETWORK + ssp_printf("can't start csp connection server, no drivers\n"); + #endif + return NULL; +} + +void *ssp_csp_connection_client_task(void *params) { + #ifdef CSP_NETWORK + ssp_printf("starting csp connection client\n"); + + Client *client = (Client *) params; + + #ifdef FREE_RTOS_PORT + + void *lock = ssp_lock_create(); + if (lock == NULL) + return NULL; + + //start out with open lock + ssp_lock_give(lock); + + client->lock = lock; + #endif + + csp_connection_client(client->remote_entity.UT_address, + client->remote_entity.UT_port, + CSP_ANY, + client->remote_entity.mtu, + client->remote_entity.total_round_trip_allowance, + client->lock, + on_send_client_callback, + on_recv_client_callback, + check_exit_client_callback, + on_exit_client_callback, + params); + + #endif + #ifndef CSP_NETWORK + ssp_printf("can't start csp connection client, no drivers\n"); + #endif + return NULL; +} + + +void *ssp_generic_client_task(void *params) { + ssp_printf("starting generic server task\n"); + return NULL; +} + +void *ssp_generic_server_task(void *params) { + ssp_printf("starting generic server task\n"); + return NULL; +} +/*------------------------------------------------------------------------------ + + free functions + +------------------------------------------------------------------------------*/ + +void ssp_cleanup_client(Client *client) { + if (client == NULL) + return; + + client->request_list->free(client->request_list, ssp_cleanup_req); + ssp_lock_give(client->lock); + ssp_lock_destory(client->lock); + ssp_free(client->buff); + ssp_free(client); +} + +void ssp_client_join(Client *client) { + + ssp_thread_join(client->client_handle); + ssp_cleanup_client(client); +} + +static void exit_client(Node *node, void *element, void *args) { + if (element == NULL) + return; + + Client *client = (Client *) element; + client->close = true; + //in case we are currently blocking in the client thread + ssp_lock_give(client->lock); +} + +void ssp_cleanup_ftp(FTP *app) { + app->request_list->free(app->request_list, ssp_cleanup_req); + app->active_clients->iterate(app->active_clients, exit_client, NULL); + app->active_clients->iterate(app->active_clients, client_check_callback, app->active_clients); + app->active_clients->freeOnlyList(app->active_clients); + ssp_free(app->buff); +} diff --git a/Program/src/csp_server_provider.c b/Program/src/csp_server_provider.c new file mode 100755 index 0000000..52d0578 --- /dev/null +++ b/Program/src/csp_server_provider.c @@ -0,0 +1,336 @@ + +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ + +#include "port.h" +#include "csp/csp.h" +#include "csp_server_provider.h" +#include "csp_conn.h" + + +/*------------------------------------------------------------------------------ + + CSP STUFF! + +------------------------------------------------------------------------------*/ + +//https://www.cs.cmu.edu/afs/cs/academic/class/15213-f99/www/class26/udpclient.c +void csp_connectionless_client(uint8_t dest_id, uint8_t dest_port, uint8_t src_port, uint32_t packet_len, + int (*onSend)(int sfd, void *addr, uint32_t size_of_addr, void *params), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *params) , + int (*checkExit)(void *params), + void (*onExit)(void *params), + void *params) +{ + + int err = 0; + uint8_t src_id = csp_get_address(); + + //csp_socket_t *socket = csp_socket(CSP_SO_XTEAREQ | CSP_SO_HMACREQ | CSP_SO_CRC32REQ); + csp_socket_t *soc = csp_socket(CSP_SO_CONN_LESS); + if (soc == NULL) { + ssp_error("ERROR: csp socket queue empty\n"); + return; + } + + err = csp_bind(soc, src_port); + if (err < 0) { + ssp_error("ERROR: couldn't bind csp\n"); + return; + } + + csp_packet_t *packet_sending; + csp_packet_t *packet_recieved; + + if (csp_buffer_remaining() != 0) { + + packet_sending = csp_buffer_get(1); + packet_sending->id.dst = dest_id; + packet_sending->id.dport = dest_port; + packet_sending->id.src = src_id; + packet_sending->id.sport = src_port; + + } + else { + ssp_error("couldn't get new packet for sending!\n"); + set_exit(); + } + char *buff = ssp_alloc(sizeof(char), packet_len); + memset(buff, 0, packet_len); + + for (;;) { + + if (get_exit() || checkExit(params)){ + ssp_printf("exiting client thread\n"); + break; + } + + onSend(-1, packet_sending, sizeof(csp_packet_t), params); + + packet_recieved = csp_recvfrom(soc, 10); + //timout + if (packet_recieved == NULL) { + continue; + } + else { + + memcpy(buff, packet_recieved->data, packet_len); + + if (onRecv(-1, buff, packet_len, NULL, packet_recieved, sizeof(csp_packet_t), params) == -1) + ssp_printf("recv failed\n"); + + csp_buffer_free(packet_recieved); + } + + } + csp_buffer_free(packet_sending); + onExit(params); +} + + + +void csp_connectionless_server(uint8_t my_port, uint32_t packet_len, uint32_t time_out, + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other), + int (*onTimeOut)(void *other), + int (*checkExit)(void *other), + void (*onExit)(void *other), + void *other) +{ + + //csp_socket_t *socket = csp_socket(CSP_SO_XTEAREQ | CSP_SO_HMACREQ | CSP_SO_CRC32REQ); + csp_socket_t *soc = csp_socket(CSP_SO_CONN_LESS); + + int err = csp_bind(soc, my_port); + + if (err < 0) { + ssp_error("ERROR: couldn't bind csp\n"); + return; + } + + for (;;) { + + if (get_exit() || checkExit(other)){ + ssp_printf("exiting server thread\n"); + break; + } + + csp_packet_t *packet = csp_recvfrom(soc, time_out); + //timeout + if (packet == NULL) { + onTimeOut(other); + } + else { + + //switch ids, we do this for the sendto function, to reply + uint8_t d_id = packet->id.dst; + uint8_t s_id = packet->id.src; + packet->id.dst = s_id; + packet->id.src = d_id; + + uint8_t d_port = packet->id.dport; + uint8_t s_port = packet->id.sport; + packet->id.dport = s_port; + packet->id.sport = d_port; + + if (onRecv(-1, (char *)packet->data, packet->length, NULL, packet, sizeof(csp_packet_t), other) == -1) + ssp_printf("recv failed\n"); + + csp_buffer_free(packet); + } + + } +} + +int csp_custom_ftp_ping(uint32_t dest_id, uint32_t port){ + + char buff[255]; + memset(buff, 0, 255); + + csp_packet_t * packet; + csp_conn_t * conn; + /* Connect to host HOST, port PORT with regular UDP-like protocol and 1000 ms timeout */ + conn = csp_connect(CSP_PRIO_NORM, dest_id, port, 1000, CSP_SO_NONE); + if (conn == NULL) { + /* Connect failed */ + ssp_printf("Connection failed\n"); + return -1; + } + + ssp_printf("connection established, sending ping to id %d port %d \n", dest_id, 1); + packet = csp_buffer_get(100); + if (packet == NULL) { + ssp_printf("couldn't get packet for ping\n"); + } + + snprintf((char *) packet->data, csp_buffer_data_size(), "FTP ping"); + packet->length = (strlen((char *) packet->data) + 1); + if (!csp_send(conn, packet, 1000)) { + ssp_printf("Send failed"); + csp_buffer_free(packet); + } + + csp_packet_t *p = csp_read(conn, 1000); + if (p == NULL) { + ssp_printf("ping failed\n"); + } else { + + memcpy(buff, (char *)p->data, p->length); + ssp_printf("received: %s\n", buff); + + csp_close(conn); + return 1; + } + + csp_close(conn); + return -1; +} + +void csp_connection_server(uint8_t my_port, uint32_t packet_len, uint32_t time_out, + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other), + int (*onTimeOut)(void *other), + int (*checkExit)(void *other), + void (*onExit)(void *other), + void *other) +{ + + int error = 0; + //csp_socket_t *socket = csp_socket(CSP_SO_XTEAREQ | CSP_SO_HMACREQ | CSP_SO_CRC32REQ); + //Create socket without any socket options //CSP_SO_NONE + csp_socket_t *sock = csp_socket(CSP_SO_NONE); + if (sock == NULL) { + ssp_error("csp socket failed to initialize"); + return; + } + + //Bind all ports to socket + error = csp_bind(sock, my_port); + if (error != CSP_ERR_NONE) { + ssp_error("csp socket failed to bind"); + return; + } + //Create 10 connections backlog queue + error = csp_listen(sock, 10); + if (error != CSP_ERR_NONE) { + ssp_error("csp socket failed to listen"); + return; + } + //Pointer to current connection and packet + csp_conn_t *conn = NULL; + csp_packet_t *packet; + + char *buff = ssp_alloc(packet_len, sizeof(char)); + if (buff == NULL) { + ssp_printf("exiting serv thread\n"); + return; + } + + + for (;;) { + + if (get_exit() || checkExit(other)) + break; + + conn = csp_accept(sock, time_out); + if (conn == NULL) { + onTimeOut(other); + continue; + } + + ssp_printf("accepted\n"); + + while(1) { + + if (get_exit() || checkExit(other)) + break; + + + while ((packet = csp_read(conn, time_out)) != NULL) { + + memset(buff, 0, packet_len); + memcpy(buff, (char *)packet->data, packet->length); + + if (onRecv(-1, buff, packet_len, NULL, conn, sizeof(struct csp_conn_s), other) == -1) + ssp_printf("recv failed\n"); + + csp_buffer_free(packet); + } + //if the request has timeout, go back to waiting for accept + if (!onTimeOut(other)) + break; + + } + + csp_close(conn); + + } + ssp_free(buff); + onExit(other); +} + + +void csp_connection_client(uint8_t dest_id, uint8_t dest_port, uint8_t my_port, uint32_t packet_len, uint32_t time_out, void*lock, + int (*onSend)(int sfd, void *addr, uint32_t size_of_addr, void *onSendParams), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *onRecvParams) , + int (*checkExit)(void *checkExitParams), + void (*onExit)(void *params), + void *params) +{ + + csp_packet_t * packet = NULL; + csp_conn_t * conn = NULL; + + char *buff = ssp_alloc(packet_len, sizeof(char)); + if (buff == NULL) { + ssp_printf("exiting client thread\n"); + return; + } + + Client *client = (Client*) params; + + while (1) { + + //lock will block, need to unlock when new request/s are started + ssp_lock_take(lock); + + if (get_exit() || checkExit(params)){ + ssp_printf("exiting client thread\n"); + break; + } + + /* Connect to host HOST, port PORT with regular UDP-like protocol and 1000 ms timeout */ + conn = csp_connect(CSP_PRIO_NORM, dest_id, dest_port, 100, CSP_SO_NONE); + if (conn == NULL) { + continue; + } + + onSend(-1, conn, sizeof(conn), params); + + while ((packet = csp_read(conn, time_out)) != NULL) { + + memcpy(buff, (char *)packet->data, packet_len); + + if (onRecv(-1, buff, packet_len, NULL, conn, sizeof(struct csp_conn_s), params) == -1) + ssp_printf("recv failed\n"); + + csp_buffer_free(packet); + + } + + ssp_printf("closing connection\n"); + csp_close(conn); + + if (lock == NULL) + break; + + } + /* Close connection */ + if (conn != NULL) + csp_close(conn); + + ssp_free(buff); + onExit(params); +} diff --git a/Program/src/file_delivery_app.c b/Program/src/file_delivery_app.c new file mode 100755 index 0000000..c60f2e7 --- /dev/null +++ b/Program/src/file_delivery_app.c @@ -0,0 +1,293 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "mib.h" +#include "port.h" +#include "file_delivery_app.h" +#include "app_control.h" +#include "stdlib.h" + + +int create_ssp_server_drivers(FTP *app) { + + void *error = NULL; + switch (app->remote_entity.type_of_network) + { + case posix_connectionless: + error = ssp_connectionless_server_task(app); + break; + case posix_connection: + error = ssp_connection_server_task(app); + break; + case csp_connectionless: + error = ssp_csp_connectionless_server_task(app); + break; + case csp_connection: + error = ssp_csp_connection_server_task(app); + break; + case generic: + error = ssp_generic_server_task(app); + break; + default: + ssp_printf("server couldn't start, 'type of network' not recognized\n"); + break; + } + if (error == NULL) { + return -1; + } + return 0; + +} + +static int create_ssp_client_drivers(Client *client) { + Remote_entity remote_entity = client->remote_entity; + + switch (remote_entity.type_of_network) + { + case posix_connectionless: + client->client_handle = ssp_thread_create(STACK_ALLOCATION, ssp_connectionless_client_task, client); + break; + case posix_connection: + client->client_handle = ssp_thread_create(STACK_ALLOCATION, ssp_connection_client_task, client); + break; + case csp_connectionless: + client->client_handle = ssp_thread_create(STACK_ALLOCATION, ssp_csp_connectionless_client_task, client); + break; + case csp_connection: + client->client_handle = ssp_thread_create(STACK_ALLOCATION, ssp_csp_connection_client_task, client); + break; + case generic: + client->client_handle = ssp_thread_create(STACK_ALLOCATION, ssp_generic_client_task, client); + break; + default: + ssp_printf("client couldn't start, 'type of network' not recognized\n"); + break; + } + if (client->client_handle == NULL) { + return -1; + } + return 0; +} + + +static void make_default_data(){ + + int error = ssp_mkdir("incomplete_requests"); + if (error < 0) { + ssp_error("couldn't make directory incomplete_requests it either already exists or there is an issue\n"); + } + + error = ssp_mkdir("mib"); + if (error < 0) { + ssp_error("couldn't make directory mib it either already exists or there is an issue\n"); + } + const char *peer_file_sat = "{\n\ + \"cfdp_id\": 1,\n\ + \"UT_address\" : 1,\n\ + \"UT_port\" : 20,\n\ + \"type_of_network\" : 3,\n\ + \"default_transmission_mode\" : 1,\n\ + \"MTU\" : 200,\n\ + \"total_round_trip_allowance\" : 10000,\n\ + \"async_NAK_interval\" : 1000,\n\ + \"transaction_inactivity_limit\" : 1500,\n\ + \"async_report_interval\" : 123,\n\ + \"immediate_nak_mode_enabled\" : 123,\n\ + \"prompt_transmission_interval\" : 123,\n\ + \"disposition_of_incomplete\" : 123,\n\ + \"CRC_required\" : 123,\n\ + \"keep_alive_discrepancy_limit\" : 123,\n\ + \"positive_ack_timer_expiration_limit\" : 123,\n\ + \"nak_timer_expiration_limit\" : 123,\n\ + \"async_keep_alive_interval\" : 123,\n\ + \"one_way_light_time\" : 123\n\ +}"; + + const char *peer_file_ground_station = "{\n\ + \"cfdp_id\": 10,\n\ + \"UT_address\" : 10,\n\ + \"UT_port\" : 1,\n\ + \"type_of_network\" : 3,\n\ + \"default_transmission_mode\" : 1,\n\ + \"MTU\" : 200,\n\ + \"total_round_trip_allowance\" : 10000,\n\ + \"async_NAK_interval\" : 1000,\n\ + \"transaction_inactivity_limit\" : 1500,\n\ + \"async_keep_alive_interval\" : 123,\n\ + \"async_report_interval\" : 123,\n\ + \"immediate_nak_mode_enabled\" : 123,\n\ + \"prompt_transmission_interval\" : 123,\n\ + \"disposition_of_incomplete\" : 123,\n\ + \"CRC_required\" : 0,\n\ + \"keep_alive_discrepancy_limit\" : 8,\n\ + \"positive_ack_timer_expiration_limit\" : 123,\n\ + \"nak_timer_expiration_limit\" : 123,\n\ + \"one_way_light_time\" : 123\n\ +}"; + + int fd = ssp_open("mib/peer_1.json", SSP_O_CREAT | SSP_O_RDWR | SSP_O_TRUNC); + if (fd < 0) { + if (fd == SSP_EEXIST) { + ssp_error("file exists\n"); + } + else + ssp_error("couldn't create default peer_0.json it either already exists or there is an issue\n"); + } else { + error = ssp_write(fd, peer_file_sat, strnlen(peer_file_sat, 1000)); + if (error < 0) { + ssp_error("couldn't write default file\n"); + } + } + + fd = ssp_open("mib/peer_10.json", SSP_O_CREAT | SSP_O_RDWR | SSP_O_TRUNC); + if (fd < 0) { + if (fd == SSP_EEXIST) { + ssp_error("file exists\n"); + } + else + ssp_error("couldn't create default peer_10.json it either already exists or there is an issue\n"); + } else { + error = ssp_write(fd, peer_file_ground_station, strnlen(peer_file_ground_station, 1000)); + if (error < 0) { + ssp_error("couldn't write default file\n"); + } + } +} + +int init_ftp(uint32_t my_cfdp_address, FTP *app) { + int error = 0; + + + //sanitize everything but the server_handle in case of race condition which sets the handler first. + void *handler = app->server_handle; + memset(app, 0, sizeof(FTP)); + app->server_handle = handler; + + make_default_data(); + + + Remote_entity remote_entity; + memset(&remote_entity, 0, sizeof(Remote_entity)); + + error = get_remote_entity_from_json(&remote_entity, my_cfdp_address); + if (error == -1) { + ssp_error("can't get configuration data, can't start server failed to start ftp.\n"); + return -1; + } + + app->packet_len = remote_entity.mtu; + app->buff = ssp_alloc(1, app->packet_len); + app->transaction_sequence_number = rand() % 255; + + if (app->buff == NULL) { + ssp_free(app); + return -1; + } + + app->my_cfdp_id = my_cfdp_address; + app->close = false; + app->remote_entity = remote_entity; + + app->active_clients = linked_list(); + if (app->active_clients == NULL) { + ssp_free(app->buff); + ssp_free(app); + return -1; + } + + app->request_list = linked_list(); + if (app->request_list == NULL){ + ssp_free(app->buff); + ssp_free(app); + app->active_clients->freeOnlyList(app->active_clients); + return -1; + } + + app->current_request = NULL; + ssp_printf("initializing ftp server task \n"); + app->initialized = true; + return create_ssp_server_drivers(app); +} + + +static void init_ftp_task(void *app){ + FTP *ap = (FTP *) app; + int error = init_ftp(ap->my_cfdp_id, ap); + if (error < 0) { + //task failed to start destroy task/thread + } +} + +void *create_ftp_task(uint32_t cfdp_id, FTP *app){ + + app->my_cfdp_id = cfdp_id; + void *handler = ssp_thread_create(STACK_ALLOCATION, init_ftp_task, app); + app->server_handle = handler; + + return handler; +} + + +Client *init_client(uint32_t dest_cfdp_id, uint32_t my_cfdp_id){ + + Remote_entity remote_entity; + int error = get_remote_entity_from_json(&remote_entity, dest_cfdp_id); + if (error < 0) { + ssp_error("couldn't get client remote_entity from mib\n"); + return NULL; + } + + Client *client = ssp_alloc(sizeof(Client), 1); + if (client == NULL) + return NULL; + + client->request_list = linked_list(); + if (client->request_list == NULL) { + ssp_free(client); + return NULL; + } + + client->packet_len = remote_entity.mtu; + client->buff = ssp_alloc(1, remote_entity.mtu); + if (client->buff == NULL){ + ssp_free(client); + client->request_list->freeOnlyList(client->request_list); + return NULL; + } + + client->close = 0; + client->remote_entity = remote_entity; + client->lock = NULL; + + get_header_from_mib(&client->pdu_header, remote_entity, my_cfdp_id); + + client->current_request = NULL; + return client; +} + +Client *ssp_client(uint32_t cfdp_id, FTP *app) { + + + Client *client = init_client(cfdp_id, app->my_cfdp_id); + if (client == NULL) { + return NULL; + } + + int error = create_ssp_client_drivers(client); + if (error < 0) { + ssp_cleanup_client(client); + return NULL; + } + + client->app = app; + error = app->active_clients->insert(app->active_clients, client, cfdp_id); + if (error == 0) { + ssp_cleanup_client(client); + ssp_printf("failed to add client to list of existing clients\n"); + return NULL; + } + return client; +} diff --git a/Program/src/filesystem_funcs.c b/Program/src/filesystem_funcs.c new file mode 100755 index 0000000..f32eb01 --- /dev/null +++ b/Program/src/filesystem_funcs.c @@ -0,0 +1,918 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "stdint.h" +#include "port.h" +#include "filesystem_funcs.h" +#include "jsmn.h" +#include "requests.h" +#include "utils.h" + +int get_file_size(char *source_file_name) { + + int fd = ssp_open(source_file_name, SSP_O_RDWR); + if (fd == -1){ + ssp_error("could not open file\n"); + return -1; + } + + int bytes = ssp_lseek(fd, 0, 2); + if (bytes == -1){ + ssp_error("could not seek file for file size\n"); + return -1; + } + + ssp_lseek(fd, 0, 0); + + if (ssp_close(fd) == -1){ + ssp_error("could not close file\n"); + return -1; + } + + return bytes; +} + +File *create_file(char *source_file_name, int clear_file_contents) { + + int fd = 0; + if (clear_file_contents){ + fd = ssp_open(source_file_name, SSP_O_RDWR | SSP_O_CREAT | SSP_O_TRUNC); + }else { + fd = ssp_open(source_file_name, SSP_O_RDWR | SSP_O_CREAT); + } + if (fd == -1){ + ssp_error("couldn't create file\n"); + fd = ssp_open(source_file_name, SSP_O_RDWR); + if (fd == -1) { + ssp_error("count not open file\n"); + return NULL; + } + } + + + int total_size = get_file_size(source_file_name); + if (total_size == -1){ + ssp_error("couldn't get file size\n"); + return NULL; + } + + File *file = ssp_alloc(1, sizeof(File)); + if (file == NULL) { + return NULL; + } + + file->fd = fd; + file->eof_checksum = 0; + file->next_offset_to_send = 0; + file->total_size = total_size; + file->partial_checksum = 0; + file->missing_offsets = linked_list(); + if (file->missing_offsets == NULL) { + ssp_free(file); + return NULL; + } + + return file; + +} + + +int does_file_exist(char *source_file_name) { + + int fd = ssp_open(source_file_name, SSP_O_RDWR); + if (fd == -1){ + ssp_printf("ERROR: couldn't open file\n"); + return -1; + } + if (ssp_close(fd) == -1){ + ssp_printf("ERROR: couldn't close file\n"); + return -1; + } + + return 1; +} + +//modifys the seek location, returns bytes read +int get_offset(File *file, void *buff, uint32_t buf_size, int offset) { + + if (offset >= file->total_size){ + ssp_error("offset greater than file size\n"); + return -1; + } + + if (ssp_lseek(file->fd, offset, SSP_SEEK_SET) == -1){ + ssp_error("could'nt set offset\n"); + } + + int bytes = ssp_read(file->fd, buff, buf_size); + if (bytes == -1){ + ssp_error("Could not read anything from file\n"); + } else if (bytes == 0) { + ssp_error("Bytes read 0\n"); + } + + return bytes; + +} + +int write_offset(File *file, void *buff, uint32_t size, uint32_t offset) { + + if (file == NULL) { + ssp_error("Could not write, File struct is not created\n"); + return -1; + } + + ssp_lseek(file->fd, (int) offset, SSP_SEEK_SET); + int bytes = ssp_write(file->fd, buff, (size_t) size); + + if (bytes == -1){ + ssp_error("Could not write\n"); + } + if (bytes < size && bytes >= 0){ + ssp_error("did not write all the bytes, this could be because the disk is full, or the file that was sent is empty!\n"); + } + return bytes; +} + +void ssp_free_file(void *file) { + + File *f = (File *) file; + f->missing_offsets->free(f->missing_offsets, ssp_free); + ssp_free(f); +} + + + +uint32_t calc_check_sum(char *data, uint32_t length) { + uint8_t remaining_bytes = length % 4; + uint32_t check_sum = 0; + uint32_t end = length - 4; + unsigned int i = 0; + uint32_t bytes_to_add = 0; + + for (i = 0; i < end; i+= 4){ + bytes_to_add = ssp_htonl(*(uint32_t *) &data[i]); + check_sum += bytes_to_add; + } + + if (remaining_bytes){ + uint8_t last_chunk[4]; + memset(last_chunk, 0, 4); + + end = length - remaining_bytes; + i = 0; + for (i = 0; i < remaining_bytes; i++) { + last_chunk[i] = data[end + i]; + } + + check_sum += ssp_htonl(*((uint32_t*) &last_chunk)); + + } + + return check_sum; +} + + +//stack buffer is the size of the packet length +uint32_t check_sum_file(File *file, uint16_t stack_buffer) { + + char buff[stack_buffer]; + uint32_t checksum = 0; + uint32_t bytes_read = 0; + int i = 0; + + for (i = 0; i < file->total_size; i+= stack_buffer) { + + bytes_read = get_offset(file, buff, stack_buffer, i); + if (bytes_read > 0) + checksum += calc_check_sum(buff, bytes_read); + } + + return checksum; +} + +static int find_nak(void *element, void* args) { + + Offset *offset_in_list = (Offset *) element; + Offset *offset_to_insert = (Offset *) args; + + if (offset_to_insert->start >= offset_in_list->start \ + && offset_to_insert->start <= offset_in_list->end \ + && offset_to_insert->end <= offset_in_list->end \ + && offset_to_insert->end >= offset_in_list->start) { + return 1; + } + return 0; +} + +//add_first_offset should be in create file +int receive_offset(File *file, uint32_t offset_start, uint32_t offset_end) { + + List * nak_list = file->missing_offsets; + + Offset offset_to_insert; + offset_to_insert.start = offset_start; + offset_to_insert.end = offset_end; + + //iterate through the list, and return the list node + Node *node = nak_list->findNode(nak_list, -1, find_nak, &offset_to_insert); + + if (node == NULL){ + ssp_printf("offset already received, can't add new offset:%u end:%u\n", offset_start, offset_end); + return 0; + } + + Offset *offset_in_list = (Offset *) node->element; + + //remove node if both start and end are equal (remove at function) + if (offset_start == offset_in_list->start && offset_end == offset_in_list->end) { + node->next->prev = node->prev; + node->prev->next = node->next; + ssp_free(node->element); + ssp_free(node); + nak_list->count--; + return 1; + + //if new offset is in the start, but the end of the offset is smaller than list's end, change the list's node's start + } else if (offset_start == offset_in_list->start && offset_start < offset_in_list->end) { + offset_in_list->start = offset_end; + return 1; + + //if offset is at the list end, but the start of the offset is larger than list's start, move previous end down + } else if (offset_end == offset_in_list->end && offset_start > offset_in_list->start) { + offset_in_list->end = offset_start; + return 1; + } + //if the offset is inbetween the 'start' and the 'end' offsets in the list, add new offset, and make the + //end of the lists first offset the start of the incoming offset, and the start of the second, the end of the incoming + Offset *new_offset = ssp_alloc(1, sizeof(Offset)); + if (new_offset == NULL) { + return -1; + } + + new_offset->start = offset_end; + new_offset->end = offset_in_list->end; + offset_in_list->end = offset_start; + + + Node *cur = node; + Node *ne = createNode(new_offset, new_offset->start); + if (ne == NULL) { + ssp_free(new_offset); + return 0; + } + + ne->next = cur->next; + ne->prev = cur; + cur->next = ne; + ne->next->prev = ne; + + nak_list->count++; + + return 1; + +} + +int add_first_offset(File *file, uint32_t file_size){ + Offset *offset = ssp_alloc(1, sizeof(Offset)); + if (offset == NULL) + return -1; + + offset->end = file_size; + offset->start = 0; + file->missing_offsets->insert(file->missing_offsets, offset, file_size); + return 1; +} + +File *create_temp_file(char *file_name, uint32_t size) { + File *file = create_file(file_name, 1); + file->is_temp = 1; + file->total_size = size; + + ssp_printf("mode acknowledged, building offset map\n"); + int error = add_first_offset(file, size); + if (error < 0) { + ssp_free_file(file); + return NULL; + } + return file; +} + +int change_tempfile_to_actual(char *temp, char *destination_file_name, uint32_t file_size, File *file) { + + ssp_printf("renaming %s to: %s\n", temp, destination_file_name); + ssp_rename(temp, destination_file_name); + + Offset* offset = (Offset*)file->missing_offsets->pop(file->missing_offsets); + if (offset == NULL) { + ssp_printf("no last node to pop\n"); + return -1; + } + + + offset->end = file_size; + file->missing_offsets->push(file->missing_offsets, offset, file_size); + file->is_temp = 0; + file->total_size = file_size; + return 0; +} + +int read_json(char *file_name, int (*callback)(char *key, char *value, void *params), void *params) { + + int number_of_tokens = 255; + jsmn_parser p; + jsmn_init(&p); + int error = 0; + char *close_file_error = "couldn't close file\n"; + + jsmntok_t tok[255]; + + int total_size = get_file_size(file_name); + + if (total_size < 0){ + ssp_error("couldn't get file size\n"); + return -1; + } + char *buff = ssp_alloc(total_size, sizeof(char)); + if (buff == NULL) + return -1; + + int fd = ssp_open(file_name, SSP_O_RDWR); + if (fd < 0) { + ssp_free(buff); + ssp_error("couldn't open file\n"); + return -1; + } + + int r = ssp_read(fd, buff, total_size); + if (r < 0) { + ssp_free(buff); + ssp_error("read failed\n"); + error = ssp_close(fd); + if (error) { + ssp_printf(close_file_error); + } + return -1; + } + + r = jsmn_parse(&p, buff, total_size, tok, number_of_tokens); + if (r < 0) { + ssp_free(buff); + ssp_error("Failed to parse JSON\n"); + error = ssp_close(fd); + if (error) { + ssp_printf(close_file_error); + } + return -1; + } + int i = 1; + for (i = 1; i < r; i++) { + + int key_size = tok[i].end - tok[i].start; + int value_size = tok[i+1].end - tok[i+1].start; + + char *key = ssp_alloc(key_size + 1, sizeof(char)); + if (key == NULL) { + error = ssp_close(fd); + if (error) { + ssp_printf(close_file_error); + } + ssp_free(buff); + return -1; + } + + key[key_size] = '\0'; + strncpy(key, &buff[tok[i].start], key_size); + + char *value = ssp_alloc(value_size + 1, sizeof(char)); + if (value == NULL) { + ssp_free(buff); + ssp_free(value); + error = ssp_close(fd); + if (error) { + ssp_printf(close_file_error); + } + return -1; + } + + value[value_size] = '\0'; + strncpy(value, &buff[tok[i+1].start], value_size); + + error = callback(key, value, params); + + ssp_free(key); + ssp_free(value); + i++; + + if (error < 0) { + error = ssp_close(fd); + if (error) { + ssp_printf(close_file_error); + } + return error; + } + + } + + + error = ssp_close(fd); + if (error) { + ssp_printf(close_file_error); + } + ssp_free(buff); + + return 0; +} + + +static int get_file_name(char *filename, uint32_t dest_cfdp_id, uint32_t cfdp_id, uint64_t trans) { + + char dir_name[MAX_PATH]; + ssp_snprintf(dir_name, MAX_PATH, "%s%u%s", "incomplete_requests/CFID:", dest_cfdp_id, "_requests"); + + int error = ssp_mkdir(dir_name); + if (error < 0) + return -1; + + ssp_snprintf(filename, MAX_PATH, "%s%u%s%u%s%u%s%llu%s", "incomplete_requests/CFID:", dest_cfdp_id, "_requests/dest_id:", dest_cfdp_id,":cfdp_id:", cfdp_id, ":trans:", trans, ".json"); + + return 1; +} + +int delete_saved_request(Request *req) { + char file_name[MAX_PATH]; + get_file_name(file_name, req->dest_cfdp_id, req->my_cfdp_id, req->transaction_sequence_number); + ssp_printf("deleting %s\n", file_name); + int error = ssp_remove(file_name); + return error; +} + + +int save_req_to_file(Request *req) { + + char file_name[255]; + memset(file_name, 0, sizeof(file_name)); + + get_file_name(file_name, req->dest_cfdp_id, req->my_cfdp_id, req->transaction_sequence_number); + + int error = write_request_json(req, file_name); + if (error == -1) { + ssp_printf("couldnt write request struct\n"); + return -1; + } + + return 0; +} + +int get_req_from_file(uint32_t dest_cfdp_id, uint64_t transaction_seq_num, uint32_t my_cfdp_id, Request *req) { + + char file_name[255]; + + get_file_name(file_name, dest_cfdp_id, my_cfdp_id, transaction_seq_num); + + int error = does_file_exist(file_name); + if (error < 0) { + return -1; + } + + error = get_request_from_json (req, file_name); + if (error < 0) { + ssp_error("couldn't get json file\n"); + return -1; + } + + return 0; +} + + +static int parse_json_missing_offset_list(List *list, char *value) { + + int i = 0; + char *parse_string = (char *) value; + int len = strnlen(parse_string, 10000); + + int value_length = 15; + char tmp_offset[value_length]; + char start[value_length]; + int value_index = 0; + + Offset *offset; + int comma_number = 0; + + //add comma at end for algorithm + parse_string[len-1] = ','; + + for (i = 1; i < len; i++) { + + if (parse_string[i] == ',') { + tmp_offset[value_index] = '\0'; + value_index = 0; + comma_number++; + + if (comma_number % 2 == 0) { + offset = ssp_alloc(1, sizeof(Offset)); + if (offset == NULL) { + ssp_printf("memory allocation failed\n"); + return -1; + } + offset->start = ssp_atol(start); + offset->end = ssp_atol(tmp_offset); + + list->push(list, offset, -1); + continue; + } + + memcpy(start, tmp_offset, value_length); + continue; + } + + tmp_offset[value_index] = parse_string[i]; + value_index++; + + } + return 0; +} + + + +enum { + REQ_my_cfdp_id, + REQ_dest_cfdp_id, + REQ_transaction_sequence_number, + REQ_file_size, + REQ_source_file_name, + REQ_destination_file_name, + REQ_transmission_mode, + REQ_paused, + REQ_resent_eof, + REQ_resent_finished, + REQ_sent_first_data_round, + REQ_local_entity_EOF_sent_indication, + REQ_local_entity_EOF_recv_indication, + REQ_local_entity_file_segment_recv_indication, + REQ_local_entity_transaction_finished_indication, + REQ_local_entity_suspended_indication, + REQ_local_entity_resumed_indication, + REQ_local_entity_Metadata_recv_indication, + REQ_local_entity_Metadata_sent_indication, + REQ_messages_to_user, + REQ_file_is_temp, + REQ_file_next_offset_to_send, + REQ_file_total_size, + REQ_file_partial_checksum, + REQ_file_eof_checksum, + REQ_file_missing_offsets, + REQ_TOTAL +}; + +static char *parse_list[REQ_TOTAL] = { + "my_cfdp_id", + "dest_cfdp_id", + "transaction_sequence_number", + "file_size", + "source_file_name", + "destination_file_name", + "transmission_mode", + "paused", + "resent_eof", + "resent_finished", + "sent_first_data_round", + "local_entity.EOF_sent_indication", + "local_entity.EOF_recv_indication", + "local_entity.file_segment_recv_indication", + "local_entity.transaction_finished_indication", + "local_entity.suspended_indication", + "local_entity.resumed_indication", + "local_entity.Metadata_recv_indication", + "local_entity.Metadata_sent_indication", + "messages_to_user", + "file.is_temp", + "file.next_offset_to_send", + "file.total_size", + "file.partial_checksum", + "file.eof_checksum", + "file.missing_offsets" +}; + +static int check_null_file(File *file){ + if (file == NULL) { + ssp_printf("the json has file data, but the request struct has no file initialized\n"); + return 1; + } + return 0; +} + +static int parse_json_request(char *key, char *value, void *params) { + + int len = 0; + int i = 0; + int error = 0; + + Request *req = (Request *) params; + + for (i = 0; i < REQ_TOTAL; i++) { + len = strnlen(parse_list[i], 50); + + //ssp_printf("parsing %s\n", parse_list[i]); + if (strncmp(key, parse_list[i], len) != 0) + continue; + + switch (i) + { + case REQ_my_cfdp_id: + req->my_cfdp_id = ssp_atol(value); + break; + case REQ_dest_cfdp_id: + req->dest_cfdp_id = ssp_atol(value); + break; + case REQ_transaction_sequence_number: + req->transaction_sequence_number = ssp_atoll(value); + break; + case REQ_file_size: + req->file_size = ssp_atol(value); + break; + case REQ_source_file_name: + strncpy(req->source_file_name, (char *) value, MAX_PATH); + break; + case REQ_destination_file_name: + strncpy(req->destination_file_name, (char *) value, MAX_PATH); + break; + case REQ_transmission_mode: + req->transmission_mode = ssp_atol(value); + break; + case REQ_paused: + req->paused = ssp_atol(value); + break; + case REQ_resent_eof: + req->resent_eof = ssp_atol(value); + break; + case REQ_resent_finished: + req->resent_finished = ssp_atol(value); + break; + case REQ_sent_first_data_round: + req->sent_first_data_round = ssp_atol(value); + break; + case REQ_local_entity_EOF_sent_indication: + req->local_entity.EOF_sent_indication = ssp_atol(value); + break; + case REQ_local_entity_EOF_recv_indication: + req->local_entity.EOF_recv_indication = ssp_atol(value); + break; + case REQ_local_entity_file_segment_recv_indication: + req->local_entity.file_segment_recv_indication = ssp_atol(value); + break; + case REQ_local_entity_transaction_finished_indication: + req->local_entity.transaction_finished_indication = ssp_atol(value); + break; + case REQ_local_entity_suspended_indication: + req->local_entity.suspended_indication = ssp_atol(value); + break; + case REQ_local_entity_resumed_indication: + req->local_entity.resumed_indication = ssp_atol(value); + break; + case REQ_local_entity_Metadata_recv_indication: + req->local_entity.Metadata_recv_indication = ssp_atol(value); + break; + case REQ_local_entity_Metadata_sent_indication: + req->local_entity.Metadata_sent_indication = ssp_atol(value); + break; + case REQ_messages_to_user: + //req->messages_to_user = ssp_atol(value); + break; + case REQ_file_is_temp: + if (check_null_file(req->file)) { + req->file = create_file(req->destination_file_name, false); + if (req->file == NULL) + return -1; + } + req->file->is_temp = ssp_atol(value); + break; + case REQ_file_next_offset_to_send: + req->file->next_offset_to_send = ssp_atol(value); + break; + case REQ_file_total_size: + req->file->total_size = ssp_atol(value); + break; + case REQ_file_partial_checksum: + req->file->partial_checksum = ssp_atol(value); + break; + case REQ_file_eof_checksum: + req->file->eof_checksum = ssp_atol(value); + break; + case REQ_file_missing_offsets: + error = parse_json_missing_offset_list(req->file->missing_offsets, value); + if (error < 0) { + ssp_printf("ERROR %d\n", error); + return error; + } + break; + case REQ_TOTAL: + break; + default: + break; + } + } + + return 0; +} + + +static struct json_write_callback { + int error; + int fd; + int bytes_written; +}; + +static void save_file_callback_json(Node *node, void *element, void *param) { + + char *error_message = "failed to write offset\n"; + + struct json_write_callback *p = (struct json_write_callback *) param; + if (p->error < 0){ + return; + } + + Offset *offset = (Offset *)element; + char buff[100]; + + int bytes_added = ssp_snprintf(buff, sizeof(buff), "%d,%d,", offset->start, offset->end); + + int err = ssp_write(p->fd, buff, bytes_added); + + if (err < 0) { + ssp_error(error_message); + p->error = err; + return; + } + p->bytes_written += err; + +} + +static int add_file_json(int fd, File *file) { + + + char buff[500]; + int size = sizeof(buff); + + ssp_snprintf(buff, size, "\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":[", + parse_list[REQ_file_is_temp], file->is_temp, + parse_list[REQ_file_next_offset_to_send], file->next_offset_to_send, + parse_list[REQ_file_total_size], file->total_size, + parse_list[REQ_file_partial_checksum], file->partial_checksum, + parse_list[REQ_file_eof_checksum], file->eof_checksum, + parse_list[REQ_file_missing_offsets]); + + int bytes = ssp_write(fd, buff, strnlen(buff, sizeof(buff))); + if (bytes < 0) { + ssp_printf("couldn't write file\n"); + return bytes; + } + + struct json_write_callback p = { + 0, + fd, + bytes, + }; + + file->missing_offsets->iterate(file->missing_offsets, save_file_callback_json, &p); + if (p.error < 0) { + ssp_printf("couldn't write file offsets\n"); + return -1; + } + + if (ssp_lseek(fd, -1, SSP_SEEK_END) == -1){ + ssp_error("could'nt set offset\n"); + } + + p.bytes_written += ssp_write(fd, "],\n", 3); + if (bytes < 0) { + ssp_printf("couldn't write file\n"); + return bytes; + } + + return p.bytes_written; + +} + +static int add_end_of_json(int fd) { + + if (ssp_lseek(fd, -2, SSP_SEEK_END) == -1){ + ssp_error("could'nt set offset\n"); + } + + int err = ssp_write(fd, "\n\ +}", 2); + if (err < 0) { + return -1; + } + return 1; +} + +static int add_base_req_json(int fd, Request *req){ + + char buff[1000]; + int size = sizeof(buff); + + ssp_snprintf(buff, size, "{\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%llu,\n\ + \"%s\":%d,\n\ + \"%s\":\"%s\",\n\ + \"%s\":\"%s\",\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":%d,\n\ + \"%s\":[],\n", + parse_list[REQ_my_cfdp_id], req->my_cfdp_id, + parse_list[REQ_dest_cfdp_id], req->dest_cfdp_id, + parse_list[REQ_transaction_sequence_number], req->transaction_sequence_number, + parse_list[REQ_file_size], req->file_size, + + parse_list[REQ_source_file_name], req->source_file_name, + parse_list[REQ_destination_file_name], req->destination_file_name, + parse_list[REQ_transmission_mode], req->transmission_mode, + parse_list[REQ_paused], req->paused, + parse_list[REQ_resent_eof], req->resent_eof, + parse_list[REQ_resent_finished], req->resent_finished, + parse_list[REQ_sent_first_data_round], req->sent_first_data_round, + parse_list[REQ_local_entity_EOF_sent_indication], req->local_entity.EOF_sent_indication, + parse_list[REQ_local_entity_EOF_recv_indication], req->local_entity.EOF_recv_indication, + parse_list[REQ_local_entity_file_segment_recv_indication], req->local_entity.file_segment_recv_indication, + parse_list[REQ_local_entity_transaction_finished_indication], req->local_entity.transaction_finished_indication, + parse_list[REQ_local_entity_suspended_indication], req->local_entity.suspended_indication, + parse_list[REQ_local_entity_resumed_indication], req->local_entity.resumed_indication, + parse_list[REQ_local_entity_Metadata_recv_indication], req->local_entity.Metadata_recv_indication, + parse_list[REQ_local_entity_Metadata_sent_indication], req->local_entity.Metadata_sent_indication, + parse_list[REQ_messages_to_user] + ); + + + int bytes = ssp_write(fd, buff, strnlen(buff, sizeof(buff))); + if (bytes < 0) { + ssp_printf("couldn't write file\n"); + return bytes; + } + + return bytes; +} + +int write_request_json (Request *req, char *file_name) { + + if (req == NULL) { + return -1; + } + + int fd = ssp_open(file_name, SSP_O_RDWR | SSP_O_CREAT | SSP_O_TRUNC); + if (fd == -1) { + ssp_error("count not open file\n"); + return fd; + } + + int bytes_added = add_base_req_json(fd, req); + + if (req->file != NULL) + bytes_added += add_file_json(fd, req->file); + + + add_end_of_json(fd); + + if (ssp_close(fd) == -1){ + ssp_error("could not close file\n"); + return -1; + } + return 0; +} + + +int get_request_from_json (Request *req, char *file_name) { + + int error = read_json(file_name, parse_json_request, req); + + if (error < 0) { + ssp_error("json parsing failed\n"); + return -1; + } + + return 0; +} + diff --git a/Program/src/generic_server_provider.c b/Program/src/generic_server_provider.c new file mode 100755 index 0000000..32c1ee3 --- /dev/null +++ b/Program/src/generic_server_provider.c @@ -0,0 +1,115 @@ + +//strong dependency for FreeRTOS here +#include "generic_server_provider.h" + +#ifdef FREE_RTOS_PORT +#include "FreeRTOS.h" +#include "queue.h" +#include "port.h" +#include "csp/csp.h" + +QueueHandle_t xQueueFtpServerReceive; +QueueHandle_t xQueueFtpClientReceive; +QueueHandle_t sendQueue; + + +void _init_queues(int server_size, int client_size){ + if (xQueueFtpServerReceive == NULL) + xQueueFtpServerReceive = xQueueCreate(server_size, sizeof (csp_packet_t*)); + if (xQueueFtpClientReceive == NULL) + xQueueFtpClientReceive = xQueueCreate(client_size, sizeof (csp_packet_t*)); +} + +void csp_generic_server( + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *app), + int (*onTimeOut)(void *app), + int (*checkExit)(void *app), + void (*onExit)(void *app), + void *app) +{ + FTP *ftp = (FTP *)app; + csp_packet_t *packet; + _init_queues(10, 10); + + for (;;) { + + bool is_not_empty = xQueueReceive(xQueueFtpServerReceive, packet, 100); + + if (!is_not_empty) + onTimeOut(app); + return; + + if (get_exit() || checkExit(app)) + break; + + if (onRecv(-1, packet->data, packet->length, packet, sizeof(csp_packet_t), 0, app) == -1) + ssp_printf("recv failed\n"); + + csp_buffer_free(packet); + + } + + onExit(app); +} + + + + +void csp_generic_client(uint8_t dest_id, uint8_t dest_port, uint8_t my_port, uint32_t packet_len, + int (*onSend)(int sfd, void *addr, uint32_t size_of_addr, void *onSendParams), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *onRecvParams) , + int (*checkExit)(void *checkExitParams), + void (*onExit)(void *params), + void *params) +{ + + csp_packet_t packet_send; + packet_send.id.dst=dest_id; + packet_send.id.src=csp_get_address(); + packet_send.id.dport=dest_port; + packet_send.id.sport=my_port; + + csp_packet_t *packet_recv; + + for (;;) { + if (get_exit() || checkExit(params)){ + ssp_printf("exiting client thread\n"); + break; + } + + onSend(-1, &packet_send, sizeof(csp_packet_t), params); + + bool is_not_empty = xQueueReceive(xQueueFtpServerReceive, packet_recv, 100); + + if (!is_not_empty) + return; + + if (onRecv(-1, packet_recv->data, packet_recv->length, NULL, packet_recv, sizeof(csp_packet_t), params) == -1) + ssp_printf("recv failed\n"); + + csp_buffer_free(packet_recv); + + } + + onExit(params); +} +#else +//for compiling without freeRTOS, but these functions do nothing. +void csp_generic_server( + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *app), + int (*onTimeOut)(void *app), + int (*checkExit)(void *app), + void (*onExit)(void *app), + void *app){ + ssp_printf("free Rtos not defined, can't do shit\n"); + } + +void csp_generic_client(uint8_t dest_id, uint8_t dest_port, uint8_t my_port, uint32_t packet_len, + int (*onSend)(int sfd, void *addr, uint32_t size_of_addr, void *onSendParams), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *onRecvParams) , + int (*checkExit)(void *checkExitParams), + void (*onExit)(void *params), + void *params){ + ssp_printf("free Rtos not defined, can't do shit\n"); + } +#endif diff --git a/Program/src/list.c b/Program/src/list.c new file mode 100755 index 0000000..366fde0 --- /dev/null +++ b/Program/src/list.c @@ -0,0 +1,328 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "list.h" +#include "port.h" + +/*------------------------------------------------------------------------------ + This function creates a new node to add into the linked list, returns the + new node, or NULL if failed +------------------------------------------------------------------------------*/ + +Node *createNode(void *element, int id) +{ + Node *newNode = ssp_alloc(sizeof(Node), 1); + if (newNode == NULL) { + return NULL; + } + newNode->element = element; + newNode->id = id; + return newNode; +} + +/*------------------------------------------------------------------------------ + This function creates a new node to add into the linked list, returns the + new node. +------------------------------------------------------------------------------*/ + + +static void freeNode(Node *node) { + if (node != NULL) + ssp_free(node); +} + +static void *pop(List *list) { + + if (list->count == 0) + return NULL; + + Node *last_data_node = list->tail->prev; + + Node *prev = last_data_node->prev; + prev->next = list->tail; + list->tail->prev = prev; + + list->count--; + void *element = last_data_node->element; + freeNode(last_data_node); + return element; +} + + +/*------------------------------------------------------------------------------ + This function creates a new node to add into the beginning of the + linked list, returns the new node. +------------------------------------------------------------------------------*/ + +static int insert(List *list, void *element, int id) { + + Node *head = list->head; + Node *node = createNode(element, id); + if (node == NULL) { + return 0; + } + + node->next = head->next; + node->next->prev = node; + node->prev = head; + + head->next = node; + list->count++; + return 1; +} + +/*------------------------------------------------------------------------------ + This function adds a new element into the linked list. returns 1 if success + 0 if failed. +------------------------------------------------------------------------------*/ + +static int push(List *list, void *element, int id) +{ + + Node *newNode = createNode(element, id); + if (newNode == NULL) { + return 0; + } + Node *tail = list->tail; + + newNode->next = tail; + newNode->prev = tail->prev; + + tail->prev->next = newNode; + tail->prev = newNode; + + if (list->count == 0) { + list->head->next = newNode; + } + + list->count++; + return 1; +} + +/*------------------------------------------------------------------------------ + This function will print out the list if given a callback that is designed + to print out an element. +------------------------------------------------------------------------------*/ + +static void iterate(List *list, void (*f)(Node *node, void *element, void *args), void *args) +{ + Node *cur = list->head->next; + Node *next; + while (cur->next != NULL && cur != list->tail) + { + next = cur->next; + f(cur, cur->element, args); + cur = next; + } +} + +//deletes the node, and returns the object, (motifys the list) +static void *removeNode(List *list, Node *node) { + + + Node *previous = node->prev; + Node *next = node->next; + + previous->next = next; + next->prev = previous; + + void *element = node->element; + list->freeNode(node); + list->count--; + return element; +} + + +/*------------------------------------------------------------------------------ + This function removes an element from the linked list, returns the node stored if success + and NULL if item not found. it can use either an id, or callback to find the + element (callback can be the find function) +------------------------------------------------------------------------------*/ + +static void *removeElement(List *list, int id, int (*f)(void *element, void *args), void *args) +{ + Node *cur = list->head; + int found_with_func = 0; + int found_with_id = 0; + while (cur->next != NULL) + { + if (f != NULL) + found_with_func = f(cur->element, args); + + if (id >= 0 && id == cur->id) + found_with_id = 1; + + if (found_with_func || found_with_id) + { + void *element = removeNode(list, cur); + return element; + } + cur = cur->next; + } + return NULL; +} + + + +/*------------------------------------------------------------------------------ + frees the linked list. Takes a free function that is a function pointer to + a function that frees and elemnent. Returns nothing, +------------------------------------------------------------------------------*/ + +static void freeList(List *list, void (*f)(void *element)) +{ + Node *cur = list->head->next; + + while (cur->next != NULL) + { + Node *n = cur; + cur = cur->next; + f(n->element); + freeNode(n); + } + ssp_free(list->head); + ssp_free(list->tail); + ssp_free(list); +} + + +static void freeNodes(List *list) { + + Node *cur = list->head->next; + + while (cur->next != NULL) + { + Node *n = cur; + cur = cur->next; + freeNode(n); + } + ssp_free(list->head); + ssp_free(list->tail); + ssp_free(list); + +} + +/*------------------------------------------------------------------------------ + This function finds an element, returns and element on success and NULL on + failure. The return value should be cast to the element type. can search with + either a callback, or id +------------------------------------------------------------------------------*/ + +static void *findElement(List *list, int id, int (*f)(void *element, void *args), void *args) +{ + + Node *cur = list->head->next; + int found_with_func = 0; + int found_with_id = 0; + while (cur->next != NULL) + { + if (f != NULL) + found_with_func = f(cur->element, args); + + if(id >= 0 && cur->id == id) + found_with_id = 1; + + if (found_with_func || found_with_id){ + return cur->element; + } + cur = cur->next; + } + return NULL; +} + +static int insertAt(List *list, void *element, int id, int (*f)(void *element, void *args), void *args) { + + Node *cur = list->head->next; + int found_with_func = 0; + int found_with_id = 0; + while (cur->next != NULL) + { + if (f != NULL) + found_with_func = f(cur->element, args); + + if(id >= 0 && cur->id == id) + found_with_id = 1; + + if (found_with_func || found_with_id) { + Node *new = createNode(element, id); + new->next = cur; + new->prev = cur->prev; + new->prev->next = new; + cur->prev = new; + list->count++; + } + + cur = cur->next; + } + return -1; +} + + +static Node *findNode(List *list, int id, int (*f)(void *element, void *args), void *args) { + + Node *cur = list->head->next; + int found_with_func = 0; + int found_with_id = 0; + while (cur->next != NULL) + { + if (f != NULL) + found_with_func = f(cur->element, args); + + if(id >= 0 && cur->id == id) + found_with_id = 1; + + if (found_with_func || found_with_id){ + return cur; + } + cur = cur->next; + } + return NULL; +} +//see header file return NULL if fails + +List *linked_list() +{ + List *newList = ssp_alloc(sizeof(List), 1); + if (newList == NULL) + return NULL; + + newList->head = createNode(NULL, 0); + if (newList->head == NULL) { + ssp_free(newList); + return NULL; + } + + newList->tail = createNode(NULL, 0); + if (newList->tail == NULL) { + ssp_free(newList->head); + ssp_free(newList); + return NULL; + } + + Node *tail = newList->tail; + Node *head = newList->head; + + tail->prev = head; + tail->next = NULL; + head->next = tail; + head->prev = NULL; + + newList->count = 0; + newList->push = push; + newList->remove = removeElement; + newList->iterate = iterate; + newList->free = freeList; + newList->insert = insert; + newList->pop = pop; + newList->find = findElement; + newList->insertAt = insertAt; + newList->findNode = findNode; + newList->freeNode = freeNode; + newList->freeOnlyList = freeNodes; + newList->removeNode = removeNode; + + return newList; +} diff --git a/Program/src/main.c b/Program/src/main.c new file mode 100755 index 0000000..219a4c3 --- /dev/null +++ b/Program/src/main.c @@ -0,0 +1,448 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#include "port.h" +//Have to include these files +#include +//for types +#include "protocol_handler.h" +//for conf +#include "utils.h" +//for put request +#include "requests.h" + +#include "types.h" +//for main app +#include "file_delivery_app.h" +//for signal handler, because its nice +#include "posix_server_provider.h" +//for ssp_thread_join, can use p_thread join on linux +#include "utils.h" +#include "app_control.h" +#include "mib.h" +#include "list.h" + +#ifdef CSP_NETWORK + #include + #include +#endif + +/*------------------------------------------------------------------------------ + Purpose: This struct if our configuration for this program, these elements + are set with getopt +------------------------------------------------------------------------------*/ +typedef struct config +{ + unsigned int timer; + uint32_t client_cfdp_id; + uint32_t my_cfdp_id; + uint8_t verbose_level; + uint32_t baudrate; + char *uart_device; + bool unackowledged_mode; + List *file_list; + +} Config; + +typedef enum request_type { + REQUEST_GET, + REQUEST_PUT +} Request_type; + + +typedef struct file_path_pair +{ + char dest_name[255]; + char src_name[255]; + Request_type type; + +} File_path_pair; + + + +//sets file_name and returns the length of the file_name +static int get_file_name(char *buff, int len, char *file_name) { + int i = 0; + for (i = 0; i < len; i++) { + if (buff[i] == ' ' || buff[i] == '\0') { + file_name[i] = '\0'; + break; + } + file_name[i] = buff[i]; + } + + return i; +} + +//parses the file name paths between source and destination filenames +int parse_file_path(char *buff, int len, char *file_name) { + int i = 0; + for (i = 0; i < len; i++) { + if (buff[i] == '|') { + file_name[i] = '\0'; + break; + } + file_name[i] = buff[i]; + } + return i; +} + + + +List *parse_file_list(char *target){ + + int buff_size = 5000; + int len = strnlen(target, buff_size); + char string_name[buff_size]; + char source_name[255]; + char dest_name[255]; + + List *list = linked_list(); + + int i = 0; + int j = 0; + int path_len = 0; + File_path_pair pair; + memset(&pair, 0, sizeof(File_path_pair)); + + while (i < len) { + i += get_file_name(&target[i], len, string_name) + 1; + if (strncmp(string_name, "PUT", 4) == 0) { + + pair.type = REQUEST_PUT; + //ssp_printf("PUT REQUEST!!!!\n"); + } else if (strncmp(string_name, "GET", 4) == 0) { + + pair.type = REQUEST_GET; + //ssp_printf("GET REQUEST!!!!\n"); + } else { + + //ssp_printf("file name: %s\n", string_name); + j = 0; + path_len = strnlen(string_name, 255); + + j += parse_file_path(&string_name[j], path_len, source_name) + 1; + //printf("source_name %s %d\n",source_name, j); + strncpy(pair.src_name, source_name, 255); + + j += parse_file_path(&string_name[j], path_len, dest_name); + //printf("dest_name %s %d\n",dest_name, j); + strncpy(pair.dest_name, dest_name, 255); + + File_path_pair *add = ssp_alloc(1, sizeof(File_path_pair)); + memcpy(add, &pair, sizeof(File_path_pair)); + + list->push(list, add, -1); + + } + } + + return list; +} + + +static Config *configuration(int argc, char **argv) +{ + int ch; + Config *conf = calloc(sizeof(Config), 1); + if (conf == NULL) + return NULL; + + conf->timer = 15; + conf->verbose_level = 0; + conf->client_cfdp_id = -1; + conf->my_cfdp_id = 0; + conf->baudrate = 9600; + conf->uart_device = NULL; + conf->unackowledged_mode = ACKNOWLEDGED_MODE; + + + uint32_t tmp; + char file_name[255]; + + while ((ch = getopt(argc, argv, "f: i: c: v: k: hu")) != -1) + { + switch (ch) + { + case 'i': + tmp = strtol(optarg, NULL, 10); + conf->my_cfdp_id = tmp; + break; + + case 'f': + conf->file_list = parse_file_list(optarg); + break; + + case 'v': + tmp = strtol(optarg, NULL, 10); + conf->verbose_level = (uint8_t) tmp; + break; + + case 'c': + tmp = strtol(optarg, NULL, 10); + conf->client_cfdp_id = tmp; + break; + + case 'k': + conf->uart_device = optarg; + break; + + case 'b': + tmp = strtol(optarg, NULL, 10); + conf->baudrate = tmp; + break; + + case 'h': + printf("\n-----------HELP MESSAGE------------\n"); + printf("\nusage: %s [options] \n\n", basename(argv[0])); + printf("Options: %s%s%s%s\n", + "-i \n", + "-c \n", + "-f list of file names eg, \"PUT local/path|/path/on/sat GET /path/on/sat|local/path ...\"\n", + "-v eg (1-3)\n" + "-k eg /dev/ttyUSB0\n" + "-b default is 9600" + "-h HelpMessage"); + + printf("Default port number is 1111\n"); + printf("\n---------------END----------------\n"); + break; + + case ':': + printf("missing argument\n"); + break; + + default: + printf("\ngot something not found using default\n"); + break; + } + } + return conf; +} + + +static int init_csp_stuff(Config conf){ + + #ifdef CSP_NETWORK + + csp_debug_level_t debug_level = 0; + // enable/disable debug levels + /* + for (csp_debug_level_t i = 0; i <= CSP_LOCK; ++i) { + csp_debug_set_level(i, (i <= debug_level) ? true : false); + } + */ + + + Remote_entity remote_entity; + int error = get_remote_entity_from_json(&remote_entity, conf.my_cfdp_id); + if (error < 0) { + ssp_error("couldn't get client remote_entity from mib\n"); + return 1; + } + + csp_conf_t csp_conf; + csp_conf_get_defaults(&csp_conf); + csp_conf.buffers = 4096; + csp_conf.address = remote_entity.UT_address; + csp_conf.buffer_data_size = 250; + + error = csp_init(&csp_conf); + if (error != CSP_ERR_NONE) { + csp_log_error("csp_init() failed, error: %d", error); + exit(1); + } + /* + for (int i = 0; i <= CSP_LOCK; ++i) { + csp_debug_set_level(i, true); + } + */ + + // Start router task with 10000 bytes of stack (priority is only supported on FreeRTOS) + csp_route_start_task(500, 0); + + // Add interface(s) + csp_iface_t * default_iface = NULL; + if (conf.uart_device != NULL) { + csp_usart_conf_t uart_conf = {.device = conf.uart_device, + .baudrate = conf.baudrate, // supported on all platforms + .databits = 8, + .stopbits = 2, + .paritysetting = 0, + .checkparity = 0}; + error = csp_usart_open_and_add_kiss_interface(&uart_conf, CSP_IF_KISS_DEFAULT_NAME, &default_iface); + if (error != CSP_ERR_NONE) { + ssp_printf("failed to add KISS interface, error: %d", error); + exit(1); + } + } + + csp_rtable_set(CSP_DEFAULT_ROUTE, 0, default_iface, CSP_NO_VIA_ADDRESS); + //printf("Connection table\r\n"); + //csp_conn_print_table(); + + //printf("Interfaces\r\n"); + //csp_route_print_interfaces(); + + //printf("Route table\r\n"); + //csp_route_print_table(); + #endif + return 0; +} + + +static int confirm(){ + char buff[100]; + memset(buff, 0, 100); + fgets(buff, 100, stdin); + if ((buff[0] == 'y' || buff[0] == 'Y') && buff[1] == '\0') { + return 1; + } + else if ((buff[0] == 'n' || buff[0] == 'N') && buff[1] == '\0') { + return 0; + + } else { + return -1; + } +} + + +static void input_daemon(uint32_t client_id, FTP *app){ + + int buff_len = 25000; + char input[buff_len]; + memset(input, 0, buff_len); + + + char src_file[MAX_PATH]; + char dest_file[MAX_PATH]; + + for (;;) { + + printf("send a file? type 'PUT ' or 'GET '\n"); + memset(src_file, 0 , MAX_PATH); + memset(dest_file, 0 , MAX_PATH); + + fgets(input, buff_len, stdin); + input[strlen(input)-1]='\0'; + + + if (get_exit()) { + break; + } + else if (strncmp(input, "exit", 5) == 0) { + set_exit(); + break; + } + + if (strncmp(input, "PUT ", 4) == 0) { + + int len = get_file_name(&input[4], buff_len, src_file); + len = get_file_name(&input[len + 5], buff_len, dest_file); + + while(1) { + printf("put source_file:%s destination_file:%s? (y/n): ", src_file, dest_file); + int confirming = confirm(); + if (confirming) { + start_request(put_request(client_id, src_file, dest_file, ACKNOWLEDGED_MODE, app)); + break; + } else if (confirming == 0) { + break; + } else { + printf("please type either 'Y' or 'N'"); + } + } + + break; + + } else if (strncmp(input, "GET ", 4) == 0) { + + int len = get_file_name(&input[4], buff_len, dest_file); + len = get_file_name(&input[len + 5], buff_len, src_file); + while(1) { + printf("get destination_file:%s source_file:%s?(y/n)\n", src_file, dest_file); + int confirming = confirm(); + if (confirming) { + start_request(get_request(client_id, src_file, dest_file, ACKNOWLEDGED_MODE, app)); + break; + } else if (confirming == 0) { + break; + } else { + printf("please type either 'Y' or 'N'"); + } + } + break; + } + } +} + +int main(int argc, char** argv) { + + + //exit handler for the main thread; + prepareSignalHandler(); + + //get-opt configuration + Config *conf = configuration(argc, argv); + + + if (conf->my_cfdp_id == 0){ + printf("can't start server, please select an ID (-i #) and client ID (-c #) \n"); + return 1; + } + + init_csp_stuff(*conf); + + FTP *app = ssp_alloc(1, sizeof(FTP)); + + uint32_t id = conf->my_cfdp_id; + void *handler = create_ftp_task(id, app); + if (handler == NULL) { + return 1; + } + + while(app->my_cfdp_id != id); + sleep(1); + + uint32_t client_id = conf->client_cfdp_id; + if (client_id != -1) { + while(conf->file_list->count > 0) { + File_path_pair *file_names = conf->file_list->pop(conf->file_list); + + if (file_names->type == REQUEST_PUT) { + ssp_printf("putting file %s path %s\n", file_names->src_name, file_names->dest_name); + start_request(put_request(client_id, file_names->src_name, file_names->dest_name, conf->unackowledged_mode, app)); + } + else if (file_names->type == REQUEST_GET) { + ssp_printf("getting file %s to path %s\n", file_names->src_name, file_names->dest_name); + + start_request(get_request(client_id, file_names->src_name, file_names->dest_name, conf->unackowledged_mode, app)); + } + free(file_names); + } + + while (app->active_clients->count != 0) { + sleep(1); + } + + ssp_printf("closing app\n"); + app->close = 1; + } + if (conf->file_list != NULL) + conf->file_list->freeOnlyList(conf->file_list); + + free(conf); + ssp_thread_join(handler); + + return 0; +} diff --git a/Program/src/makefile b/Program/src/makefile new file mode 100755 index 0000000..e66f503 --- /dev/null +++ b/Program/src/makefile @@ -0,0 +1,106 @@ + +PROJDIR = $(CURDIR)/.. +#PROJDIR = $(CURDIR)/../../../../../ +INCLUDE += -I$(PROJDIR)/include + +#---------------------------FOR FREE RTOS INTEGRATION--------------------------- +#path to source includes +#INCLUDE += -I$(PROJDIR)/Source/include + +#path to compiler includes (portmacro.h) +#INCLUDE += -I$(PROJDIR)/Source/portable/GCC/POSIX/ + +#path to FreeRTOSconfig +#INCLUDE += -I$(PROJDIR)/Project/ + +#------------------------------------------------------------------------------- +#---------------------------FOR CSP INTEGRATION--------------------------------- +#path to source includes +INCLUDE += -I$(PROJDIR)/libcsp/include/csp +INCLUDE += -I$(PROJDIR)/libcsp/include +INCLUDE += -I$(PROJDIR)/libcsp/build/include +INCLUDE += -I$(PROJDIR)/libcsp/src +INCLUDE += -I$(PROJDIR)/libcsp +#------------------------------------------------------------------------------- +CC = gcc + +#---------------------------File Names--------------------------- +CFILES += port.c +CFILES += file_delivery_app.c +CFILES += utils.c +CFILES += app_control.c +CFILES += csp_server_provider.c +CFILES += protocol_handler.c +CFILES += mib.c +CFILES += filesystem_funcs.c +CFILES += requests.c +CFILES += list.c +CFILES += packet.c +CFILES += generic_server_provider.c +CFILES += posix_server_provider.c #used for poxix port +#----------------------------test files------------------------- + +#entrypoint +MAIN = main + +#---------------------------Compiler Warnings--------------------------- +CWARNS += -W +CWARNS += -Wall +#CWARNS += -Werror +CWARNS += -Wextra +CWARNS += -Wformat +CWARNS += -Wmissing-braces +CWARNS += -Wno-cast-align +CWARNS += -Wparentheses +CWARNS += -Wshadow +CWARNS += -Wno-sign-compare +CWARNS += -Wswitch +CWARNS += -Wuninitialized +CWARNS += -Wunknown-pragmas +CWARNS += -Wunused-function +CWARNS += -Wunused-label +#CWARNS += -Wunused-parameter +CWARNS += -Wno-unused-parameter + +CWARNS += -Wunused-value +CWARNS += -Wunused-variable +CWARNS += -Wmissing-prototypes + +#---------------------------Libs--------------------------- +LINKFLAGS = +LIBS = -pthread +STATIC_FILES += $(PROJDIR)/libcsp/build/libcsp.a +#STATIC_FILES += $(PROJDIR)/FreeRtos + +#---------------------------Compiler flags--------------------------- + +#32 bit machines, leave this out if your machine is 64 bit +#CFLAGS += -m32 +CFLAGS += -m64 + +CFLAGS += -g -UUSE_STDIO -D__GCC_POSIX__=1 +CFLAGS += -pthread +CFLAGS += -DDEBUG=1 + +# MAX_NUMBER_OF_TASKS = max pthreads used in the POSIX port. +# Default value is 64 (_POSIX_THREAD_THREADS_MAX), the minimum number required by POSIX. +CFLAGS += -DMAX_NUMBER_OF_TASKS=300 +CFLAGS += $(INCLUDE) $(CWARNS) + +#---------------------------Build--------------------------- +OBJS_FILES = $(patsubst %.c, %.o, $(CFILES)) + +all: $(MAIN) + +.PHONY: clean lib + +$(MAIN): $(OBJS_FILES) $(STATIC_FILES) +# mv *.o obj + +lib: $(OBJS_FILES) + ar -rsc file_delivery_app.a $(OBJS_FILES) + +clean: + rm -f *.o $(MAIN) + + diff --git a/Program/src/mib.c b/Program/src/mib.c new file mode 100755 index 0000000..0949df9 --- /dev/null +++ b/Program/src/mib.c @@ -0,0 +1,177 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "mib.h" +#include "port.h" +#include "types.h" +#include "filesystem_funcs.h" +#include "packet.h" + + +//todo +int get_header_from_mib(Pdu_header *pdu_header, Remote_entity remote, uint32_t my_cfdp_id) { + + pdu_header->reserved_bit_0 = 0; + pdu_header->reserved_bit_1 = 0; + pdu_header->reserved_bit_2 = 0; + pdu_header->CRC_flag = remote.CRC_required; + pdu_header->direction = 1; + pdu_header->PDU_type = 0; + pdu_header->transaction_seq_num_len = 1; + pdu_header->length_of_entity_IDs = 1; + pdu_header->transmission_mode = remote.default_transmission_mode; + pdu_header->destination_id = remote.cfdp_id; + pdu_header->source_id = my_cfdp_id; + pdu_header->reserved_space_for_header = (pdu_header->length_of_entity_IDs * 2) + (pdu_header->transaction_seq_num_len) + PACKET_STATIC_HEADER_LEN; + return 0; +} + + +enum { + PARSE_cfdp_id, + PARSE_UT_address, + PARSE_UT_port, + PARSE_type_of_network, + PARSE_default_transmission_mode, + PARSE_one_way_light_time, + PARSE_total_round_trip_allowance, + PARSE_async_NAK_interval, + PARSE_async_keep_alive_interval, + PARSE_async_report_interval, + PARSE_immediate_nak_mode_enabled, + PARSE_prompt_transmission_interval, + PARSE_disposition_of_incomplete, + PARSE_CRC_required, + PARSE_mtu, + PARSE_keep_alive_discrepancy_limit, + PARSE_positive_ack_timer_expiration_limit, + PARSE_nak_timer_expiration_limit, + PARSE_transaction_inactivity_limit, + PARSE_TOTAL, +}; + +static char *parse_list[PARSE_TOTAL] = { + "cfdp_id" , + "UT_address", + "UT_port" , + "type_of_network", + "default_transmission_mode" , + "one_way_light_time" , + "total_round_trip_allowance" , + "async_NAK_interval", + "async_keep_alive_interval", + "async_report_interval" , + "immediate_nak_mode_enabled" , + "prompt_transmission_interval" , + "disposition_of_incomplete" , + "CRC_required" , + "MTU" , + "keep_alive_discrepancy_limit" , + "positive_ack_timer_expiration_limit" , + "nak_timer_expiration_limit" , + "transaction_inactivity_limit" +}; + + +static int parse_mib(char *key, char *value, void *params) { + + int len = 0; + int i = 0; + for (i = 0; i < PARSE_TOTAL; i++) { + len = strnlen(parse_list[i], 50); + + if (strncmp(key, parse_list[i], len) != 0) + continue; + + Remote_entity *remote = (Remote_entity *) params; + + switch (i) + { + case PARSE_cfdp_id: + remote->cfdp_id = ssp_atol(value); + break; + case PARSE_UT_address: + remote->UT_address = ssp_atol(value); + break; + case PARSE_UT_port: + remote->UT_port = ssp_atol(value); + break; + case PARSE_type_of_network: + remote->type_of_network = ssp_atol(value); + break; + case PARSE_default_transmission_mode: + remote->default_transmission_mode = ssp_atol(value); + break; + case PARSE_one_way_light_time: + remote->one_way_light_time = ssp_atol(value); + break; + case PARSE_total_round_trip_allowance: + remote->total_round_trip_allowance = ssp_atol(value); + break; + case PARSE_async_NAK_interval: + remote->async_NAK_interval = ssp_atol(value); + break; + case PARSE_async_keep_alive_interval: + remote->async_keep_alive_interval = ssp_atol(value); + break; + case PARSE_async_report_interval: + remote->async_report_interval = ssp_atol(value); + break; + case PARSE_immediate_nak_mode_enabled: + remote->immediate_nak_mode_enabled = ssp_atol(value); + break; + case PARSE_prompt_transmission_interval: + remote->prompt_transmission_interval = ssp_atol(value); + break; + case PARSE_disposition_of_incomplete: + remote->disposition_of_incomplete = ssp_atol(value); + break; + case PARSE_CRC_required: + remote->CRC_required = ssp_atol(value); + break; + case PARSE_mtu: + remote->mtu = ssp_atol(value); + break; + case PARSE_keep_alive_discrepancy_limit: + remote->keep_alive_discrepancy_limit = ssp_atol(value); + break; + case PARSE_positive_ack_timer_expiration_limit: + remote->positive_ack_timer_expiration_limit = ssp_atol(value); + break; + case PARSE_nak_timer_expiration_limit: + remote->nak_timer_expiration_limit = ssp_atol(value); + break; + case PARSE_transaction_inactivity_limit: + remote->transaction_inactivity_limit = ssp_atol(value); + break; + default: + break; + } + } + return 0; +} + +int get_remote_entity_from_json (Remote_entity *remote, uint32_t cfdp_id) { + + char file_name[50]; + memset(file_name, 0, 50); + ssp_snprintf(file_name, 50, "%s%d%s", "mib/peer_", cfdp_id, ".json"); + + int error = read_json(file_name, parse_mib, remote); + + if (error < 0) { + ssp_error("could not get remote, json parsing failed\n"); + return -1; + } + + return 0; + +} + + +void ssp_cleanup_pdu_header(Pdu_header *pdu_header) { + ssp_free(pdu_header); +} diff --git a/Program/src/mib/peer_1.json b/Program/src/mib/peer_1.json new file mode 100755 index 0000000..9ae6348 --- /dev/null +++ b/Program/src/mib/peer_1.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 1, + "UT_address" : 2130706433, + "UT_port" : 1111, + "type_of_network" : 1, + "default_transmission_mode" : 1, + "MTU" : 1500, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/src/mib/peer_2.json b/Program/src/mib/peer_2.json new file mode 100755 index 0000000..7ef92a4 --- /dev/null +++ b/Program/src/mib/peer_2.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 2, + "UT_address" : 2130706433, + "UT_port" : 1112, + "type_of_network" : 1, + "default_transmission_mode" : 1, + "MTU" : 1500, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/src/mib/peer_3.json b/Program/src/mib/peer_3.json new file mode 100755 index 0000000..daed8d0 --- /dev/null +++ b/Program/src/mib/peer_3.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 3, + "UT_address" : 2130706433, + "UT_port" : 1113, + "type_of_network" : 0, + "default_transmission_mode" : 1, + "MTU" : 1500, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/src/mib/peer_4.json b/Program/src/mib/peer_4.json new file mode 100755 index 0000000..8b31a83 --- /dev/null +++ b/Program/src/mib/peer_4.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 4, + "UT_address" : 2130706433, + "UT_port" : 1114, + "type_of_network" : 0, + "default_transmission_mode" : 1, + "MTU" : 1500, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/src/mib/peer_5.json b/Program/src/mib/peer_5.json new file mode 100755 index 0000000..41b5969 --- /dev/null +++ b/Program/src/mib/peer_5.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 5, + "UT_address" : 1, + "UT_port" : 1, + "type_of_network" : 2, + "default_transmission_mode" : 1, + "MTU" : 250, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/src/mib/peer_6.json b/Program/src/mib/peer_6.json new file mode 100755 index 0000000..2d8f164 --- /dev/null +++ b/Program/src/mib/peer_6.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 6, + "UT_address" : 1, + "UT_port" : 2, + "type_of_network" : 2, + "default_transmission_mode" : 1, + "MTU" : 250, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/src/mib/peer_7.json b/Program/src/mib/peer_7.json new file mode 100755 index 0000000..c71bac5 --- /dev/null +++ b/Program/src/mib/peer_7.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 7, + "UT_address" : 1, + "UT_port" : 5, + "type_of_network" : 3, + "default_transmission_mode" : 1, + "MTU" : 250, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/src/mib/peer_8.json b/Program/src/mib/peer_8.json new file mode 100755 index 0000000..7ef170c --- /dev/null +++ b/Program/src/mib/peer_8.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 8, + "UT_address" : 1, + "UT_port" : 4, + "type_of_network" : 3, + "default_transmission_mode" : 1, + "MTU" : 250, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/src/packet.c b/Program/src/packet.c new file mode 100755 index 0000000..c0f85b4 --- /dev/null +++ b/Program/src/packet.c @@ -0,0 +1,822 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "types.h" +#include "packet.h" +#include "utils.h" +#include "port.h" +#include "filesystem_funcs.h" +#include "requests.h" + +/*------------------------------------------------------------------------------ + + creating packets + +------------------------------------------------------------------------------*/ +void set_bits_to_protocol_byte(char *byte, uint8_t from_position, uint8_t to_position, uint8_t value) { + char bit_mask = value; + uint8_t bits_to_shift_left = 7-to_position; + char bits_to_add = bit_mask << bits_to_shift_left; + *byte = *byte | bits_to_add; +} + +// if is_data_packet is false, then is directive pacnket +static void set_packet_header(char *packet, uint16_t data_len, bool is_data_packet) { + set_bits_to_protocol_byte(&packet[0], 3,3, is_data_packet); + set_data_length(packet, data_len); +} + + +//get bits fromleft to right +uint8_t get_bits_from_protocol_byte(char byte, uint8_t from_position, uint8_t to_position){ + uint8_t bits_to_shift_left = from_position; + uint8_t bits_to_shift_right = 8 - (to_position - from_position + 1); + uint8_t bits_to_get = byte << bits_to_shift_left; + uint32_t value = bits_to_get >> bits_to_shift_right; + return value; +} + +void set_packet_directive(char *packet, uint32_t location, uint8_t directive){ + packet[location] = directive; +} + +//returns length of ids or -1 on error +int copy_id_to_packet(char *bytes, uint64_t id) { + + + uint32_t length = 0; + if (id < 0xFF){ + + bytes[0] = (uint8_t) id; + length = 1; + } else if (id < 0xFFFF) { + uint16_t network_byte_order = ssp_htons((uint16_t) id); + memcpy(bytes, &network_byte_order, sizeof(uint16_t)); + length = 2; + } else if (id < 0xFFFFFF) { + uint32_t network_byte_order = ssp_htonl((uint32_t) id); + memcpy(bytes, &network_byte_order, sizeof(uint32_t)); + length = 4; + } else if (id < 0xFFFFFFFF) { + uint64_t network_byte_order = ssp_htonll(id); + memcpy(bytes, &network_byte_order, sizeof(uint64_t)); + length = 8; + } else { + ssp_error("copying to packet, id size is not supported, please user 1, 2 or 4, 8"); + return -1; + } + + + return length; +} + +//returns id or -1 on error +uint64_t copy_id_from_packet(char *bytes, uint32_t length_of_ids) { + + uint64_t host_byte_order = 0; + if (length_of_ids == 8) { + host_byte_order = ssp_ntohll(*(uint64_t*) bytes); + }else if (length_of_ids == 4) { + host_byte_order = ssp_ntohl(*(uint32_t*) bytes); + } else if (length_of_ids == 2) { + host_byte_order = ssp_ntohs(*(uint16_t*) bytes); + } else if (length_of_ids == 1){ + host_byte_order = (uint8_t)bytes[0]; + } else { + ssp_error("copying from packet id size is not supported, please user 1, 2 or 4"); + return -1; + } + + return host_byte_order; +} + +//returns amount of bytes written or -1 on error +int copy_id_lv_to_packet(char *bytes, uint64_t id) { + + int len = copy_id_to_packet(&bytes[1], id); + if (len < 0) { + return -1; + } + bytes[0] = len; + + return len + 1; +} + +//returns length of the id on success and sets id to the id, or -1 on error +int copy_id_lv_from_packet(char *bytes, uint64_t *id){ + + uint8_t len = bytes[0]; + uint64_t error = -1; + + uint64_t id_recv = copy_id_from_packet(&bytes[1], len); + if (id_recv == error) { + ssp_printf("failed to copy id from packet %d\n", id_recv); + return -1; + } + + *id = id_recv; + return len; +} + + +void ssp_print_header(Pdu_header *pdu_header){ + ssp_printf("---------------------------------------------\n"); + ssp_printf("pdu_header->version %d\n",pdu_header->version); + ssp_printf("pdu_header->PDU_type %d\n",pdu_header->PDU_type); + ssp_printf("pdu_header->direction %d\n",pdu_header->direction); + ssp_printf("pdu_header->transmission_mode %d\n",pdu_header->transmission_mode); + ssp_printf("pdu_header->CRC_flag %d\n",pdu_header->CRC_flag); + ssp_printf("pdu_header->reserved_bit_0 %d\n",pdu_header->reserved_bit_0); + ssp_printf("pdu_header->PDU_data_field_len %d\n",pdu_header->PDU_data_field_len); + ssp_printf("pdu_header->reserved_bit_1 %d\n",pdu_header->reserved_bit_1 ); + ssp_printf("pdu_header->length_of_entity_IDs %d\n",pdu_header->length_of_entity_IDs); + ssp_printf("pdu_header->reserved_bit_2 %d\n",pdu_header->reserved_bit_2); + ssp_printf("pdu_header->transaction_seq_num_len %d\n",pdu_header->transaction_seq_num_len); + ssp_printf("pdu_header->source_id %d\n",pdu_header->source_id); + ssp_printf("pdu_header->transaction_sequence_number %llu\n",pdu_header->transaction_sequence_number); + ssp_printf("pdu_header->destination_id %d\n",pdu_header->destination_id); + ssp_printf("---------------------------------------------\n"); + + +} + +int get_pdu_header_from_packet(char *packet, Pdu_header *pdu_header){ + + pdu_header->version = get_bits_from_protocol_byte(packet[0], 0, 2); + pdu_header->PDU_type = get_bits_from_protocol_byte(packet[0], 3, 3); + pdu_header->direction = get_bits_from_protocol_byte(packet[0], 4, 4); + pdu_header->transmission_mode = get_bits_from_protocol_byte(packet[0], 5, 5); + pdu_header->CRC_flag = get_bits_from_protocol_byte(packet[0], 6, 6); + pdu_header->reserved_bit_0 = get_bits_from_protocol_byte(packet[0], 7, 7); + + pdu_header->PDU_data_field_len = get_data_length(packet); + + pdu_header->reserved_bit_1 = get_bits_from_protocol_byte(packet[3], 0, 0); + pdu_header->length_of_entity_IDs = get_bits_from_protocol_byte(packet[3], 1, 3); + pdu_header->reserved_bit_2 = get_bits_from_protocol_byte(packet[3], 4, 4); + pdu_header->transaction_seq_num_len = get_bits_from_protocol_byte(packet[3], 5, 7); + + + //ssp_printf("length of entities %d\n", pdu_header->length_of_entity_IDs); + uint64_t error = -1; + + int32_t source_id_location = PACKET_STATIC_HEADER_LEN; + pdu_header->source_id = copy_id_from_packet(&packet[source_id_location], pdu_header->length_of_entity_IDs); + if (pdu_header->source_id == error) { + ssp_error("failed to get source_id"); + return -1; + } + + int32_t transaction_number_location = source_id_location + pdu_header->length_of_entity_IDs; + pdu_header->transaction_sequence_number = copy_id_from_packet(&packet[transaction_number_location], pdu_header->transaction_seq_num_len); + if (pdu_header->transaction_sequence_number == error) { + ssp_error("failed to get transaction_sequence_number"); + return -1; + } + + int32_t dest_id_location = transaction_number_location + pdu_header->transaction_seq_num_len; + pdu_header->destination_id = copy_id_from_packet(&packet[dest_id_location], pdu_header->length_of_entity_IDs); + if (pdu_header->destination_id == error) { + ssp_error("failed to get destination_id"); + return -1; + } + + pdu_header->reserved_space_for_header = dest_id_location + pdu_header->length_of_entity_IDs; + return pdu_header->reserved_space_for_header; +} + +//returns the location in the packet where the next pointer for tthe packet will start after the header +int build_pdu_header(char *packet, uint64_t transaction_sequence_number, uint32_t transmission_mode, uint16_t data_len, Pdu_header *pdu_header) { + memset(packet, 0, 4); + + set_bits_to_protocol_byte(&packet[0], 0,2, pdu_header->version); + set_bits_to_protocol_byte(&packet[0], 3,3, pdu_header->PDU_type); + set_bits_to_protocol_byte(&packet[0], 4,4, pdu_header->direction); + set_bits_to_protocol_byte(&packet[0], 5,5, transmission_mode); + set_bits_to_protocol_byte(&packet[0], 6,6, pdu_header->CRC_flag); + set_bits_to_protocol_byte(&packet[0], 7,7, pdu_header->reserved_bit_0); + set_data_length(packet, data_len); + set_bits_to_protocol_byte(&packet[3], 0,0, pdu_header->reserved_bit_1); + set_bits_to_protocol_byte(&packet[3], 1,3, pdu_header->length_of_entity_IDs); + set_bits_to_protocol_byte(&packet[3], 4,4, pdu_header->reserved_bit_2); + set_bits_to_protocol_byte(&packet[3], 5,7, pdu_header->transaction_seq_num_len); + + uint32_t source_id_location = PACKET_STATIC_HEADER_LEN; + int len = copy_id_to_packet(&packet[source_id_location], pdu_header->source_id); + if (len < 0 || len != pdu_header->length_of_entity_IDs) { + ssp_error("failed copy source_id"); + return -1; + } + + uint32_t transaction_number_location = source_id_location + pdu_header->length_of_entity_IDs; + len = copy_id_to_packet(&packet[transaction_number_location], transaction_sequence_number); + if (len < 0 || len != pdu_header->transaction_seq_num_len) { + ssp_printf("failed copy transaction_number_location %d suppose to be %d\n", len, pdu_header->transaction_seq_num_len); + return -1; + } + uint32_t dest_id_location = transaction_number_location + pdu_header->transaction_seq_num_len; + len = copy_id_to_packet(&packet[dest_id_location], pdu_header->destination_id); + if (len < 0 || len != pdu_header->length_of_entity_IDs) { + ssp_error("failed copy destination_id"); + return -1; + } + + return dest_id_location + pdu_header->length_of_entity_IDs; +} + +/* +typedef struct pdu_finished { + unsigned int condition_code : 4; + + // 0 generated by waypoint 1 generated by end system. + unsigned int end_system_status : 1; + + //0 data complete, 1 data incomplete + unsigned int delivery_code : 1; + + //see above + unsigned int file_status : 2; + TLV file_store_responses; + TLV fault_location; + +}Pdu_finished; +*/ +void get_finished_pdu(char *packet, Pdu_finished *pdu_finished) { + + pdu_finished->condition_code = get_bits_from_protocol_byte(packet[0], 0, 3); + pdu_finished->end_system_status = get_bits_from_protocol_byte(packet[0], 4, 4); + pdu_finished->delivery_code = get_bits_from_protocol_byte(packet[0], 5, 5); + pdu_finished->file_status = get_bits_from_protocol_byte(packet[0], 6, 7); + +} + + +uint8_t build_finished_pdu(char *packet, uint32_t start) { + + uint32_t packet_index = start; + uint16_t data_len = 0; + + packet[packet_index] = FINISHED_PDU; + packet_index++; + + set_bits_to_protocol_byte(&packet[packet_index], 0, 3, COND_NO_ERROR); + + // 0 generated by waypoint 1 generated by end system (ext features) + set_bits_to_protocol_byte(&packet[packet_index], 4, 4, 0); + + //0 data complete, 1 data incomplete (ext features) + set_bits_to_protocol_byte(&packet[packet_index], 5, 5, 0); + + //file_status + set_bits_to_protocol_byte(&packet[packet_index], 6, 7, FILE_RETAINED_SUCCESSFULLY); + + packet_index += 1; + data_len = packet_index - start; + + + set_packet_header(packet, data_len, DIRECTIVE); + + return data_len; +} +/* +typedef struct pdu_meta_data { + //0 Record boundaries respeced (read as array of octets), 1 not respected (variable length) + unsigned int segmentation_control : 1; + + unsigned int reserved_bits: 7; + + //length of the file in octets, set all 0 for unbounded size + uint32_t file_size; + LV source_file_name; + LV destination_file_name; + + + //Options include: + // Filestore requests, + // Messages to user. + // Fault Handler overrides. + // Flow Label. + + TLV *options; + +} Pdu_meta_data; +*/ + +//returns packet_index for data, to get length of meta data, subtract start from return value +uint8_t build_put_packet_metadata(char *packet, uint32_t start, Request *req) { + + uint8_t packet_index = start; + + //set directive 1 byte + set_packet_directive(packet, packet_index, META_DATA_PDU); + packet_index += SIZE_OF_DIRECTIVE_CODE; + + //Set segmentation_control bit and 7 reserved bits (to 0) + set_bits_to_protocol_byte(&packet[packet_index], 0, 0, req->segmentation_control); + packet_index++; + + //4 bytes + uint32_t network_bytes = ssp_htonl(req->file_size); + memcpy(&packet[packet_index], &network_bytes, sizeof(uint32_t)); + packet_index += 4; + + //variable length params + uint8_t src_file_name_length = strnlen(req->source_file_name, MAX_PATH); + uint8_t destination_file_length = strnlen(req->destination_file_name, MAX_PATH); + + //copy source length to packet (1 bytes) + memcpy(&packet[packet_index], &src_file_name_length, 1); + packet_index++; + //copy source name to packet (length bytes) + memcpy(&packet[packet_index], req->source_file_name, src_file_name_length); + packet_index += src_file_name_length; + //copy length to packet (1 byte) + memcpy(&packet[packet_index], &destination_file_length, 1); + packet_index++; + //copy destination name to packet (length bytes) + memcpy(&packet[packet_index], req->destination_file_name, destination_file_length); + packet_index += destination_file_length; + + //add messages to metadata + packet_index += add_messages_to_packet(packet, packet_index, req->messages_to_user); + + uint8_t data_len = packet_index - start; + set_packet_header(packet, data_len, DIRECTIVE); + + return packet_index; +} + +/* +typedef struct file_data_pdu_contents { + //in octets + uint32_t offset; + //variable sized + unsigned char *data; +} File_data_pdu_contents; +*/ +//returns the length of the packet, or -1 on error +//end is how long the buffer/mtu is, offset is the place in the file we want to read from +int build_data_packet(char *packet, uint32_t start, uint32_t end, uint32_t offset, File *file){ + + uint16_t data_size = end - start; + uint32_t packet_index = start; + + uint32_t net_offset = ssp_ntohl(offset); + memcpy(&packet[packet_index], &net_offset, sizeof(uint32_t)); + + packet_index += sizeof(uint32_t); + + //the -4 is because wehave to include the 'bet_offset' bytes + int bytes = get_offset(file, &packet[packet_index], data_size-4, offset); + if (bytes <= 0){ + ssp_error("could not get offset, this could because the file is empty!\n"); + return -1; + } + + uint16_t data_len = bytes + sizeof(uint32_t); + set_packet_header(packet, data_len, DATA); + return data_len; +} + +//returns the offset in the file (for the data packet) +uint32_t get_data_offset_from_packet(char *packet) { + uint32_t offset = 0; + memcpy(&offset, packet, sizeof(uint32_t)); + + offset = ssp_ntohl(offset); + return offset; +} + + +/* +typedef struct pdu_eof { + unsigned int condition_code : 4; + unsigned int spare : 4; + uint32_t checksum; + + //In octets. This value shall be the total number of file data octets + //transmitted by the sender, regardless of the condition code + //(i.e., it shall be supplied even if the condition code is other than + //‘No error’). + uint32_t file_size; + + + //Omitted if condition code is ‘No error’. Otherwise, entity ID in the + //TLV is the ID of the entity at which transaction cancellation was + //initiated. + TLV fault_location; + +} Pdu_eof; +*/ +void get_eof_from_packet(char *packet, Pdu_eof *eof){ + uint32_t packet_index = 0; + uint32_t filesize = 0; + uint32_t checksum = 0; + + eof->condition_code = get_bits_from_protocol_byte(packet[packet_index], 0, 3); + eof->spare = get_bits_from_protocol_byte(packet[packet_index], 4, 7); + + packet_index++; + memcpy(&filesize, &packet[packet_index], sizeof(uint32_t)); + eof->file_size = ssp_ntohl(filesize); + + packet_index += sizeof(uint32_t); + memcpy(&checksum, &packet[packet_index], sizeof(uint32_t)); + eof->checksum = ssp_ntohl(checksum); + + //todo add fault location parsing +} + +void build_eof_packet(char *packet, uint32_t start, uint32_t file_size, uint32_t checksum) { + + uint8_t packet_index = start; + + //set directive 1 byte + set_packet_directive(packet, packet_index, EOF_PDU); + packet_index += SIZE_OF_DIRECTIVE_CODE; + + //this will be need to set from the req struct probably. + set_bits_to_protocol_byte(&packet[packet_index], 0, 3, COND_NO_ERROR); + + //4 bits reserved bits + set_bits_to_protocol_byte(&packet[packet_index], 4, 7, 0); + + packet_index++; + + //4 bytes + uint32_t network_file_size = ssp_htonl(file_size); + memcpy(&packet[packet_index], &network_file_size, sizeof(uint32_t)); + packet_index += sizeof(uint32_t); + + //4 bytes + uint32_t network_checksum = ssp_htonl(checksum); + memcpy(&packet[packet_index], &network_checksum, sizeof(uint32_t)); + packet_index += sizeof(uint32_t); + + //TODO addTLV fault_location + + uint16_t data_len = packet_index - start; + set_packet_header(packet, data_len, DIRECTIVE); +} + +//this is a callback for building nak_array server side +struct packet_nak_helper { + char*packet; + uint64_t max_number_of_nak_segments; + uint64_t current_number_of_segments; +}; + +void fill_nak_array_callback(Node *node, void *element, void *args){ + struct packet_nak_helper *holder = (struct packet_nak_helper *)args; + + if (holder->current_number_of_segments == holder->max_number_of_nak_segments) + return; + + Offset *offset = (Offset *)element; + Offset offset_to_copy; + //ssp_printf("sending nak:end %d:%d\n", offset->start, offset->end); + offset_to_copy.start = ssp_htonl(offset->start); + offset_to_copy.end = ssp_htonl(offset->end); + + memcpy(holder->packet, &offset_to_copy, sizeof(Offset)); + + holder->current_number_of_segments++; + holder->packet+=sizeof(Offset); +} + + +/* +typedef struct pdu_nak { + uint32_t start_scope; + uint32_t end_scope; + + //number of Nak_segments + uint64_t segment_requests; + void *segments; +} Pdu_nak; +*/ +//return length of Nak packet +int get_nak_packet(char *packet, Pdu_nak *nak) { + + + uint32_t packet_index = 0; + uint32_t start_scope = 0; + uint32_t end_scope = 0; + uint64_t segment_requests = 0; + + memcpy(&start_scope, &packet[packet_index], sizeof(uint32_t)); + nak->start_scope = ssp_ntohl(start_scope); + + packet_index += sizeof(uint32_t); + + memcpy(&end_scope, &packet[packet_index], sizeof(uint32_t)); + nak->end_scope = ssp_ntohl(end_scope); + + packet_index += sizeof(uint32_t); + + memcpy(&segment_requests, &packet[packet_index], sizeof(uint64_t)); + nak->segment_requests = ssp_ntohll(segment_requests); + + packet_index += sizeof(uint64_t); + nak->segments = &packet[packet_index]; + + + + return packet_index; +} + +//this function is weird because it uses a callback into an iterator. We fill the array +//with as many 'offsets' as we can. +uint32_t build_nak_packet(char *packet, uint32_t start, Request *req) { + + packet[start] = NAK_PDU; + + uint32_t packet_index = start + 1; + packet_index += 16; + + struct packet_nak_helper holder; + holder.max_number_of_nak_segments = (req->res.packet_len - packet_index) / sizeof(Offset); + holder.packet = &packet[packet_index]; + holder.current_number_of_segments = 0; + + req->file->missing_offsets->iterate(req->file->missing_offsets, fill_nak_array_callback, &holder); + + uint32_t start_scope = ((Offset *)req->file->missing_offsets->head->next->element)->start; + uint32_t end_scope = ((Offset *)req->file->missing_offsets->tail->prev->element)->end; + + uint32_t net_start_scope = ssp_htonl(start_scope); + uint32_t net_end_scope = ssp_htonl(end_scope); + uint64_t net_segments_number = ssp_htonll(holder.current_number_of_segments); + + ssp_printf("building nak packet offset start:end %d:%d\n", start_scope, end_scope); + packet_index = start + 1; + + memcpy(&packet[packet_index], &net_start_scope, sizeof(uint32_t)); + packet_index += sizeof(uint32_t); + + memcpy(&packet[packet_index], &net_end_scope, sizeof(uint32_t)); + packet_index += sizeof(uint32_t); + + memcpy(&packet[packet_index], &net_segments_number, sizeof(uint64_t)); + packet_index += sizeof(uint64_t); + + packet_index += sizeof(Offset) * holder.current_number_of_segments; + + uint16_t data_len = packet_index - start; + set_packet_header(packet, data_len, DIRECTIVE); + + return data_len; +} + +/* + +typedef struct pdu_ack { + //Directive code of the acknowledged PDU. + unsigned int directive_code : 4; + + //Values depend on directive code. For ACK of Finished PDU: binary ‘0000’ + //if Finished PDU is generated by waypoint, binary ‘0001’ if Finished + //PDU is generated by end system. (NOTE: this discrimination is + //meaningful if the Extended Procedures are implemented.) + //Binary ‘0000’ for ACKs of all other file directives. + + unsigned int directive_subtype_code : 4; + + //Condition code of the acknowledged PDU. + unsigned int condition_code : 4; + unsigned int spare : 2; + + //Status of the transaction in the context of the entity that is issuing the acknowledgment. + unsigned int transaction_status : 2; + +} Pdu_ack; +*/ + +//this function should be called at the buffer location after an ACK_PDU bits were set +void get_ack_from_packet(char *packet, Pdu_ack *ack){ + ack->directive_code = get_bits_from_protocol_byte(packet[0], 0, 3); + ack->directive_subtype_code = get_bits_from_protocol_byte(packet[0], 4, 7); + ack->condition_code = get_bits_from_protocol_byte(packet[1], 0, 3); + ack->spare = get_bits_from_protocol_byte(packet[1], 4, 5); + ack->transaction_status = get_bits_from_protocol_byte(packet[1], 6, 7); +} + +uint8_t build_ack(char*packet, uint32_t start, uint8_t type) { + //first byte indicates that its a ACK_PDU + packet[start] = ACK_PDU; + uint32_t packet_index = start + 1; + + set_bits_to_protocol_byte(&packet[packet_index], 0, 3, type); + set_bits_to_protocol_byte(&packet[packet_index], 4, 7, ACK_FINISHED_END); + + packet_index += 1; + //will need more/propper conditions + set_bits_to_protocol_byte(&packet[packet_index], 0, 3, COND_NO_ERROR); + + packet_index += 1; + uint16_t data_len = packet_index - start; + set_packet_header(packet, data_len, DIRECTIVE); + return data_len; +} + +uint8_t build_nak_directive(char *packet, uint32_t start, uint8_t directive) { + uint8_t data_len = 2; + packet[start] = NAK_DIRECTIVE; + packet[start + 1] = directive; + + set_packet_header(packet, data_len, DIRECTIVE); + return data_len; +} + +void set_data_length(char*packet, uint16_t data_len){ + uint16_t bytes = ssp_htons(data_len); + memcpy(&packet[1], &bytes, sizeof(uint16_t)); +} + +uint16_t get_data_length(char*packet) { + uint16_t bytes = 0; + memcpy(&bytes, &packet[1], sizeof(uint16_t)); + uint16_t len = ssp_ntohs(bytes); + return len; +} + +struct packet_callback_params { + char *packet; + uint32_t *packet_index; +}; + +static void add_messages_callback(Node *node, void *element, void *args) { + struct packet_callback_params *params = (struct packet_callback_params *) args; + char *packet = params->packet; + uint32_t packet_index = *(params->packet_index); + Message *message = (Message *) element; + int bytes = 0; + + //since this is a callback functions, we can't return -1, intead we just log + char *error_msg = "there was an issue copying bytes for the message: %s\n"; + + //5 bytes to copy cfdp\0 into the buffer + memcpy(&packet[packet_index], message->header.message_id_cfdp, 5); + packet_index += 5; + + //one byte for message type + memcpy(&packet[packet_index], &message->header.message_type, 1); + packet_index += 1; + + Message_put_proxy *proxy_put; + Message_cont_part_request *proxy_cont_part; + switch (message->header.message_type) + { + case PROXY_PUT_REQUEST: + proxy_put = (Message_put_proxy *) message->value; + + bytes = copy_id_lv_to_packet(&packet[packet_index], proxy_put->destination_id); + if (bytes < 0) { + ssp_printf(error_msg, "PROXY_PUT_REQUEST"); + return; + } + packet_index += bytes; + packet_index += copy_lv_to_buffer(&packet[packet_index], proxy_put->source_file_name); + packet_index += copy_lv_to_buffer(&packet[packet_index], proxy_put->destination_file_name); + break; + + case CONTINUE_PARTIAL: + proxy_cont_part = (Message_cont_part_request *) message->value; + + bytes = copy_id_lv_to_packet(&packet[packet_index], proxy_cont_part->destination_id); + if (bytes < 0) { + ssp_printf(error_msg, "CONTINUE_PARTIAL"); + return; + } + packet_index += bytes; + + bytes = copy_id_lv_to_packet(&packet[packet_index], proxy_cont_part->originator_id); + if (bytes < 0) { + ssp_printf(error_msg, "CONTINUE_PARTIAL"); + return; + } + packet_index += bytes; + + bytes = copy_id_lv_to_packet(&packet[packet_index], proxy_cont_part->transaction_id); + if (bytes < 0) { + ssp_printf(error_msg, "CONTINUE_PARTIAL"); + return; + } + packet_index += bytes; + break; + + default: + break; + } + + *(params->packet_index) = packet_index; + +} + +//returns length of added messages, including the start; copys messages into packet +uint32_t add_messages_to_packet(char *packet, uint32_t start, List *messages_to_user) { + + uint32_t packet_index = start; + struct packet_callback_params params = {packet, &packet_index}; + messages_to_user->iterate(messages_to_user, add_messages_callback, ¶ms); + return packet_index; +} + + +//adds messages from packet into request, returns the location of the next message +uint32_t get_message_from_packet(char *packet, uint32_t start, Request *req) { + + if (strncmp(&packet[start], "cfdp", 5) != 0) { + ssp_error("messages are poorly formatted\n"); + return 0; + } + + Message *m; + Message_put_proxy *proxy_put; + Message_cont_part_request *proxy_cont_part; + + uint32_t message_type = start + 5; + uint32_t message_start = start + 6; + + int id_len = 0; + switch (packet[message_type]) + { + case PROXY_PUT_REQUEST: + m = create_message(PROXY_PUT_REQUEST); + + m->value = ssp_alloc(1, sizeof(Message_put_proxy)); + if (m->value == NULL) { + return -1; + } + proxy_put = (Message_put_proxy *) m->value; + + id_len = copy_id_lv_from_packet(&packet[message_start], &proxy_put->destination_id); + if (id_len < 0) { + ssp_free(m->value); + return -1; + } + + message_start += id_len + 1; + + copy_lv_from_buffer(&proxy_put->source_file_name, packet, message_start); + message_start += proxy_put->source_file_name.length + 1; + + + copy_lv_from_buffer(&proxy_put->destination_file_name, packet, message_start); + message_start += proxy_put->destination_file_name.length + 1; + break; + + case CONTINUE_PARTIAL: + m = create_message(CONTINUE_PARTIAL); + + m->value = ssp_alloc(1, sizeof(Message_cont_part_request)); + proxy_cont_part = (Message_cont_part_request *) m->value; + + id_len = copy_id_lv_from_packet(&packet[message_start], &proxy_cont_part->destination_id); + if (id_len < 0) { + ssp_free(m->value); + return -1; + } + message_start += id_len + 1; + + id_len = copy_id_lv_from_packet(&packet[message_start], &proxy_cont_part->originator_id); + if (id_len < 0) { + ssp_free(m->value); + return -1; + } + message_start += id_len + 1; + + id_len = copy_id_lv_from_packet(&packet[message_start], &proxy_cont_part->transaction_id); + if (id_len < 0) { + ssp_free(m->value); + return -1; + } + message_start += id_len + 1; + + default: + break; + } + + req->messages_to_user->push(req->messages_to_user, m, 0); + return message_start; +} + + +uint32_t get_messages_from_packet(char *packet, uint32_t start, uint32_t data_length, Request *req) { + + uint32_t packet_index = start; + char *cfdp = "cfdp"; + uint32_t len = strnlen(cfdp, 5); + + while (packet_index < data_length - len) { + + if (strncmp(&packet[packet_index], cfdp, len) != 0) + break; + + packet_index = get_message_from_packet(packet, packet_index, req); + } + return packet_index; +} diff --git a/Program/src/port.c b/Program/src/port.c new file mode 100755 index 0000000..8af3e68 --- /dev/null +++ b/Program/src/port.c @@ -0,0 +1,543 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "port.h" + +static int exit_now = 0; + +#ifdef POSIX_PORT + #include + #include + #include + #include + #include + #include + #include + +#endif + +#ifdef POSIX_FILESYSTEM + #include + #include + #include + #include "utils.h" +#endif + +#ifdef RED_FS + #include +#endif + +#ifdef FREE_RTOS_PORT + #include "FreeRTOS.h" + #include + #include + //#include "portable.h" //not sure what i need here for sat build + + //make sure these are available in FREERTOS + #include + #include + #include + #include + #include + #include + #include + +#endif + +#ifdef CSP_NETWORK + #include "csp/csp.h" +#endif + + +/*------------------------------------------------------------------------------ + File system port functions, these are used to interchange different + File systems, will add RELIANCE_EDGE here in the future +------------------------------------------------------------------------------*/ + +int get_exit() { + return exit_now; +} + +void set_exit() { + exit_now = 1; +} + + +int ssp_mkdir(char *dir_name) { + #ifdef POSIX_FILESYSTEM + int error = mkdir(dir_name, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (error < 0) { + if(errno == EEXIST) { + return 1; + } + //ssp_printf("couldn't make dir\n"); + return -1; + } + else { + //ssp_printf("%s directory created\n", dir_name); + return 1; + } + return -1; + + #endif + #ifdef RED_FS + + int error = red_mkdir(dir_name); + if (error < 0) { + if(red_errno == RED_EEXIST) { + return 1; + } + ssp_printf("couldn't make dir\n"); + return -1; + } + else { + ssp_printf("%s directory created\n", dir_name); + return 1; + } + return -1; + #endif + return -1; + +} + +void *ssp_opendir(char *dir_name) { + + #ifdef POSIX_FILESYSTEM + DIR *dir; + dir = opendir(dir_name); + if(dir == NULL){ + ssp_error("Unable to open directory"); + return NULL; + } + return dir; + #endif + #ifdef RED_FS + + REDDIR *dir; + dir = red_opendir(dir_name); + if(dir == NULL){ + ssp_error("Unable to open directory"); + return NULL; + } + return dir; + + #endif + return NULL; +} + +int ssp_readdir(void *dir, char *file){ + #ifdef POSIX_FILESYSTEM + struct dirent *file_read; + DIR *d = (DIR*)dir; + + file_read=readdir(d); + if (file_read == NULL) { + return 0; + } + ssp_memcpy(file, file_read->d_name, MAX_PATH); + + return 1; + #endif + #ifdef RED_FS + + REDDIRENT *file_read; + REDDIR *d = (REDDIR*)dir; + + file_read=red_readdir(d); + if (file_read == NULL) { + return 0; + } + ssp_memcpy(file, file_read->d_name, MAX_PATH); + + return 1; + #endif + + return -1; + +} + +#include "packet.h" +/*------------------------------------------------------------------------------ + Network port functions, these are used to interchange different network + stacks +------------------------------------------------------------------------------*/ +#ifdef FREE_RTOS_PORT +extern QueueHandle_t sendQueue; +#endif + +void ssp_sendto(Response res) { + + #ifdef CSP_NETWORK + csp_packet_t *packet_sending; + csp_packet_t *packet; + csp_conn_t *conn; + #endif + int err = 0; + + switch (res.type_of_network) + { + case generic: + /* code */ + #ifdef FREE_RTOS_PORT + while (true) { + if (xQueueSendToBack(sendQueue, res.msg, 100) != pdPASS) + ssp_printf("queue full, failed to post packet, blocking task untill sent\n"); + else + break; + } + return; + #else + ssp_printf("FreeRtos not defined, can't use generic queues"); + #endif + break; + case csp_connectionless: + #ifdef CSP_NETWORK + packet = (csp_packet_t *) res.addr; + + if (csp_buffer_remaining() != 0) { + packet_sending = csp_buffer_get(1); + if (packet_sending == NULL) { + ssp_printf("couldn't get packet, is NULL"); + } + + //ssp_printf("sending packet to dest %d port %d srcaddr %d srcport %d \n", packet->id.dst, packet->id.dport, packet->id.src, packet->id.sport); + + ssp_memcpy(packet_sending->data, res.msg, res.packet_len); + packet_sending->length = res.packet_len; + + err = csp_sendto(packet->id.pri, packet->id.dst, packet->id.dport, packet->id.sport, 0, packet_sending, 100); + + if (err < 0) { + ssp_error("ERROR in ssp_sento"); + csp_buffer_free(packet_sending); + } + } + else + ssp_error("couldn't get new packet for sending!\n"); + #else + ssp_printf("CSP network not defined, but network is trying to use it\n"); + #endif + break; + + case csp_connection: + #ifdef CSP_NETWORK + conn = (csp_conn_t*) res.addr; + + if (csp_buffer_remaining() != 0) { + packet_sending = csp_buffer_get(1); + if (packet_sending == NULL) { + ssp_printf("couldn't get packet, is NULL"); + } + ssp_memcpy(packet_sending->data, res.msg, res.packet_len); + packet_sending->length = res.packet_len; + /* 5. Send packet */ + if (!csp_send(conn, packet_sending, 1000)) { + /* Send failed */ + csp_log_error("Send failed"); + csp_buffer_free(packet_sending); + } + } + #else + ssp_printf("CSP network not defined, but network is trying to use it\n"); + #endif + break; + + case test: + break; + + default: + #ifdef POSIX_PORT + + //ssp_print_bits(res.msg, 10); + //Pdu_header header; + //memset(&header, 0, sizeof(Pdu_header)); + //get_pdu_header_from_packet(res.msg, &header); + //ssp_print_header(&header); + + err = sendto(res.sfd, res.msg, res.packet_len, 0, (struct sockaddr*)res.addr, sizeof(struct sockaddr)); + if (err < 0) { + ssp_printf("res.sfd %d, res.packet_len %d, addr %d, addr size %d\n", res.sfd, res.packet_len, (struct sockaddr*)res.addr, sizeof(struct sockaddr)); + ssp_error("ERROR in sendto"); + } + break; + #else + ssp_printf("Posix network not defined, but network is trying to use it\n"); + #endif + break; + } + +} + + + +/*------------------------------------------------------------------------------ + Std lib functions, for custom memory allocation, and stdio +------------------------------------------------------------------------------*/ + +void *ssp_alloc(uint32_t n_memb, size_t size) { + + #ifdef FREE_RTOS_PORT + void *mem = pvPortMalloc(n_memb * size); + memset(mem, 0, n_memb * size); + return mem; + #else + void *mem = calloc(n_memb, size); + #endif + if (mem == NULL) + ssp_error("Memory failed to alloc!\n"); + + return mem; +} + +void ssp_free(void *pointer) { + + if (pointer == NULL) + return; + + #ifdef FREE_RTOS_PORT + vPortFree(pointer); + #else + free(pointer); + #endif + +} + +//what kind of errorno functions do we have in RED_FS? +void ssp_error(char *error){ + ssp_printf("ERROR: "); + perror(error); +} + +//this can be switched to printing to a log file in the future, not sure +//if FREE_RTOS has va_list +void ssp_printf(char *stuff, ...) { + va_list args; + va_start(args, stuff); + #ifdef FREE_RTOS_PORT + vfprintf(stdout, stuff, args); + va_end (args); + fflush(stdout); + #else + + char log_string[2000]; + vsnprintf(log_string, sizeof(log_string), stuff, args); + log_ftp("INFO", log_string); + va_end (args); + + #endif + +} + +#ifdef FREE_RTOS_PORT + //replace with real time clock + static int shit_time = 1; +#endif +//returns seconds elapsed, need FREE RTOS realtime clock lib to properly port +int ssp_time_count() { + + #ifdef FREE_RTOS_PORT + return shit_time++;//INSERT TIME FUNCTIONS get delta time since last call + #else + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec; + #endif +} + + +/*------------------------------------------------------------------------------ + Threading and task functions +------------------------------------------------------------------------------*/ +void *ssp_thread_create(int stack_size, void * (thread_func)(void *params), void *params) { + + #ifdef FREE_RTOS_PORT + + TaskHandle_t *xHandle = ssp_alloc(1, sizeof(TaskHandle_t)); + BaseType_t xReturned; + + // Create the task, storing the handle. + xReturned = xTaskCreate( + thread_func, // Function that implements the task. + "FTP", // Text name for the task. + stack_size, // Stack size in words, not bytes. + params, // Parameter passed into the task. + 1,// Priority at which the task is created. + xHandle ); // Used to pass out the created task's handle. + + if (xReturned == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) { + ssp_error("Not enough memory to start task\n"); + return NULL; + } + + return xReturned; + + #else //pthreads + pthread_t *handler = ssp_alloc(1, sizeof(pthread_t)); + if (handler == NULL) + return NULL; + + pthread_attr_t *attr = ssp_alloc(1, sizeof(pthread_attr_t)); + if (attr == NULL) { + ssp_free(handler); + return NULL; + } + + int err = pthread_attr_init(attr); + if (0 != err) { + ssp_error("pthread_init failed"); + ssp_free(attr); + ssp_free(handler); + } + err = pthread_attr_setstacksize(attr, stack_size); + if (0 != err) + ssp_error("pthread_attr_setstacksize %d"); + + if (EINVAL == err) { + ssp_printf("the stack size is less that PTHREAD_STACK_MIN %d\n", PTHREAD_STACK_MIN); + ssp_free(attr); + ssp_free(handler); + return NULL; + } + + err = pthread_create(handler, attr, thread_func, params); + if (0 != err) { + ssp_error("pthread_create"); + ssp_free(handler); + } + ssp_free(attr); + + return handler; + #endif +} + + +void *ssp_lock_create() { + #ifdef FREE_RTOS_PORT + + /* Create a mutex type semaphore. */ + void *lock = xSemaphoreCreateBinary(); + if (lock == NULL) { + ssp_printf("mutex init failed\n"); + return NULL; + } + + #else + pthread_mutex_t *lock = ssp_alloc(1, sizeof(pthread_mutex_t)); + if (lock == NULL) { + return NULL; + } + + if (pthread_mutex_init(lock, NULL) != 0) { + ssp_printf("mutex init failed\n"); + ssp_free(lock); + return NULL; + } + + #endif + return lock; +} + +int ssp_lock_destory(void *lock) { + if (lock == NULL) { + return 0; + } + + #ifdef FREE_RTOS_PORT + #else + int error = pthread_mutex_destroy(lock); + + if (!error) { + ssp_free(lock); + return 1; + + } else if (error == EBUSY){ + ssp_printf("mutex is currently locked\n"); + } + #endif + return 0; +} + +int ssp_lock_give(void *lock) { + if (lock == NULL) { + return 0; + } + + #ifdef FREE_RTOS_PORT + SemaphoreHandle_t xSemaphore = (SemaphoreHandle_t) lock; + + if (xSemaphoreGive( xSemaphore ) == pdTRUE) { + //ssp_printf("gave the lock\n"); + return 1; + } + ssp_printf("couldn't give the lock\n"); + return 0; + + #else + pthread_mutex_t *mutex = (pthread_mutex_t *) lock; + + int error = pthread_mutex_unlock(mutex); + if (!error) { + return 1; + + } else if (error == EBUSY){ + ssp_printf("mutex is currently locked\n"); + } + + + + #endif + return 0; +} + +int ssp_lock_take(void *lock) { + if (lock == NULL) { + return 0; + } + + #ifdef FREE_RTOS_PORT + SemaphoreHandle_t xSemaphore = (SemaphoreHandle_t) lock; + if( xSemaphore == NULL ) { + return 0; + } + + //ssp_printf("waiting for client lock\n"); + if (xSemaphoreTake( xSemaphore, portMAX_DELAY) == pdTRUE) { + //ssp_printf("took the lock\n"); + return 1; + } else { + ssp_printf("couldn't take the lock\n"); + return 0; + } + #else + + pthread_mutex_t *mutex = (pthread_mutex_t *) lock; + + int error = pthread_mutex_lock(mutex); + if (!error) { + return 1; + + } else { + ssp_printf("mutex lock take error %d\n", error); + } + + return 0; + #endif +} + + + +//not required for Free_rtos +void ssp_thread_join(void *thread_handle) { + #ifdef POSIX_PORT + pthread_t * handle = (pthread_t*) thread_handle; + pthread_join(*handle, NULL); + ssp_free(thread_handle); + #endif + #ifdef FREE_RTOS_PORT + ssp_printf("deleting client task\n"); + // Doesn't work vTaskDelete(NULL); + #endif +} diff --git a/Program/src/posix_server_provider.c b/Program/src/posix_server_provider.c new file mode 100755 index 0000000..04e8198 --- /dev/null +++ b/Program/src/posix_server_provider.c @@ -0,0 +1,519 @@ +#include "port.h" +#ifdef POSIX_PORT + +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include "posix_server_provider.h" + +#include + +static int ssp_recvfrom(int sfd, void *buff, size_t packet_len, int flags, void *server_addr, uint32_t *server_addr_len) { + int count = 0; + count = recvfrom(sfd, buff, packet_len, flags, (struct sockaddr*)server_addr, (socklen_t*)server_addr_len); + return count; +} + +static void *ssp_init_socket_set(size_t *size) { + fd_set *socket_set = ssp_alloc(1, sizeof(fd_set)); + *size = sizeof(fd_set); + return (void *)socket_set; +} + + +static void ssp_fd_zero(void *socket_set){ + FD_ZERO((fd_set*) socket_set); +} + +static void ssp_fd_set(int sfd, void *socket_set) { + FD_SET(sfd, (fd_set*) socket_set); +} + +static int ssp_fd_is_set(int sfd, void *socket_set){ + int is_set = 0; + is_set = FD_ISSET(sfd, (fd_set*) socket_set); + return is_set; +} + +static void ssp_fd_clr(int sfd, void *socket_set) { + FD_CLR(sfd, (fd_set *) socket_set); +} + +static int ssp_select(int sfd, void *read_socket_set, void *write_socket_set, void *restrict_socket_set, uint32_t timeout_in_usec) { + + struct timeval timeout = { + .tv_sec = 0, + .tv_usec = timeout_in_usec + }; + + int nrdy = select(sfd + 1, (fd_set *) read_socket_set, (fd_set *) write_socket_set, (fd_set *) restrict_socket_set, &timeout); + + return nrdy; +} + +static void *ssp_init_sockaddr_struct(size_t *size_of_addr) { + + *size_of_addr = sizeof(struct sockaddr_storage); + void *addr = ssp_alloc(1, sizeof(struct sockaddr_storage)); + if (addr == NULL) + return NULL; + + return addr; +} + + +//if conn_typ == 1, tcp/ bind_to_host == 1 for binding local, 2 for connect +static int prepareHost(char *host_name, void *addr, size_t *size_of_addr, char *port, int conn_type, int bind_to_host) +{ + + struct addrinfo hints, *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_flags = AI_V4MAPPED; + hints.ai_socktype = conn_type; + + + int err = getaddrinfo(host_name, port, &hints, &res); + + if (err != 0) { + ssp_error("get addr info"); + return -1; + } + + int sfd; + struct addrinfo *cur; + + for (cur = res; cur != NULL; cur = cur->ai_next) + { + + sfd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol); + + if (sfd < 0) { + ssp_error("socket"); + } + + int val = 1; + err = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + if (err == -1) { + ssp_error("set sock opt"); + ssp_close(sfd); + continue; + } + + if (bind_to_host == 1) { + err = bind(sfd, cur->ai_addr, cur->ai_addrlen); + if (err == -1) { + ssp_error("bind"); + ssp_close(sfd); + continue; + } + } else { + err = connect(sfd, cur->ai_addr, cur->ai_addrlen); + if (err == -1) { + ssp_error("connection with the server failed...\n"); + ssp_close(sfd); + continue; + } + } + + ssp_memcpy(addr, cur->ai_addr, cur->ai_addrlen); + *size_of_addr = cur->ai_addrlen; + + freeaddrinfo(cur); + break; + } + + if (cur == NULL) + { + ssp_printf("failed connecting with server\n"); + freeaddrinfo(res); + return -1; + } + + return sfd; +} +/*------------------------------------------------------------------------------ + This function is the inturpt handler for sigaction, right now i just + handle sigint, so we can exit nicely. +------------------------------------------------------------------------------*/ + +static void interuptHandler(int signum) +{ + switch (signum) + { + case SIGINT: + set_exit(); + break; + } +} + + +//see header file +int prepareSignalHandler() +{ + struct sigaction actionData; + sigemptyset(&actionData.sa_mask); + actionData.sa_handler = interuptHandler; + actionData.sa_flags = 0; + + if (sigaction(SIGINT, &actionData, NULL) == -1) + { + ssp_error("sigaction sigint failed\n"); + return -1; + } + return 1; + +} + +static int resizeBuff(char **buffer, uint32_t *newBufferSize, uint32_t *prev_buff_size) { + + if (*newBufferSize != *prev_buff_size) { + *buffer = realloc(*buffer, *newBufferSize); + if (buffer == NULL) { + return 1; + } + return 0; + } + return 1; +} +//see header file +void connection_server(char *host_name, char* port, int initial_buff_size, int connection_limit, + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other), + int (*onTimeOut)(void *other), + int (*checkExit)(void *other), + void (*onExit)(void *other), + void *other) +{ + uint32_t size_of_addr = 0; + void *addr = ssp_init_sockaddr_struct((size_t*)&size_of_addr); + + int sfd = prepareHost(host_name, addr, &size_of_addr, port, SOCK_STREAM, 1); + if (sfd < 0) + set_exit(); + + int err = listen(sfd, connection_limit); + + if (err == -1) + ssp_error("listen failed\n"); + + size_t size_of_socket_struct = 0; + + void *socket_set = ssp_init_socket_set(&size_of_socket_struct); + void *read_socket_set = ssp_init_socket_set(&size_of_socket_struct); + + ssp_fd_zero(socket_set); + ssp_fd_set(sfd, socket_set); + ssp_fd_set(STDIN_FILENO, socket_set); + + uint32_t *buff_size = ssp_alloc(1, sizeof(uint32_t)); + if (buff_size == NULL) + set_exit(); + + *buff_size = initial_buff_size; + uint32_t prev_buff_size = *buff_size; + + char *buff = ssp_alloc(sizeof(char), *buff_size); + if (buff_size == NULL) + set_exit(); + + for (;;) + { + + if (get_exit() || checkExit(other)){ + ssp_printf("exiting server thread\n"); + break; + } + + ssp_memcpy(read_socket_set, socket_set, size_of_socket_struct); + int nrdy = ssp_select(connection_limit + 1, read_socket_set, NULL, NULL, 100e3); + + if(!resizeBuff(&buff, buff_size, &prev_buff_size)){ + ssp_printf("packet too large, cannot resize buffer\n"); + } + + if (nrdy == -1) { + ssp_error("select"); + continue; + } + //timeout + if (nrdy == 0) { + if (onTimeOut(other) == -1) + ssp_printf("timeout failed\n"); + + continue; + } + + for(int i = 0; i < connection_limit + 1; i++) { + + if (ssp_fd_is_set(i, read_socket_set)) { + + if (i == sfd) { + int new_socket = accept(i, (struct sockaddr*) addr, &size_of_addr); + if (new_socket < 0) + ssp_error ("accept failed"); + + ssp_fd_set(new_socket, socket_set); + break; + } + + int count = 0; + while (count < *buff_size) { + count += ssp_recvfrom(i, &buff[count], *buff_size - count, 0, NULL, NULL); + if (count < 0) { + ssp_error("recv failed server"); + break; + } + else if (count == 0) { + ssp_error("connection finished"); + ssp_fd_clr(i, socket_set); + ssp_close(i); + break; + } + } + + if (count <= 0) + continue; + + int bytes_parsed = onRecv(i, buff, count, buff_size, addr, size_of_addr, other); + if (bytes_parsed == -1) { + ssp_printf("recv failed somewhere in parsing\n"); + } + + + } + } + } + ssp_free(addr); + ssp_free(read_socket_set); + ssp_free(socket_set); + ssp_free(buff_size); + ssp_free(buff); + ssp_close(sfd); + onExit(other); +} + + + + +//see header file +void connectionless_server(char *host_name, char* port, int initial_buff_size, + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other), + int (*onTimeOut)(void *other), + int (*checkExit)(void *other), + void (*onExit)(void *other), + void *other) +{ + + uint32_t size_of_addr = 0; + void *addr = ssp_init_sockaddr_struct((size_t*)&size_of_addr); + + int sfd = prepareHost(host_name, addr, &size_of_addr, port, SOCK_DGRAM, 1); + if (sfd < 0) + set_exit(); + + size_t size_of_socket_struct = 0; + + void *socket_set = ssp_init_socket_set(&size_of_socket_struct); + void *read_socket_set = ssp_init_socket_set(&size_of_socket_struct); + + ssp_fd_zero(socket_set); + ssp_fd_set(sfd, socket_set); + ssp_fd_set(STDIN_FILENO, socket_set); + + uint32_t *buff_size = ssp_alloc(1, sizeof(uint32_t)); + if (buff_size == NULL) + set_exit(); + + *buff_size = initial_buff_size + 10; + uint32_t prev_buff_size = *buff_size; + + char *buff = ssp_alloc(sizeof(char), *buff_size); + if (buff == NULL) + set_exit(); + + for (;;) { + + if (get_exit() || checkExit(other)){ + ssp_printf("exiting server thread\n"); + break; + } + + ssp_memcpy(read_socket_set, socket_set, size_of_socket_struct); + int nrdy = ssp_select(sfd + 1, read_socket_set, NULL, NULL, 100e3); + + if(!resizeBuff(&buff, buff_size, &prev_buff_size)){ + ssp_printf("packet too large, cannot resize buffer\n"); + } + + if (nrdy == -1) { + ssp_error("select"); + continue; + } + + if (nrdy == 0) { + if (onTimeOut(other) == -1) + ssp_printf("timeout failed\n"); + continue; + } + + if (ssp_fd_is_set(sfd, read_socket_set)) { + int count = ssp_recvfrom(sfd, buff, *buff_size, 0, addr, &size_of_addr); + + if (count == -1) + continue; + + else if (count >= *buff_size) { + ssp_printf("packet too large\n"); + } + else { + if (onRecv(sfd, buff, count, buff_size, addr, size_of_addr, other) == -1) + ssp_printf("recv failed server\n"); + } + } + } + ssp_free(addr); + ssp_free(read_socket_set); + ssp_free(socket_set); + ssp_free(buff_size); + ssp_free(buff); + ssp_close(sfd); + onExit(other); + +} + + + + +//https://www.cs.cmu.edu/afs/cs/academic/class/15213-f99/www/class26/udpclient.c +void connectionless_client(char *hostname, char*port, int packet_len, void *params, + int (*onSend)(int sfd, void *addr, size_t size_of_addr, void *params), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *params) , + int (*checkExit)(void *params), + void (*onExit)(void *params)) +{ + + int sfd, count; + + uint32_t size_of_addr = 0; + void *addr = ssp_init_sockaddr_struct((size_t*)&size_of_addr); + + sfd = prepareHost(hostname, addr, &size_of_addr, port, SOCK_DGRAM, 0); + if (sfd < 0) + set_exit(); + + uint32_t *buff_size = ssp_alloc(1, sizeof(uint32_t)); + if (buff_size == NULL) + set_exit(); + + *buff_size = packet_len + 10; + + uint32_t prev_buff_size = *buff_size; + + char *buff = ssp_alloc(sizeof(char), prev_buff_size); + if (buff == NULL) + set_exit(); + + + for (;;) { + if (get_exit() || checkExit(params)) + break; + + if(!resizeBuff(&buff, buff_size, &prev_buff_size)){ + ssp_error("packet too large, cannot resize buffer\n"); + } + + if (onSend(sfd, addr, size_of_addr, params)) + ssp_error("send failed\n"); + + count = ssp_recvfrom(sfd, buff, packet_len, MSG_DONTWAIT, addr, &size_of_addr); + + if (count == -1) + continue; + + else if (count >= *buff_size){ + ssp_error("packet too large\n"); + continue; + } + else{ + if (onRecv(sfd, buff, count, buff_size, addr, size_of_addr, params) == -1) + ssp_error("recv failed client\n"); + } + + } + + ssp_free(addr); + ssp_free(buff_size); + ssp_free(buff); + ssp_close(sfd); + onExit(params); +} + + +//https://www.cs.cmu.edu/afs/cs/academic/class/15213-f99/www/class26/udpclient.c +void connection_client(char *hostname, char*port, int packet_len, void *params, + int (*onSend)(int sfd, void *addr, size_t size_of_addr, void *params), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *params) , + int (*checkExit)(void *params), + void (*onExit)(void *params)) +{ + + int sfd, count; + + uint32_t size_of_addr = 0; + void *addr = ssp_init_sockaddr_struct((size_t*)&size_of_addr); + + sfd = prepareHost(hostname, addr, &size_of_addr, port, SOCK_STREAM, 0); + if (sfd < 0) + set_exit(); + + uint32_t *buff_size = ssp_alloc(1, sizeof(uint32_t)); + if (buff_size == NULL) + set_exit(); + + *buff_size = packet_len; + uint32_t prev_buff_size = *buff_size; + + char *buff = ssp_alloc(prev_buff_size, sizeof(char)); + if (buff == NULL) + set_exit(); + + + for (;;) { + + if (get_exit() || checkExit(params)) + break; + + if(!resizeBuff(&buff, buff_size, &prev_buff_size)){ + ssp_printf("packet too large, cannot resize buffer\n"); + } + + if (onSend(sfd, addr, size_of_addr, params)) + ssp_error("send failed here\n"); + + count = ssp_recvfrom(sfd, buff, packet_len, MSG_DONTWAIT, NULL, &size_of_addr); + + if (count < 0) + continue; + + if (onRecv(sfd, buff, count, buff_size, addr, size_of_addr, params) == -1) { + ssp_error("recv failed client\n"); + set_exit(); + } + + } + ssp_free(addr); + ssp_free(buff_size); + ssp_free(buff); + ssp_close(sfd); + onExit(params); +} + +#endif diff --git a/Program/src/protocol_handler.c b/Program/src/protocol_handler.c new file mode 100755 index 0000000..327a14c --- /dev/null +++ b/Program/src/protocol_handler.c @@ -0,0 +1,930 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "mib.h" +#include "port.h" +#include "protocol_handler.h" +#include "packet.h" +#include "filesystem_funcs.h" +#include "requests.h" +#include "types.h" +#include "utils.h" +#include "app_control.h" + +static void transasction_log(char *msg, uint64_t transaction_sequence_number){ + char log_message[2000]; + ssp_snprintf(log_message, sizeof(log_message), "%s%llu|%s\n", "transaction:", transaction_sequence_number, msg); + ssp_printf(log_message); +} + +static void build_temperary_file(Request *req, uint32_t size) { + + ssp_snprintf(req->source_file_name, 75, "%s%llu%s", "incomplete_requests/.temp_", req->transaction_sequence_number, ".jpeg"); + ssp_printf("haven't received metadata yet, building temperary file %s\n", req->source_file_name); + req->file = create_temp_file(req->source_file_name, size); +} + +static void send_ack(Request *req, Response res, unsigned int type){ + if (req->transmission_mode == UN_ACKNOWLEDGED_MODE) + return; + + uint8_t start = build_pdu_header(req->buff, req->transaction_sequence_number, req->transmission_mode, 0, &req->pdu_header); + build_ack(req->buff, start, type); + ssp_sendto(res); +} + +static void send_nak(Request *req, Response res, unsigned int type) { + if (req->transmission_mode == UN_ACKNOWLEDGED_MODE) + return; + + uint8_t start = build_pdu_header(req->buff, req->transaction_sequence_number, req->transmission_mode, 0, &req->pdu_header); + build_nak_directive(req->buff, start, type); + + ssp_sendto(res); +} + +static void resend_eof_ack(Request *req, Response res) { + + transasction_log("sending eof ack", req->transaction_sequence_number); + send_ack(req,res, EOF_PDU); + req->resent_eof++; +} + +/*------------------------------------------------------------------------------ + + Processing Packets + +------------------------------------------------------------------------------*/ + +//for finding the struct in the list +struct request_search_params { + uint32_t source_id; + uint32_t transaction_sequence_number; +}; + +//for finding the struct in the list +static int find_request(void *element, void *args) { + Request *req = (Request *) element; + struct request_search_params *params = (struct request_search_params *) args; + if (req->dest_cfdp_id == params->source_id && req->transaction_sequence_number == params->transaction_sequence_number){ + return 1; + } + + //ssp_printf("cant find request req->dest_cfdp_id %d params->source_id %d transaction_sequence_number %d:%d\n", req->dest_cfdp_id, params->source_id,req->transaction_sequence_number, params->transaction_sequence_number ); + return 0; +} + + + Request *new_incomming_request(uint32_t source_id, + uint32_t transmission_mode, + uint32_t transaction_sequence_number, + Response res, + FTP *app) { + + Remote_entity remote_entity; + int error = get_remote_entity_from_json(&remote_entity, source_id); + if (error < 0) { + ssp_error("could not get remote entity for incoming packet \n"); + return NULL; + } + Pdu_header pdu_header; + error = get_header_from_mib(&pdu_header, remote_entity, app->my_cfdp_id); + if (error < 0) { + ssp_printf("Couldn't make PDU HEADER IS NULL\n"); + return NULL; + } + + Request *found_req = init_request(app->buff, app->packet_len); + if (found_req == NULL) { + ssp_error("could not get allocate for new request \n"); + return NULL; + } + transasction_log("incoming new request", transaction_sequence_number); + + //Make new request and add it + found_req->transmission_mode = transmission_mode; + found_req->transaction_sequence_number = transaction_sequence_number; + found_req->dest_cfdp_id = source_id; + found_req->pdu_header = pdu_header; + found_req->my_cfdp_id = app->my_cfdp_id; + found_req->remote_entity = remote_entity; + found_req->procedure = none; + + found_req->res.addr = ssp_alloc(1, res.size_of_addr); + + if (found_req->res.addr == NULL) { + ssp_cleanup_req(found_req); + return NULL; + } + + ssp_memcpy(found_req->res.addr, res.addr, res.size_of_addr); + + found_req->res.packet_len = remote_entity.mtu; + found_req->res.sfd = res.sfd; + found_req->res.transmission_mode = app->remote_entity.default_transmission_mode; + found_req->res.type_of_network = app->remote_entity.type_of_network; + found_req->res.msg = found_req->buff; + found_req->res.size_of_addr = res.size_of_addr; + + found_req->paused = false; + return found_req; +} + + +/*creates a request struct if there is none for the incomming request based on transaction sequence number or +finds the correct request struct and replaces req with the new pointer. Returns the possition in the packet +where the data portion is, also sets incoming_pdu_header... returns -1 on fail*/ + +int process_pdu_header(char*packet, uint8_t is_server, Pdu_header *incoming_pdu_header, Response res, Request **req, List *request_list, FTP *app) { + + Pdu_header header; + memset(&header, 0, sizeof(Pdu_header)); + + int error = get_pdu_header_from_packet(packet, &header); + *incoming_pdu_header = header; + + //ssp_printf("received packet is server %d\n", is_server); + //ssp_print_header(&header); + + if (error < 0) { + ssp_error("failed to get pdu header, bad formatting"); + return -1; + } + if (app->my_cfdp_id != header.destination_id){ + ssp_print_bits(packet, 12); + ssp_printf("someone is sending packets here that are not for my id %u, dest_id: %u\n", app->my_cfdp_id, header.destination_id); + ssp_print_header(&header); + return -1; + } + + uint16_t len = header.PDU_data_field_len; + if (len > app->packet_len){ + ssp_printf("packet received %d that was too big for our buffer %d\n", len, app->packet_len); + return -1; + } + + //if packet is from the same request, don't' change current request + Request *current_req = (*req); + + if (current_req != NULL && current_req->transaction_sequence_number == header.transaction_sequence_number && current_req->dest_cfdp_id == header.source_id){ + return header.reserved_space_for_header; + } + + //look for active request in list + struct request_search_params params = { + header.source_id, + header.transaction_sequence_number, + }; + + Request *found_req = (Request *) request_list->find(request_list, -1, find_request, ¶ms); + + //if server, create new request (can probably move this out of this function) + if (found_req == NULL && is_server) + { + + found_req = new_incomming_request(header.source_id, + header.transmission_mode, + header.transaction_sequence_number, + res, + app); + + if (found_req == NULL) { + ssp_error("could not create request for incomming transmission"); + return -1; + } + + app->request_list->push(app->request_list, found_req, header.transaction_sequence_number); + } + + else if (found_req == NULL) { + ssp_error("could not find request \n"); + return -1; + } + + *req = found_req; + + return header.reserved_space_for_header; + +} + +// receives the offset +// writes offset to a file +// calclulates checksum +void process_data_packet(char *packet, uint32_t data_len, File *file) { + + if(file == NULL) { + ssp_error("file struct is null, can't write to file"); + return; + } + + uint32_t offset_start = get_data_offset_from_packet(packet); + uint32_t packet_index = 4; + + //ssp_printf("received data packet offset:%d\n", offset_start); + // size of 'offset' bytes in packet + uint32_t offset_end = offset_start + data_len - packet_index; + + //ssp_printf("received offset %d:%d\n", offset_start, offset_end); + + if (!receive_offset(file, offset_start, offset_end)) { + ssp_printf("throwing out packet\n"); + return; + } + + uint32_t remaining_buffer_length = data_len - packet_index; + int bytes = write_offset(file, &packet[packet_index], remaining_buffer_length, offset_start); + if (bytes <= 0) { + ssp_error("no new data was written\n"); + return; + } + + file->partial_checksum += calc_check_sum(&packet[packet_index], bytes); + + if (file->missing_offsets->count == 0) + return; +} + + +/* +typedef struct pdu_meta_data { + //0 Record boundaries respeced (read as array of octets), 1 not respected (variable length) + unsigned int segmentation_control : 1; + + unsigned int reserved_bits: 7; + + //length of the file in octets, set all 0 for unbounded size + uint32_t file_size; + LV source_file_name; + LV destination_file_name; + + + //Options include: + // Filestore requests, + // Messages to user. + // Fault Handler overrides. + // Flow Label. + + TLV *options; + +} Pdu_meta_data; +*/ +uint32_t parse_metadata_packet(char *packet, uint32_t start, Request *req_to_fill) { + + + memset(req_to_fill->source_file_name, 0, MAX_PATH); + memset(req_to_fill->destination_file_name, 0, MAX_PATH); + + req_to_fill->segmentation_control = get_bits_from_protocol_byte(packet[start], 0, 0); + uint8_t packet_index = start + 1; + + uint32_t file_len = 0; + memcpy(&file_len, &packet[packet_index], 4); + + req_to_fill->file_size = ssp_ntohl(file_len); + packet_index += 4; + + uint8_t file_name_len = packet[packet_index]; + packet_index++; + + ssp_memcpy(req_to_fill->source_file_name, &packet[packet_index], file_name_len); + packet_index += file_name_len; + + file_name_len = packet[packet_index]; + packet_index++; + + ssp_memcpy(req_to_fill->destination_file_name, &packet[packet_index], file_name_len); + + packet_index += file_name_len; + + return packet_index; +} + + +void process_messages(Request *req, FTP *app) { + + while (req->messages_to_user->count) { + Message *message = req->messages_to_user->pop(req->messages_to_user); + Message_put_proxy *p_put; + Message_cont_part_request *p_cont; + int error = 0; + char *error_msg = "couldn't process messages: %s"; + + //on failure, these will send back an error message to the requester + switch (message->header.message_type) + { + case PROXY_PUT_REQUEST: + + p_put = (Message_put_proxy *) message->value; + ssp_printf("received proxy request for source file name: %s dest file name %s, to id %llu\n", + (char *)p_put->source_file_name.value, + (char *)p_put->destination_file_name.value, + p_put->destination_id); + + start_request(put_request(p_put->destination_id, + (char *)p_put->source_file_name.value, + (char *)p_put->destination_file_name.value, req->remote_entity.default_transmission_mode, app)); + + break; + + case CONTINUE_PARTIAL: + + p_cont = (Message_cont_part_request *) message->value; + + uint64_t dest_id = p_cont->destination_id; + uint64_t orig_id = p_cont->originator_id; + uint64_t tran_id = p_cont->transaction_id; + + ssp_printf("received message request to continue one way communication destination id %llu, originator id %llu, transaction id %llu\n", + dest_id, orig_id, tran_id); + + if (orig_id != app->my_cfdp_id) { + ssp_printf(error_msg, "continue partial request, wrong originator ID"); + return; + } + + error = init_cont_partial_request(p_cont, app->buff, app->packet_len); + if (error < 0) + ssp_printf(error_msg, "continue partial request\n"); + break; + + default: + ssp_printf("message type not recognized\n"); + break; + } + ssp_free_message(message); + } + +} + + +/*------------------------------------------------------------------------------ + + Client + aka: handles responses from server + +------------------------------------------------------------------------------*/ + +static void resend_finished_ack(Request *req, Response res) { + transasction_log("sending finished ack", req->transaction_sequence_number); + send_ack(req, res, FINISHED_PDU); + req->resent_finished++; +} + +static void send_put_metadata(Request *req, Response res) { + + uint32_t start = build_pdu_header(req->buff, req->transaction_sequence_number, req->transmission_mode, 0, &req->pdu_header); + transasction_log("sending metadata pdu", req->transaction_sequence_number); + start = build_put_packet_metadata(req->buff, start, req); + + req->local_entity.Metadata_sent_indication = true; + ssp_sendto(res); +} + +static void send_eof_pdu(Request *req, Response res) { + uint32_t start = build_pdu_header(req->buff, req->transaction_sequence_number, req->transmission_mode, 0, &req->pdu_header); + transasction_log("sending eof pdu", req->transaction_sequence_number); + if (req->file_size == 0) + build_eof_packet(req->buff, start, 0, 0); + else + build_eof_packet(req->buff, start, req->file->total_size, req->file->partial_checksum); + + req->local_entity.EOF_sent_indication = true; + ssp_sendto(res); + return; +} + +int create_data_burst_packets(char *packet, uint32_t start, File *file, uint32_t length) { + + if (file->next_offset_to_send >= file->total_size){ + return 0; + } + + uint32_t packet_index = start; + uint32_t size_of_offset_bytes = sizeof(uint32_t); + + int data_len = build_data_packet(packet, packet_index, length, file->next_offset_to_send, file); + + packet_index += size_of_offset_bytes; + + //number of bytes sent + int bytes = data_len - size_of_offset_bytes; + + //calculate checksum for data packet, this is used to calculate in transit checksums + file->partial_checksum += calc_check_sum(&packet[packet_index], bytes); + + //ssp_printf("sending packet data offset:size %d:%d\n", file->next_offset_to_send, file->next_offset_to_send + bytes); + file->next_offset_to_send += bytes; + + if (file->next_offset_to_send == file->total_size) { + ssp_printf("sending packet data offset_start:offset_end:total_size %d:%d:%d\n", file->next_offset_to_send - bytes, file->next_offset_to_send, file->total_size); + return 1; + } + + ssp_printf("sending packet data offset_start:offset_end:total_size %d:%d:%d\n", file->next_offset_to_send - bytes, file->next_offset_to_send, file->total_size); + + return 0; +} + +static struct cont_partial_params { + uint32_t start; + Response *res; + Request *req; +}; + +static void continue_partials_send_partials(Node *node, void *element, void *args) { + + struct cont_partial_params *params = (struct cont_partial_params *) args; + + + Request *req = params->req; + Response *res = params->res; + uint32_t start = params->start; + Offset *o = (Offset *) element; + + int i = 0; + for (i = o->start; i < o->end; i += res->packet_len) { + req->file->next_offset_to_send = i; + create_data_burst_packets(req->buff, start, req->file, res->packet_len); + ssp_sendto(*res); + } + +} + +static void continue_partials_start(Request *req, Response res, bool *close) { + uint32_t start = build_pdu_header(req->buff, req->transaction_sequence_number, req->transmission_mode, 0, &req->pdu_header); + int i = 0; + + for (i = 0; i < RESEND_META_TIMES; i++) { + send_put_metadata(req, res); + } + + + struct cont_partial_params params = { + start, + &res, + req, + }; + + //send all missing offsets + req->file->missing_offsets->iterate(req->file->missing_offsets, continue_partials_send_partials, ¶ms); + + for (i = 0; i < RESEND_EOF_TIMES; i++) { + send_eof_pdu(req, res); + } + + req->procedure = none; +} + + +static void acknowledged_start(Request *req, Response res, bool *close) { + uint32_t start = build_pdu_header(req->buff, req->transaction_sequence_number, req->transmission_mode, 0, &req->pdu_header); + + send_put_metadata(req, res); + + if (req->file_size == 0 ){ + req->procedure = none; + return; + } + + while (!create_data_burst_packets(req->buff, start, req->file, res.packet_len)) { + ssp_sendto(res); + if (*close) { + return; + } + } + ssp_sendto(res); + + send_eof_pdu(req, res); + req->procedure = none; +} + +static void unacknowledged_start(Request *req, Response res, bool *close){ + + uint32_t start = build_pdu_header(req->buff, req->transaction_sequence_number, req->transmission_mode, 0, &req->pdu_header); + int i = 0; + + for (i = 0; i < RESEND_META_TIMES; i++) { + send_put_metadata(req, res); + } + + if (req->file_size == 0 ){ + req->procedure = none; + return; + } + + while (!create_data_burst_packets(req->buff, start, req->file, res.packet_len)) { + ssp_sendto(res); + if (*close) { + return; + } + } + + ssp_sendto(res); + + for (i = 0; i < RESEND_EOF_TIMES; i++) { + send_eof_pdu(req, res); + } + + req->procedure = none; +} + +//if no file attached to request, set procedure to none +static void start_sequence(Request *req, Response res, bool *close) { + + if (req->transmission_mode == UN_ACKNOWLEDGED_MODE) { + unacknowledged_start(req, res, close); + return; + } + acknowledged_start(req, res, close); + //set timeout to 0, databurst can take a while, timeout should start after data burst + reset_timeout(&req->timeout_before_cancel); + +} + + +static int segment_offset_into_data_packets(char *packet, uint32_t start, uint32_t offset_start, uint32_t offset_end, Request *req, Response res){ + + + int i = 0; + int error = 0; + + //the segment length has to reduce the length of the segment by the 'offset' bytes in the data packet + uint32_t segment_len = req->buff_len - start - sizeof(uint32_t); + + for (i = offset_start; i < offset_end; i+= segment_len) { + + if (offset_end - i < segment_len){ + segment_len = offset_end - i; + } + + //ssp_printf("sending offset start %d to %d\n", i, i + segment_len); + //ssp_printf("segment len %d\n",segment_len); + + error = build_data_packet(packet, start, req->buff_len, i, req->file); + if (error < 0) { + ssp_printf("couldn't create data packet for offset %d\n", i); + continue; + } + ssp_sendto(res); + } + return 0; +} + + + + +int process_nak_pdu(char *packet, Request *req, Response res, Client *client){ + Pdu_nak nak; + + uint32_t packet_index = get_nak_packet(packet, &nak); + uint32_t offset_start = 0; + uint32_t offset_end = 0; + //build new header for outgoing packets + uint32_t outgoing_packet_index = build_pdu_header(req->buff, req->transaction_sequence_number, 0, 0, &client->pdu_header); + int i = 0; + + ssp_printf("sending offset packet start %d offset end %d\n", nak.start_scope, nak.end_scope); + //ssp_printf("number of segments requests %d\n", nak.segment_requests); + + for (i = 0; i < nak.segment_requests; i++){ + + memcpy(&offset_start, &packet[packet_index], sizeof(uint32_t)); + offset_start = ssp_ntohl(offset_start); + packet_index += 4; + + memcpy(&offset_end, &packet[packet_index], sizeof(uint32_t)); + offset_end = ssp_ntohl(offset_end); + packet_index += 4; + + + //ssp_printf("offset_start %d offset_end %d \n", offset_start, offset_end); + + segment_offset_into_data_packets(req->buff, outgoing_packet_index, offset_start, offset_end, req, res); + } + + return 1; +} + + + +//fills the current request with packet data, responses from servers +void parse_packet_client(char *packet, uint32_t packet_index, Response res, Request *req, Client* client) { + + uint8_t directive = packet[packet_index]; + packet_index += 1; + + switch(directive) { + case FINISHED_PDU: + transasction_log("received finished pdu", req->transaction_sequence_number); + req->local_entity.transaction_finished_indication = true; + resend_finished_ack(req, res); + break; + case NAK_PDU: + transasction_log("received Nak pdu", req->transaction_sequence_number); + process_nak_pdu(&packet[packet_index], req, res, client); + break; + case ACK_PDU: + + switch (packet[packet_index]) + { + case EOF_PDU: + transasction_log("received Eof ack", req->transaction_sequence_number); + req->local_entity.EOF_recv_indication = true; + break; + + case META_DATA_PDU: + transasction_log("received meta_data ack", req->transaction_sequence_number); + req->local_entity.Metadata_recv_indication = true; + + default: + break; + } + + break; + case NAK_DIRECTIVE: + switch (packet[packet_index]) + { + case META_DATA_PDU: + transasction_log("resending metadata pdu", req->transaction_sequence_number); + send_put_metadata(req, res); + break; + + case EOF_PDU: + transasction_log("resending eof pdu", req->transaction_sequence_number); + send_eof_pdu(req, res); + break; + + default: + break; + } + + break; + default: + break; + } +} + +//current user request, to send to server +void user_request_handler(Response res, Request *req, Client* client) { + + if (req == NULL || req->paused) + return; + + switch (req->procedure) + { + case sending_nak_data: + continue_partials_start(req, res, &client->close); + break; + + case sending_start: + start_sequence(req, res, &client->close); + break; + + case clean_up: // will close the request happens in the previous functions + case none: + break; + default: + break; + } +} +/*------------------------------------------------------------------------------ + + SERVER SIDE + aka: handles responses from remote + +------------------------------------------------------------------------------*/ + +static void request_eof(Request *req, Response res) { + transasction_log("sending eof nak pdu", req->transaction_sequence_number); + send_nak(req, res, EOF_PDU); +} + +static void request_metadata(Request *req, Response res) { + transasction_log("sending meta_data nak pdu", req->transaction_sequence_number); + send_nak(req, res, META_DATA_PDU); +} + +static void request_data(Request *req, Response res) { + uint8_t start = build_pdu_header(req->buff, req->transaction_sequence_number, 1, 0, &req->pdu_header); + transasction_log("sending data nak pdu", req->transaction_sequence_number); + build_nak_packet(req->buff, start, req); + ssp_sendto(res); +} + +static void resend_finished_pdu(Request *req, Response res) { + + uint8_t start = build_pdu_header(req->buff, req->transaction_sequence_number, 1, 0, &req->pdu_header); + transasction_log("sending finished pdu", req->transaction_sequence_number); + build_finished_pdu(req->buff, start); + ssp_sendto(res); + req->resent_finished++; +} + + +//processes the eof packet, sets checksum, indication, and filesize. +void process_pdu_eof(char *packet, Request *req, Response res) { + + Pdu_eof eof_packet; + get_eof_from_packet(packet, &eof_packet); + + if (req->file == NULL && req->local_entity.Metadata_recv_indication) { + build_temperary_file(req, eof_packet.file_size); + } + + req->local_entity.EOF_recv_indication = true; + req->file->eof_checksum = eof_packet.checksum; + req->file->total_size = eof_packet.file_size; + +} + + + +int process_file_request_metadata(Request *req) { + + char temp[75]; + if (req->file == NULL) + req->file = create_file(req->destination_file_name, 1); + + else if (req->file->is_temp) { + ssp_snprintf(temp, 75, "%s%llu%s", "incomplete_requests/.temp_", req->transaction_sequence_number, ".jpeg"); + change_tempfile_to_actual(temp, req->destination_file_name, req->file_size, req->file); + return 1; + } + + if (req->file == NULL) { + return -1; + } + + int error = add_first_offset(req->file, req->file_size); + if (error < 0) { + ssp_free_file(req->file); + return -1; + } + + return 1; +} + +static void process_metadata(char *packet, uint32_t packet_index, Response res, Request *req, FTP *app) { + + req->local_entity.Metadata_recv_indication = true; + + packet_index = parse_metadata_packet(packet, packet_index, req); + uint16_t data_len = get_data_length(packet); + + get_messages_from_packet(packet, packet_index, data_len, req); + process_messages(req, app); + + send_ack(req, res, META_DATA_PDU); + + if (req->file_size != 0) + process_file_request_metadata(req); + else { + ssp_printf("just receiving messages, closing request\n"); + req->local_entity.EOF_recv_indication = true; + //TODO this was set to clean_up on FreeRTOS because we didn't have a correct clock yet to, set to NONE when clock is right + //it kind of creates a weird timing issue when the request closes before the finacks are sent and received + req->procedure = clean_up; + req->paused = true; + } +} + + +void on_server_time_out(Response res, Request *req) { + + + if (req->paused || req->transmission_mode == UN_ACKNOWLEDGED_MODE) + return; + + if (req->local_entity.transaction_finished_indication == true && RESEND_FINISHED_TIMES != req->resent_finished){ + resend_finished_pdu(req, res); + return; + } + if (req->resent_finished == RESEND_FINISHED_TIMES && req->local_entity.transaction_finished_indication) { + req->procedure = clean_up; + ssp_printf("file sent, closing request transaction: %llu\n", req->transaction_sequence_number); + return; + } + //send request for metadata + if (!req->local_entity.Metadata_recv_indication) { + request_metadata(req, res); + return; + } + + //receiving just messages, send back finished + if (req->file_size == 0 && RESEND_FINISHED_TIMES != req->resent_finished) { + resend_finished_pdu(req, res); + return; + } + + //send missing eofs + if (!req->local_entity.EOF_recv_indication) { + request_eof(req, res); + } + + //received EOF, send back 3 eof ack packets + else if (req->local_entity.EOF_recv_indication && req->resent_eof < RESEND_EOF_TIMES) { + resend_eof_ack(req, res); + } + + + //if have not received metadata for a file tranaction, this should not ever trigger //TODO add asert + if (req->file == NULL) { + ssp_printf("file is null, not sending data naks"); + return; + } + //send missing NAKS + if (req->file->missing_offsets->count > 0) { + request_data(req, res); + return; + + } else { + //finished transaction, should have checksum complete, and received eof notification + if (req->file->eof_checksum == req->file->partial_checksum && req->local_entity.EOF_recv_indication) { + ssp_printf("checksum have: %u checksum_need: %u\n", req->file->partial_checksum, req->file->eof_checksum); + req->local_entity.transaction_finished_indication = true; + resend_finished_pdu(req, res); + return; + } + + //ssp_printf("checksum have: %u checksum_need: %u\n", req->file->partial_checksum, req->file->eof_checksum); + //uint32_t checksum = check_sum_file(req->file, 1000); + //ssp_printf("checksum re-calculated: %u\n", checksum); + + } + +} + +//fills the current_request struct for the server, incomming requests +int parse_packet_server(char *packet, uint32_t packet_index, Response res, Request *req, Pdu_header incoming_header, FTP *app) { + + if (packet_index == 0) + return -1; + + uint16_t data_len = get_data_length(packet); + uint32_t packet_len = packet_index + data_len; + int error = 0; + + //process file data + if (incoming_header.PDU_type == DATA) { + if (!req->local_entity.Metadata_recv_indication) { + if (req->file == NULL) { + transasction_log("file is null", incoming_header.transaction_sequence_number); + error = get_req_from_file(incoming_header.source_id, incoming_header.transaction_sequence_number, incoming_header.destination_id, req); + if (error < 0) + build_temperary_file(req, TEMP_FILESIZE); + + } + } + process_data_packet(&packet[packet_index], data_len, req->file); + return packet_len; + } + + + Pdu_directive *directive = (Pdu_directive *) &packet[packet_index]; + Pdu_ack ack_packet; + packet_index++; + + switch (directive->directive_code) + { + case META_DATA_PDU: + if (req->local_entity.Metadata_recv_indication) + break; + + transasction_log("received metadata packet transaction", incoming_header.transaction_sequence_number); + process_metadata(packet, packet_index, res, req, app); + break; + + case EOF_PDU: + if (req->local_entity.EOF_recv_indication) + break; + + transasction_log("received eof packet", incoming_header.transaction_sequence_number); + process_pdu_eof(&packet[packet_index], req, res); + break; + + case ACK_PDU: + + get_ack_from_packet(&packet[packet_index], &ack_packet); + + switch (ack_packet.directive_code) + { + case FINISHED_PDU: + //get_finished_pdu(char *packet, Pdu_finished *pdu_finished) + transasction_log("received finished packet Ack", incoming_header.transaction_sequence_number); + req->local_entity.transaction_finished_indication = true; + break; + + default: + break; + } + break; + default: + break; + } + + return packet_len; +} + diff --git a/Program/src/requests.c b/Program/src/requests.c new file mode 100755 index 0000000..31f565f --- /dev/null +++ b/Program/src/requests.c @@ -0,0 +1,662 @@ + +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------------ + + USER STUFF + aka: request from person + +------------------------------------------------------------------------------*/ +#include "port.h" +#include "requests.h" +#include "utils.h" +#include "types.h" +#include "filesystem_funcs.h" +#include "file_delivery_app.h" +#include "mib.h" + + +//returns total space taken up in the packet from the added lv +uint16_t copy_lv_to_buffer(char *buffer, LV lv) { + uint16_t packet_index = 0; + buffer[packet_index] = lv.length; + packet_index++; + ssp_memcpy(&buffer[packet_index], lv.value, lv.length); + packet_index += lv.length; + return packet_index; +} + +void free_lv(LV lv) { + ssp_free(lv.value); +} + +//this should return -1 on alloc fail +void create_lv(LV *lv, int len, void *value) { + + lv->value = ssp_alloc(len, sizeof(char)); + ssp_memcpy(lv->value, value, len); + lv->length = len; +} + +//lv is what we copy into, packet is the buffer, and start is where in the buffer +//we start copying the lv to +void copy_lv_from_buffer(LV *lv, char *packet, uint32_t start) { + uint8_t len = packet[start]; + create_lv(lv, len, &packet[start + 1]); + return; +} + + +Message *create_message(uint8_t type) { + + Message *message = ssp_alloc(1, sizeof(Message)); + if (message == NULL) + return NULL; + + //message->header.message_id_cfdp = ssp_alloc(5, sizeof(char)); + ssp_memcpy(message->header.message_id_cfdp, "cfdp", 5); + message->header.message_type = type; + return message; +} + + + + +/*------------------------------------------------------------------------------ + Messages (additional minor requests, things like mv files) +------------------------------------------------------------------------------*/ + +//this will turn a incoming request to a request that will go out later +int init_cont_partial_request(Message_cont_part_request *p_cont, char *buff, uint32_t buff_len) { + + //don't need these params, just using this function for the req + Request *req = init_request(buff, buff_len); + if (req == NULL) + return -1; + + uint32_t dest_id = p_cont->destination_id; + uint64_t trans_num = p_cont->transaction_id; + uint32_t src_id = p_cont->originator_id; + + int error = get_req_from_file(src_id, trans_num, dest_id, req); + if (error < 0) { + ssp_error("couldn't get request from file system\n"); + ssp_cleanup_req(req); + return error; + } + Request old_request = *req; + + req->dest_cfdp_id = dest_id; + req->res.addr = NULL; + + error = get_remote_entity_from_json(&req->remote_entity, dest_id); + if (error < 0) { + ssp_error("couldn't get remote config from file system\n"); + ssp_cleanup_req(req); + return error; + } + req->local_entity.EOF_sent_indication = req->local_entity.EOF_recv_indication; + req->local_entity.Metadata_sent_indication = req->local_entity.Metadata_recv_indication; + req->local_entity.resumed_indication = 0; + req->local_entity.suspended_indication = 0; + req->local_entity.transaction_finished_indication = 0; + req->my_cfdp_id = src_id; + + get_header_from_mib(&req->pdu_header, req->remote_entity, req->dest_cfdp_id); + + error = save_req_to_file(req); + if (error < 0) { + ssp_error("couldn't save req to file\n"); + ssp_cleanup_req(req); + return -1; + } + //delete old file + error = delete_saved_request(&old_request); + if (error < 0) { + ssp_error("couldn't remove saved request\n"); + ssp_cleanup_req(req); + return -1; + } + ssp_cleanup_req(req); + return 1; +} + + +//Omission of source and destination filenames shall indicate that only Meta +//data will be delivered +Message_put_proxy * +create_message_put_proxy(uint32_t beneficial_cfid, + uint8_t length_of_id, + char *source_name, + char *dest_name) { + + Message_put_proxy *message = ssp_alloc(1, sizeof(Message_put_proxy)); + if (message == NULL) + return NULL; + + create_lv(&message->destination_file_name, strnlen(dest_name, MAX_PATH) + 1, dest_name); + create_lv(&message->source_file_name, strnlen(source_name, MAX_PATH) + 1, source_name); + message->destination_id = beneficial_cfid; + + return message; +} + +//beneficial_cfid is the destination id that the proxy will send to, length_of_id is in octets (or bytes) +int add_proxy_message_to_request(uint32_t beneficial_cfid, + uint8_t length_of_id, + char *source_name, + char *dest_name, + Request *req) { + + Message *message = create_message(PROXY_PUT_REQUEST); + if (message == NULL) + return -1; + + message->value = create_message_put_proxy(beneficial_cfid, length_of_id, source_name, dest_name); + if (message->value == NULL) { + ssp_free(message); + return -1; + } + + req->messages_to_user->push(req->messages_to_user, message, 0); + return 1; +} + +Message_cont_part_request * +create_message_cont_partial_request(uint32_t beneficial_cfid, + uint32_t originator_id, + uint32_t transaction_id) { + + Message_cont_part_request *message = ssp_alloc(1, sizeof(Message_cont_part_request)); + if (message == NULL) + return NULL; + + message->destination_id = beneficial_cfid; + message->originator_id = originator_id; + message->transaction_id = transaction_id; + return message; +} + +//beneficial_cfid is the destination id that the proxy will send to, originator +//is the sender's id +int add_cont_partial_message_to_request(uint32_t beneficial_cfid, + uint32_t originator_id, + uint32_t transaction_id, + Request *req){ + + Message *message = create_message(CONTINUE_PARTIAL); + if (message == NULL) + return -1; + + message->value = create_message_cont_partial_request(beneficial_cfid, + originator_id, + transaction_id + ); + if (message->value == NULL) { + ssp_free(message); + return -1; + } + + req->messages_to_user->push(req->messages_to_user, message, 0); + return 1; +} + + +static void ssp_free_put_proxy_message(Message_put_proxy* message) { + + free_lv(message->destination_file_name); + free_lv(message->source_file_name); + +} +static void ssp_free_proxy_cont_partial_request(Message_cont_part_request *message) { + +} + +void ssp_free_message(void *params) { + + Message *message = (Message*) params; + Message_put_proxy* proxy_request; + Message_cont_part_request* proxy_cont_partial_request; + + switch (message->header.message_type) + { + case PROXY_PUT_REQUEST: + proxy_request = (Message_put_proxy *) message->value; + ssp_free_put_proxy_message(proxy_request); + break; + + case CONTINUE_PARTIAL: + proxy_cont_partial_request = (Message_cont_part_request *) message->value; + ssp_free_proxy_cont_partial_request(proxy_cont_partial_request); + break; + default: + break; + } + ssp_free(message->value); + ssp_free(message); +} + +/*------------------------------------------------------------------------------ + Requests (major functions to initialize requests +------------------------------------------------------------------------------*/ + +void ssp_cleanup_req(void *request) { + + if (request == NULL) + return; + + Request *req = (Request *) request; + + if (req->file != NULL) + ssp_free_file(req->file); + + if (req->messages_to_user->count > 0) + req->messages_to_user->free(req->messages_to_user, ssp_free_message); + else + req->messages_to_user->freeOnlyList(req->messages_to_user); + + ssp_free(req->res.addr); + ssp_free(req); + +} + +Request *init_request_no_client() { + + Request *req = ssp_alloc(1, sizeof(Request)); + if (req == NULL) + return NULL; + + memset(req, 0, sizeof(Request)); + + req->file = NULL; + req->procedure = none; + req->paused = true; + req->timeout_before_cancel = ssp_time_count(); + req->timeout_before_journal = ssp_time_count(); + + req->messages_to_user = linked_list(); + if (req->messages_to_user == NULL) { + ssp_free(req->buff); + return NULL; + } + return req; +} + + +Request *init_request(char *buff, uint32_t buff_len) { + + Request *req = init_request_no_client(); + if (req == NULL) + return NULL; + + req->buff_len = buff_len; + req->buff = buff; + req->res.msg = req->buff; + + return req; +} + + +Client *start_client(FTP *app, uint8_t dest_id) { + + + while (!app->initialized); + //spin up a new client thread + Client *client = (Client *) app->active_clients->find(app->active_clients, dest_id, NULL, NULL); + + if (client == NULL) { + ssp_printf("Spinning up a new client thread\n"); + client = ssp_client(dest_id, app); + if (client == NULL) { + ssp_printf("client is null, couln't spin up client thread"); + return NULL; + } + + } else { + ssp_printf("adding request to existing client thread\n"); + } + return client; +} + + +//adds generic request to client, will set the pdu_header to new client +void add_request_to_client(Request *req, Client *client) { + + req->dest_cfdp_id = client->remote_entity.cfdp_id; + req->pdu_header = client->pdu_header; + req->my_cfdp_id = client->app->my_cfdp_id; + req->buff = client->buff; + req->buff_len = client->packet_len; + client->request_list->insert(client->request_list, req, -1); + + //unlock if lock is present + ssp_lock_give(client->lock); + +} + +int put_request_no_client( + Request *req, + char *source_file_name, + char *destination_file_name, + uint8_t transmission_mode, + FTP *app) { + + uint32_t file_size = 0; + + //build a request + req->my_cfdp_id = app->my_cfdp_id; + req->transmission_mode = transmission_mode; + req->procedure = sending_start; + + if (source_file_name == NULL && destination_file_name == NULL) { + req->transaction_sequence_number = app->transaction_sequence_number++; + return 0; + } + + if (strnlen(source_file_name, MAX_PATH) == 0 || strnlen(destination_file_name, MAX_PATH) == 0) { + ssp_printf("ERROR: no file names present in put request, if you want to just send messages, make both source and dest NULL\n"); + return -1; + } + + bool exists = does_file_exist(source_file_name); + if (exists == false) { + ssp_printf("ERROR: File does not exist\n"); + return -1; + } + + file_size = get_file_size(source_file_name); + if (file_size == -1) { + ssp_printf("ERROR: couldn't get file size\n"); + return -1; + } + + req->file = create_file(source_file_name, false); + if (req->file == NULL) { + ssp_printf("ERROR: couldn't create file\n"); + return -1; + } + + //this could probably go into 'create_file' + int error = add_first_offset(req->file, req->file->total_size); + if (error < 0) { + ssp_free_file(req->file); + return -1; + } + + req->file_size = file_size; + req->transaction_sequence_number = app->transaction_sequence_number++; + ssp_memcpy(req->source_file_name, source_file_name ,strnlen(source_file_name, MAX_PATH)); + ssp_memcpy(req->destination_file_name, destination_file_name, strnlen(destination_file_name, MAX_PATH)); + return 0; +} +/*NULL for source and destination filenames shall indicate that only Meta +data will be delivered. Side effect: add request to client request list +returns the request*/ +Request *put_request( + uint32_t dest_id, + char *source_file_name, + char *destination_file_name, + uint8_t transmission_mode, + FTP *app + ) { + + + Request *req = init_request_no_client(); + if (req == NULL) { + ssp_error("couldn't init request"); + return NULL; + } + + if (put_request_no_client(req, source_file_name, destination_file_name, transmission_mode, app) < 0){ + ssp_error("couldn't configure request"); + return NULL; + } + + Client *client = start_client(app, dest_id); + if (client == NULL) { + ssp_printf("client failed to start\n"); + return NULL; + } + + add_request_to_client(req, client); + return req; +} + +Request *get_request( + uint32_t dest_id, + char *source_file_name, + char *destination_file_name, + uint8_t transmission_mode, + FTP *app){ + + Request *req = init_request_no_client(); + put_request_no_client(req, NULL, NULL, transmission_mode, app); + add_proxy_message_to_request(app->my_cfdp_id, 1, source_file_name, destination_file_name, req); + + Client *client = start_client(app, dest_id); + if (client == NULL) { + ssp_printf("client failed to start\n"); + } else + add_request_to_client(req, client); + + return req; +} +/*NULL for source and destination filenames shall indicate that only Meta +data will be delivered. Side effect: add request to client request list +returns the request*/ + +int schedule_request(Request *req, uint32_t dest_id, FTP *app) { + req->dest_cfdp_id = dest_id; + req->my_cfdp_id = app->my_cfdp_id; + req->procedure = sending_start; + int error = save_req_to_file(req); + return error; +} + +int schedule_put_request( + uint32_t dest_id, + char *source_file_name, + char *destination_file_name, + uint8_t transmission_mode, + FTP *app + ) { + int error = 0; + + Request *req = init_request_no_client(); + if (req == NULL) { + ssp_error("couldn't init request"); + return -1; + } + + + error = put_request_no_client(req, source_file_name, destination_file_name, transmission_mode, app); + if (error < 0){ + ssp_error("couldn't configure request"); + return -1; + } + + error = schedule_request(req, dest_id, app); + if (error < 0) { + ssp_error("failed to schedule request"); + return -1; + } + return 0; +} +/* +static void clean_up_start_scheduled_requests(int fd, Request *req){ + + if (req != NULL) + ssp_cleanup_req(req); + + int error = ssp_close(fd); + if (error < 0) { + ssp_error("there was an error closing the file descriptor"); + } +} +*/ + +int start_scheduled_requests(uint32_t dest_id, FTP *app){ + + char dir_name[MAX_PATH]; + ssp_snprintf(dir_name, MAX_PATH, "%s%u%s", "incomplete_requests/CFID:", dest_id, "_requests"); + ssp_printf("opening dir %s\n", dir_name); + + void *dir; + char file[MAX_PATH]; + + dir = ssp_opendir(dir_name); + if(dir == NULL){ + ssp_error("Unable to open directory"); + return -1; + } + int error = 0; + Request *req = NULL; + Client *client = NULL; + + //adding +2 here because file->name is of max 256 size, and then we add a /. + char file_path[MAX_PATH + 2]; + + client = start_client(app, dest_id); + if (client == NULL) { + ssp_error("couldn't start new request"); + return -1; + } + + while( (ssp_readdir(dir, file)) ) + { + if (strncmp(file, ".", 1) == 0 || strncmp(file, "..", 2) == 0) + continue; + + ssp_snprintf(file_path, sizeof(file_path), "%s/%s", dir_name, file); + + req = init_request_no_client(); + if (req == NULL) { + ssp_cleanup_req(req); + ssp_error("couldn't init request"); + continue; + } + + error = get_request_from_json(req, file_path); + if (error < 0) { + ssp_cleanup_req(req); + ssp_error("couldn't read in json request"); + continue; + } + + add_request_to_client(req, client); + } + + ssp_closedir(dir); + return 0; +} + + +void start_request(Request *req){ + if (req == NULL) { + ssp_printf("ERROR: couldn't start request\n"); + return; + } + ssp_printf("started request\n"); + req->paused = false; +} + + + +static void print_messages_callback(Node *node, void *element, void *args) { + + Message *m = (Message*) element; + + ssp_printf("----------------Printing Message---------------\n"); + ssp_printf("Message type: %d\n", m->header.message_type); + ssp_printf("id: %s\n", m->header.message_id_cfdp); + Message_put_proxy *proxy; + + if (m->header.message_type == PROXY_PUT_REQUEST) { + proxy = (Message_put_proxy *)m->value; + ssp_printf("Message type: PROXY_PUT_REQUST\n"); + ssp_printf("dest filename: %s\n", proxy->destination_file_name.value); + ssp_printf("source filename: %s\n", proxy->source_file_name.value); + ssp_printf("id: %llu\n", proxy->destination_id); + + } + +} + +void print_request_state(Request *req) { + + ssp_printf("----------------Transaction %llu---------------\n", req->transaction_sequence_number); + + ssp_printf("local_entity id and stats: \n"); + + ssp_printf("destination id %d: \n", req->dest_cfdp_id); + ssp_printf("EOF_recv indication %d\n", req->local_entity.EOF_recv_indication); + ssp_printf("EOF_sent indication %d\n", req->local_entity.EOF_sent_indication); + ssp_printf("Metadata_recv indication %d\n", req->local_entity.Metadata_recv_indication); + ssp_printf("Metadata_sent indication %d\n", req->local_entity.Metadata_sent_indication); + ssp_printf("Resume indication %d\n", req->local_entity.resumed_indication); + ssp_printf("Suspended indication %d\n", req->local_entity.suspended_indication); + ssp_printf("Transaction finished indication %d\n", req->local_entity.transaction_finished_indication); + + if (req->file != NULL) { + ssp_printf("checksum received = %u checksum calculated = %u\n", req->file->eof_checksum, req->file->partial_checksum); + ssp_printf("offset list count %d\n", req->file->missing_offsets->count); + } + print_request_procedure(req); + + ssp_printf("current message count %d\n", req->messages_to_user->count); + ssp_printf("current messages: \n"); + req->messages_to_user->iterate(req->messages_to_user, print_messages_callback, NULL); + + ssp_printf("request header destination id: %d\n", req->pdu_header.destination_id); + ssp_printf("request header source id: %d\n", req->pdu_header.source_id); + ssp_printf("request header crc flag: %d\n", req->pdu_header.CRC_flag); + ssp_printf("request header direction: %d\n", req->pdu_header.direction); + ssp_printf("request header length of Ids: %d\n", req->pdu_header.length_of_entity_IDs); + ssp_printf("request header PDU_data_field_len: %d\n", req->pdu_header.PDU_data_field_len); + ssp_printf("request header pdu type: %d\n", req->pdu_header.PDU_type); + ssp_printf("request header transaction_seq_num_len: %d\n", req->pdu_header.transaction_seq_num_len); + ssp_printf("request header transaction_sequence_number: %llu\n", req->pdu_header.transaction_sequence_number); + ssp_printf("request header transmission_mode: %d\n", req->pdu_header.transmission_mode); + ssp_printf("request header version: %d\n", req->pdu_header.version); + ssp_printf("---------------------------------------------\n"); +} + + +void print_request_procedure(Request *req){ + + ssp_printf("current procedure: "); + switch (req->procedure) + { + + case sending_start: + ssp_printf("sending_start\n"); + break; + + case clean_up: // will close the request happens in the previous functions + ssp_printf("clean_up\n"); + break; + + case none: + ssp_printf("none\n"); + break; + + default: + break; + } +} + + +void print_res(Response res){ + + ssp_printf("addr %d\n", res.addr); + ssp_printf("msg %d\n", res.msg); + ssp_printf("packet_len %d\n", res.packet_len); + ssp_printf("sfd %d\n", res.sfd); + ssp_printf("size_of_addr %d\n", res.size_of_addr); + ssp_printf("transmission_mode %d\n", res.transmission_mode); + ssp_printf("type_of_network %d\n", res.type_of_network); +} diff --git a/Program/src/utils.c b/Program/src/utils.c new file mode 100755 index 0000000..f20598d --- /dev/null +++ b/Program/src/utils.c @@ -0,0 +1,118 @@ +/*------------------------------------------------------------------------------ +This file is protected under copyright. If you want to use it, +please include this text, that is my only stipulation. + +Author: Evan Giese +------------------------------------------------------------------------------*/ +#include "port.h" +#include "utils.h" +#include +#include +#include + +//size is the number of bytes we want to print +void ssp_print_hex(char *stuff, int size){ + + uint32_t current_packet_index = 0; + ssp_printf("printing number of bytes: %u\n", size); + int j = 0; + for (j = 0; j < size; j += 1) { + ssp_printf("%x.", + stuff[current_packet_index]); + current_packet_index += 1; + } + ssp_printf("\n"); +} + + +static int log_fd = -1; +void log_ftp(char *info, char *stuff){ + + #ifdef POSIX_PORT + time_t current_time; + char c_time_string[1000]; + current_time = time(NULL); + + if (current_time == -1) { + ssp_printf("Failure to obtain the current time.\n"); + } + + struct tm *time = localtime(¤t_time); + + ssp_snprintf(c_time_string, sizeof(c_time_string), "%d-%d-%dT%d:%d:%dZ|%s|%s", + time->tm_year, + time->tm_mon, + time->tm_mday, + time->tm_hour, + time->tm_min, + time->tm_sec, + info, + stuff + ); + + if (c_time_string == NULL) { + printf("Failure to obtain the current time string.\n"); + return; + } + + if (log_fd < 0) { + + int log_fd = ssp_open("log.txt", SSP_O_RDWR, 0655); + if (log_fd == -1){ + log_fd = ssp_open("log.txt", SSP_O_CREAT | SSP_O_RDWR, 0655); + } + else { + log_fd = ssp_open("log.txt", SSP_O_RDWR | O_APPEND); + + } + + int size = strnlen(c_time_string, sizeof(c_time_string)); + if (size < 0) { + printf("Failure to obtain the current time string.\n"); + return; + } + + int bytes = write(log_fd, c_time_string, size); + if (bytes < 0) { + printf("Failure to write log string.\n"); + return; + } + + close(log_fd); + } + #endif + return; +} + +void ssp_print_bits(char *stuff, int size){ + + ssp_printf("printing number of bytes: %u\n", size); + int j, i, bit_set, byte = 0; + unsigned char bit_mask = 0; + + for (i = 0; i < size; i++) { + byte = stuff[i]; + bit_mask = 128; + for (j = 7; j >= 0 ; j--) { + bit_set = bit_mask & byte; + bit_mask = bit_mask >> 1; + + if (bit_set){ + ssp_printf("1"); + } else { + ssp_printf("0"); + } + } + ssp_printf(" "); + } + ssp_printf("\n"); +} + + +//replace strings with this + +char *safe_strncpy(char *to, char*from, int len){ + void *ret = strncpy(to, from, len); + to[len] = '\0'; + return ret; +} diff --git a/Program/test/filesystem_tests.c b/Program/test/filesystem_tests.c new file mode 100755 index 0000000..5d1cbdd --- /dev/null +++ b/Program/test/filesystem_tests.c @@ -0,0 +1,144 @@ +#include "filesystem_funcs.h" +#include "unit_tests.h" +#include "port.h" +#include "mib.h" +#include "requests.h" +#include +#include "test.h" + + + +static void nak_print(Node *node, void *element, void *args){ + Offset *offset = (Offset *)element; + ssp_printf("start: %u end: %u\n", offset->start, offset->end); +} + +static int receive_offset_tests(){ + + File *file = create_temp_file("temp_test", 2000); + receive_offset(file, 5, 50); + + file->missing_offsets->iterate(file->missing_offsets, nak_print, 0); + receive_offset(file, 100, 1000); + + file->missing_offsets->iterate(file->missing_offsets, nak_print, 0); + receive_offset(file, 50, 100); + + file->missing_offsets->iterate(file->missing_offsets, nak_print, 0); + return 0; + +} + +void print_json(char *key, char *value, void *params) { + + ssp_printf("%s:%s\n", key, value); + +} + + +int read_json_file_test() { + + int error = read_json("mib/peer_1.json", print_json, NULL); + + return 1; +} + +int test_write_request_json() { + + DECLARE_NEW_TEST("test writing and getting json data"); + char *json_file = "test_incomeplete_file.json"; + Request req; + + req.file = NULL; + req.file = create_file("test_files/dest.jpg", 0); + + strncpy(req.source_file_name, "FileName", 20); + strncpy(req.destination_file_name, "DestinationFileName", 100); + + + add_first_offset(req.file, 3000); + receive_offset(req.file, 1000, 2000); + + req.my_cfdp_id = 5; + req.dest_cfdp_id = 10; + req.transaction_sequence_number = 56423487523; + req.paused = 0; + req.resent_eof = 2; + req.resent_finished = 2; + req.sent_first_data_round = 1; + req.timeout_before_cancel = 50; + req.timeout_before_journal = 40; + req.transmission_mode = 1; + req.file_size = 150; + req.local_entity.EOF_recv_indication = 1; + req.local_entity.EOF_sent_indication = 1; + req.local_entity.file_segment_recv_indication = 1; + req.local_entity.Metadata_recv_indication = 1; + req.local_entity.Metadata_sent_indication = 1; + req.local_entity.resumed_indication = 1; + req.local_entity.suspended_indication = 2; + req.local_entity.transaction_finished_indication = 1; + + int error = write_request_json(&req, json_file); + if (error < 0) { + ssp_printf("failed to write json test file\n"); + } + + Request req2; + + error = get_request_from_json(&req2, "test_incomeplete_file.json"); + if (error < 0) { + ssp_printf("failed to read json test file\n"); + return -1; + } + + ASSERT_EQUALS_INT("my_cfdp_id", req.my_cfdp_id, req2.my_cfdp_id); + ASSERT_EQUALS_INT("dest_cfdp_id", req.dest_cfdp_id, req2.dest_cfdp_id); + ASSERT_EQUALS_INT("transaction_sequence_number", req.transaction_sequence_number, req2.transaction_sequence_number); + ASSERT_EQUALS_INT("paused", req.paused, req2.paused); + ASSERT_EQUALS_STR("FileName", req.source_file_name, req2.source_file_name, strnlen(req.source_file_name, 100)); + ASSERT_EQUALS_STR("DestinationFileName", req.destination_file_name, req2.destination_file_name, strnlen(req.destination_file_name, 100)); + ASSERT_EQUALS_INT("resent_eof", req.resent_eof, req2.resent_eof); + ASSERT_EQUALS_INT("resent_finished", req.resent_finished, req2.resent_finished); + ASSERT_EQUALS_INT("sent_first_data_round", req.sent_first_data_round, req2.sent_first_data_round); + ASSERT_EQUALS_INT("transmission_mode", req.transmission_mode, req2.transmission_mode); + ASSERT_EQUALS_INT("file_size", req.file_size, req2.file_size); + ASSERT_EQUALS_INT("local_entity.EOF_recv_indication", req.local_entity.EOF_recv_indication, req2.local_entity.EOF_recv_indication); + ASSERT_EQUALS_INT("local_entity.EOF_sent_indication", req.local_entity.EOF_sent_indication, req2.local_entity.EOF_sent_indication); + ASSERT_EQUALS_INT("local_entity.file_segment_recv_indication", req.local_entity.file_segment_recv_indication, req2.local_entity.file_segment_recv_indication); + ASSERT_EQUALS_INT("local_entity.Metadata_recv_indication", req.local_entity.Metadata_recv_indication, req2.local_entity.Metadata_recv_indication); + ASSERT_EQUALS_INT("local_entity.Metadata_sent_indication", req.local_entity.Metadata_sent_indication, req2.local_entity.Metadata_sent_indication); + ASSERT_EQUALS_INT("local_entity.resumed_indication", req.local_entity.resumed_indication, req2.local_entity.resumed_indication); + ASSERT_EQUALS_INT("local_entity.suspended_indication", req.local_entity.suspended_indication, req2.local_entity.suspended_indication); + ASSERT_EQUALS_INT("local_entity.transaction_finished_indication", req.local_entity.transaction_finished_indication, req2.local_entity.transaction_finished_indication); + + + Offset *o1 = req2.file->missing_offsets->pop(req2.file->missing_offsets); + ASSERT_EQUALS_INT("offset start", 2000, o1->start); + ASSERT_EQUALS_INT("offset end", 3000, o1->end); + ssp_free(o1); + + Offset *o2 = req2.file->missing_offsets->pop(req2.file->missing_offsets); + ASSERT_EQUALS_INT("offset start", 0, o2->start); + ASSERT_EQUALS_INT("offset end", 1000, o2->end); + ssp_free(o2); + + + ssp_free_file(req2.file); + ssp_free_file(req.file); + + return error; + +} + + + + +int file_system_tests() { + int error = 0; + + error = test_write_request_json(); + + return error; +} + diff --git a/Program/test/list_tests.c b/Program/test/list_tests.c new file mode 100755 index 0000000..3d4e2d0 --- /dev/null +++ b/Program/test/list_tests.c @@ -0,0 +1,95 @@ + + + +#include "list.h" + +#include "protocol_handler.h" +#include "requests.h" + +#include "stdio.h" +#include "test.h" +#include "stdlib.h" +#include "string.h" + +#include "unit_tests.h" + + +List *populate_request_list() { + List *list = linked_list(); + Request *r; + for (int i = 0; i < 5; i++) { + r = mock_empty_request(); + r->dest_cfdp_id = i; + list->push(list, r, i); + } + return list; +} + + +void print_list(Node *node, void *element, void *args) { + Request *r = (Request *) element; + printf("source file name: %s\n", r->source_file_name); +} + +void print_list_ids(Node *node, void *element, void *args) { + Request *r = (Request *) element; + ASSERT_EQUALS_INT("should equal ids", node->id, r->dest_cfdp_id); + +} + +void remove_node_test(Node *node, void *element, void *args) { + Request *r = (Request *) element; + List *l = (List*) args; + + if (r->dest_cfdp_id == 2) { + void *element = l->removeNode(l, node); + ssp_cleanup_req(element); + } +} + +void test_remove_node() { + + List *l = populate_request_list(); + l->iterate(l, remove_node_test, l); + l->iterate(l, print_list_ids, l); + l->free(l, ssp_cleanup_req); +} + + + +int list_tests() { + + DECLARE_NEW_TEST("list.c tests"); + List *list = linked_list(); + + Request *r = mock_empty_request(); + Request *r2 = mock_empty_request(); + + memcpy(r->source_file_name, "mybestfriend", 12); + memcpy(r2->source_file_name, "secondrequest", 12); + + list->push(list, r, 1); + ASSERT_EQUALS_INT("should equal 1", 1, list->count); + + Request *r3 = (Request *) list->pop(list); + ASSERT_EQUALS_STR("list string should equal", "mybestfriend", r3->source_file_name, 12); + ASSERT_EQUALS_INT("should equal 0", 0, list->count); + + list->push(list, r2, 1); + list->push(list, r, 2); + ASSERT_EQUALS_INT("should equal 2", 2, list->count); + + //test removals + Request *r4 = list->remove(list, 1, NULL, NULL); + ASSERT_EQUALS_INT("request id should equal 1", 1, list->count); + ASSERT_EQUALS_STR("request source file_name should equal", "secondrequest", r4->source_file_name, 12); + + list->push(list, r4, 0); + list->iterate(list, print_list, NULL); + list->free(list, ssp_cleanup_req); + + + test_remove_node(); + + return 0; +} \ No newline at end of file diff --git a/Program/test/main.c b/Program/test/main.c new file mode 100755 index 0000000..68a3a84 --- /dev/null +++ b/Program/test/main.c @@ -0,0 +1,17 @@ + +#include "unit_tests.h" + +int main () { + + int error = 0; + + //error = file_system_tests(); + //error = request_tests(); + //error = packet_tests(); + //error = protocol_handler_test(); + //error = tasks_tests(); + error = server_tests(); + //error = list_tests(); + + return error; +} \ No newline at end of file diff --git a/Program/test/makefile b/Program/test/makefile new file mode 100755 index 0000000..0323780 --- /dev/null +++ b/Program/test/makefile @@ -0,0 +1,99 @@ + +PROJDIR = $(CURDIR)/.. +INCLUDE += -I$(PROJDIR)/include +INCLUDE += -I$(PROJDIR)/test + +#---------------------------FOR CSP INTEGRATION--------------------------------- +#path to source includes +INCLUDE += -I$(PROJDIR)/libcsp/include/csp +INCLUDE += -I$(PROJDIR)/libcsp/include +INCLUDE += -I$(PROJDIR)/libcsp/build/include +#------------------------------------------------------------------------------- + + +CC = gcc + +#---------------------------File Names--------------------------- +CFILES += $(PROJDIR)/src/utils.c +CFILES += $(PROJDIR)/src/filesystem_funcs.c +CFILES += $(PROJDIR)/src/port.c +CFILES += $(PROJDIR)/src/posix_server_provider.c +CFILES += $(PROJDIR)/src/csp_server_provider.c +CFILES += $(PROJDIR)/src/protocol_handler.c +CFILES += $(PROJDIR)/src/mib.c +CFILES += $(PROJDIR)/src/requests.c +CFILES += $(PROJDIR)/src/app_control.c +CFILES += $(PROJDIR)/src/file_delivery_app.c +CFILES += $(PROJDIR)/src/list.c +CFILES += $(PROJDIR)/src/packet.c + + +CFILES += $(PROJDIR)/test/packet_tests.c +CFILES += $(PROJDIR)/test/test.c +CFILES += $(PROJDIR)/test/mock.c +CFILES += $(PROJDIR)/test/request_tests.c +CFILES += $(PROJDIR)/test/tasks_tests.c +CFILES += $(PROJDIR)/test/list_tests.c + +CFILES += $(PROJDIR)/test/filesystem_tests.c +CFILES += $(PROJDIR)/test/protocol_handler_tests.c +CFILES += $(PROJDIR)/test/list_tests.c +CFILES += $(PROJDIR)/test/server_tests.c + + +#entrypoint +MAIN = main + +#---------------------------Compiler Warnings--------------------------- +#CWARNS += -W +#CWARNS += -Wall +#CWARNS += -Werror +#CWARNS += -Wextra +#CWARNS += -Wformat +#CWARNS += -Wmissing-braces +#CWARNS += -Wno-cast-align +#CWARNS += -Wparentheses +#CWARNS += -Wshadow +#CWARNS += -Wno-sign-compare +#CWARNS += -Wswitch +CWARNS += -Wuninitialized +#CWARNS += -Wunknown-pragmas +#CWARNS += -Wunused-function +#CWARNS += -Wunused-label +#CWARNS += -Wunused-parameter +#CWARNS += -Wunused-value +#CWARNS += -Wunused-variable +#CWARNS += -Wmissing-prototypes + +#---------------------------Libs--------------------------- +LINKFLAGS = +LIBS = -pthread +STATIC_FILES += $(PROJDIR)/libcsp/build/libcsp.a + +#---------------------------Compiler flags--------------------------- + +#32 bit machines, leave this out if your machine is 64 bit +#CFLAGS += -m32 +CFLAGS += -g -UUSE_STDIO -D__GCC_POSIX__=1 +CFLAGS += -pthread +CFLAGS += -DDEBUG=1 + +# MAX_NUMBER_OF_TASKS = max pthreads used in the POSIX port. +# Default value is 64 (_POSIX_THREAD_THREADS_MAX), the minimum number required by POSIX. +CFLAGS += -DMAX_NUMBER_OF_TASKS=300 +CFLAGS += $(INCLUDE) $(CWARNS) + +#---------------------------Build--------------------------- +OBJS_FILES = $(patsubst %.c, %.o, $(CFILES)) + +all: $(MAIN) + +.PHONY: clean + +$(MAIN): $(OBJS_FILES) $(STATIC_FILES) + +clean: + rm -f *.o $(MAIN) + + + diff --git a/Program/test/mib/peer_0.json b/Program/test/mib/peer_0.json new file mode 100755 index 0000000..48f258f --- /dev/null +++ b/Program/test/mib/peer_0.json @@ -0,0 +1,21 @@ +{ + "cfdp_id": 0, + "UT_address" : 0, + "UT_port" : 20, + "type_of_network" : 3, + "default_transmission_mode" : 1, + "MTU" : 250, + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_1.json b/Program/test/mib/peer_1.json new file mode 100755 index 0000000..9ae6348 --- /dev/null +++ b/Program/test/mib/peer_1.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 1, + "UT_address" : 2130706433, + "UT_port" : 1111, + "type_of_network" : 1, + "default_transmission_mode" : 1, + "MTU" : 1500, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_10.json b/Program/test/mib/peer_10.json new file mode 100755 index 0000000..db30b59 --- /dev/null +++ b/Program/test/mib/peer_10.json @@ -0,0 +1,21 @@ +{ + "cfdp_id": 10, + "UT_address" : 10, + "UT_port" : 1, + "type_of_network" : 3, + "default_transmission_mode" : 1, + "MTU" : 250, + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_2.json b/Program/test/mib/peer_2.json new file mode 100755 index 0000000..7ef92a4 --- /dev/null +++ b/Program/test/mib/peer_2.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 2, + "UT_address" : 2130706433, + "UT_port" : 1112, + "type_of_network" : 1, + "default_transmission_mode" : 1, + "MTU" : 1500, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_3.json b/Program/test/mib/peer_3.json new file mode 100755 index 0000000..daed8d0 --- /dev/null +++ b/Program/test/mib/peer_3.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 3, + "UT_address" : 2130706433, + "UT_port" : 1113, + "type_of_network" : 0, + "default_transmission_mode" : 1, + "MTU" : 1500, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_4.json b/Program/test/mib/peer_4.json new file mode 100755 index 0000000..8b31a83 --- /dev/null +++ b/Program/test/mib/peer_4.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 4, + "UT_address" : 2130706433, + "UT_port" : 1114, + "type_of_network" : 0, + "default_transmission_mode" : 1, + "MTU" : 1500, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_5.json b/Program/test/mib/peer_5.json new file mode 100755 index 0000000..41b5969 --- /dev/null +++ b/Program/test/mib/peer_5.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 5, + "UT_address" : 1, + "UT_port" : 1, + "type_of_network" : 2, + "default_transmission_mode" : 1, + "MTU" : 250, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_6.json b/Program/test/mib/peer_6.json new file mode 100755 index 0000000..2d8f164 --- /dev/null +++ b/Program/test/mib/peer_6.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 6, + "UT_address" : 1, + "UT_port" : 2, + "type_of_network" : 2, + "default_transmission_mode" : 1, + "MTU" : 250, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_7.json b/Program/test/mib/peer_7.json new file mode 100755 index 0000000..c71bac5 --- /dev/null +++ b/Program/test/mib/peer_7.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 7, + "UT_address" : 1, + "UT_port" : 5, + "type_of_network" : 3, + "default_transmission_mode" : 1, + "MTU" : 250, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mib/peer_8.json b/Program/test/mib/peer_8.json new file mode 100755 index 0000000..7ef170c --- /dev/null +++ b/Program/test/mib/peer_8.json @@ -0,0 +1,23 @@ +{ + "cfdp_id": 8, + "UT_address" : 1, + "UT_port" : 4, + "type_of_network" : 3, + "default_transmission_mode" : 1, + "MTU" : 250, + + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/mock.c b/Program/test/mock.c new file mode 100755 index 0000000..9346a50 --- /dev/null +++ b/Program/test/mock.c @@ -0,0 +1,122 @@ + +#include "types.h" +#include "filesystem_funcs.h" +#include "port.h" +#include "protocol_handler.h" +#include "mib.h" +#include "file_delivery_app.h" +#include "packet.h" +#include "unit_tests.h" +#include "requests.h" + +#include +#include +#include + + +void mock_mock_remote_entity(Remote_entity *remote_entity, uint32_t cfdp_id) { + + remote_entity->UT_address = 1; + remote_entity->UT_port = 1; + remote_entity->type_of_network = 1; + remote_entity->one_way_light_time = 1; + remote_entity->total_round_trip_allowance = 1; + remote_entity->async_NAK_interval = 1; + remote_entity->async_keep_alive_interval = 1; + remote_entity->async_report_interval= 1; + remote_entity->immediate_nak_mode_enabled= 1; + remote_entity->prompt_transmission_interval= 1; + remote_entity->default_transmission_mode= 1; + remote_entity->disposition_of_incomplete= 1; + remote_entity->CRC_required= 1; + remote_entity->mtu = 1500; + remote_entity->keep_alive_discrepancy_limit= 1; + remote_entity->positive_ack_timer_expiration_limit= 1; + remote_entity->nak_timer_expiration_limit= 1; + remote_entity->transaction_inactivity_limit= 1; + remote_entity->cfdp_id = cfdp_id; + +} + +int mock_packet(char *packet, uint32_t dest_id, uint32_t src_id) { + + Pdu_header pdu_header; + Remote_entity remote_entity; + + mock_mock_remote_entity(&remote_entity, dest_id); + + int error = get_header_from_mib(&pdu_header, remote_entity, src_id); + + pdu_header.transaction_seq_num_len = 1; + pdu_header.transaction_sequence_number = 1; + + + int packet_index = build_pdu_header(packet, 1, 0, 0, &pdu_header); + return packet_index; +} + +File *mock_eof_packet(char *packet, uint32_t dest_id, uint32_t src_id, char *file_name) { + + File *file = create_file(file_name, false); + file->partial_checksum = check_sum_file(file, 1000); + build_eof_packet(packet, 10, file->total_size, file->partial_checksum); + + return file; +} + +Client *mock_client() { + Client *c = init_client(2, 1); + return c; + +} + +Response *mock_response() { + Response *res = calloc(1, sizeof(Response)); + int addr = 16; + res->addr = &addr; + res->sfd = 1; + res->packet_len = 1500; + res->size_of_addr = 16; + res->type_of_network = posix_connectionless; + res->transmission_mode = UN_ACKNOWLEDGED_MODE; + res->packet_len = 255; + res->type_of_network = test; + return res; +} + +Request *mock_empty_request() { + void *buff = calloc(1500, sizeof(char)); + return init_request(buff, 1500); +} + +Request *mock_request() { + + + Request *req = mock_empty_request(); + + char *dest = "test_files/dest"; + char *src = "test_files/src"; + uint32_t id = 1; + + req->dest_cfdp_id = id; + req->file = create_file("test_files/dest_received.jpg", true); + + ssp_memcpy (req->source_file_name, dest, strnlen(dest, 255)); + ssp_memcpy (req->destination_file_name, src, strnlen(src, 255)); + + mock_mock_remote_entity(&req->remote_entity, id); + get_header_from_mib(&req->pdu_header, req->remote_entity, 1); + + int addr = 16; + req->res.addr = malloc(5); + ssp_memcpy(req->res.addr, &addr, 4); + + req->res.sfd = 1; + req->res.packet_len = 250; + req->res.size_of_addr = 16; + req->res.type_of_network = posix_connectionless; + req->res.transmission_mode = UN_ACKNOWLEDGED_MODE; + req->res.msg = req->buff; + + return req; +} \ No newline at end of file diff --git a/Program/test/packet_tests.c b/Program/test/packet_tests.c new file mode 100755 index 0000000..cad8f39 --- /dev/null +++ b/Program/test/packet_tests.c @@ -0,0 +1,792 @@ + +#define TESTING 1 + +#include "test.h" +#include "utils.h" +#include +#include "filesystem_funcs.h" + +#include "port.h" +#include "protocol_handler.h" +#include "mib.h" +#include "file_delivery_app.h" +#include "packet.h" +#include "unit_tests.h" +#include "requests.h" +#include "stdlib.h" + +#define PACKET_TEST_SIZE 2000 + +static int test_build_eof_packet(char *packet, int packet_start) { + + DECLARE_NEW_TEST("testing eof_packet"); + + File *file = create_file("dest.jpg", false); + + //need to set partialcheckus to checksum, because it gets set from reading in data + file->partial_checksum = check_sum_file(file, 1000); + memset(&packet[packet_start], 0, 10); + + file->partial_checksum = 1231251; + file->total_size = 141254; + + build_eof_packet(packet, packet_start, file->total_size, file->partial_checksum); + + int packet_index = packet_start; + + uint8_t directive = packet[packet_index]; + ASSERT_EQUALS_INT("EOF_PDU directive correct", directive, EOF_PDU); + + packet_index++; + + Pdu_eof eof; + + get_eof_from_packet(&packet[packet_index], &eof); + + ASSERT_EQUALS_INT("condition_code should equal COND_NO_ERROR", eof.condition_code, COND_NO_ERROR); + ASSERT_EQUALS_INT("filesize should equal", eof.file_size, file->total_size); + ASSERT_EQUALS_INT("checksum should equal", eof.checksum, file->partial_checksum); + + ssp_free_file(file); + + //testing this + return 0; +} + + +static int test_respond_metadata_request() { + + return 0; +} + +static void test_build_data_packet(char *packet, uint32_t packet_index){ + + DECLARE_NEW_TEST("testing data packet"); + + File *file = create_file("test_files/testfile", 0); + + create_data_burst_packets(packet, packet_index, file, 1500); + + uint32_t offset = get_data_offset_from_packet(&packet[packet_index]); + + ASSERT_EQUALS_INT("test proper datapacket offset set", offset, 0); + ASSERT_EQUALS_STR("test proper datapacket creation", &packet[packet_index + 4], "testfileyo", 10); + + ssp_free_file(file); +} + + +static void nak_print(Node *node, void *element, void *args){ + Offset *offset = (Offset *)element; + ssp_printf("start: %u end: %u\n", offset->start, offset->end); +} + +static int test_build_nak_packet(char* packet, uint32_t start) { + + DECLARE_NEW_TEST("testing build nak packet"); + Request *req = mock_empty_request(); + + req->file_size = 100000; + ssp_memcpy(req->destination_file_name, "testestest", 15); + ssp_memcpy(req->source_file_name, "someotherfile", 15); + + process_file_request_metadata(req); + + uint64_t count = req->file->missing_offsets->count; + uint32_t data_len = build_nak_packet(packet, start, req); + + ASSERT_EQUALS_INT("NAK directive code set", packet[start], NAK_PDU); + //25 = start_scope + end_scope + 1 offset + 1byte NAK_PDU code + ASSERT_EQUALS_INT("length of packet", 25, data_len); + uint32_t packet_index = start + 1; + Pdu_nak *nak = (Pdu_nak *) &packet[packet_index]; + + uint32_t start_scope = ntohl(nak->start_scope); + uint32_t end_scope = ntohl(nak->end_scope); + + ASSERT_EQUALS_INT("start offset == 0 ", start_scope, 0); + ASSERT_EQUALS_INT("end scope == 100000 ", end_scope, 100000); + + uint64_t number_of_segments = ssp_ntohll(nak->segment_requests); + ASSERT_EQUALS_INT("number of segments == 1 ", number_of_segments, 1); + + Offset offset[count]; + ssp_memcpy(offset, &nak->segments, sizeof(Offset) * count); + start_scope = ntohl(offset->start); + ssp_printf("test start_scope %d\n", start_scope); + end_scope = ntohl(offset->end); + ssp_printf("test end_scope %d\n", end_scope); + + ASSERT_EQUALS_INT("start offset == 0 ", start_scope, 0); + ASSERT_EQUALS_INT("end scope == 100000 ", end_scope, 100000); + + receive_offset(req->file, 1250, 5000); + receive_offset(req->file, 6000, 9000); + receive_offset(req->file, 10000, 15000); + + data_len = build_nak_packet(packet, start, req); + set_data_length(packet, data_len); + + number_of_segments = ssp_ntohll(nak->segment_requests); + + ASSERT_EQUALS_INT("number of segments == 4 ", number_of_segments, 4); + + start_scope = ntohl(nak->start_scope); + end_scope = ntohl(nak->end_scope); + ASSERT_EQUALS_INT("correct packet start", start_scope, 0); + ASSERT_EQUALS_INT("correct packet end", end_scope, 100000); + packet_index += 16; + + //outgoing_packet_index + ssp_memcpy(&start_scope, &packet[packet_index], 4); + start_scope = ntohl(start_scope); + packet_index += 4; + ssp_memcpy(&end_scope, &packet[packet_index], 4); + end_scope = ntohl(end_scope); + packet_index += 4; + ASSERT_EQUALS_INT("correct packet offset 0 start", start_scope, 0); + ASSERT_EQUALS_INT("correct packet offset 0 end", end_scope, 1250); + + ssp_memcpy(&start_scope, &packet[packet_index], 4); + start_scope = ntohl(start_scope); + packet_index += 4; + ssp_memcpy(&end_scope, &packet[packet_index], 4); + end_scope = ntohl(end_scope); + packet_index += 4; + ASSERT_EQUALS_INT("correct packet offset 1 start", start_scope, 5000); + ASSERT_EQUALS_INT("correct packet offset 1 end", end_scope, 6000); + + ssp_memcpy(&start_scope, &packet[packet_index], 4); + start_scope = ntohl(start_scope); + packet_index += 4; + ssp_memcpy(&end_scope, &packet[packet_index], 4); + end_scope = ntohl(end_scope); + packet_index += 4; + ASSERT_EQUALS_INT("correct packet offset 2 start", start_scope, 9000); + ASSERT_EQUALS_INT("correct packet offset 2 end", end_scope, 10000); + + ssp_memcpy(&start_scope, &packet[packet_index], 4); + start_scope = ntohl(start_scope); + packet_index += 4; + ssp_memcpy(&end_scope, &packet[packet_index], 4); + end_scope = ntohl(end_scope); + packet_index += 4; + ASSERT_EQUALS_INT("correct packet offset 3 start", start_scope, 15000); + ASSERT_EQUALS_INT("correct packet offset 3 end", end_scope, 100000); + + ASSERT_EQUALS_INT("correct packet data_len", data_len, get_data_length(packet)); + + ssp_cleanup_req(req); + return 0; +} + + + +static int test_receive_end_offset(File *file){ + add_first_offset(file, file->total_size); + ASSERT_EQUALS_INT("file filesize", 150033, file->total_size); + ASSERT_EQUALS_INT("offset length should be 1", 1, file->missing_offsets->count); + + receive_offset(file, 100000, 150033); + + ASSERT_EQUALS_INT("offset length should be 1 after insert end", 1, file->missing_offsets->count); + Offset *of =(Offset *) file->missing_offsets->pop(file->missing_offsets); + ASSERT_EQUALS_INT("offset start should be 0 after insert end", 0, of->start); + ASSERT_EQUALS_INT("offset end should be 100000 after insert end", 100000, of->end); + ssp_free(of); +} + + +static int test_receive_start_offset(File *file){ + add_first_offset(file, file->total_size); + ASSERT_EQUALS_INT("file filesize", 150033, file->total_size); + ASSERT_EQUALS_INT("offset length should be 1", 1, file->missing_offsets->count); + + receive_offset(file, 0, 10000); + + ASSERT_EQUALS_INT("offset length should be 1 after insert end", 1, file->missing_offsets->count); + Offset *of =(Offset *) file->missing_offsets->pop(file->missing_offsets); + ASSERT_EQUALS_INT("offset start should be 10000 after insert start", 10000, of->start); + ASSERT_EQUALS_INT("offset end should be 150033 after insert start", file->total_size, of->end); + ssp_free(of); +} + + +static int test_receive_middle_offset(File *file){ + add_first_offset(file, file->total_size); + ASSERT_EQUALS_INT("file filesize", 150033, file->total_size); + ASSERT_EQUALS_INT("offset length should be 1", 1, file->missing_offsets->count); + + receive_offset(file, 5000, 10000); + + ASSERT_EQUALS_INT("offset length should be 1 after insert end", 2, file->missing_offsets->count); + Offset *of =(Offset *) file->missing_offsets->pop(file->missing_offsets); + + ASSERT_EQUALS_INT("offset start should be 10000 after insert mid", 10000, of->start); + ASSERT_EQUALS_INT("offset end should be 150033 after insert mid", file->total_size, of->end); + + ASSERT_EQUALS_INT("offset length should be 1 after insert end", 1, file->missing_offsets->count); + ssp_free(of); + of =(Offset *) file->missing_offsets->pop(file->missing_offsets); + + ASSERT_EQUALS_INT("offset start should be 0 after insert mid", 0, of->start); + ASSERT_EQUALS_INT("offset end should be 5000 after insert mid", 5000, of->end); + ssp_free(of); +} + + +static int test_receive_parts_offset(File *file){ + add_first_offset(file, file->total_size); + ASSERT_EQUALS_INT("file filesize", 150033, file->total_size); + ASSERT_EQUALS_INT("offset length should be 1", 1, file->missing_offsets->count); + + receive_offset(file, 500, 1000); + receive_offset(file, 1000, 10000); + receive_offset(file, 0, 500); + receive_offset(file, 10000, 10500); + receive_offset(file, 10500, 150033); + + + ASSERT_EQUALS_INT("offset length should be 0 after insert end", 0, file->missing_offsets->count); +} + +static int test_receive_final_offset(File *file){ + add_first_offset(file, file->total_size); + ASSERT_EQUALS_INT("file filesize", 150033, file->total_size); + ASSERT_EQUALS_INT("offset length should be 1", 1, file->missing_offsets->count); + + receive_offset(file, 0, 150033); + ASSERT_EQUALS_INT("offset length should be 0 after insert end", 0, file->missing_offsets->count); + + add_first_offset(file, file->total_size); + receive_offset(file, 0, 100000); + receive_offset(file, 100000, 150032); + receive_offset(file, 150032, 150033); + + ASSERT_EQUALS_INT("offset length should be 0 after insert end", 0, file->missing_offsets->count); +} + +static int test_nak_print(File *file){ + + add_first_offset(file, file->total_size); + ASSERT_EQUALS_INT("file filesize", 150033, file->total_size); + ASSERT_EQUALS_INT("offset length should be 1", 1, file->missing_offsets->count); + + + receive_offset(file, 148900, 150033); + + file->missing_offsets->iterate(file->missing_offsets, nak_print, NULL); +} + +static int test_receive_offset(){ + + File *file = create_file("test_files/test_receive.jpg", 0); + test_receive_end_offset(file); + test_receive_start_offset(file); + test_receive_middle_offset(file); + test_receive_parts_offset(file); + test_receive_final_offset(file); + test_nak_print(file); + + + +} + +static int test_build_very_large_nak_packet(char* packet, uint32_t start) { + + DECLARE_NEW_TEST("testing build very large nak packet"); + + File *file = create_file("test_files/vid.mp4", 0); + Request *req = mock_empty_request(); + + req->file = file; + req->file_size = file->total_size; + process_file_request_metadata(req); + + //fail receiving weird offsets + for (int i = 0; i < 10000; i+=10) { + receive_offset(file, i, i+10); + i++; + } + uint64_t count = file->missing_offsets->count; + + uint32_t data_len = build_nak_packet(packet, start, req); + ssp_printf("data length%d\n", data_len); + ssp_printf("start%d\n", start); + + uint32_t offsets_sent = (data_len - 17) / sizeof(Offset); + + ssp_printf("offsets that fit in packet %d\n", offsets_sent); + + ASSERT_EQUALS_INT("data length of NAK fits in the packet", 1489, data_len); + + ssp_printf("data_len = %d", data_len); + + ssp_cleanup_req(req); + return 0; +} +int test_build_ack_finished_pdu(char *packet, uint32_t start) { + + DECLARE_NEW_TEST("testing finish ack packet"); + printf("testing finished ack creation\n"); + Request *req; + + Pdu_directive *pdu_d = (Pdu_directive *)&packet[start]; + + ASSERT_EQUALS_INT("ACK_PDU directive correct", pdu_d->directive_code, ACK_PDU); + + Pdu_ack *ack = (Pdu_ack *)&packet[start + 1]; + ASSERT_EQUALS_INT("EOF_PDU directive correct", EOF_PDU, ack->directive_code); + ASSERT_EQUALS_INT("COND_NO_ERROR correct", COND_NO_ERROR, ack->condition_code); + ASSERT_EQUALS_INT("ACK_FINISHED_END correct", ack->directive_subtype_code, ACK_FINISHED_END); + + return 0; +} + + +int test_build_ack_eof_pdu(char *packet, uint32_t start) { + //empty request + + DECLARE_NEW_TEST("testing eof ack packet"); + + Request *req; + memset(&packet[start], 0, 10); + + uint8_t len = build_ack(packet, start, EOF_PDU); + + uint8_t directive = packet[start]; + ASSERT_EQUALS_INT("ACK_PDU directive correct", directive, ACK_PDU); + uint32_t packet_index = start + 1; + + Pdu_ack ack; + get_ack_from_packet(&packet[packet_index], &ack); + + ASSERT_EQUALS_INT("EOF_PDU directive correct", EOF_PDU, ack.directive_code); + ASSERT_EQUALS_INT("COND_NO_ERROR correct", COND_NO_ERROR, ack.condition_code); + ASSERT_EQUALS_INT("ACK_FINISHED_END correct", ack.directive_subtype_code, ACK_FINISHED_END); + + return 0; +} + +int test_build_pdu_header(char *packet, Pdu_header *header) { + + + DECLARE_NEW_TEST("testing header creation"); + uint32_t packet_index = 0; + //ssp_print_bits(header, 4); + Pdu_header new_header; + header->transaction_sequence_number = 325; + header->transaction_seq_num_len = 2; + + memset(&new_header, 0, 4); + + int length = build_pdu_header(packet, header->transaction_sequence_number, header->transmission_mode, header->PDU_data_field_len, header); + if (length < 0) { + ssp_printf("failed to build pdu header\n"); + } + + ssp_print_bits(&packet[4], 10); + get_pdu_header_from_packet(packet, &new_header); + + + ASSERT_EQUALS_INT("CRC = CRC", header->CRC_flag, new_header.CRC_flag); + ASSERT_EQUALS_INT("direction = direction", header->direction, new_header.direction); + ASSERT_EQUALS_INT("length_of_entity_IDs = length_of_entity_IDs ", header->length_of_entity_IDs, new_header.length_of_entity_IDs); + ASSERT_EQUALS_INT("PDU data_field_len = PDU_data_field_len ", header->PDU_data_field_len, new_header.PDU_data_field_len); + ASSERT_EQUALS_INT("PDU_type = PDU_type", header->PDU_type, new_header.PDU_type); + ASSERT_EQUALS_INT("reserved_bit_0 = reserved_bit_0 ", header->reserved_bit_0, new_header.reserved_bit_0); + ASSERT_EQUALS_INT("reserved_bit_1 = reserved_bit_1 ", header->reserved_bit_1, new_header.reserved_bit_1); + ASSERT_EQUALS_INT("reserved_bit_2 = reserved_bit_2 ", header->reserved_bit_2, new_header.reserved_bit_2); + ASSERT_EQUALS_INT("version = version", header->version, new_header.version); + ASSERT_EQUALS_INT("transaction_seq_num_len = transaction_seq_num_len ", header->transaction_seq_num_len, new_header.transaction_seq_num_len); + ASSERT_EQUALS_INT("transmission_mode = transmission_mode ", header->transmission_mode, new_header.transmission_mode); + + ASSERT_EQUALS_INT("packet length: ", length, (header->transaction_seq_num_len + (header->length_of_entity_IDs * 2) + 4)); + ASSERT_EQUALS_INT("packet source id ", header->source_id, new_header.source_id); + + ssp_printf("%d\n", new_header.transaction_sequence_number); + ASSERT_EQUALS_INT("transaction_sequence_number correctly placed ", header->transaction_sequence_number, new_header.transaction_sequence_number); + ASSERT_EQUALS_INT("packet destination id ", header->destination_id, new_header.destination_id); + + return length; +} + + +int test_build_metadata_packet(char *packet, uint32_t start) { + + memset(&packet[start], 0, 20); + DECLARE_NEW_TEST("testing metadata packets"); + + Request *req = mock_request(); + Request *recv_request = mock_empty_request(); + + req->file_size = 35; + + ssp_printf("%s\n", req->source_file_name); + + uint8_t len = build_put_packet_metadata(packet, start, req); + parse_metadata_packet(packet, start + SIZE_OF_DIRECTIVE_CODE, recv_request); + + ssp_printf("%s\n", recv_request->source_file_name); + + + ASSERT_EQUALS_INT("test packet filesize", req->file_size, recv_request->file_size); + ASSERT_EQUALS_STR("test src_file_name", req->source_file_name, recv_request->source_file_name, strnlen(req->source_file_name, MAX_PATH)); + ASSERT_EQUALS_STR("test dest_file_name", req->destination_file_name, recv_request->destination_file_name, strnlen(req->source_file_name, MAX_PATH)); + char *str = "HELLO WORLDSSSSSSSSSSSSSS"; + + ssp_memcpy(req->destination_file_name, str, strnlen(str, MAX_PATH) ); + ssp_memcpy(req->source_file_name, str, strnlen(str, MAX_PATH) ); + + len = build_put_packet_metadata(packet, start, req); + parse_metadata_packet(packet, start + SIZE_OF_DIRECTIVE_CODE, recv_request); + + ASSERT_EQUALS_INT("test packet filesize", req->file_size, recv_request->file_size); + ASSERT_EQUALS_STR("test src_file_name", req->source_file_name, recv_request->source_file_name, strnlen(req->source_file_name, MAX_PATH)); + ASSERT_EQUALS_STR("test dest_file_name", req->destination_file_name, recv_request->destination_file_name, strnlen(req->source_file_name, MAX_PATH)); + + uint16_t data_len = get_data_length(packet); + ASSERT_EQUALS_INT("test metadata set data length", data_len, len-start); + + + ssp_cleanup_req(req); + ssp_cleanup_req(recv_request); + + return 0; +} + + +int test_add_cont_part_to_packet(char *packet, uint32_t start){ + + DECLARE_NEW_TEST("testing add_message_cont_part_to_packet"); + + uint32_t packet_index = start; + + uint64_t dest_id_original = 5; + uint64_t original_id_original = 33; + uint64_t transaction_id_original = 66; + + Request *req = mock_empty_request(); + int error = add_cont_partial_message_to_request(dest_id_original,original_id_original,transaction_id_original,req); + + memset(&packet[start], 0, 100); + packet_index = add_messages_to_packet(packet, packet_index, req->messages_to_user); + + ASSERT_EQUALS_STR("'cfdp' should be at the start of the message", &packet[start], "cfdp", 5); + ASSERT_EQUALS_INT("testing CONTINUE_PARTIAL code", (uint8_t) packet[start + 5], CONTINUE_PARTIAL); + + uint64_t dest_id, original_id, transaction_id; + + packet_index = start + 6; + error = copy_id_lv_from_packet(&packet[packet_index], &dest_id); + if (error < 0) { + ssp_printf("failed to copy contents from buffer \n"); + return -1; + } + + ASSERT_EQUALS_INT("dest_id", dest_id, dest_id_original); + packet_index += 1 + 1; + + error = copy_id_lv_from_packet(&packet[packet_index], &original_id); + if (error < 0) { + ssp_printf("failed to copy contents from buffer \n"); + return -1; + } + + ASSERT_EQUALS_INT("original_id", original_id, original_id_original); + packet_index += 1 + 1; + + error = copy_id_lv_from_packet(&packet[packet_index], &transaction_id); + if (error < 0) { + ssp_printf("failed to copy contents from buffer \n"); + return -1; + } + + ASSERT_EQUALS_INT("transaction_id", transaction_id, transaction_id_original); + ssp_cleanup_req(req); + + + + + +} + + +int test_add_messages_to_packet(char *packet, uint32_t start) { + + char *dest = "dest"; + char *src = "src"; + uint32_t id = 2; + uint8_t len = 1; + + uint32_t packet_index = start; + DECLARE_NEW_TEST("testing add_messages_to_packet"); + + Request *req = mock_empty_request(); + int error = add_proxy_message_to_request(id, len, src, dest, req); + + memset(&packet[start], 0, 100); + packet_index = add_messages_to_packet(packet, packet_index, req->messages_to_user); + + ASSERT_EQUALS_STR("'cfdp' should be at the start of the message", &packet[start], "cfdp", 5); + ASSERT_EQUALS_INT("testing PROXY_PUT_REQUEST code", (uint8_t) packet[start + 5], PROXY_PUT_REQUEST); + + LV dest_file, src_file, dest_id; + + packet_index = start + 6; + copy_lv_from_buffer(&dest_id, packet, packet_index); + ASSERT_EQUALS_INT("dest_file.length", dest_id.length, len); + ASSERT_EQUALS_INT("dest_file.value", *(uint8_t*) (dest_id.value), id); + packet_index += dest_id.length + 1; + free_lv(dest_id); + + + copy_lv_from_buffer(&src_file, packet, packet_index); + ASSERT_EQUALS_INT("src_file.length", src_file.length, strnlen(src, 100) + 1); + ASSERT_EQUALS_STR("src_file.value", src, (char *) src_file.value, src_file.length); + packet_index += src_file.length + 1; + free_lv(src_file); + + + copy_lv_from_buffer(&dest_file, packet, packet_index); + ASSERT_EQUALS_INT("dest_file.length", dest_file.length, strnlen(dest, 100) + 1); + ASSERT_EQUALS_STR("dest_file.value", dest, (char *)dest_file.value, dest_file.length); + free_lv(dest_file); + + ssp_cleanup_req(req); + return 0; +} + +int test_get_message_from_packet(char *packet, uint32_t start) { + + char *dest = "dest"; + char *src = "src"; + uint32_t id = 2; + uint8_t len = 1; + + DECLARE_NEW_TEST("testing add_messages_from_packet"); + + uint32_t packet_index = start; + + Request *req = mock_empty_request(); + int error = add_proxy_message_to_request(id, len, src, dest, req); + + uint32_t length_of_message = add_messages_to_packet(packet, start, req->messages_to_user); + + Request *req2 = mock_empty_request(); + uint32_t next_message = get_message_from_packet(packet, start, req2); + + Message *m = req2->messages_to_user->pop(req2->messages_to_user); + Message_put_proxy *p_message = m->value; + + ASSERT_EQUALS_STR("dest_file.value", p_message->destination_file_name.value, dest, strnlen(dest, 100)); + ASSERT_EQUALS_STR("src_file.value", src, p_message->source_file_name.value, strnlen(src, 100)); + ASSERT_EQUALS_INT("dest_id.value", p_message->destination_id, id); + + ASSERT_EQUALS_INT("next message should be at index ", next_message, length_of_message); + + ssp_free_message(m); + ssp_cleanup_req(req); + ssp_cleanup_req(req2); + return 0; + +} + +int test_get_cont_partial_from_packet(char *packet, uint32_t start){ + + DECLARE_NEW_TEST("testing add_message_cont_part_to_packet"); + + uint32_t packet_index = start; + + Request *req = mock_empty_request(); + int error = add_cont_partial_message_to_request(1,1,1,req); + + memset(&packet[start], 0, 100); + add_messages_to_packet(packet, packet_index, req->messages_to_user); + + Request *req2 = mock_empty_request(); + get_message_from_packet(packet, packet_index, req2); + + Message *m = req2->messages_to_user->pop(req2->messages_to_user); + Message_cont_part_request *p_message = (Message_cont_part_request *) m->value; + + + ASSERT_EQUALS_INT("destination_id.value", p_message->destination_id, 1); + + ASSERT_EQUALS_INT("originator_id.value", p_message->originator_id, 1); + + ASSERT_EQUALS_INT("transaction_id.value", p_message->transaction_id, 1); + + + +} + +//test multiple messages +int test_get_messages_from_packet(char *packet, uint32_t start) { + + + DECLARE_NEW_TEST("testing get_messages_from_packet"); + + char *dest = "dest"; + char *src = "src"; + uint32_t id = 2; + uint8_t len = 1; + + uint32_t packet_index = start; + + Request *req = mock_empty_request(); + + + int error = add_proxy_message_to_request(id, len, src, dest, req); + + uint32_t length_of_message = add_messages_to_packet(packet, start, req->messages_to_user); + length_of_message = add_messages_to_packet(packet, length_of_message, req->messages_to_user); + length_of_message = add_messages_to_packet(packet, length_of_message, req->messages_to_user); + + Request *req2 = mock_empty_request(); + get_messages_from_packet(packet, start, 1000 - start, req2); + + int message_count = req2->messages_to_user->count; + + for (int i = 0; i < message_count; i++) { + + Message *message = req2->messages_to_user->pop(req2->messages_to_user); + + if (message->header.message_type == PROXY_PUT_REQUEST) { + + Message_put_proxy *p_message = (Message_put_proxy *) message->value; + ASSERT_EQUALS_INT("received proxy messages: dest.id", p_message->destination_id, id); + ASSERT_EQUALS_STR("received proxy messages: src file", src, (char *) p_message->source_file_name.value, p_message->source_file_name.length); + ASSERT_EQUALS_STR("received proxy messages: dest file", dest, (char *) p_message->destination_file_name.value, p_message->destination_file_name.length); + + } + + ssp_free_message(message); + } + + ssp_cleanup_req(req); + ssp_cleanup_req(req2); + +} + +int test_build_finished_pdu(char *packet, uint32_t start) { + + + DECLARE_NEW_TEST("testing creation and parsing of finished pdu"); + memset(&packet[start], 0, 100); + + uint32_t packet_index = start; + + uint32_t data_len = build_finished_pdu(packet, start); + + packet_index += SIZE_OF_DIRECTIVE_CODE; + + Pdu_finished fin; + get_finished_pdu(&packet[packet_index], &fin); + + ASSERT_EQUALS_INT("condition_code set", fin.condition_code, COND_NO_ERROR); + ASSERT_EQUALS_INT("delivery_code not set", fin.delivery_code, 0); + ASSERT_EQUALS_INT("end_system_status not set", fin.end_system_status, 0); + ASSERT_EQUALS_INT("file_status FILE_RETAINED_SUCCESSFULLY set", fin.file_status, FILE_RETAINED_SUCCESSFULLY); +} + + +int test_copying_lvs(){ + + DECLARE_NEW_TEST("testing copying and reading LVs "); + uint64_t id = 184; + char packet[250]; + + int error = copy_id_lv_to_packet(packet, id); + + uint64_t id_received = 0; + uint64_t len = copy_id_lv_from_packet(packet, &id_received); + if (len < 0) { + ssp_printf("failed to copy contents from buffer \n"); + return -1; + } + ASSERT_EQUALS_INT("ids should equal 232", (uint32_t)id_received, 184); + ASSERT_EQUALS_INT("len should be 1", len, 1); + + + id = 6699; + error = copy_id_lv_to_packet(packet, id); + + id_received = 0; + len = copy_id_lv_from_packet(packet, &id_received); + if (len < 0) { + ssp_printf("failed to copy contents from buffer \n"); + return -1; + } + ASSERT_EQUALS_INT("ids should equal 6699", (uint32_t)id_received, 6699); + ASSERT_EQUALS_INT("len should be 2", len, 2); + + id = 15406584; + error = copy_id_lv_to_packet(packet, id); + + id_received = 0; + len = copy_id_lv_from_packet(packet, &id_received); + if (len < 0) { + ssp_printf("failed to copy contents from buffer \n"); + return -1; + } + ASSERT_EQUALS_INT("ids should equal 15406584", (uint32_t)id_received, 15406584); + ASSERT_EQUALS_INT("len should be 4", len, 4); + id = 32500405; + error = copy_id_lv_to_packet(packet, id); + + id_received = 0; + len = copy_id_lv_from_packet(packet, &id_received); + if (len < 0) { + ssp_printf("failed to copy contents from buffer \n"); + return -1; + } + ASSERT_EQUALS_INT("ids should equal 32500405", (uint32_t)id_received, 32500405); + ASSERT_EQUALS_INT("len should be 8", len, 8); + +} +int packet_tests() { + + //setting host name for testing + char *host_name = "127.0.0.1"; + uint32_t addr[sizeof(uint32_t)]; + inet_pton(AF_INET, host_name, addr); + + char packet[PACKET_TEST_SIZE]; + + Pdu_header pdu_header; + Remote_entity remote_entity; + + int error = get_remote_entity_from_json (&remote_entity, 1); + get_header_from_mib(&pdu_header, remote_entity, 2); + + int data_start_index = test_build_pdu_header(packet, &pdu_header); + /* + error = test_copying_lvs(); + + test_build_metadata_packet(packet, data_start_index); + test_get_messages_from_packet(packet, data_start_index); + test_add_cont_part_to_packet(packet, data_start_index); + test_get_cont_partial_from_packet(packet, data_start_index); + test_build_ack_eof_pdu(packet, data_start_index); +*/ + test_build_eof_packet(packet, data_start_index); + + test_build_data_packet(packet, data_start_index); + + //test_build_nak_packet(packet, data_start_index); + test_receive_offset(); + /* + test_build_finished_pdu(packet, data_start_index); + test_add_messages_to_packet(packet, data_start_index); + test_get_message_from_packet(packet, data_start_index); + */ + //next up + + //Skip for now, will fix after connection server works + //test_build_very_large_nak_packet(packet, data_start_index); + return 0; + +} + + diff --git a/Program/test/protocol_handler_tests.c b/Program/test/protocol_handler_tests.c new file mode 100755 index 0000000..866b88e --- /dev/null +++ b/Program/test/protocol_handler_tests.c @@ -0,0 +1,274 @@ + + +#include "protocol_handler.h" +#include "stdlib.h" +#include "mib.h" +#include "test.h" +#include "file_delivery_app.h" +#include "unit_tests.h" +#include "port.h" +#include "requests.h" +#include "filesystem_funcs.h" + + +static int test_process_pdu_eof() { +//char *packet, Request *req, Response res + + DECLARE_NEW_TEST("testing process eof pdu"); + + char packet[2000]; + + //builds the packet at offset 10 + File *file = mock_eof_packet(packet, 1, 2, "test_files/dest.jpg"); + uint32_t first_checksum = file->partial_checksum; + uint32_t first_file_size = file->total_size; + + Request *req = mock_empty_request(); + req->file = create_file("test_files/eof_test", true); + + process_pdu_eof(&packet[11], req, req->res); + + ASSERT_EQUALS_INT("received eof, increment EOF_rec_indication", req->local_entity.EOF_recv_indication, true); + ASSERT_EQUALS_INT("received eof, checksum should equal", req->file->eof_checksum, first_checksum); + ASSERT_EQUALS_INT("received eof, filesize should equal", req->file->total_size, first_file_size); + + ssp_cleanup_req(req); + + //test empty files + file = mock_eof_packet(packet, 1, 2, "test_files/empty.txt"); + req = mock_empty_request(); + req->file = file; + + process_pdu_eof(&packet[11], req, req->res); + + ASSERT_EQUALS_INT("received eof, increment EOF_rec_indication", req->local_entity.EOF_recv_indication, true); + ASSERT_EQUALS_INT("received eof, checksum should equal", req->file->eof_checksum, file->partial_checksum); + ASSERT_EQUALS_INT("received eof, filesize should equal", req->file->total_size, file->total_size); + + ssp_cleanup_req(req); + return 0; +} + +//main state machine +int test_on_server_time_out() { + + //Response res, Request *req + Request *req = mock_request(); + req->paused = false; + + //no meta data received, sending request for new one (can't send NAKs yet, because we don't know filesize) + on_server_time_out(req->res, req); + req->local_entity.Metadata_recv_indication = true; + on_server_time_out(req->res, req); + + req->local_entity.EOF_recv_indication = true; + on_server_time_out(req->res, req); + + req->local_entity.transaction_finished_indication = true; + on_server_time_out(req->res, req); + + on_server_time_out(req->res, req); + + ssp_cleanup_req(req); + return 0; + +} + + +static int test_wrong_id(FTP *app) { + + + DECLARE_NEW_TEST("testing wrong id"); + + int error = 0; + Response *res = mock_response(); + char packet[2000]; + + int packet_index = mock_packet(packet, 2, 1); + + Request **req_container = &app->current_request; + Pdu_header incoming_pdu_header; + + error = process_pdu_header(packet, true, &incoming_pdu_header, *res, req_container, app->request_list, app); + error = ASSERT_EQUALS_INT("process pdu should error", -1, error); + Request *req = (*req_container); + + error = ASSERT_NULL("Test wrong id, Request should be NULL", req); + error = ASSERT_EQUALS_INT("Length of request list should be 0", app->request_list->count, 0); + + free(res); + return error; +} + +static int test_correct_id(FTP *app) { + + DECLARE_NEW_TEST("testing correct id"); + + int error = 0; + Response *res = mock_response(); + char packet[2000]; + int packet_index = mock_packet(packet, 1, 2); + + set_data_length(packet, 100); + Request **req_container = &app->current_request; + Pdu_header incoming_pdu_header; + + process_pdu_header(packet, true, &incoming_pdu_header, *res, req_container, app->request_list, app); + Request *req = (*req_container); + + error = ASSERT_NOT_NULL("Test request, Request should not be NULL", req); + error = ASSERT_EQUALS_INT("Length of request should be 1", app->request_list->count, 1); + error = ASSERT_EQUALS_INT("request transaction number should equal", req->transaction_sequence_number, 1); + error = ASSERT_EQUALS_INT("source id should equal 2", req->dest_cfdp_id, 2); + + free(res); + return error; +} + +static int test_process_pdu_header() { + char buff[1500]; + int error = 0; + FTP *app = ssp_alloc(1, sizeof(FTP)); + + app->request_list = linked_list(); + app->packet_len = 1500; + app->buff = buff; + app->my_cfdp_id = 1; + + error = test_wrong_id(app); + error = test_correct_id(app); + + app->request_list->free(app->request_list, ssp_cleanup_req); + ssp_free(app); + + return error; +} +int test_process_data_packet() { + + + DECLARE_NEW_TEST("testing process data packet"); + + char packet[1500]; + uint32_t start = 20; + uint32_t packet_len = 120; + memset(packet, 0, 1500); + + File *file = create_file("test_files/peer_0.json", 0); + File *file2 = create_file("test_files/received_peer.json", 1); + + //mimics process_file_request_metadata function + add_first_offset(file2, file->total_size); + + + int error = 0; + uint32_t data_len = 0; + + int i = 0; + for (i = 0; i < 10; i++) { + create_data_burst_packets(packet, start, file, packet_len); + data_len = get_data_length(packet); + process_data_packet(&packet[start], data_len, file2); + ASSERT_EQUALS_INT("Checksum parital calculations", file2->partial_checksum, file->partial_checksum); + } + + uint32_t checksum = check_sum_file(file, packet_len-start-4); + + ASSERT_EQUALS_INT("Checksum", file2->partial_checksum, checksum); + ASSERT_EQUALS_INT("Length of offset list should equal 1 ", file2->missing_offsets->count, 0); + + ssp_free_file(file); + ssp_free_file(file2); + +} + +int test_process_nak() { + + + DECLARE_NEW_TEST("testing nak client response"); + char packet[2000]; + uint32_t start = 20; + uint32_t data_len = sizeof(packet) - start; + memset(packet, 0, 1500); + + Request *req = mock_request(); + Response *res = mock_response(); + Client *client = mock_client(); + + ssp_free_file(req->file); + req->file = create_file("test_files/nak_test.jpg", 0); + add_first_offset(req->file, req->file->total_size); + + build_nak_packet(packet, start, req); + + File *file = create_file(req->destination_file_name, 1); + add_first_offset(file, req->file->total_size); + + process_nak_pdu(&packet[start + SIZE_OF_DIRECTIVE_CODE], req, *res, client); + + int error = 0; + error = ASSERT_EQUALS_INT("Nak list count should be of size 1", file->missing_offsets->count, 1); + + Offset *offset = (Offset*) file->missing_offsets->pop(file->missing_offsets); + + error = ASSERT_EQUALS_INT("offset start should be 0", offset->start, 0); + error = ASSERT_EQUALS_INT("offset end should be file size", offset->end, 150033); + + ssp_free(offset); + ssp_free_file(file); + ssp_cleanup_req(req); + ssp_cleanup_client(client); + ssp_free(res); + + return error; + +} + +int test_user_request_handler_send_nak_data(){ + + DECLARE_NEW_TEST("user request (client side) initiation starts"); + + Client client; + mock_client(&client); + char buff[2000]; + + Response *res = mock_response(); + Request *req = mock_request(); + + ssp_printf("file size %d\n", req->file_size); + + int error = add_first_offset(req->file, 100000); + if (error < 0) { + ssp_free_file(req->file); + return NULL; + } + + receive_offset(req->file, 3000, 5000); + receive_offset(req->file, 10000, 40000); + + ssp_printf("offset count %d\n", req->file->missing_offsets->count); + + req->paused = false; + req->procedure = sending_nak_data; + res->type_of_network = test; + res->msg = req->buff; + + user_request_handler(*res, req, &client); + + ssp_cleanup_req(req); + return 0; +} + + + +int protocol_handler_test() { + int error = 0; + + //error = test_process_pdu_header(); + //error = test_process_pdu_eof(); + //error = test_on_server_time_out(); + //error = test_process_data_packet(); + //error = test_process_nak(); + error = test_user_request_handler_send_nak_data(); + + return error; +} \ No newline at end of file diff --git a/Program/test/request_tests.c b/Program/test/request_tests.c new file mode 100755 index 0000000..0e37f4c --- /dev/null +++ b/Program/test/request_tests.c @@ -0,0 +1,348 @@ +#include "protocol_handler.h" +#include "port.h" +#include "requests.h" +#include "string.h" +#include "test.h" +#include "filesystem_funcs.h" +#include "string.h" +#include "file_delivery_app.h" +#include "unit_tests.h" + +static void list_print_id(Node *node, void *element, void *args) { + Request *req = (Request *) element; + printf("id: %d trans number: %llu\n", req->dest_cfdp_id, req->transaction_sequence_number); +} + +//for finding the struct in the list +struct request_search_params { + uint32_t source_id; + uint32_t transaction_sequence_number; +}; + +//for finding the struct in the list +static int find_request(void *element, void *args) { + Request *req = (Request *) element; + struct request_search_params *params = (struct request_search_params *) args; + if (req->dest_cfdp_id = params->source_id && req->transaction_sequence_number == params->transaction_sequence_number) + return 1; + return 0; +} + +static void list_print(Node *node, void *element, void *args) { + + Request *req = (Request *) element; + ssp_printf("%s\n", req->source_file_name); +} + +static int request_finding_test() { + + List *list = linked_list(); + + Request *req = mock_empty_request(); + req->dest_cfdp_id = 1; + req->transaction_sequence_number = 1; + list->push(list, req, req->dest_cfdp_id); + + Request *req2 = mock_empty_request(); + req2->dest_cfdp_id = 3; + req2->transaction_sequence_number = 1; + list->push(list, req2, req2->dest_cfdp_id); + + Request *req3 = mock_empty_request(); + req3->dest_cfdp_id = 2; + req3->transaction_sequence_number = 2; + list->push(list, req3, req3->dest_cfdp_id); + + + struct request_search_params params = { + req->dest_cfdp_id, + req->transaction_sequence_number, + }; + + Request *found = list->find(list, -1, find_request, ¶ms); + params.source_id = 3; + params.transaction_sequence_number = 1; + Request *found2 = list->find(list, -1, find_request, ¶ms); + params.source_id = 2; + params.transaction_sequence_number = 2; + Request *found3 = list->find(list, -1, find_request, ¶ms); + + if (found == NULL) { + ssp_printf("CAN't FIND request IS NULL\n"); + list->free(list, ssp_cleanup_req); + return 1; + } + + ASSERT_EQUALS_INT("finding test, should equal", req->dest_cfdp_id, found->dest_cfdp_id); + ASSERT_EQUALS_INT("finding test, should equal", req2->dest_cfdp_id, found2->dest_cfdp_id); + ASSERT_EQUALS_INT("finding test, should equal", req3->dest_cfdp_id, found3->dest_cfdp_id); + + list->free(list, ssp_cleanup_req); + return 0; +} + + +static void request_test_list_storage() { + Request *req = mock_empty_request(); + List *list = linked_list(); + + req->file = create_file("testfile.txt", 0); + ssp_memcpy(req->source_file_name, "stuff", 6); + list->insert(list, req, 1); + + Request *req2 = mock_empty_request(); + req2->file = create_file("testfile.txt", 0); + ssp_memcpy(req2->source_file_name, "stuff2", 7); + list->insert(list, req2, 2); + + Request *req3 = mock_empty_request(); + req3->file = create_file("testfile.txt", 0); + ssp_memcpy(req3->source_file_name, "stuff3", 7); + list->insert(list, req3, 3); + + ssp_cleanup_req(list->pop(list)); + list->iterate(list, list_print, NULL); + list->free(list, ssp_cleanup_req); +} + +static int add_proxy_message() { + + Request *req = mock_empty_request(); + + char *dest = "dest"; + char *src = "src"; + uint32_t id = 2; + uint8_t len = 1; + + int error = add_proxy_message_to_request(id, len, src, dest, req); + + Message *message = req->messages_to_user->pop(req->messages_to_user); + ASSERT_EQUALS_STR("message header should have asci: cfdp", message->header.message_id_cfdp, "cfdp", 5); + + Message_put_proxy *proxy = (Message_put_proxy *) message->value; + ASSERT_EQUALS_INT("proxy dest_id should equal 2", proxy->destination_id, id); + ASSERT_EQUALS_STR("proxy src file", proxy->source_file_name.value, src, proxy->source_file_name.length); + ASSERT_EQUALS_STR("proxy dest file", proxy->destination_file_name.value, dest, proxy->destination_file_name.length); + + ssp_free_message(message); + ssp_cleanup_req(req); + return 0; +} + + +int init_cont_partial_request_test_fail() { + + + DECLARE_NEW_TEST("Test fail to create init partial"); + + Message_cont_part_request p_cont; + uint32_t dest_id = 0; + uint32_t org_id = 1; + uint64_t tran_id = 0; + Request *req = mock_request(); + + p_cont.destination_id = dest_id; + p_cont.originator_id = org_id; + p_cont.transaction_id = tran_id; + uint32_t buff_len = 1500; + char buff[buff_len]; + + int error = init_cont_partial_request(&p_cont, buff, buff_len); + ASSERT_EQUALS_INT("failed to write, errored", error, -1); + ssp_cleanup_req(req); +} + +int init_cont_partial_request_test() { + + + DECLARE_NEW_TEST("Test create cont partial"); + + Message_cont_part_request p_cont; + uint32_t dest_id = 3; + uint32_t org_id = 2; + uint64_t tran_id = 1; + + Request *req = mock_request(); + req->transaction_sequence_number = 1; + req->dest_cfdp_id = 2; + req->my_cfdp_id = 3; + req->local_entity.EOF_recv_indication = 1; + req->local_entity.Metadata_recv_indication = 1; + + save_req_to_file(req); + + p_cont.destination_id = dest_id; + p_cont.originator_id = org_id; + p_cont.transaction_id = tran_id; + uint32_t buff_len = 1500; + char buff[buff_len]; + + int error = init_cont_partial_request(&p_cont, buff, buff_len); + + Request *req2 = mock_empty_request(); + get_req_from_file(dest_id, tran_id, org_id, req2); + + ASSERT_EQUALS_INT("eof received, don't resend. local_entity.eof_recv = 1", + req->local_entity.EOF_recv_indication, + req2->local_entity.EOF_sent_indication); + + ASSERT_EQUALS_INT("metadata received, don't resend. local_entity.Metadata_sent = 1", + req->local_entity.Metadata_recv_indication, + req2->local_entity.Metadata_sent_indication); + + ssp_cleanup_req(req); + ssp_cleanup_req(req2); +} + +static int add_continue_partial_message_test() { + + + DECLARE_NEW_TEST("Test add cont partial to request"); + int error = 0; + Request *req = mock_empty_request(); + + uint32_t src_id = 1; + uint32_t dest_id = 1; + uint32_t transaction_id = 4444; + + uint8_t len = 1; + + error = add_cont_partial_message_to_request( + dest_id, + src_id, + transaction_id, + req); + + Message *message = req->messages_to_user->pop(req->messages_to_user); + ASSERT_EQUALS_STR("message header should have asci: cfdp", message->header.message_id_cfdp, "cfdp", 5); + + Message_cont_part_request *proxy = (Message_cont_part_request *) message->value; + ASSERT_EQUALS_INT("proxy dest_id should equal 1", proxy->destination_id, dest_id); + ASSERT_EQUALS_INT("proxy originator id should equal 1", proxy->originator_id, src_id); + ASSERT_EQUALS_INT("proxy transaction id should equal 4444", proxy->transaction_id, transaction_id); + + ssp_free_message(message); + ssp_cleanup_req(req); + + return error; +} + + +int test_lv_functions() { + + + DECLARE_NEW_TEST("Testing LV functions"); + + char packet[100]; + + char *str = "suphomie"; + LV lv; + create_lv(&lv, strnlen(str, 100), str); + + uint32_t len = strnlen(str, 100); + + ASSERT_EQUALS_INT("create_lv length works", lv.length, len); + ASSERT_EQUALS_STR("create_lv value works", str, lv.value, len); + + uint16_t packet_index = copy_lv_to_buffer(packet, lv); + ASSERT_EQUALS_INT("copy lv, length", packet[0], lv.length); + ASSERT_EQUALS_STR("copy lv, value", &packet[1], lv.value, lv.length); + + free_lv(lv); + + copy_lv_from_buffer(&lv, packet, 0); + ASSERT_EQUALS_INT("copy lv length from packet", lv.length, len); + ASSERT_EQUALS_STR("copy lv value from packet", str, lv.value, len); + free_lv(lv); + +} + + +int request_user_input_tests() { + + + DECLARE_NEW_TEST("Add requests to app"); + + FTP app; + void *handler = create_ftp_task(1, &app); + if (handler == NULL) { + ssp_printf("couldn't create thread\n"); + return -1; + } + //need to let task initialize first. + sleep(1); + ASSERT_NOT_NULL("app handler should be not null\n", app.server_handle); + + Request *req = put_request(2, "", "", 0, &app); + ASSERT_NULL("request should be null\n", req); + + req = put_request(2, NULL, NULL, 0, &app); + ASSERT_NOT_NULL("request should not be null\n", req); + +} +int scheduled_requests_test() { + + DECLARE_NEW_TEST("Save scheduled requests"); + FTP app; + void *handler = create_ftp_task(1, &app); + + int error = schedule_put_request(1, "test_files/dest.jpg", "test_files/scheduled_file_sent", ACKNOWLEDGED_MODE, &app); + ASSERT_EQUALS_INT("couldn't schedule request when should have been able to ", error, 0); + + error = schedule_put_request(1, "test_files/dest.jp", "test_files/scheduled_file_fail", ACKNOWLEDGED_MODE, &app); + ASSERT_EQUALS_INT("scheduled request failed", error, -1); + + //error = schedule_put_request(1, NULL, NULL, ACKNOWLEDGED_MODE, &app); + //ASSERT_EQUALS_INT("scheduling just messages", error, 0); + + return error; +} + +int schedule_requests_start_test(){ + + DECLARE_NEW_TEST("Start scheduled requests"); + + FTP app; + void *handler = create_ftp_task(1, &app); + + int error = 0; + sleep(1); + + error = schedule_put_request(5, "test_files/dest.jpg", "test_files/success_sent_scheduled", ACKNOWLEDGED_MODE, &app); + ASSERT_EQUALS_INT("scheduling requests", error, 0); + + error = schedule_put_request(5, "test_files/nak_test.jpg", "test_files/success_sent_scheduled", ACKNOWLEDGED_MODE, &app); + ASSERT_EQUALS_INT("scheduling requests", error, 0); + + error = start_scheduled_requests(4, &app); + ASSERT_EQUALS_INT("start request batch successfully", error, 0); + + ssp_printf("count %d\n", app.active_clients->count); + ASSERT_EQUALS_INT("app has filled request list", app.active_clients->count, 3); + + + return error; +} + +int test_process_messages() { + + //process_messages(Request *req, FTP *app) + return 0; +} + +int request_tests() { + + int error = 0; + + error = request_finding_test(); + error = request_user_input_tests(); + error = add_proxy_message(); + error = test_lv_functions(); + error = add_continue_partial_message_test(); + error = init_cont_partial_request_test_fail(); + error = init_cont_partial_request_test(); + error = scheduled_requests_test(); + error = schedule_requests_start_test(); + + return error; +} \ No newline at end of file diff --git a/Program/test/server_tests.c b/Program/test/server_tests.c new file mode 100755 index 0000000..8955bad --- /dev/null +++ b/Program/test/server_tests.c @@ -0,0 +1,511 @@ + +#include "unit_tests.h" +#include +#include "posix_server_provider.h" +#include "port.h" +#include "app_control.h" + +#include "csp_server_provider.h" + +//-------------------------------- +#include +#include +#include +#include +#include +#include +#include +#include "csp.h" + +#define BUF_SIZE 500 +static int clin(char *host, char*port) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int sfd, s, j; + size_t len; + ssize_t nread; + char buf[BUF_SIZE]; + + + /* Obtain address(es) matching host/port */ + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + + s = getaddrinfo(host, port, &hints, &result); + if (s != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + exit(EXIT_FAILURE); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully connect(2). + If socket(2) (or connect(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (sfd == -1) + continue; + + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* Success */ + + close(sfd); + } + + if (rp == NULL) { /* No address succeeded */ + fprintf(stderr, "Could not connect\n"); + exit(EXIT_FAILURE); + } + + freeaddrinfo(result); /* No longer needed */ + + /* Send remaining command-line arguments as separate + datagrams, and read responses from server */ + + if (write(sfd, "stuff", 5) != 5) { + fprintf(stderr, "partial/failed write\n"); + exit(EXIT_FAILURE); + } + + nread = read(sfd, buf, BUF_SIZE); + if (nread == -1) { + perror("read"); + exit(EXIT_FAILURE); + } + + return 0; +} + +static int serv (char *port) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int sfd, s; + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len; + ssize_t nread; + char buf[BUF_SIZE]; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + s = getaddrinfo(NULL, port, &hints, &result); + if (s != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + exit(EXIT_FAILURE); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (sfd == -1) + continue; + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + close(sfd); + } + + if (rp == NULL) { /* No address succeeded */ + fprintf(stderr, "Could not bind\n"); + exit(EXIT_FAILURE); + } + + freeaddrinfo(result); /* No longer needed */ + + /* Read datagrams and echo them back to sender */ + + for (;;) { + peer_addr_len = sizeof(struct sockaddr_storage); + nread = recvfrom(sfd, buf, BUF_SIZE, 0, + (struct sockaddr *) &peer_addr, &peer_addr_len); + if (nread == -1) + continue; /* Ignore failed request */ + + char host[NI_MAXHOST], service[NI_MAXSERV]; + + s = getnameinfo((struct sockaddr *) &peer_addr, + peer_addr_len, host, NI_MAXHOST, + service, NI_MAXSERV, NI_NUMERICSERV); + if (s == 0) + printf("Received %zd bytes from %s:%s\n", + nread, host, service); + else + fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s)); + + if (sendto(sfd, buf, nread, 0, + (struct sockaddr *) &peer_addr, + peer_addr_len) != nread) + fprintf(stderr, "Error sending response\n"); + } +} + + + +static int servCon (char *port) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int sfd, s; + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len; + ssize_t nread; + char buf[BUF_SIZE]; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + s = getaddrinfo(NULL, port, &hints, &result); + if (s != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + exit(EXIT_FAILURE); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (sfd == -1) + continue; + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + close(sfd); + } + + if (rp == NULL) { /* No address succeeded */ + fprintf(stderr, "Could not bind\n"); + exit(EXIT_FAILURE); + } + + freeaddrinfo(result); /* No longer needed */ + + int err = listen(sfd, 10); + if (err < 0) { + printf("error\n listen"); + return; + } + + /* Read datagrams and echo them back to sender */ + + for (;;) { + peer_addr_len = sizeof(struct sockaddr_storage); + + printf("accepting\n"); + int cfd = accept(sfd, &peer_addr, peer_addr_len); + + printf("accepted\n"); + + //nread = recvfrom(cfd, buf, BUF_SIZE, 0, + // (struct sockaddr *) &peer_addr, &peer_addr_len); + + nread = recv(cfd, buf, BUF_SIZE, 0); + if (nread == -1) { + printf("failed to read\n"); + continue; + } + + + + char host[NI_MAXHOST], service[NI_MAXSERV]; + + s = getnameinfo((struct sockaddr *) &peer_addr, + peer_addr_len, host, NI_MAXHOST, + service, NI_MAXSERV, NI_NUMERICSERV); + if (s == 0) + printf("Received %zd bytes from %s:%s\n", + nread, host, service); + else + fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s)); + + if (sendto(sfd, buf, nread, 0, + (struct sockaddr *) &peer_addr, + peer_addr_len) != nread) + fprintf(stderr, "Error sending response\n"); + } +} + + + + +//-------------------------------------------------------------------- + +static int onRecvServer(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *other){ + + printf("received: %s\n", packet); + + + Response res; + res.addr = addr; + res.msg = "hello back!!\n"; + res.packet_len = 10; + res.size_of_addr = size_of_addr; + res.sfd = sfd; + res.type_of_network = test; + res.transmission_mode = ACKNOWLEDGED_MODE; + + ssp_sendto(res); + + + return 0; +} + +static int onTimeOut(void *other) { + //printf("timeout\n"); + return 0; +} +static int onStdIn(void *other){ + return 0; +} +static int checkExit(void *other) { + return 0; +} +static void onExit(void *other) { +} + +//client stuff +static int onSend(int sfd, void *addr, size_t size_of_addr, void *onSendParams) { + Response res; + res.addr = addr; + res.msg = "hello server!!\n"; + res.packet_len = 12; + res.sfd = sfd; + res.type_of_network = test; + res.transmission_mode = ACKNOWLEDGED_MODE; + printf("sending!!!\n"); + + ssp_sendto(res); + + return 0; +} +static int onRecvClient(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *onRecvParams) { + printf("client received %s\n", packet); + return 0; +} + +static int checkExitClient(void *params) { + return 0; +} + +static void onExitClient(void *params) { + +} + +void *ssp_csp_connectionless_server_task_test(void *params) { + /* + printf("starting csp connectionless server\n"); + + csp_connectionless_server( + 1, + onRecvServer, + onTimeOut, + onStdIn, + checkExit, + onExit, + params); + */ + return NULL; +} + + +void *ssp_csp_connectionless_client_task_test(void *params) { + + printf("starting csp connectionless client\n"); + /* + csp_connectionless_client(1, + 1, + 2, + onSend, onRecvClient, checkExitClient, onExitClient, params); + return NULL; + */ +} + + +void *ssp_csp_connection_server_task_test(void *params) { + /* + csp_connection_server(1, + onRecvServer, + onTimeOut, + onStdIn, + checkExit, + onExit, + params); + */ + +} + + +void *ssp_csp_connection_client_task_test(void *params) { + + ssp_printf("starting csp connection client\n"); + Client *client = (Client *) params; + + csp_connection_client(1, + 1, + CSP_ANY, + 200, + 1000, + client->lock, + onSend, + onRecvClient, + checkExitClient, + onExitClient, + params); +} +/* +void *ssp_csp_connectionless_client_task_test(void *params) { + csp_connection_client(uint8_t dest_id, uint8_t dest_port, uint8_t src_port, + int (*onSend)(int sfd, void *addr, uint32_t size_of_addr, void *onSendParams), + int (*onRecv)(int sfd, char *packet, uint32_t packet_len, uint32_t *buff_size, void *addr, size_t size_of_addr, void *onRecvParams) , + int (*checkExit)(void *checkExitParams), + void (*onExit)(void *params), + void *params); +} +*/ + + +int test_lock_create(){ + void *lock = ssp_lock_create(); + ASSERT_NOT_NULL("lock initialized", lock); + + if (lock == NULL) + return 1; + + int success = ssp_lock_destory(lock); + ASSERT_EQUALS_INT("lock destroy success", success, 1); + + return 0; + +} + +int test_lock_get(){ + + void *lock = ssp_lock_create(); + if (lock == NULL) + return 1; + + int success = ssp_lock_take(lock); + ASSERT_EQUALS_INT("successfully got lock", success, 1); + + success = ssp_lock_give(lock); + ASSERT_EQUALS_INT("successfully gave lock", success, 1); + + success = ssp_lock_destory(lock); + ASSERT_EQUALS_INT("lock destroy success", success, 1); + + return success; +} + + +int test_lock_give(){ + void *lock = ssp_lock_create(); + if (lock == NULL) + return 1; + + int success = ssp_lock_give(lock); + ASSERT_EQUALS_INT("successfully gave lock", success, 1); + + success = ssp_lock_destory(lock); + ASSERT_EQUALS_INT("lock destroy success", success, 1); + + return success; + +} + + + +int server_tests() { + + int buffsize = 10000; + char buff[buffsize]; + + /* Init buffer system with 10 packets of maximum 300 bytes each */ + printf("Initialising CSP\r\n"); + + csp_conf_t csp_conf; + csp_conf_get_defaults(&csp_conf); + csp_conf.buffers = 4096; + csp_conf.address = 1; + csp_conf.buffer_data_size = 250; + + int error = csp_init(&csp_conf); + if (error != CSP_ERR_NONE) { + csp_log_error("csp_init() failed, error: %d", error); + exit(1); + } + + + error = test_lock_create(); + error = test_lock_give(); + error = test_lock_get(); + + + + Client client; + memset(&client, 0, sizeof(Client)); + + void *lock = ssp_lock_create(); + client.lock = lock; + + + //void *handle = ssp_thread_create(20000, ssp_csp_connectionless_server_task_test, NULL); + //void *handle2 = ssp_thread_create(20000, ssp_csp_connectionless_client_task_test, NULL); + //void *handle = ssp_thread_create(20000, ssp_csp_connection_server_task_test, NULL); + void *handle2 = ssp_thread_create(20000, ssp_csp_connection_client_task_test, &client); + //test_csp_connectionless_server(); + + sleep(5); + ssp_lock_give(client.lock); + + //ssp_thread_join(handle); + ssp_thread_join(handle2); + + + /* + if (client) { + printf("I'm a client!\n"); + //connection_client("127.0.0.1", "1111", buffsize, NULL, NULL, NULL, NULL, onSend, onRecvClient, checkExitClient, onExitClient); + //connectionless_client("localhost", "1111", buffsize, NULL, NULL, NULL, NULL, onSend, onRecvClient, checkExitClient, onExitClient); + //csp_connectionless_client(1, 1, 2, 2, NULL, NULL, NULL, NULL, onSend, onRecvClient, checkExitClient, onExitClient); + //csp_connection_client(); + //clin("127.0.0.1", "1111"); + } + else { + printf("I'm a server!\n"); + //connectionless_server("1111", buffsize, onRecvServer, onTimeOut, onStdIn, checkExit, onExit, NULL); + + //servCon ("1111"); + //csp_connectionless_server(1, 1, onRecvServer, onTimeOut, onStdIn, checkExit, onExit, NULL); + //csp_connection_server(); + } + */ + return 0; +} diff --git a/Program/test/tasks_tests.c b/Program/test/tasks_tests.c new file mode 100755 index 0000000..742f349 --- /dev/null +++ b/Program/test/tasks_tests.c @@ -0,0 +1,34 @@ +#include "unit_tests.h" +#include "test.h" +#include "requests.h" +#include "list.h" + + +void check_request_callback(Node *node, void *request, void *args){ + Request *r = (Request *) request; + printf("id's remaining in list: %d\n", r->dest_cfdp_id); + +} + +void test_remove_request() { + + + List *l = populate_request_list(); + Request *request = (Request *) l->find(l, 3, NULL, NULL); + request->procedure = clean_up; + //run a task here, or a removal function + + l->iterate(l, check_request_callback, NULL); + l->free(l, ssp_cleanup_req); +} + + + +int tasks_tests() { + DECLARE_NEW_TEST("Tasks"); + + test_remove_request(); + + int error = 0; + return error; +} \ No newline at end of file diff --git a/Program/test/temp/test_file_save b/Program/test/temp/test_file_save new file mode 100755 index 0000000..0a8273a Binary files /dev/null and b/Program/test/temp/test_file_save differ diff --git a/Program/test/test.c b/Program/test/test.c new file mode 100755 index 0000000..3c1539c --- /dev/null +++ b/Program/test/test.c @@ -0,0 +1,133 @@ + +#include "test.h" +#include +#include + +int test_num = 0; + + + +void DECLARE_NEW_TEST(char *description) { + printf("\x1b[33m"); + printf("-----------------------------------%s-----------------------------------\n", description); + printf("\033[0m"); +} + + +int assert_equals_int(char *file_name, int line_num, char* description, int val1, int val2) { + + test_num++; + if (val1 == val2){ + printf("\033[0;32m"); + printf("%s", description); + printf(" pass # %d\n", test_num); + } + else { + printf("\033[0;31m"); + printf("%s", description); + printf(" fail # %d %s:%d\n", test_num, file_name, line_num); + printf("\033[0m"); + return 1; + } + printf("\033[0m"); + return 0; +} + + +int assert_not_equals_int(char *file_name, int line_num, char* description, int val1, int val2) { + + test_num++; + if (val1 == val2){ + printf("\033[0;31m"); + printf("%s", description); + printf(" fail # %d %s:%d\n", test_num, file_name, line_num); + printf("\033[0m"); + return 1; + } + else { + printf("\033[0;32m"); + printf("%s", description); + printf(" pass # %d\n", test_num); + } + printf("\033[0m"); + return 0; +} + + +int assert_equals_str(char *file_name, int line_num, char* description, void *val1, void* val2, size_t size) { + + test_num++; + if (!memcmp(val1, val2, size)) { + printf("\033[0;32m"); + printf("%s", description); + printf(" pass # %d\n", test_num); + + } else { + printf("\033[0;31m"); + printf("%s", description); + printf(" fail # %d %s:%d\n", test_num, file_name, line_num); + printf("\033[0m"); + return 1; + + } + printf("\033[0m"); + return 0; +} + +int assert_not_equals_str(char *file_name, int line_num, char* description, void *val1, void* val2, size_t size) { + + test_num++; + if (!memcmp(val1, val2, size)) { + + printf("\033[0;31m"); + printf("%s", description); + printf(" fail # %d %s:%d\n", test_num, file_name, line_num); + printf("\033[0m"); + return 1; + + } else { + printf("\033[0;32m"); + printf("%s", description); + printf(" pass # %d\n", test_num); + } + printf("\033[0m"); + return 0; +} + + +int assert_null(char *file_name, int line_num, char* description, void *val1) { + test_num++; + if (val1 != NULL) { + printf("\033[0;31m"); + printf("%s", description); + printf(" fail # %d %s:%d\n", test_num, file_name, line_num); + printf("\033[0m"); + return 1; + } else { + printf("\033[0;32m"); + printf("%s", description); + printf(" pass # %d\n", test_num); + } + printf("\033[0m"); + return 0; + +} + +int assert_not_null(char *file_name, int line_num, char* description, void *val1) { + + test_num++; + if (val1 == NULL) { + printf("\033[0;31m"); + printf("%s", description); + printf(" fail # %d %s:%d\n", test_num, file_name, line_num); + printf("\033[0m"); + return 1; + + } else { + printf("\033[0;32m"); + printf("%s", description); + printf(" pass # %d\n", test_num); + } + printf("\033[0m"); + return 0; +} \ No newline at end of file diff --git a/Program/test/test.h b/Program/test/test.h new file mode 100755 index 0000000..5e1dc33 --- /dev/null +++ b/Program/test/test.h @@ -0,0 +1,35 @@ + +#ifndef PACKET_TEST_H +#define PACKET_TEST_H +#include + + +int assert_equals_str(char *file_name, int line_num, char* description, void *val1, void* val2, size_t size); +int assert_not_equals_int(char *file_name, int line_num, char* description, int val1, int val2); +int assert_equals_int(char *file_name, int line_num, char* description, int val1, int val2); +int assert_not_equals_str(char *file_name, int line_num, char* description, void *val1, void* val2, size_t size); +int assert_null(char *file_name, int line_num, char* description, void *val1); +int assert_not_null(char *file_name, int line_num, char* description, void *val1); + +void DECLARE_NEW_TEST(char *description); + +#define ASSERT_EQUALS_STR(description, val1, val2, size)\ + assert_equals_str(__FILE__, __LINE__, description, val1, val2, size); + +#define ASSERT_NOT_EQUALS_INT(description, val1, val2)\ + assert_not_equals_int(__FILE__, __LINE__, description, val1, val2); + +#define ASSERT_EQUALS_INT(description, val1, val2)\ + assert_equals_int(__FILE__, __LINE__, description, val1, val2); + +#define ASSERT_NOT_EQUALS_STR(description, val1, val2, size)\ + assert_not_equals_str(__FILE__, __LINE__, description, val1, val2, size); + +#define ASSERT_NULL(description, val1)\ + assert_null(__FILE__, __LINE__, description, val1); + +#define ASSERT_NOT_NULL(description, val1)\ + assert_not_null(__FILE__, __LINE__, description, val1); + + +#endif \ No newline at end of file diff --git a/Program/test/test_files/empty.txt b/Program/test/test_files/empty.txt new file mode 100755 index 0000000..e69de29 diff --git a/Program/test/test_files/eof_test b/Program/test/test_files/eof_test new file mode 100755 index 0000000..e69de29 diff --git a/Program/test/test_files/peer_0.json b/Program/test/test_files/peer_0.json new file mode 100755 index 0000000..48f258f --- /dev/null +++ b/Program/test/test_files/peer_0.json @@ -0,0 +1,21 @@ +{ + "cfdp_id": 0, + "UT_address" : 0, + "UT_port" : 20, + "type_of_network" : 3, + "default_transmission_mode" : 1, + "MTU" : 250, + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/test_files/received_peer.json b/Program/test/test_files/received_peer.json new file mode 100755 index 0000000..48f258f --- /dev/null +++ b/Program/test/test_files/received_peer.json @@ -0,0 +1,21 @@ +{ + "cfdp_id": 0, + "UT_address" : 0, + "UT_port" : 20, + "type_of_network" : 3, + "default_transmission_mode" : 1, + "MTU" : 250, + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 +} \ No newline at end of file diff --git a/Program/test/test_files/testfile b/Program/test/test_files/testfile new file mode 100755 index 0000000..184be45 --- /dev/null +++ b/Program/test/test_files/testfile @@ -0,0 +1 @@ +testfileyo \ No newline at end of file diff --git a/Program/test/udpClient.c b/Program/test/udpClient.c new file mode 100755 index 0000000..373acfe --- /dev/null +++ b/Program/test/udpClient.c @@ -0,0 +1,81 @@ +/* + * udpclient.c - A simple UDP client + * usage: udpclient + * https://www.cs.cmu.edu/afs/cs/academic/class/15213-f99/www/class26/udpclient.c + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 320 + +/* + * error - wrapper for perror + */ +void error(char *msg) { + perror(msg); + exit(0); +} + +int main(int argc, char **argv) { + int sockfd, portno, n; + int serverlen; + struct sockaddr_in serveraddr; + struct hostent *server; + char *hostname; + char buf[BUFSIZE]; + + /* check command line arguments */ + if (argc != 3) { + fprintf(stderr,"usage: %s \n", argv[0]); + exit(0); + } + hostname = argv[1]; + portno = atoi(argv[2]); + + /* socket: create the socket */ + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + error("ERROR opening socket"); + + /* gethostbyname: get the server's DNS entry */ + server = gethostbyname(hostname); + if (server == NULL) { + fprintf(stderr,"ERROR, no such host as %s\n", hostname); + exit(0); + } + + /* build the server's Internet address */ + bzero((char *) &serveraddr, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + bcopy((char *)server->h_addr, (char *)&serveraddr.sin_addr.s_addr, server->h_length); + serveraddr.sin_port = htons(portno); + + int msg = "some stuff to send"; + /* get a message from the user */ + + ssp_memcpy(buf, msg, strnlen(msg, BUFSIZE)); + for (;;) { + //bzero(buf, BUFSIZE); + //printf("Please enter msg: "); + //fgets(buf, BUFSIZE, stdin); + + /* send the message to the server */ + serverlen = sizeof(serveraddr); + n = sendto(sockfd, buf, strlen(buf), 0, &serveraddr, serverlen); + if (n < 0) + error("ERROR in sendto"); + sleep(2); + /* print the server's reply */ + n = recvfrom(sockfd, buf, BUFSIZE, 0, &serveraddr, &serverlen); + if (n < 0) + error("ERROR in recvfrom"); + printf("Echo from server: %s\n", buf); + } + return 0; +} \ No newline at end of file diff --git a/Program/test/unit_tests.h b/Program/test/unit_tests.h new file mode 100755 index 0000000..5ecbc0e --- /dev/null +++ b/Program/test/unit_tests.h @@ -0,0 +1,29 @@ +#ifndef SSP_UNIT_TEST_H +#define SSP_UNIT_TEST_H +#include "types.h" +#include "test.h" + + + +int tasks_tests(); +int packet_tests(); +int protocol_handler_test(); +int list_tests(); +int file_system_tests(); +int request_tests(); +int protocol_handler_test(); +int server_tests(); +int utils_tests(); + +int mock_packet(char *packet, uint32_t dest_id, uint32_t src_id); +Response *mock_response(); +File *mock_eof_packet(char *packet, uint32_t dest_id, uint32_t src_id, char *file_name); +Request *mock_request(); +Request *mock_empty_request(); +List *populate_request_list(); +Client *mock_client(); + + +#define TEMP_FILESIZE 1000 + +#endif \ No newline at end of file diff --git a/Program/test/utils_tests.c b/Program/test/utils_tests.c new file mode 100755 index 0000000..f26f9bf --- /dev/null +++ b/Program/test/utils_tests.c @@ -0,0 +1,9 @@ + + +#include "utils.h" +#include "unit_tests.h" + +int utils_tests() { + log_ftp("WARN", "log this"); + return 0; +} \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index d9184c4..de34525 --- a/README.md +++ b/README.md @@ -1 +1,301 @@ -# CMPT496_SpaceNetworking \ No newline at end of file +This is not a complete version of the protocol, Just put images, and proxy put (get). + +This is a FTP protocol that is partially (mostly) adheres to the CCSDS (Consultative Committee for Space Data Systems) spec for sending files into space CCSDS 727.0-B-4 (https://public.ccsds.org/pubs/727x0b4.pdf). This project is being built for a student lead space initiative at the Univerity of Alberta called ABsat. + + The main difference this implementation offers that has deviates from the above + specifications is that this implementation offers a metadata nak (non acknowledgment). + This Nak lets the file sender user quickly resend a metadata packet if it was dropped. + Since metadata packets are crucial to file management, and round trip times + can range to minutes or hours in space, I thought it prudent to handle + this edge case. Furthermore, this implementation will build a + 'temporary file' if it has missed a metadata packet. This temporary file + will allow storage of data packets until a metadata packet is received, + allowing us to save minutes on data re-transmissions in the event a metadata + packet is missed. + +Supported operation systems: +- Posix, +- FREE-RTOS + +Supported network stacks: +- tcp +- udp +- csp (cube sate protocol) +- generic (other, including no network at all!) + + +# Compilation Notes: + +master branch is currently configured to be compiled on the Satellite Exalta-2. To +compile if for linux, change the files in /Program/include/port.h to include +POSIX_FILESYSTEM and POSIX_PORT, and comment out FREE_RTOS_PORT and RED_FS +what it should look like: + + /*------------------------------------------------------------------------------ + This file is protected under copyright. If you want to use it, + please include this text, that is my only stipulation. + Author: Evan Giese + ------------------------------------------------------------------------------*/ + #ifndef PORT_H + #define PORT_H + + //#define FREE_RTOS_PORT + //#define RED_FS + #define CSP_NETWORK + + #define POSIX_FILESYSTEM + #define POSIX_PORT + +### Building + +To build for linux, we need to first install the CSP dependecy. We can +do this automatically by running: + + git submodule init + +followed by: + + git submodule update + +Make sure you followed the comment steps in 'Compilation Notes' above, then change to the Program/src directory and run: + + make + +### Updating CSP: + +first, one needs to build the .a file for your specific cpu architecture. +instructions can be found here: https://github.com/libcsp/libcsp + +archive file: +Once one has the .a file by following the above instructions. simply link to it my adding the path to +STATIC_FILES in our makefile: STATIC_FILES += /path/to/libcsp.a + +### Compiling with FreeRTOS (using make -- not for you Exalta-2): +The best way to compile with FreeRTOS is to do the same thing as we did +with libscp -- create an .a file, and link to the .h files. + +There are examples to help you with linking in the makefile. + +Once again, make sure that FREE_RTOS_PORT is defined in port.h +and that POSIX_PORT is not defined. + +# Getting started: + + +### Running using the linux build premade (basic) client + +To get a file: + + sudo ./main -i 10 -c 1 -k "/dev/ttyUSB0" -f "GET |" + +To push a file: + + sudo ./main -i 10 -c 1 -k "/dev/ttyUSB0" -f "PUT |" + +To get the help menu: + ./main -h + + -----------HELP MESSAGE------------ + + usage: main [options] + + Options: -i + -c + -f list of file names eg, "PUT local/path|/path/on/sat GET /path/on/sat|local/path ..." + -v eg (1-3) + -k eg /dev/ttyUSB0 + -b default is 9600 + -h HelpMessage + + ---------------END---------------- + +To track the file progress, one can tail the log file: + + tail -F Program/src/log.txt + +### Running in C + +init the app or task with: + + void *handler = create_ftp_task(id, &app); + if (handler == NULL) { + return 1; + } + +this will spin up a thread in posix, or a task in FreeRTOS. +if you want to join this thread, and it is a posix thread, you can run: +ssp_thread_join(app->server_handle); + +if you want to exit this task for any reason set app->close = 1; +this will run the exiting subroutines and close the thread. Free RTOS has +issues exiting tasks. FreeRTOS will spin up a task for every new +peer one wishes to commucate with, and block/deschedule if there is no activity. + +if you wish to send a file to a peer: + +params: + + : id of destination, + : source file path, If this is not an absolute path, it will start its path from the 'src' directory. + : destination file path, + : ACKNOWLEDGED_MODE/UN_ACKNOWLEDGED_MODE (ACKNOWLEDGED_MODE will allow for acks/naks to be sent.), + : The FTP app struct pointer. + +example: + + Request *req = put_request(, , , , <&app>); + start_request(req); + + +if you wish to get a file from a peer, you can call get_request: + +params: + + : id of destination, + : source file path, + : destination file path, If this is not an absolute path, it will start its path from the 'src' directory. + : ACKNOWLEDGED_MODE/UN_ACKNOWLEDGED_MODE (ACKNOWLEDGED_MODE will allow for acks/naks to be sent.), + : The FTP app struct pointer. + +example: + + Request *req = get_request(, , , , &app); + start_request(req); + +if you wish to send a file from a peer via a proxy node, we need to add a 'message' onto a put request, +if we jsut want to send messages, we can set the filenames to NULL: + +params: + + : id of destination, + : source file path, + : destination file path, If this is not an absolute path, it will start its path from the 'src' directory. + : ACKNOWLEDGED_MODE/UN_ACKNOWLEDGED_MODE (ACKNOWLEDGED_MODE will allow for acks/naks to be sent.), + : The FTP app struct pointer. + : The constructed Request struct from the 'request' + +example: + + Request *req = put_request(, NULL, NULL, , <&app>); + + add_proxy_message_to_request(, , , ); + + start_request(req); + +### Running in Python + +One can look at the 'test.py' file to get an idea of how it works. + +params: + + #: source file path, If this is not an absolute path, it will start its path from the 'src' directory. + #: destination file path, + #: to block the python program or not. + + #ftp_python.put_request(, , ) + +example: + + from Program import ftp_python + ftp_python.put_request("pictures/log.txt", "log.txt", block=True) + +params: + + #: source file path, + #: destination file path, If this is not an absolute path, it will start its path from the 'src' directory. + #: to block the python program or not. + + #ftp_python.get_request(, , ) + +example: + + from Program import ftp_python + ftp_python.get_request("log.txt", "/home/evan/SAT/CCSDS_FileDeliveryProtocol/logreceived.txt", block=True) + +# MIB (management information base) + +This base holds the configuration information for users (peers or applications). +This data is used for determining how and who we can send file or commands +to. Files will not be sent to users that are not included in the MIB. + +The MIB is currently configured as key value pairs. These pairs are formatted +in the JSON format, the name of the file is peer_. + +Here is an example of a MIB entry: + + { + "cfdp_id": 1, + "UT_address" : 2130706433, + "UT_port" : 1111, + "type_of_network" : 1, + "default_transmission_mode" : 1, + "MTU" : 1500, + "one_way_light_time" : 123, + "total_round_trip_allowance" : 123, + "async_NAK_interval" : 123, + "async_keep_alive_interval" : 123, + "async_report_interval" : 123, + "immediate_nak_mode_enabled" : 123, + "prompt_transmission_interval" : 123, + "disposition_of_incomplete" : 123, + "CRC_required" : 0, + "keep_alive_discrepancy_limit" : 8, + "positive_ack_timer_expiration_limit" : 123, + "nak_timer_expiration_limit" : 123, + "transaction_inactivity_limit" : 123 + } + + +Below are the meanings of the fields for the MIB + +- cfdp_id + This is the unique identifier of a peer on the network. We can start it at 1 + and increment it from there. This is an unsigned integer (32 bit) value; + +- UT_address + This is an Underlying Transmission address. For example, in an IP stack, this + would be an IP4 Ip address. This value is a decimal representation of an IP + address. This particular one is 127.0.0.1. + +- UT_port + This os an Underlying Transmission port. For example, in an IP stack, this + would be a 16 bit value -- like port 8080. combined with the UT_address, + together they form a complete UT address. The one above would be equal to + 127.0.0.1:1111. This is an unsigned integer (16 bit) value + +- type_of_network + This number represents what type of network the underlying UT address takes. + currently, the only acceptable values are 0 and 1. + - 0: UDP + - 1: TCP + - 2: csp (connectionless) + - 3: csp (connection based) + +- MTU + This number represents the 'maximum transmissible unit' This value is the size of the packet the peer expects. Make sure your underlaying network layer is big enough! + +- async_NAK_interval + This number represents the time in miliseconds we wait to recv a packet. If it expires, we send NAKs + +- total_round_trip_allowance + This is the maximum time that a program will accept packets for (CSP stack only). This gets reset if the program receives a packet. In miliseconds. + +- transaction_inactivity_limit + This is the maximum time in miliseconds, that a program will keep a transaction 'open' to receive packets. If connection is lost, it + can be regained and continue the transaction while within this timeframe. The request will also be 'saved' in which we create + a metadata file with the current state of the transaction. This happens every transaction_inactivity_limit / 2. + +- default_transmission_mode: not implemented +- one_way_light_time : not implemented +- async_keep_alive_interval : not implemented +- async_report_interval : not implemented +- immediate_nak_mode_enabled : not implemented +- prompt_transmission_interval : not implemented +- disposition_of_incomplete : not implemented +- CRC_required : not implemented +- keep_alive_discrepancy_limit : not implemented +- positive_ack_timer_expiration_limit : not implemented +- nak_timer_expiration_limit : not implemented + +if you want to get in contact with me +email me at evangiese77@gmail.com diff --git a/build_instructions_code_composer_studio.txt b/build_instructions_code_composer_studio.txt new file mode 100644 index 0000000..b143a3e --- /dev/null +++ b/build_instructions_code_composer_studio.txt @@ -0,0 +1,12 @@ +right click /Program/src/main.c and 'exclude from build' +right click /Program/test directory and 'exclude from build' +right click /Program/src/posix_server_provider.c directory and 'exclude from build' + +project -> properties -> include Options -> add ${workspace_loc:/${ProjName}/libcsp/src} +project -> properties -> include Options -> add ${workspace_loc:/${ProjName}/ex2_ftp/Program/include} +project -> properties -> include Options -> add ${workspace_loc:/${ProjName}/libcsp/include} +project -> properties -> include Options -> add${workspace_loc:/${ProjName}/include/ex2_os} + +then right click the ext_ftp folder and go to the 'includes' and select restore defaults + +make sure the connection in Project -> properties -> general -> connection isset to XDS110 USB \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..7ba84e6 --- /dev/null +++ b/test.py @@ -0,0 +1,11 @@ + +from Program import ftp_python +import os + +#ftp_python.put_request("pictures/log.txt", "log.txt", block=True) + +print("RUNNING") +ftp_python.get_request("log.txt", "/home/evan/SAT/CCSDS_FileDeliveryProtocol/logreceived.txt", block=True) + +#ftp_python.put_request("pictures/pic.jpeg", "sat_path.c", block=True) +