-/*
- These are all the possible configurable values
-*/
-static internal_config_t hazahaza[] = {
-/* Main configuration file keywords */
- { "Name", tincname, TYPE_NAME },
- { "ConnectTo", connectto, TYPE_NAME },
- { "PingTimeout", pingtimeout, TYPE_INT },
- { "TapDevice", tapdevice, TYPE_NAME },
- { "TapSubnet", tapsubnet, TYPE_IP },
- { "PrivateKey", privatekey, TYPE_NAME },
- { "KeyExpire", keyexpire, TYPE_INT },
- { "Hostnames", resolve_dns, TYPE_BOOL },
- { "Interface", interface, TYPE_NAME },
- { "InterfaceIP", interfaceip, TYPE_IP },
-/* Host configuration file keywords */
- { "Address", address, TYPE_NAME },
- { "Port", port, TYPE_INT },
- { "PublicKey", publickey, TYPE_NAME },
- { "Subnet", subnet, TYPE_NAME },
- { "RestrictHosts", restricthosts, TYPE_BOOL },
- { "RestrictSubnets", restrictsubnets, TYPE_BOOL },
- { "RestrictAddress", restrictaddress, TYPE_BOOL },
- { "RestrictPort", restrictport, TYPE_BOOL },
- { "IndirectData", indirectdata, TYPE_BOOL },
- { "TCPonly", tcponly, TYPE_BOOL },
- { NULL, 0, 0 }
-};
+ /* give priority to command line options */
+ result = !b->file - !a->file;
+
+ if(result) {
+ return result;
+ }
+
+ result = a->line - b->line;
+
+ if(result) {
+ return result;
+ } else {
+ return a->file ? strcmp(a->file, b->file) : 0;
+ }
+}
+
+void init_configuration(splay_tree_t **config_tree) {
+ *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config);
+}
+
+void exit_configuration(splay_tree_t **config_tree) {
+ splay_delete_tree(*config_tree);
+ *config_tree = NULL;
+}
+
+config_t *new_config(void) {
+ return xzalloc(sizeof(config_t));
+}
+
+void free_config(config_t *cfg) {
+ if(cfg->variable) {
+ free(cfg->variable);
+ }
+
+ if(cfg->value) {
+ free(cfg->value);
+ }
+
+ if(cfg->file) {
+ free(cfg->file);
+ }
+
+ free(cfg);
+}
+
+void config_add(splay_tree_t *config_tree, config_t *cfg) {
+ splay_insert(config_tree, cfg);
+}
+
+config_t *lookup_config(splay_tree_t *config_tree, char *variable) {
+ config_t cfg, *found;
+
+ cfg.variable = variable;
+ cfg.file = NULL;
+ cfg.line = 0;
+
+ found = splay_search_closest_greater(config_tree, &cfg);
+
+ if(!found) {
+ return NULL;
+ }
+
+ if(strcasecmp(found->variable, variable)) {
+ return NULL;
+ }
+
+ return found;
+}
+
+config_t *lookup_config_next(splay_tree_t *config_tree, const config_t *cfg) {
+ splay_node_t *node;
+ config_t *found;
+
+ node = splay_search_node(config_tree, cfg);
+
+ if(node) {
+ if(node->next) {
+ found = node->next->data;
+
+ if(!strcasecmp(found->variable, cfg->variable)) {
+ return found;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+bool get_config_bool(const config_t *cfg, bool *result) {
+ if(!cfg) {
+ return false;
+ }
+
+ if(!strcasecmp(cfg->value, "yes")) {
+ *result = true;
+ return true;
+ } else if(!strcasecmp(cfg->value, "no")) {
+ *result = false;
+ return true;
+ }
+
+ logger(DEBUG_ALWAYS, LOG_ERR, "\"yes\" or \"no\" expected for configuration variable %s in %s line %d",
+ cfg->variable, cfg->file, cfg->line);
+
+ return false;
+}
+
+bool get_config_int(const config_t *cfg, int *result) {
+ if(!cfg) {
+ return false;
+ }
+
+ if(sscanf(cfg->value, "%d", result) == 1) {
+ return true;
+ }
+
+ logger(DEBUG_ALWAYS, LOG_ERR, "Integer expected for configuration variable %s in %s line %d",
+ cfg->variable, cfg->file, cfg->line);
+
+ return false;
+}
+
+bool get_config_string(const config_t *cfg, char **result) {
+ if(!cfg) {
+ return false;
+ }
+
+ *result = xstrdup(cfg->value);
+
+ return true;
+}
+
+bool get_config_address(const config_t *cfg, struct addrinfo **result) {
+ struct addrinfo *ai;
+
+ if(!cfg) {
+ return false;
+ }
+
+ ai = str2addrinfo(cfg->value, NULL, 0);
+
+ if(ai) {
+ *result = ai;
+ return true;
+ }
+
+ logger(DEBUG_ALWAYS, LOG_ERR, "Hostname or IP address expected for configuration variable %s in %s line %d",
+ cfg->variable, cfg->file, cfg->line);
+
+ return false;
+}
+
+bool get_config_subnet(const config_t *cfg, subnet_t **result) {
+ subnet_t subnet = {NULL};
+
+ if(!cfg) {
+ return false;
+ }
+
+ if(!str2net(&subnet, cfg->value)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Subnet expected for configuration variable %s in %s line %d",
+ cfg->variable, cfg->file, cfg->line);
+ return false;
+ }
+
+ /* Teach newbies what subnets are... */
+
+ if(((subnet.type == SUBNET_IPV4)
+ && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(subnet.net.ipv4.address)))
+ || ((subnet.type == SUBNET_IPV6)
+ && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(subnet.net.ipv6.address)))) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
+ cfg->variable, cfg->file, cfg->line);
+ return false;
+ }
+
+ *(*result = new_subnet()) = subnet;
+
+ return true;
+}