/* * Active Router Transport Protocol (ARTP) implementation * Copyright (c) 2004, Tomas Rebok * All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the "BSD License" which is * distributed with the software in the file LICENSE. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the BSD * License for more details. */ /** @file * definitions of @c ARTP options and their manipulating functions. * @author Tomas Rebok * @date 2004 */ #include #include #include #include "options.h" #include "config.h" #include "errors.h" /** array for storing global (sessions' default) options. This options are * used for as initial options for all new sessions). */ static void *global_options[OPTIONS_COUNT]; int options_init() { int i; /* initialize options array */ for (i = 0; i < OPTIONS_COUNT; i++) global_options[i] = NULL; /* set defaultly used options */ if ((global_options[RETRANSMITS_TIMEOUT] = (void *) malloc(sizeof(RETR_TIMEOUT_TYPE))) == NULL) return E_MEMORY_FAIL; *((RETR_TIMEOUT_TYPE *) global_options[RETRANSMITS_TIMEOUT]) = 0; return 0; } int set_global_option(char *option_name, char *option_value, int use) { MAX_DGRAM_LEN_TYPE temp_dgram_len; MSS_TYPE temp_mss; char *endptr; endptr = option_value; /* set given option to be used */ if (strcmp(option_name, "MAX_MSS") == 0) { if (use == 0) { /* we cannot use this option as default */ free(global_options[MAX_MSS]); global_options[MAX_MSS] = NULL; } else { /* option is wanted, try to allocate space for it */ if ((global_options[MAX_MSS] == NULL) && ((global_options[MAX_MSS] = (void *) malloc(sizeof(MSS_TYPE))) == NULL)) return E_MEMORY_FAIL; /* set option value */ temp_mss = strtol(option_value, &endptr, 10); if ((endptr[0] != '\0') || (temp_mss <= 0)) return E_INVALID_OPT_VALUE; *((MSS_TYPE *) global_options[MAX_MSS]) = temp_mss; } } else if (strcmp(option_name, "MAX_DGRAM_LEN") == 0) { if (use == 0) { /* we cannot use this option as default */ free(global_options[MAX_DGRAM_LEN]); global_options[MAX_DGRAM_LEN] = NULL; } else { /* option is wanted, try to allocate space for it */ if ((global_options[MAX_DGRAM_LEN] == NULL) && ((global_options[MAX_DGRAM_LEN] = (void *) malloc(sizeof(MAX_DGRAM_LEN_TYPE))) == NULL)) return E_MEMORY_FAIL; /* set option value */ temp_dgram_len = strtol(option_value, &endptr, 10); if ((endptr[0] != '\0') || (temp_dgram_len <= 0)) return E_INVALID_OPT_VALUE; *((MAX_DGRAM_LEN_TYPE *) global_options[MAX_DGRAM_LEN]) = temp_dgram_len; } } else if (strcmp(option_name, "RETRANSMITS_TIMEOUT") == 0) { if (use == 0) { /* we cannot use this option as default */ free(global_options[RETRANSMITS_TIMEOUT]); global_options[RETRANSMITS_TIMEOUT] = NULL; } else { /* option is wanted, try to allocate space for it */ if ((global_options[RETRANSMITS_TIMEOUT] == NULL) && ((global_options[RETRANSMITS_TIMEOUT] = (void *) malloc(sizeof(RETR_TIMEOUT_TYPE))) == NULL)) return E_MEMORY_FAIL; /* set option value */ *((RETR_TIMEOUT_TYPE *) global_options[RETRANSMITS_TIMEOUT]) = 0; } } else return E_INVALID_OPTION; return 0; } int set_default_options(struct session_item *session) { int i; /* find default options */ for (i = 0; i < OPTIONS_COUNT; i++) if (global_options[i] != NULL) { switch (i) { case MAX_MSS: /* try to allocate space for this option */ if ((session->options[i] = (void *) malloc(sizeof(MSS_TYPE))) == NULL) return E_MEMORY_FAIL; /* copy option value */ *((MSS_TYPE *) session->options[i]) = *((MSS_TYPE *) global_options[i]); break; case MAX_DGRAM_LEN: /* try to allocate space for this option */ if ((session->options[i] = (void *) malloc(sizeof(MAX_DGRAM_LEN_TYPE))) == NULL) return E_MEMORY_FAIL; /* copy option value */ *((MAX_DGRAM_LEN_TYPE *) session->options[i]) = *((MAX_DGRAM_LEN_TYPE *) global_options[i]); break; case RETRANSMITS_TIMEOUT: /* try to allocate space for this option */ if ((session->options[i] = (void *) malloc(sizeof(RETR_TIMEOUT_TYPE))) == NULL) return E_MEMORY_FAIL; /* copy option value */ *((RETR_TIMEOUT_TYPE *) session->options[i]) = *((RETR_TIMEOUT_TYPE *) global_options[i]); break; } } return 0; } int get_session_options(struct session_item *session, struct artp_dgram *dgram, char **options, int *size) { int i; int position; /* there cannot be returned any options when session isn't established */ if (session->session_type == NON_EST) { *options = NULL; *size = 0; return 0; } *options = NULL; position = 0; for (i = 0; i < OPTIONS_COUNT; i++) if (session->options[i] != NULL) { /* there's any option which must be sent - allocate enough space * for it (if isn't) - later it will be reduced to the exact size. */ if ((*options == NULL) && ((*options = (char *) malloc(MAX_ARTP_PACKET_SIZE)) == NULL)) return E_MEMORY_FAIL; /* save all information */ switch (i) { case MAX_MSS: *((OPTS_SZ_TYPE *) (*options + position)) = htons(sizeof(OPTS_SZ_TYPE) + sizeof(OPTS_OPTID_TYPE) + sizeof(MSS_TYPE)); position += sizeof(OPTS_SZ_TYPE); *((OPTS_OPTID_TYPE *) (*options + position)) = htons(MAX_MSS); position += sizeof(OPTS_OPTID_TYPE); *((MSS_TYPE *) (*options + position)) = htons(*((MSS_TYPE *) session->options[i])); position += sizeof(MSS_TYPE); break; case MAX_DGRAM_LEN: *((OPTS_SZ_TYPE *) (*options + position)) = htons(sizeof(OPTS_SZ_TYPE) + sizeof(OPTS_OPTID_TYPE) + sizeof(MAX_DGRAM_LEN_TYPE)); position += sizeof(OPTS_SZ_TYPE); *((OPTS_OPTID_TYPE *) (*options + position)) = htons(MAX_DGRAM_LEN); position += sizeof(OPTS_OPTID_TYPE); *((MAX_DGRAM_LEN_TYPE *) (*options + position)) = htonl(*((MAX_DGRAM_LEN_TYPE *) session->options[i])); position += sizeof(MAX_DGRAM_LEN_TYPE); break; case RETRANSMITS_TIMEOUT: *((OPTS_SZ_TYPE *) (*options + position)) = htons(sizeof(OPTS_SZ_TYPE) + sizeof(OPTS_OPTID_TYPE) + sizeof(RETR_TIMEOUT_TYPE)); position += sizeof(OPTS_SZ_TYPE); *((OPTS_OPTID_TYPE *) (*options + position)) = htons(RETRANSMITS_TIMEOUT); position += sizeof(OPTS_OPTID_TYPE); *((RETR_TIMEOUT_TYPE *) (*options + position)) = htons(session->retries_timeout); position += sizeof(RETR_TIMEOUT_TYPE); break; default: return E_INVALID_OPTION; } } /* reduce the size of allocated memory */ if ((*options != NULL) && (realloc(*options, position) == NULL)) return E_MEMORY_FAIL; *size = position; return 0; } /** Evaluate received option. * This function evaluates reveived option which was previously parsed with * proper function. All necessary session parameters will be set if there're * any (which has to be set). * * @param session * the pointer to the place where the session information is stored. * * @param option_id * the option identification number. * * @param data * the pointer to the memory space where the value of this option is * stored. * * @param data_size * the size of option data. * * @return zero * success * * @return nonzero * related error code if something failed (for further information see * documentation of file @c errors.h). */ static int evaluate_option(struct session_item *session, OPTS_OPTID_TYPE option_id, char *data, int data_size) { switch (option_id) { case MAX_MSS: /* check whether the size of option value is correct */ if (data_size != sizeof(MSS_TYPE)) return E_INVALID_OPT_SIZE; /* check whether the option value is correct */ if (ntohs(*((MSS_TYPE *) data)) <= 0) return E_INVALID_OPT_VALUE; /* check whether there's any space allocated for this option * (in structure for partner options). If there isn't, allocate * it. */ if ((session->partner_options[option_id] == NULL) && ((session->partner_options[option_id] = (void *) malloc(sizeof(MSS_TYPE))) == NULL)) return E_MEMORY_FAIL; /* save the value of this option */ *((MSS_TYPE *) session->partner_options[MAX_MSS]) = ntohs(*((MSS_TYPE *) data)); /* we has to decrease actual MSS for this session if it's greater * than our partner wishes. */ if (session->mss > *((MSS_TYPE *) session->partner_options[MAX_MSS])) session->mss = *((MSS_TYPE *) session->partner_options[MAX_MSS]); break; case MAX_DGRAM_LEN: /* check whether the size of option value is correct */ if (data_size != sizeof(MAX_DGRAM_LEN_TYPE)) return E_INVALID_OPT_SIZE; /* check whether the option value is correct */ if (ntohl(*((MAX_DGRAM_LEN_TYPE *) data)) <= 0) return E_INVALID_OPT_VALUE; /* check whether there's any space allocated for this option * (in structure for partner options). If there isn't, allocate * it. */ if ((session->partner_options[option_id] == NULL) && ((session->partner_options[option_id] = (void *) malloc(sizeof(MAX_DGRAM_LEN_TYPE))) == NULL)) return E_MEMORY_FAIL; /* save the value of this option */ *((MAX_DGRAM_LEN_TYPE *) session->partner_options[MAX_DGRAM_LEN]) = ntohl(*((MAX_DGRAM_LEN_TYPE *) data)); break; case RETRANSMITS_TIMEOUT: /* check whether the size of option value is correct */ if (data_size != sizeof(RETR_TIMEOUT_TYPE)) return E_INVALID_OPT_SIZE; /* check whether the option value is correct */ if (ntohs(*((RETR_TIMEOUT_TYPE *) data)) <= 0) return E_INVALID_OPT_VALUE; /* check whether there's any space allocated for this option * (in structure for partner options). If there isn't, allocate * it. */ if ((session->partner_options[option_id] == NULL) && ((session->partner_options[option_id] = (void *) malloc(sizeof(RETR_TIMEOUT_TYPE))) == NULL)) return E_MEMORY_FAIL; /* save the value of this option */ *((RETR_TIMEOUT_TYPE *) session->partner_options[RETRANSMITS_TIMEOUT]) = ntohs(*((RETR_TIMEOUT_TYPE *) data)); break; default: return E_INVALID_OPTION; } return 0; } int parse_session_options(struct session_item *session, char *options, int size) { int position = 0; int i; int64_t used_options = 0; int64_t current_option; OPTS_SZ_TYPE option_size; OPTS_SZ_TYPE data_size; OPTS_OPTID_TYPE option_id; /* incoming options for non-established sessions are banned */ if (session->session_type == NON_EST) { return E_NONEST_SESSION; } /* obtain all options and evaluate them */ while (position < size) { option_size = ntohs(*((OPTS_SZ_TYPE *) (options + position))); position += sizeof(OPTS_SZ_TYPE); option_id = ntohs(*((OPTS_OPTID_TYPE *) (options + position))); position += sizeof(OPTS_OPTID_TYPE); data_size = option_size - sizeof(OPTS_SZ_TYPE) - sizeof(OPTS_OPTID_TYPE); evaluate_option(session, option_id, options + position, data_size); position += data_size; /* remember that this option was used */ current_option = 1; current_option <<= option_id; used_options |= current_option; } /* unallocate all previously used options (they wasn't used actually). */ for (i = 0; i < OPTIONS_COUNT; i++) { current_option = 1; current_option <<= i; if ((used_options & current_option) == 0) { free(session->partner_options[i]); session->partner_options[i] = NULL; } } return 0; } int use_options(struct session_item *session, int use, enum artp_session_options option_id, va_list *ap) { MAX_DGRAM_LEN_TYPE temp_dgram_len; MSS_TYPE temp_mss; /* check whether 'use' parameter is correctly set */ if ((use != 0) && (use != 1)) return E_BAD_OPT_USE; if (use == 0) { /* do not use this option -- unallocate it's space */ free(session->options[option_id]); session->options[option_id] = NULL; } else { /* set using of this option */ switch (option_id) { case MAX_MSS: /* save given value */ temp_mss = va_arg(*ap, unsigned int); /* check whether the given option value is correct */ if (temp_mss <= 0) return E_INVALID_OPT_VALUE; /* check whether there's any space allocated for this option * If there isn't, allocate it. */ if ((session->options[option_id] == NULL) && ((session->options[option_id] = (void *) malloc(sizeof(MSS_TYPE))) == NULL)) return E_MEMORY_FAIL; /* save the value of this option */ *((MSS_TYPE *) session->options[option_id]) = temp_mss; break; case MAX_DGRAM_LEN: /* save given value */ temp_dgram_len = va_arg(*ap, unsigned int); /* check whether the given option value is correct */ if (temp_dgram_len <= 0) return E_INVALID_OPT_VALUE; /* check whether there's any space allocated for this option * If there isn't, allocate it. */ if ((session->options[option_id] == NULL) && ((session->options[option_id] = (void *) malloc(sizeof(MAX_DGRAM_LEN_TYPE))) == NULL)) return E_MEMORY_FAIL; /* save the value of this option */ *((MAX_DGRAM_LEN_TYPE *) session->options[option_id]) = temp_dgram_len; break; case RETRANSMITS_TIMEOUT: /* no value is expected*/ /* check whether there's any space allocated for this option * If there isn't, allocate it. */ if ((session->options[option_id] == NULL) && ((session->options[option_id] = (void *) malloc(sizeof(RETR_TIMEOUT_TYPE))) == NULL)) return E_MEMORY_FAIL; /* save the value of this option to zero */ *((RETR_TIMEOUT_TYPE *) session->options[option_id]) = 0; break; default: return E_INVALID_OPTION; } } return 0; } /* vim: set ts=4 : */