-int ack_h(connection_t *c)
-{
- char address[MAX_STRING_SIZE];
- char port[MAX_STRING_SIZE];
- char hisport[MAX_STRING_SIZE];
- char *hisaddress, *dummy;
- int weight;
- int options;
- node_t *n;
- connection_t *other;
- avl_node_t *node;
-cp
- if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d", hisport, address, port, &weight, &options) != 5)
- {
- syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ACK", c->name, c->hostname);
- return -1;
- }
-
- /* Check if we already have a node_t for him */
-
- n = lookup_node(c->name);
-
- if(!n)
- {
- n = new_node();
- n->name = xstrdup(c->name);
- node_add(n);
- }
- else
- {
- if(n->connection)
- {
- /* Oh dear, we already have a connection to this node. */
- if(debug_lvl >= DEBUG_CONNECTIONS)
- syslog(LOG_DEBUG, _("Established a second connection with %s (%s), closing old connection"), n->name, n->hostname);
- terminate_connection(n->connection, 0);
- }
-
- /* FIXME: check if information in existing node matches that of the other end of this connection */
- }
-
- n->connection = c;
- c->node = n;
- c->options |= options;
-
- /* Create an edge_t for this connection */
-
- c->edge = new_edge();
-cp
- c->edge->from.node = myself;
- c->edge->from.tcpaddress = str2sockaddr(address, port);
- c->edge->from.udpaddress = str2sockaddr(address, myport);
- c->edge->to.node = n;
- c->edge->to.tcpaddress = c->address;
- sockaddr2str(&c->address, &hisaddress, &dummy);
- c->edge->to.udpaddress = str2sockaddr(hisaddress, hisport);
- free(hisaddress);
- free(dummy);
- c->edge->weight = (weight + c->estimated_weight) / 2;
- c->edge->connection = c;
- c->edge->options = c->options;
-cp
- edge_add(c->edge);
-
- /* Activate this connection */
-
- c->allow_request = ALL;
- c->status.active = 1;
-
- if(debug_lvl >= DEBUG_CONNECTIONS)
- syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), c->name, c->hostname);
-
-cp
- /* Send him everything we know */
-
- send_everything(c);
-
- /* Notify others of this connection */
-
- for(node = connection_tree->head; node; node = node->next)
- {
- other = (connection_t *)node->data;
-
- if(other->status.active && other != c)
- send_add_edge(other, c->edge);
- }
-
- /* Run MST and SSSP algorithms */
-
- graph();
-
- /* Succesful connection, reset timeout if this is an outgoing connection. */
-
- if(c->outgoing)
- c->outgoing->timeout = 0;
-cp
- return 0;
+bool chal_reply_h(connection_t *c, const char *request) {
+ char hishash[MAX_STRING_SIZE];
+
+ if(sscanf(request, "%*d " MAX_STRING, hishash) != 1) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name,
+ c->hostname);
+ return false;
+ }
+
+ /* Convert the hash to binary format */
+
+ size_t inlen = hex2bin(hishash, hishash, sizeof(hishash));
+
+ /* Check if the length of the hash is all right */
+
+ if(inlen != digest_length(c->outdigest)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply length");
+ return false;
+ }
+
+
+ /* Verify the hash */
+
+ if(!digest_verify(c->outdigest, c->hischallenge, rsa_size(c->rsa), hishash)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
+ return false;
+ }
+
+ /* Identity has now been positively verified.
+ Send an acknowledgement with the rest of the information needed.
+ */
+
+ free(c->hischallenge);
+ c->hischallenge = NULL;
+ c->allow_request = ACK;
+
+ if(!c->outgoing) {
+ send_chal_reply(c);
+ }
+
+ return send_ack(c);
+}
+
+static bool send_upgrade(connection_t *c) {
+ /* Special case when protocol_minor is 1: the other end is Ed25519 capable,
+ * but doesn't know our key yet. So send it now. */
+
+ char *pubkey = ecdsa_get_base64_public_key(myself->connection->ecdsa);
+
+ if(!pubkey) {
+ return false;
+ }
+
+ bool result = send_request(c, "%d %s", ACK, pubkey);
+ free(pubkey);
+ return result;
+}
+#else
+bool send_metakey(connection_t *c) {
+ (void)c;
+ return false;
+}
+
+bool metakey_h(connection_t *c, const char *request) {
+ (void)c;
+ (void)request;
+ return false;
+}
+
+bool send_challenge(connection_t *c) {
+ (void)c;
+ return false;
+}
+
+bool challenge_h(connection_t *c, const char *request) {
+ (void)c;
+ (void)request;
+ return false;
+}
+
+bool send_chal_reply(connection_t *c) {
+ (void)c;
+ return false;
+}
+
+bool chal_reply_h(connection_t *c, const char *request) {
+ (void)c;
+ (void)request;
+ return false;
+}
+
+static bool send_upgrade(connection_t *c) {
+ (void)c;
+ return false;
+}
+#endif
+
+bool send_ack(connection_t *c) {
+ if(c->protocol_minor == 1) {
+ return send_upgrade(c);
+ }
+
+ /* ACK message contains rest of the information the other end needs
+ to create node_t and edge_t structures. */
+
+ struct timeval now;
+ bool choice;
+
+ /* Estimate weight */
+
+ gettimeofday(&now, NULL);
+ c->estimated_weight = (now.tv_sec - c->start.tv_sec) * 1000 + (now.tv_usec - c->start.tv_usec) / 1000;
+
+ /* Check some options */
+
+ if((get_config_bool(lookup_config(c->config_tree, "IndirectData"), &choice) && choice) || myself->options & OPTION_INDIRECT) {
+ c->options |= OPTION_INDIRECT;
+ }
+
+ if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &choice) && choice) || myself->options & OPTION_TCPONLY) {
+ c->options |= OPTION_TCPONLY | OPTION_INDIRECT;
+ }
+
+ if(myself->options & OPTION_PMTU_DISCOVERY && !(c->options & OPTION_TCPONLY)) {
+ c->options |= OPTION_PMTU_DISCOVERY;
+ }
+
+ choice = myself->options & OPTION_CLAMP_MSS;
+ get_config_bool(lookup_config(c->config_tree, "ClampMSS"), &choice);
+
+ if(choice) {
+ c->options |= OPTION_CLAMP_MSS;
+ }
+
+ if(!get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight)) {
+ get_config_int(lookup_config(config_tree, "Weight"), &c->estimated_weight);
+ }
+
+ return send_request(c, "%d %s %d %x", ACK, myport, c->estimated_weight, (c->options & 0xffffff) | (experimental ? (PROT_MINOR << 24) : 0));
+}
+
+static void send_everything(connection_t *c) {
+ /* Send all known subnets and edges */
+
+ if(disablebuggypeers) {
+ static struct {
+ vpn_packet_t pkt;
+ char pad[MAXBUFSIZE - MAXSIZE];
+ } zeropkt;
+
+ memset(&zeropkt, 0, sizeof(zeropkt));
+ zeropkt.pkt.len = MAXBUFSIZE;
+ send_tcppacket(c, &zeropkt.pkt);
+ }
+
+ if(tunnelserver) {
+ for splay_each(subnet_t, s, myself->subnet_tree) {
+ send_add_subnet(c, s);
+ }
+
+ return;
+ }
+
+ for splay_each(node_t, n, node_tree) {
+ for splay_each(subnet_t, s, n->subnet_tree) {
+ send_add_subnet(c, s);
+ }
+
+ for splay_each(edge_t, e, n->edge_tree) {
+ send_add_edge(c, e);
+ }
+ }
+}
+
+static bool upgrade_h(connection_t *c, const char *request) {
+ char pubkey[MAX_STRING_SIZE];
+
+ if(sscanf(request, "%*d " MAX_STRING, pubkey) != 1) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name, c->hostname);
+ return false;
+ }
+
+ if(ecdsa_active(c->ecdsa) || read_ecdsa_public_key(c)) {
+ char *knownkey = ecdsa_get_base64_public_key(c->ecdsa);
+ bool different = strcmp(knownkey, pubkey);
+ free(knownkey);
+
+ if(different) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Already have an Ed25519 public key from %s (%s) which is different from the one presented now!", c->name, c->hostname);
+ return false;
+ }
+
+ logger(DEBUG_ALWAYS, LOG_INFO, "Already have Ed25519 public key from %s (%s), ignoring.", c->name, c->hostname);
+ c->allow_request = TERMREQ;
+ return send_termreq(c);
+ }
+
+ c->ecdsa = ecdsa_set_base64_public_key(pubkey);
+
+ if(!c->ecdsa) {
+ logger(DEBUG_ALWAYS, LOG_INFO, "Got bad Ed25519 public key from %s (%s), not upgrading.", c->name, c->hostname);
+ return false;
+ }
+
+ logger(DEBUG_ALWAYS, LOG_INFO, "Got Ed25519 public key from %s (%s), upgrading!", c->name, c->hostname);
+ append_config_file(c->name, "Ed25519PublicKey", pubkey);
+ c->allow_request = TERMREQ;
+
+ if(c->outgoing) {
+ c->outgoing->timeout = 0;
+ }
+
+ return send_termreq(c);
+}
+
+bool ack_h(connection_t *c, const char *request) {
+ if(c->protocol_minor == 1) {
+ return upgrade_h(c, request);
+ }
+
+ char hisport[MAX_STRING_SIZE];
+ int weight, mtu;
+ uint32_t options;
+ node_t *n;
+ bool choice;
+
+ if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name,
+ c->hostname);
+ return false;
+ }
+
+ /* Check if we already have a node_t for him */
+
+ n = lookup_node(c->name);
+
+ if(!n) {
+ n = new_node();
+ n->name = xstrdup(c->name);
+ node_add(n);
+ } else {
+ if(n->connection) {
+ /* Oh dear, we already have a connection to this node. */
+ logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
+
+ if(n->connection->outgoing) {
+ if(c->outgoing) {
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Two outgoing connections to the same node!");
+ } else {
+ c->outgoing = n->connection->outgoing;
+ }
+
+ n->connection->outgoing = NULL;
+ }
+
+ terminate_connection(n->connection, false);
+ /* Run graph algorithm to purge key and make sure up/down scripts are rerun with new IP addresses and stuff */
+ graph();
+ }
+ }
+
+ n->connection = c;
+ c->node = n;
+
+ if(!(c->options & options & OPTION_PMTU_DISCOVERY)) {
+ c->options &= ~OPTION_PMTU_DISCOVERY;
+ options &= ~OPTION_PMTU_DISCOVERY;
+ }
+
+ c->options |= options;
+
+ if(get_config_int(lookup_config(c->config_tree, "PMTU"), &mtu) && mtu < n->mtu) {
+ n->mtu = mtu;
+ }
+
+ if(get_config_int(lookup_config(config_tree, "PMTU"), &mtu) && mtu < n->mtu) {
+ n->mtu = mtu;
+ }
+
+ if(get_config_bool(lookup_config(c->config_tree, "ClampMSS"), &choice)) {
+ if(choice) {
+ c->options |= OPTION_CLAMP_MSS;
+ } else {
+ c->options &= ~OPTION_CLAMP_MSS;
+ }
+ }
+
+ /* Activate this connection */
+
+ c->allow_request = ALL;
+
+ logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection with %s (%s) activated", c->name,
+ c->hostname);
+
+ /* Send him everything we know */
+
+ send_everything(c);
+
+ /* Create an edge_t for this connection */
+
+ c->edge = new_edge();
+ c->edge->from = myself;
+ c->edge->to = n;
+ sockaddrcpy(&c->edge->address, &c->address);
+ sockaddr_setport(&c->edge->address, hisport);
+ sockaddr_t local_sa;
+ socklen_t local_salen = sizeof(local_sa);
+
+ if(getsockname(c->socket, &local_sa.sa, &local_salen) < 0) {
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get local socket address for connection with %s", c->name);
+ } else {
+ sockaddr_setport(&local_sa, myport);
+ c->edge->local_address = local_sa;
+ }
+
+ c->edge->weight = (weight + c->estimated_weight) / 2;
+ c->edge->connection = c;
+ c->edge->options = c->options;
+
+ edge_add(c->edge);
+
+ /* Notify everyone of the new edge */
+
+ if(tunnelserver) {
+ send_add_edge(c, c->edge);
+ } else {
+ send_add_edge(everyone, c->edge);
+ }
+
+ /* Run MST and SSSP algorithms */
+
+ graph();
+
+ return true;