bool node_write_devclass(meshlink_handle_t *mesh, node_t *n) { if(n->devclass < 0 || n->devclass > _DEV_CLASS_MAX) return false; bool result = false; splay_tree_t *config_tree; init_configuration(&config_tree); // ignore read errors; in case the file does not exist we will create it read_host_config(mesh, config_tree, n->name); config_t* cnf = lookup_config(config_tree, "DeviceClass"); if(!cnf) { cnf = new_config(); cnf->variable = xstrdup("DeviceClass"); config_add(config_tree, cnf); } set_config_int(cnf, n->devclass); if(!write_host_config(mesh, config_tree, n->name)) goto fail; result = true; fail: exit_configuration(&config_tree); return result; }
bool read_ecdsa_public_key(meshlink_handle_t *mesh, connection_t *c) { if(ecdsa_active(c->ecdsa)) return true; char *p; if(!c->config_tree) { init_configuration(&c->config_tree); if(!read_host_config(mesh, c->config_tree, c->name)) return false; } /* First, check for simple ECDSAPublicKey statement */ if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) { c->ecdsa = ecdsa_set_base64_public_key(p); free(p); return c->ecdsa; } return false; }
bool node_read_ecdsa_public_key(meshlink_handle_t *mesh, node_t *n) { if(ecdsa_active(n->ecdsa)) return true; splay_tree_t *config_tree; char *p; init_configuration(&config_tree); if(!read_host_config(mesh, config_tree, n->name)) goto exit; /* First, check for simple ECDSAPublicKey statement */ if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) { n->ecdsa = ecdsa_set_base64_public_key(p); free(p); } exit: exit_configuration(&config_tree); return n->ecdsa; }
bool node_read_devclass(meshlink_handle_t *mesh, node_t *n) { splay_tree_t *config_tree; char *p; init_configuration(&config_tree); if(!read_host_config(mesh, config_tree, n->name)) goto exit; if(get_config_string(lookup_config(config_tree, "DeviceClass"), &p)) { n->devclass = atoi(p); free(p); } if(n->devclass < 0 || n->devclass > _DEV_CLASS_MAX) { n->devclass = _DEV_CLASS_MAX; } exit: exit_configuration(&config_tree); return n->devclass != 0; }
bool id_h(connection_t *c, const char *request) { char name[MAX_STRING_SIZE]; if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) { logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name, c->hostname); return false; } /* Check if this is a control connection */ if(name[0] == '^' && !strcmp(name + 1, controlcookie)) { c->status.control = true; c->allow_request = CONTROL; c->last_ping_time = now.tv_sec + 3600; free(c->name); c->name = xstrdup("<control>"); return send_request(c, "%d %d %d", ACK, TINC_CTL_VERSION_CURRENT, getpid()); } if(name[0] == '?') { if(!invitation_key) { logger(DEBUG_ALWAYS, LOG_ERR, "Got invitation from %s but we don't have an invitation key", c->hostname); return false; } c->ecdsa = ecdsa_set_base64_public_key(name + 1); if(!c->ecdsa) { logger(DEBUG_ALWAYS, LOG_ERR, "Got bad invitation from %s", c->hostname); return false; } c->status.invitation = true; char *mykey = ecdsa_get_base64_public_key(invitation_key); if(!mykey) return false; if(!send_request(c, "%d %s", ACK, mykey)) return false; free(mykey); c->protocol_minor = 2; return sptps_start(&c->sptps, c, false, false, invitation_key, c->ecdsa, "tinc invitation", 15, send_meta_sptps, receive_invitation_sptps); } /* Check if identity is a valid name */ if(!check_id(name)) { logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ID", c->name, c->hostname, "invalid name"); return false; } /* If this is an outgoing connection, make sure we are connected to the right host */ if(c->outgoing) { if(strcmp(c->name, name)) { logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s is %s instead of %s", c->hostname, name, c->name); return false; } } else { if(c->name) free(c->name); c->name = xstrdup(name); } /* Check if version matches */ if(c->protocol_major != myself->connection->protocol_major) { logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s (%s) uses incompatible version %d.%d", c->name, c->hostname, c->protocol_major, c->protocol_minor); return false; } if(bypass_security) { if(!c->config_tree) init_configuration(&c->config_tree); c->allow_request = ACK; return send_ack(c); } if(!experimental) c->protocol_minor = 0; if(!c->config_tree) { init_configuration(&c->config_tree); if(!read_host_config(c->config_tree, c->name)) { logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s had unknown identity (%s)", c->hostname, c->name); return false; } if(experimental) read_ecdsa_public_key(c); /* Ignore failures if no key known yet */ } if(c->protocol_minor && !ecdsa_active(c->ecdsa)) c->protocol_minor = 1; /* Forbid version rollback for nodes whose Ed25519 key we know */ if(ecdsa_active(c->ecdsa) && c->protocol_minor < 2) { logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s (%s) tries to roll back protocol version to %d.%d", c->name, c->hostname, c->protocol_major, c->protocol_minor); return false; } c->allow_request = METAKEY; if(c->protocol_minor >= 2) { c->allow_request = ACK; char label[25 + strlen(myself->name) + strlen(c->name)]; if(c->outgoing) snprintf(label, sizeof label, "tinc TCP key expansion %s %s", myself->name, c->name); else snprintf(label, sizeof label, "tinc TCP key expansion %s %s", c->name, myself->name); return sptps_start(&c->sptps, c, c->outgoing, false, myself->connection->ecdsa, c->ecdsa, label, sizeof label, send_meta_sptps, receive_meta_sptps); } else { return send_metakey(c); } }
/* Configure node_t mesh->self and set up the local sockets (listen only) */ bool setup_myself(meshlink_handle_t *mesh) { char *name; char *address = NULL; if(!(name = get_name(mesh))) { logger(mesh, MESHLINK_ERROR, "Name for MeshLink instance required!"); return false; } mesh->self = new_node(); mesh->self->connection = new_connection(); mesh->self->name = name; mesh->self->devclass = mesh->devclass; mesh->self->connection->name = xstrdup(name); read_host_config(mesh, mesh->config, name); if(!get_config_string(lookup_config(mesh->config, "Port"), &mesh->myport)) { logger(mesh, MESHLINK_ERROR, "Port for MeshLink instance required!"); return false; } mesh->self->connection->options = 0; mesh->self->connection->protocol_major = PROT_MAJOR; mesh->self->connection->protocol_minor = PROT_MINOR; mesh->self->options |= PROT_MINOR << 24; if(!read_ecdsa_private_key(mesh)) return false; /* Ensure mesh->myport is numeric */ if(!atoi(mesh->myport)) { struct addrinfo *ai = str2addrinfo("localhost", mesh->myport, SOCK_DGRAM); sockaddr_t sa; if(!ai || !ai->ai_addr) return false; free(mesh->myport); memcpy(&sa, ai->ai_addr, ai->ai_addrlen); sockaddr2str(&sa, NULL, &mesh->myport); } /* Check some options */ if(!setup_myself_reloadable(mesh)) return false; /* Compression */ // TODO: drop compression in the packet layer? mesh->self->incompression = 0; mesh->self->connection->outcompression = 0; /* Done */ mesh->self->nexthop = mesh->self; mesh->self->via = mesh->self; mesh->self->status.reachable = true; mesh->self->last_state_change = mesh->loop.now.tv_sec; node_write_devclass(mesh, mesh->self); node_add(mesh, mesh->self); graph(mesh); load_all_nodes(mesh); /* Open sockets */ mesh->listen_sockets = 0; if(!add_listen_address(mesh, address, NULL)) return false; if(!mesh->listen_sockets) { logger(mesh, MESHLINK_ERROR, "Unable to create any listening socket!"); return false; } xasprintf(&mesh->self->hostname, "MYSELF port %s", mesh->myport); mesh->self->connection->hostname = xstrdup(mesh->self->hostname); /* Done. */ mesh->last_config_check = mesh->loop.now.tv_sec; return true; }