+ if(get_config_string(lookup_config(&config, "Ed25519PublicKey"), &p)) {
+ n->ecdsa = ecdsa_set_base64_public_key(p);
+ free(p);
+ goto exit;
+ }
+
+ /* Else, check for Ed25519PublicKeyFile statement and read it */
+
+ if(!get_config_string(lookup_config(&config, "Ed25519PublicKeyFile"), &pubname)) {
+ xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, n->name);
+ }
+
+ fp = fopen(pubname, "r");
+
+ if(!fp) {
+ goto exit;
+ }
+
+ n->ecdsa = ecdsa_read_pem_public_key(fp);
+ fclose(fp);
+
+exit:
+ splay_empty_tree(&config);
+ free(pubname);
+ return n->ecdsa;
+}
+
+static bool read_invitation_key(void) {
+ FILE *fp;
+ char fname[PATH_MAX];
+
+ if(invitation_key) {
+ ecdsa_free(invitation_key);
+ invitation_key = NULL;
+ }
+
+ snprintf(fname, sizeof(fname), "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase);
+
+ fp = fopen(fname, "r");
+
+ if(fp) {
+ invitation_key = ecdsa_read_pem_private_key(fp);
+ fclose(fp);
+
+ if(!invitation_key) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
+ }
+ }
+
+ return invitation_key;
+}
+
+#ifndef DISABLE_LEGACY
+static timeout_t keyexpire_timeout;
+
+static void keyexpire_handler(void *data) {
+ regenerate_key();
+ timeout_set(data, &(struct timeval) {
+ keylifetime, jitter()
+ });
+}
+#endif
+
+void regenerate_key(void) {
+ logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
+ send_key_changed();
+
+ for splay_each(node_t, n, &node_tree) {
+ n->status.validkey_in = false;
+ }
+}
+
+void load_all_nodes(void) {
+ DIR *dir;
+ struct dirent *ent;
+ char dname[PATH_MAX];
+
+ snprintf(dname, sizeof(dname), "%s" SLASH "hosts", confbase);
+ dir = opendir(dname);
+
+ if(!dir) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
+ return;
+ }
+
+ while((ent = readdir(dir))) {
+ if(!check_id(ent->d_name)) {
+ continue;
+ }
+
+ node_t *n = lookup_node(ent->d_name);
+
+ splay_tree_t config;
+ init_configuration(&config);
+ read_config_options(&config, ent->d_name);
+ read_host_config(&config, ent->d_name, true);
+
+ if(!n) {
+ n = new_node();
+ n->name = xstrdup(ent->d_name);
+ node_add(n);
+ }
+
+ if(strictsubnets) {
+ for(config_t *cfg = lookup_config(&config, "Subnet"); cfg; cfg = lookup_config_next(&config, cfg)) {
+ subnet_t *s, *s2;
+
+ if(!get_config_subnet(cfg, &s)) {
+ continue;
+ }
+
+ if((s2 = lookup_subnet(n, s))) {
+ s2->expires = -1;
+ free(s);
+ } else {
+ subnet_add(n, s);
+ }
+ }
+ }
+
+ if(lookup_config(&config, "Address")) {
+ n->status.has_address = true;
+ }
+
+ splay_empty_tree(&config);
+ }
+
+ closedir(dir);
+}
+
+char *get_name(void) {
+ char *name = NULL;
+ char *returned_name;
+
+ get_config_string(lookup_config(&config_tree, "Name"), &name);
+
+ if(!name) {
+ return NULL;
+ }
+
+ returned_name = replace_name(name);
+ free(name);
+ return returned_name;
+}
+
+bool setup_myself_reloadable(void) {
+ free(scriptinterpreter);
+ scriptinterpreter = NULL;
+
+ get_config_string(lookup_config(&config_tree, "ScriptsInterpreter"), &scriptinterpreter);
+
+ free(scriptextension);
+
+ if(!get_config_string(lookup_config(&config_tree, "ScriptsExtension"), &scriptextension)) {
+ scriptextension = xstrdup("");