+static char *myname;
+static io_t device_io;
+devops_t devops;
+bool device_standby = false;
+
+char *proxyhost;
+char *proxyport;
+char *proxyuser;
+char *proxypass;
+proxytype_t proxytype;
+bool autoconnect;
+bool disablebuggypeers;
+
+char *scriptinterpreter;
+char *scriptextension;
+
+bool node_read_ecdsa_public_key(node_t *n) {
+ if(ecdsa_active(n->ecdsa))
+ return true;
+
+ splay_tree_t *config_tree;
+ FILE *fp;
+ char *pubname = NULL;
+ char *p;
+
+ init_configuration(&config_tree);
+ if(!read_host_config(config_tree, n->name))
+ goto exit;
+
+ /* First, check for simple Ed25519PublicKey statement */
+
+ if(get_config_string(lookup_config(config_tree, "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_tree, "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:
+ exit_configuration(&config_tree);
+ free(pubname);
+ return n->ecdsa;
+}
+
+bool read_ecdsa_public_key(connection_t *c) {
+ if(ecdsa_active(c->ecdsa))
+ return true;
+
+ FILE *fp;
+ char *fname;
+ char *p;
+
+ if(!c->config_tree) {
+ init_configuration(&c->config_tree);
+ if(!read_host_config(c->config_tree, c->name))
+ return false;
+ }
+
+ /* First, check for simple Ed25519PublicKey statement */
+
+ if(get_config_string(lookup_config(c->config_tree, "Ed25519PublicKey"), &p)) {
+ c->ecdsa = ecdsa_set_base64_public_key(p);
+ free(p);
+ return c->ecdsa;
+ }
+
+ /* Else, check for Ed25519PublicKeyFile statement and read it */
+
+ if(!get_config_string(lookup_config(c->config_tree, "Ed25519PublicKeyFile"), &fname))
+ xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
+
+ fp = fopen(fname, "r");
+
+ if(!fp) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error reading Ed25519 public key file `%s': %s",
+ fname, strerror(errno));
+ free(fname);
+ return false;
+ }
+
+ c->ecdsa = ecdsa_read_pem_public_key(fp);
+
+ if(!c->ecdsa && errno != ENOENT)
+ logger(DEBUG_ALWAYS, LOG_ERR, "Parsing Ed25519 public key file `%s' failed.", fname);
+
+ fclose(fp);
+ free(fname);
+ return c->ecdsa;
+}
+
+#ifndef DISABLE_LEGACY
+bool read_rsa_public_key(connection_t *c) {
+ FILE *fp;
+ char *fname;
+ char *n;
+
+ /* First, check for simple PublicKey statement */
+
+ if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) {
+ c->rsa = rsa_set_hex_public_key(n, "FFFF");
+ free(n);
+ return c->rsa;
+ }
+
+ /* Else, check for PublicKeyFile statement and read it */
+
+ if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
+ xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
+
+ fp = fopen(fname, "r");
+
+ if(!fp) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
+ free(fname);
+ return false;
+ }
+
+ c->rsa = rsa_read_pem_public_key(fp);
+ fclose(fp);
+
+ if(!c->rsa)
+ logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
+ free(fname);
+ return c->rsa;
+}
+#endif
+
+static bool read_ecdsa_private_key(void) {
+ FILE *fp;
+ char *fname;
+
+ /* Check for PrivateKeyFile statement and read it */
+
+ if(!get_config_string(lookup_config(config_tree, "Ed25519PrivateKeyFile"), &fname))
+ xasprintf(&fname, "%s" SLASH "ed25519_key.priv", confbase);
+
+ fp = fopen(fname, "r");
+
+ if(!fp) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error reading Ed25519 private key file `%s': %s", fname, strerror(errno));
+ if(errno == ENOENT)
+ logger(DEBUG_ALWAYS, LOG_INFO, "Create an Ed25519 keypair with `tinc -n %s generate-ed25519-keys'.", netname ?: ".");
+ free(fname);
+ return false;
+ }
+
+#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
+ struct stat s;
+
+ if(fstat(fileno(fp), &s)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat Ed25519 private key file `%s': %s'", fname, strerror(errno));
+ free(fname);
+ return false;
+ }
+
+ if(s.st_mode & ~0100700)
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for Ed25519 private key file `%s'!", fname);
+#endif
+
+ myself->connection->ecdsa = ecdsa_read_pem_private_key(fp);
+ fclose(fp);
+
+ if(!myself->connection->ecdsa)
+ logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
+ free(fname);
+ return myself->connection->ecdsa;
+}
+
+static bool read_invitation_key(void) {
+ FILE *fp;
+ char *fname;
+
+ if(invitation_key) {
+ ecdsa_free(invitation_key);
+ invitation_key = NULL;
+ }
+
+ xasprintf(&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);
+ }
+
+ free(fname);
+ return invitation_key;
+}
+
+#ifndef DISABLE_LEGACY
+static bool read_rsa_private_key(void) {
+ FILE *fp;
+ char *fname;
+ char *n, *d;
+
+ /* First, check for simple PrivateKey statement */
+
+ if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
+ if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "PrivateKey used but no PublicKey found!");
+ free(d);
+ return false;
+ }
+ myself->connection->rsa = rsa_set_hex_private_key(n, "FFFF", d);
+ free(n);
+ free(d);
+ return myself->connection->rsa;
+ }
+
+ /* Else, check for PrivateKeyFile statement and read it */
+
+ if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
+ xasprintf(&fname, "%s" SLASH "rsa_key.priv", confbase);
+
+ fp = fopen(fname, "r");
+
+ if(!fp) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA private key file `%s': %s",
+ fname, strerror(errno));
+ if(errno == ENOENT)
+ logger(DEBUG_ALWAYS, LOG_INFO, "Create an RSA keypair with `tinc -n %s generate-rsa-keys'.", netname ?: ".");
+ free(fname);
+ return false;
+ }
+
+#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
+ struct stat s;
+
+ if(fstat(fileno(fp), &s)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat RSA private key file `%s': %s'", fname, strerror(errno));
+ free(fname);
+ return false;
+ }
+
+ if(s.st_mode & ~0100700)
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
+#endif
+
+ myself->connection->rsa = rsa_read_pem_private_key(fp);
+ fclose(fp);
+
+ if(!myself->connection->rsa)
+ logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA private key file `%s' failed: %s", fname, strerror(errno));
+ free(fname);
+ return myself->connection->rsa;
+}
+#endif
+
+static timeout_t keyexpire_timeout;
+
+static void keyexpire_handler(void *data) {
+ regenerate_key();
+ timeout_set(data, &(struct timeval){keylifetime, rand() % 100000});
+}
+
+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;
+}
+
+/*
+ Read Subnets from all host config files
+*/
+void load_all_subnets(void) {
+ DIR *dir;
+ struct dirent *ent;
+ char *dname;
+
+ xasprintf(&dname, "%s" SLASH "hosts", confbase);
+ dir = opendir(dname);
+ if(!dir) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
+ free(dname);
+ return;
+ }
+
+ while((ent = readdir(dir))) {
+ if(!check_id(ent->d_name))
+ continue;
+
+ node_t *n = lookup_node(ent->d_name);
+ #ifdef _DIRENT_HAVE_D_TYPE
+ //if(ent->d_type != DT_REG)
+ // continue;
+ #endif
+
+ splay_tree_t *config_tree;
+ init_configuration(&config_tree);
+ read_config_options(config_tree, ent->d_name);
+ read_host_config(config_tree, ent->d_name);
+
+ if(!n) {
+ n = new_node();
+ n->name = xstrdup(ent->d_name);
+ node_add(n);
+ }
+
+ for(config_t *cfg = lookup_config(config_tree, "Subnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
+ subnet_t *s, *s2;
+
+ if(!get_config_subnet(cfg, &s))
+ continue;
+
+ if((s2 = lookup_subnet(n, s))) {
+ s2->expires = -1;
+ } else {
+ subnet_add(n, s);
+ }
+ }
+
+ exit_configuration(&config_tree);
+ }
+
+ closedir(dir);
+}
+
+void load_all_nodes(void) {
+ DIR *dir;
+ struct dirent *ent;
+ char *dname;
+
+ xasprintf(&dname, "%s" SLASH "hosts", confbase);
+ dir = opendir(dname);
+ if(!dir) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
+ free(dname);
+ return;
+ }
+
+ while((ent = readdir(dir))) {
+ if(!check_id(ent->d_name))
+ continue;
+
+ node_t *n = lookup_node(ent->d_name);
+ if(n)
+ continue;
+
+ n = new_node();
+ n->name = xstrdup(ent->d_name);
+ node_add(n);
+ }
+
+ closedir(dir);
+}
+