Use datagram SPTPS for packet exchange between nodes.
authorGuus Sliepen <guus@tinc-vpn.org>
Mon, 30 Jul 2012 16:36:59 +0000 (18:36 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Mon, 30 Jul 2012 16:36:59 +0000 (18:36 +0200)
When two nodes which support SPTPS want to send packets to each other, they now
always use SPTPS. The node initiating the SPTPS session send the first SPTPS
packet via an extended REQ_KEY messages. All other handshake messages are sent
using ANS_KEY messages. This ensures that intermediate nodes using an older
version of tinc can still help with NAT traversal. After the authentication
phase is over, SPTPS packets are sent via UDP, or are encapsulated in extended
REQ_KEY messages instead of PACKET messages.

12 files changed:
src/graph.c
src/meta.c
src/meta.h
src/net.h
src/net_packet.c
src/node.c
src/node.h
src/protocol.h
src/protocol_key.c
src/sptps.c
src/sptps.h
src/sptps_test.c

index b3a2c255c5945c4a1fe26a97f8de43f6ff01cfdb..506b6df57e09b81256053d27bb4056d63af0a908 100644 (file)
@@ -263,10 +263,16 @@ static void check_reachability(void) {
 
                        subnet_update(n, NULL, n->status.reachable);
 
-                       if(!n->status.reachable)
+                       if(!n->status.reachable) {
                                update_node_udp(n, NULL);
-                       else if(n->connection)
-                               send_ans_key(n);
+                       } else if(n->connection) {
+                               if(experimental && OPTION_VERSION(n->options) >= 2) {
+                                       if(n->connection->outgoing)
+                                               send_req_key(n);
+                               } else {
+                                       send_ans_key(n);
+                               }
+                       }
                }
        }
 }
index 84094d46f56a7db859c02782ca36c67e79aa1056..d44b2dd49a7212cf3e5fd02c4c652d78ef5b07dc 100644 (file)
@@ -31,7 +31,7 @@
 #include "utils.h"
 #include "xalloc.h"
 
-bool send_meta_sptps(void *handle, const char *buffer, size_t length) {
+bool send_meta_sptps(void *handle, uint8_t type, const char *buffer, size_t length) {
        connection_t *c = handle;
 
        if(!c) {
index 011aff514318853c12da5b58207efa18ef6a156d..d44d39af033fd422ad7843f1b9edd09dbe50bea1 100644 (file)
@@ -24,7 +24,7 @@
 #include "connection.h"
 
 extern bool send_meta(struct connection_t *, const char *, int);
-extern bool send_meta_sptps(void *, const char *, size_t);
+extern bool send_meta_sptps(void *, uint8_t, const char *, size_t);
 extern bool receive_meta_sptps(void *, uint8_t, const char *, uint16_t);
 extern void broadcast_meta(struct connection_t *, const char *, int);
 extern bool receive_meta(struct connection_t *);
index bd1cc428e355355ca4a6af95318f08ddb60cea5c..667409e0d7f4c7d373a80465a8195b3419adfbde 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -83,6 +83,18 @@ typedef struct vpn_packet_t {
        uint8_t data[MAXSIZE];
 } vpn_packet_t;
 
+/* Packet types when using SPTPS */
+
+#define PKT_COMPRESSED 1
+#define PKT_MAC 2
+#define PKT_PROBE 4
+
+typedef enum packet_type_t {
+       PACKET_NORMAL,
+       PACKET_COMPRESSED,
+       PACKET_PROBE
+} packet_type_t;
+
 typedef struct listen_socket_t {
        struct event ev_tcp;
        struct event ev_udp;
@@ -146,6 +158,8 @@ extern bool do_outgoing_connection(struct connection_t *);
 extern void handle_new_meta_connection(int, short, void *);
 extern int setup_listen_socket(const sockaddr_t *);
 extern int setup_vpn_in_socket(const sockaddr_t *);
+extern bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len);
+extern bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len);
 extern void send_packet(struct node_t *, vpn_packet_t *);
 extern void receive_tcppacket(struct connection_t *, const char *, int);
 extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
index e9fd10ce53c9a8856452cc5d5debf6f63a59264b..4e651555e4ccb88f8c8a56f00850ce88d25eaaec 100644 (file)
@@ -265,6 +265,11 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        vpn_packet_t *outpkt = pkt[0];
        size_t outlen;
 
+       if(experimental && OPTION_VERSION(n->options) >= 2) {
+               sptps_receive_data(&n->sptps, (char *)inpkt->data - 4, inpkt->len);
+               return;
+       }
+
        if(!cipher_active(&n->incipher)) {
                logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
                                        n->name, n->hostname);
@@ -430,6 +435,14 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
                return;
        }
 
+       if(experimental && OPTION_VERSION(n->options) >= 2) {
+               uint8_t type = 0;
+               if(!(inpkt->data[12] | inpkt->data[13]))
+                       type = PKT_PROBE;
+               sptps_send_record(&n->sptps, type, (char *)inpkt->data, inpkt->len);
+               return;
+       }
+
        /* Compress the packet */
 
        if(n->outcompression) {
@@ -531,6 +544,75 @@ end:
        origpkt->len = origlen;
 }
 
+bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
+       node_t *to = handle;
+
+       if(type >= SPTPS_HANDSHAKE) {
+               char buf[len * 4 / 3 + 5];
+               b64encode(data, buf, len);
+               if(!to->status.validkey)
+                       return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 -1", ANS_KEY, myself->name, to->name, buf);
+               else
+                       return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_SPTPS, buf);
+       }
+
+       /* Send the packet */
+
+       struct sockaddr *sa;
+       socklen_t sl;
+       int sock;
+
+       sa = &(to->address.sa);
+       sl = SALEN(to->address.sa);
+       sock = to->sock;
+
+       if(sendto(listen_socket[sock].udp, data, len, 0, sa, sl) < 0 && !sockwouldblock(sockerrno)) {
+               if(sockmsgsize(sockerrno)) {
+                       if(to->maxmtu >= len)
+                               to->maxmtu = len - 1;
+                       if(to->mtu >= len)
+                               to->mtu = len - 1;
+               } else {
+                       logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", to->name, to->hostname, sockstrerror(sockerrno));
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len) {
+       node_t *from = handle;
+
+       if(type == SPTPS_HANDSHAKE) {
+               from->status.validkey = true;
+               logger(DEBUG_META, LOG_INFO, "SPTPS key exchange with %s (%s) succesful", from->name, from->hostname);
+               return true;
+       }
+
+       if(len > MTU) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Packet from %s (%s) larger than maximum supported size (%d > %d)", from->name, from->hostname, len, MTU);
+               return false;
+       }
+
+       vpn_packet_t inpkt;
+       inpkt.len = len;
+       memcpy(inpkt.data, data, len);
+
+       if(type == PKT_PROBE) {
+               mtu_probe_h(from, &inpkt, len);
+               return true;
+
+       }
+       if(type != 0) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Unexpected SPTPS record type %d len %d from %s (%s)", type, len, from->name, from->hostname);
+               return false;
+       }
+
+       receive_packet(from, &inpkt);
+       return true;
+}
+
 /*
   send a packet to the given vpn ip.
 */
index 18a02a4ccf0c0355a60f35ec75b476097df04ccc..dfd11e2a337a74cf436ebc518858983f7e341768 100644 (file)
@@ -85,8 +85,8 @@ void free_node(node_t *n) {
        cipher_close(&n->outcipher);
        digest_close(&n->outdigest);
 
-       ecdh_free(&n->ecdh);
        ecdsa_free(&n->ecdsa);
+       sptps_stop(&n->sptps);
 
        if(timeout_initialized(&n->mtuevent))
                event_del(&n->mtuevent);
index b338815de82a76df404fd50f5b79cc9a1767fee0..23be3624d68b75da8d534502b82043d1a4526bbc 100644 (file)
@@ -25,7 +25,6 @@
 #include "cipher.h"
 #include "connection.h"
 #include "digest.h"
-#include "ecdh.h"
 #include "subnet.h"
 
 typedef struct node_status_t {
@@ -50,7 +49,7 @@ typedef struct node_t {
        time_t last_req_key;
 
        ecdsa_t ecdsa;                          /* His public ECDSA key */
-       ecdh_t ecdh;                            /* State for ECDH key exchange */
+       sptps_t sptps;
 
        cipher_t incipher;                        /* Cipher for UDP packets */
        digest_t indigest;                        /* Digest for UDP packets */  
index a56153946f6ee20e0180221a7c24b119cda8f30a..41f74ab4bcd5f736df1641cac6a12410f390d2f3 100644 (file)
@@ -46,6 +46,7 @@ typedef enum request_t {
        /* Tinc 1.1 requests */
        CONTROL,
        REQ_PUBKEY, ANS_PUBKEY,
+       REQ_SPTPS,
        LAST                                            /* Guardian for the highest request number */
 } request_t;
 
index 39643cc34a589eca738ba005e1b0aa7ffa45d568..98b934cb85bef9037086328beca097206426e173 100644 (file)
 #include "cipher.h"
 #include "connection.h"
 #include "crypto.h"
-#include "ecdh.h"
 #include "logger.h"
 #include "net.h"
 #include "netutl.h"
 #include "node.h"
 #include "prf.h"
 #include "protocol.h"
+#include "sptps.h"
 #include "utils.h"
 #include "xalloc.h"
 
@@ -46,8 +46,20 @@ void send_key_changed(void) {
 
        for(node = connection_tree->head; node; node = node->next) {
                c = node->data;
-               if(c->status.active && c->node && c->node->status.reachable)
-                       send_ans_key(c->node);
+               if(c->status.active && c->node && c->node->status.reachable) {
+                       if(!experimental || OPTION_VERSION(c->node->options) < 2)
+                               send_ans_key(c->node);
+               }
+       }
+
+       /* Force key exchange for connections using SPTPS */
+
+       if(experimental) {
+               for(node = node_tree->head; node; node = node->next) {
+                       node_t *n = node->data;
+                       if(n->status.reachable && n->status.validkey && OPTION_VERSION(n->options) >= 2)
+                               sptps_force_kex(&n->sptps);
+               }
        }
 }
 
@@ -72,8 +84,10 @@ bool key_changed_h(connection_t *c, const char *request) {
                return true;
        }
 
-       n->status.validkey = false;
-       n->last_req_key = 0;
+       if(OPTION_VERSION(n->options) < 2) {
+               n->status.validkey = false;
+               n->last_req_key = 0;
+       }
 
        /* Tell the others */
 
@@ -83,11 +97,26 @@ bool key_changed_h(connection_t *c, const char *request) {
        return true;
 }
 
+static bool send_initial_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
+       node_t *to = handle;
+       to->sptps.send_data = send_sptps_data;
+       char buf[len * 4 / 3 + 5];
+       b64encode(data, buf, len);
+       return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_KEY, buf);
+}
+
 bool send_req_key(node_t *to) {
        if(experimental && OPTION_VERSION(to->options) >= 2) {
-               if(!node_read_ecdsa_public_key(to))
+               if(!node_read_ecdsa_public_key(to)) {
+                       logger(DEBUG_ALWAYS, LOG_DEBUG, "No ECDSA key known for %s (%s)", to->name, to->hostname);
                        send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, REQ_PUBKEY);
+                       return true;
+               }
+                char label[25 + strlen(myself->name) + strlen(to->name)];
+               snprintf(label, sizeof label, "tinc UDP key expansion %s %s", myself->name, to->name);
+               return sptps_start(&to->sptps, to, true, true, myself->connection->ecdsa, to->ecdsa, label, sizeof label, send_initial_sptps_data, receive_sptps_record); 
        }
+
        return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
 }
 
@@ -108,7 +137,7 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, in
                                return true;
                        }
 
-                       char pubkey[4096];
+                       char pubkey[MAX_STRING_SIZE];
                        if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, pubkey) != 1 || !ecdsa_set_base64_public_key(&from->ecdsa, pubkey)) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_PUBKEY", from->name, from->hostname, "invalid pubkey");
                                return true;
@@ -119,6 +148,30 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, in
                        return true;
                }
 
+               case REQ_KEY: {
+                       if(!node_read_ecdsa_public_key(from)) {
+                               logger(DEBUG_ALWAYS, LOG_DEBUG, "No ECDSA key known for %s (%s)", from->name, from->hostname);
+                               send_request(from->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, from->name, REQ_PUBKEY);
+                               return true;
+                       }
+               }
+
+               case REQ_SPTPS: {
+                       char buf[MAX_STRING_SIZE];
+                       if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1) {
+                               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", from->name, from->hostname, "invalid SPTPS data");
+                               return true;
+                       }
+                       int len = b64decode(buf, buf, strlen(buf));
+
+
+                       char label[25 + strlen(from->name) + strlen(myself->name)];
+                       snprintf(label, sizeof label, "tinc UDP key expansion %s %s", from->name, myself->name);
+                       sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, sizeof label, send_sptps_data, receive_sptps_record); 
+                       sptps_receive_data(&from->sptps, buf, len);
+                       return true;
+               }
+
                default:
                        logger(DEBUG_ALWAYS, LOG_ERR, "Unknown extended REQ_KEY request from %s (%s): %s", from->name, from->hostname, request);
                        return true;
@@ -181,31 +234,9 @@ bool req_key_h(connection_t *c, const char *request) {
        return true;
 }
 
-bool send_ans_key_ecdh(node_t *to) {
-       int siglen = ecdsa_size(&myself->connection->ecdsa);
-       char key[(ECDH_SIZE + siglen) * 2 + 1];
-
-       if(!ecdh_generate_public(&to->ecdh, key))
-               return false;
-
-       if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE))
-               return false;
-
-       b64encode(key, key, ECDH_SIZE + siglen);
-
-       int result = send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
-                                               myself->name, to->name, key,
-                                               cipher_get_nid(&myself->incipher),
-                                               digest_get_nid(&myself->indigest),
-                                               (int)digest_length(&myself->indigest),
-                                               myself->incompression);
-
-       return result;
-}
-
 bool send_ans_key(node_t *to) {
        if(experimental && OPTION_VERSION(to->options) >= 2)
-               return send_ans_key_ecdh(to);
+               abort();
 
        size_t keylen = cipher_keylength(&myself->incipher);
        char key[keylen * 2 + 1];
@@ -296,6 +327,29 @@ bool ans_key_h(connection_t *c, const char *request) {
                return send_request(to->nexthop->connection, "%s", request);
        }
 
+       /* SPTPS or old-style key exchange? */
+
+       if(experimental && OPTION_VERSION(from->options) >= 2) {
+               char buf[strlen(key)];
+               int len = b64decode(key, buf, strlen(key));
+
+               if(!sptps_receive_data(&from->sptps, buf, len))
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Error processing SPTPS data from %s (%s)", from->name, from->hostname);
+
+               if(from->status.validkey) {
+                       if(*address && *port) {
+                               logger(DEBUG_PROTOCOL, LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
+                               sockaddr_t sa = str2sockaddr(address, port);
+                               update_node_udp(from, &sa);
+                       }
+
+                       if(from->options & OPTION_PMTU_DISCOVERY)
+                               send_mtu_probe(from);
+               }
+
+               return true;
+       }
+
        /* Check and lookup cipher and digest algorithms */
 
        if(!cipher_open_by_nid(&from->outcipher, cipher)) {
@@ -320,99 +374,19 @@ bool ans_key_h(connection_t *c, const char *request) {
 
        from->outcompression = compression;
 
-       /* ECDH or old-style key exchange? */
-       
-       if(experimental && OPTION_VERSION(from->options) >= 2) {
-               /* Check if we already have an ECDSA public key for this node. */
-
-               if(!node_read_ecdsa_public_key(from)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "No ECDSA public key known for %s (%s), cannot verify ECDH key exchange!", from->name, from->hostname);
-                       return true;
-               }
-
-               int siglen = ecdsa_size(&from->ecdsa);
-               int keylen = b64decode(key, key, sizeof key);
-
-               if(keylen != ECDH_SIZE + siglen) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses wrong keylength! %d != %d", from->name, from->hostname, keylen, ECDH_SIZE + siglen);
-                       return true;
-               }
-
-               if(ECDH_SHARED_SIZE < cipher_keylength(&from->outcipher)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "ECDH key too short for cipher of %s!", from->name);
-                       return true;
-               }
+       /* Process key */
 
-               if(!ecdsa_verify(&from->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", from->name, from->hostname, "invalid ECDSA signature");
-                       return true;
-               }
-
-               if(!from->ecdh) {
-                       if(!send_ans_key_ecdh(from))
-                               return true;
-               }
+       keylen = hex2bin(key, key, sizeof key);
 
-               char shared[ECDH_SHARED_SIZE * 2 + 1];
-
-               if(!ecdh_compute_shared(&from->ecdh, key, shared))
-                       return true;
-
-               /* Update our crypto end */
-
-               size_t mykeylen = cipher_keylength(&myself->incipher);
-               size_t hiskeylen = cipher_keylength(&from->outcipher);
-
-               char *mykey;
-               char *hiskey;
-               char *seed;
-               
-               if(strcmp(myself->name, from->name) < 0) {
-                       mykey = key;
-                       hiskey = key + mykeylen * 2;
-                       xasprintf(&seed, "tinc UDP key expansion %s %s", myself->name, from->name);
-               } else {
-                       mykey = key + hiskeylen * 2;
-                       hiskey = key;
-                       xasprintf(&seed, "tinc UDP key expansion %s %s", from->name, myself->name);
-               }
-
-               if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
-                       return true;
-
-               free(seed);
-
-               cipher_open_by_nid(&from->incipher, cipher_get_nid(&myself->incipher));
-               digest_open_by_nid(&from->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
-               from->incompression = myself->incompression;
-
-               cipher_set_key(&from->incipher, mykey, false);
-               digest_set_key(&from->indigest, mykey + mykeylen, mykeylen);
-
-               cipher_set_key(&from->outcipher, hiskey, true);
-               digest_set_key(&from->outdigest, hiskey + hiskeylen, hiskeylen);
-
-               // Reset sequence number and late packet window
-               mykeyused = true;
-               from->received_seqno = 0;
-               if(replaywin)
-                       memset(from->late, 0, replaywin);
-
-               if(strcmp(myself->name, from->name) < 0)
-                       memmove(key, key + mykeylen * 2, hiskeylen * 2);
-       } else {
-               keylen = hex2bin(key, key, sizeof key);
-
-               if(keylen != cipher_keylength(&from->outcipher)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
-                       return true;
-               }
+       if(keylen != cipher_keylength(&from->outcipher)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
+               return true;
+       }
 
-               /* Update our copy of the origin's packet key */
+       /* Update our copy of the origin's packet key */
 
-               cipher_set_key(&from->outcipher, key, true);
-               digest_set_key(&from->outdigest, key, keylen);
-       }
+       cipher_set_key(&from->outcipher, key, true);
+       digest_set_key(&from->outdigest, key, keylen);
 
        from->status.validkey = true;
        from->sent_seqno = 0;
index ff7c4168e0410b3564ceb32e74fce0da780b4f4a..422940c9df26d0bc864d6f880ca426c119059e80 100644 (file)
@@ -78,10 +78,10 @@ static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data
                if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
                        return false;
 
-               return s->send_data(s->handle, buffer + 2, len + 21UL);
+               return s->send_data(s->handle, type, buffer + 2, len + 21UL);
        } else {
                // Otherwise send as plaintext
-               return s->send_data(s->handle, buffer + 2, len + 5UL);
+               return s->send_data(s->handle, type, buffer + 2, len + 5UL);
        }
 }
 // Send a record (private version, accepts all record types, handles encryption and authentication).
@@ -110,10 +110,10 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_
                if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
                        return false;
 
-               return s->send_data(s->handle, buffer + 4, len + 19UL);
+               return s->send_data(s->handle, type, buffer + 4, len + 19UL);
        } else {
                // Otherwise send as plaintext
-               return s->send_data(s->handle, buffer + 4, len + 3UL);
+               return s->send_data(s->handle, type, buffer + 4, len + 3UL);
        }
 }
 
@@ -438,6 +438,9 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
                        return error(s, EIO, "Application record received before handshake finished");
                if(!s->receive_record(s->handle, type, buffer + 7, len - 21))
                        return false;
+       } else if(type == SPTPS_HANDSHAKE) {
+               if(!receive_handshake(s, buffer + 7, len - 21))
+                       return false;
        } else {
                return error(s, EIO, "Invalid record type");
        }
index 3854ec24a6eedaf878e9e56eaac27aaaa063bb24..d8ce3dae8d2372def8ad6bce5924a3e82578dd75 100644 (file)
@@ -40,7 +40,7 @@
 #define SPTPS_SIG 2           // Waiting for a SIGnature record
 #define SPTPS_ACK 3           // Waiting for an ACKnowledgement record
 
-typedef bool (*send_data_t)(void *handle, const char *data, size_t len);
+typedef bool (*send_data_t)(void *handle, uint8_t type, const char *data, size_t len);
 typedef bool (*receive_record_t)(void *handle, uint8_t type, const char *data, uint16_t len);
 
 typedef struct sptps {
index 30cc33c087157e6ae1cf559fb43ebef6a5d7ccaa..2999d27fcf45b3fe2ec7a3032567a0576cf1e859 100644 (file)
@@ -32,7 +32,7 @@ char *send_meta;
 
 ecdsa_t mykey, hiskey;
 
-static bool send_data(void *handle, const char *data, size_t len) {
+static bool send_data(void *handle, uint8_t type, const char *data, size_t len) {
        char hex[len * 2 + 1];
        bin2hex(data, hex, len);
        fprintf(stderr, "Sending %d bytes of data:\n%s\n", (int)len, hex);