Use void pointers for opaque data blobs in the SPTPS code.
[tinc] / src / net_packet.c
index a1b174e..7ca1894 100644 (file)
@@ -1,7 +1,7 @@
 /*
     net_packet.c -- Handles in- and outgoing VPN packets
     Copyright (C) 1998-2005 Ivo Timmermans,
-                  2000-2013 Guus Sliepen <guus@tinc-vpn.org>
+                  2000-2014 Guus Sliepen <guus@tinc-vpn.org>
                   2010      Timothy Redaelli <timothy@redaelli.eu>
                   2010      Brandon Black <blblack@gmail.com>
 
@@ -348,12 +348,12 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) {
 
 static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
        if(n->status.sptps)
-               return sptps_verify_datagram(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
+               return sptps_verify_datagram(&n->sptps, ((sptps_packet_t *)inpkt)->data, inpkt->len);
 
        if(!digest_active(n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(n->indigest))
                return false;
 
-       return digest_verify(n->indigest, &inpkt->seqno, inpkt->len - digest_length(n->indigest), (const char *)&inpkt->seqno + inpkt->len - digest_length(n->indigest));
+       return digest_verify(n->indigest, (const char *)&inpkt->seqno, inpkt->len - digest_length(n->indigest), (const char *)&inpkt->seqno + inpkt->len - digest_length(n->indigest));
 }
 
 static bool receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
@@ -372,7 +372,11 @@ static bool receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
                        }
                        return false;
                }
-               return sptps_receive_data(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
+               if(!sptps_receive_data(&n->sptps, ((sptps_packet_t *)inpkt)->data, inpkt->len)) {
+                       logger(DEBUG_TRAFFIC, LOG_ERR, "Got bad packet from %s (%s)", n->name, n->hostname);
+                       return false;
+               }
+               return true;
        }
 
        if(!n->status.validkey) {
@@ -415,9 +419,7 @@ static bool receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        /* Check the sequence number */
 
        inpkt->len -= sizeof inpkt->seqno;
-       uint32_t seqno;
-       memcpy(&seqno, inpkt->seqno, sizeof seqno);
-       seqno = ntohl(seqno);
+       uint32_t seqno = ntohl(inpkt->seqno);
 
        if(replaywin) {
                if(seqno != n->received_seqno + 1) {
@@ -497,19 +499,27 @@ void receive_tcppacket(connection_t *c, const char *buffer, int len) {
        receive_packet(c->node, &outpkt);
 }
 
+static bool try_sptps(node_t *n) {
+       if(n->status.validkey)
+               return true;
+
+       logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
+
+       if(!n->status.waitingforkey)
+               send_req_key(n);
+       else if(n->last_req_key + 10 < now.tv_sec) {
+               logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
+               sptps_stop(&n->sptps);
+               n->status.waitingforkey = false;
+               send_req_key(n);
+       }
+
+       return false;
+}
+
 static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
-       if(!n->status.validkey) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
-               if(!n->status.waitingforkey)
-                       send_req_key(n);
-               else if(n->last_req_key + 10 < now.tv_sec) {
-                       logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
-                       sptps_stop(&n->sptps);
-                       n->status.waitingforkey = false;
-                       send_req_key(n);
-               }
+       if (!try_sptps(n))
                return;
-       }
 
        uint8_t type = 0;
        int offset = 0;
@@ -687,8 +697,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
 
        /* Add sequence number */
 
-       uint32_t seqno = htonl(++(n->sent_seqno));
-       memcpy(inpkt->seqno, &seqno, sizeof inpkt->seqno);
+       inpkt->seqno = htonl(++(n->sent_seqno));
        inpkt->len += sizeof inpkt->seqno;
 
        /* Encrypt the packet */
@@ -697,7 +706,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
                outpkt = pkt[nextpkt++];
                outlen = MAXSIZE;
 
-               if(!cipher_encrypt(n->outcipher, inpkt->seqno, inpkt->len, outpkt->seqno, &outlen, true)) {
+               if(!cipher_encrypt(n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
                        logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
                        goto end;
                }
@@ -709,7 +718,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        /* Add the message authentication code */
 
        if(digest_active(n->outdigest)) {
-               if(!digest_create(n->outdigest, inpkt->seqno, inpkt->len, inpkt->seqno + inpkt->len)) {
+               if(!digest_create(n->outdigest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) {
                        logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
                        goto end;
                }
@@ -737,7 +746,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        }
 #endif
 
-       if(sendto(listen_socket[sock].udp.fd, inpkt->seqno, inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
+       if(sendto(listen_socket[sock].udp.fd, &inpkt->seqno, inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
                if(sockmsgsize(sockerrno)) {
                        if(n->maxmtu >= origlen)
                                n->maxmtu = origlen - 1;
@@ -751,16 +760,21 @@ end:
        origpkt->len = origlen;
 }
 
-static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const char *data, size_t len) {
+static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void *data, size_t len) {
        node_t *relay = (to->via != myself && (type == PKT_PROBE || (len - SPTPS_DATAGRAM_OVERHEAD) <= to->via->minmtu)) ? to->via : to->nexthop;
        bool direct = from == myself && to == relay;
        bool relay_supported = (relay->options >> 24) >= 4;
+       bool tcponly = (myself->options | relay->options) & OPTION_TCPONLY;
+
+       /* We don't really need the relay's key, but we need to establish a UDP tunnel with it and discover its MTU. */
+       if (!direct && relay_supported && !tcponly)
+               try_sptps(relay);
 
        /* Send it via TCP if it is a handshake packet, TCPOnly is in use, this is a relay packet that the other node cannot understand, or this packet is larger than the MTU.
           TODO: When relaying, the original sender does not know the end-to-end PMTU (it only knows the PMTU of the first hop).
                 This can lead to scenarios where large packets are sent over UDP to relay, but then relay has no choice but fall back to TCP. */
 
-       if(type == SPTPS_HANDSHAKE || ((myself->options | relay->options) & OPTION_TCPONLY) || (!direct && !relay_supported) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > relay->minmtu)) {
+       if(type == SPTPS_HANDSHAKE || tcponly || (!direct && !relay_supported) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > relay->minmtu)) {
                char buf[len * 4 / 3 + 5];
                b64encode(data, buf, len);
                /* If no valid key is known yet, send the packets using ANS_KEY requests,
@@ -779,7 +793,7 @@ static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const char
        if(relay_supported) {
                if(direct) {
                        /* Inform the recipient that this packet was sent directly. */
-                       node_id_t nullid = {0};
+                       node_id_t nullid = {};
                        memcpy(buf_ptr, &nullid, sizeof nullid); buf_ptr += sizeof nullid;
                } else {
                        memcpy(buf_ptr, &to->id, sizeof to->id); buf_ptr += sizeof to->id;
@@ -1014,14 +1028,15 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
 void handle_incoming_vpn_data(void *data, int flags) {
        listen_socket_t *ls = data;
        vpn_packet_t pkt;
+       sptps_packet_t *spkt = (sptps_packet_t *)&pkt;
        char *hostname;
-       sockaddr_t from = {{0}};
-       socklen_t fromlen = sizeof from;
-       node_t *n = NULL;
-       node_t *to = myself;
-       int len;
+       node_id_t nullid = {};
+       sockaddr_t addr = {};
+       socklen_t addrlen = sizeof addr;
+       node_t *from, *to;
+       bool direct = false;
 
-       len = recvfrom(ls->udp.fd, &pkt.dstid, MAXSIZE, 0, &from.sa, &fromlen);
+       int len = recvfrom(ls->udp.fd, &pkt.seqno, MAXSIZE, 0, &addr.sa, &addrlen);
 
        if(len <= 0 || len > MAXSIZE) {
                if(!sockwouldblock(sockerrno))
@@ -1031,65 +1046,66 @@ void handle_incoming_vpn_data(void *data, int flags) {
 
        pkt.len = len;
 
-       sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
+       sockaddrunmap(&addr); /* Some braindead IPv6 implementations do stupid things. */
 
-       bool direct = false;
-       if(len >= sizeof pkt.dstid + sizeof pkt.srcid) {
-               n = lookup_node_id(&pkt.srcid);
-               if(n) {
-                       node_id_t nullid = {0};
-                       if(memcmp(&pkt.dstid, &nullid, sizeof nullid) == 0) {
-                               /* A zero dstid is used to indicate a direct, non-relayed packet. */
-                               direct = true;
-                       } else {
-                               to = lookup_node_id(&pkt.dstid);
-                               if(!to) {
-                                       logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet presumably sent by %s (%s) but with unknown destination ID", n->name, n->hostname);
-                                       return;
-                               }
-                       }
-                       pkt.len -= sizeof pkt.dstid + sizeof pkt.srcid;
-               }
-       }
-
-       if(to != myself) {
-               /* We are being asked to relay this packet. */
+       // Try to figure out who sent this packet.
 
-               /* Don't allow random strangers to relay through us. Note that we check for *any* known address since we are not necessarily the first relay. */
-               if (!lookup_node_udp(&from)) {
-                       logger(DEBUG_PROTOCOL, LOG_WARNING, "Refusing to relay packet from (presumably) %s (%s) to (presumably) %s (%s) because the packet comes from an unknown address", n->name, n->hostname, to->name, to->hostname);
-                       return;
-               }
-
-               send_sptps_data_priv(to, n, 0, pkt.seqno, pkt.len);
-               return;
-       }
+       node_t *n = lookup_node_udp(&addr);
 
        if(!n) {
-               /* Most likely an old-style packet without node IDs. */
-               direct = true;
-               memmove(pkt.seqno, &pkt.dstid, sizeof pkt - offsetof(vpn_packet_t, seqno));
-               n = lookup_node_udp(&from);
+               // It might be from a 1.1 node, which might have a source ID in the packet.
+               from = lookup_node_id(&spkt->srcid);
+               if(from && !memcmp(&spkt->dstid, &nullid, sizeof nullid) && from->status.sptps) {
+                       if(sptps_verify_datagram(&from->sptps, spkt->data, spkt->len - sizeof(spkt->srcid) - sizeof(spkt->dstid)))
+                               n = from;
+                       else
+                               goto skip_harder;
+               }
        }
 
        if(!n)
-               n = try_harder(&from, &pkt);
+               n = try_harder(&addr, &pkt);
 
+skip_harder:
        if(!n) {
                if(debug_level >= DEBUG_PROTOCOL) {
-                       hostname = sockaddr2hostname(&from);
+                       hostname = sockaddr2hostname(&addr);
                        logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
                        free(hostname);
                }
                return;
        }
 
-       if(!receive_udppacket(n, &pkt))
+       if(n->status.sptps) {
+               if(!memcmp(&spkt->dstid, &nullid, sizeof nullid)) {
+                       direct = true;
+                       from = n;
+                       to = myself;
+               } else {
+                       from = lookup_node_id(&spkt->srcid);
+                       to = lookup_node_id(&spkt->dstid);
+               }
+               if(!from || !to) {
+                       logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname);
+                       return;
+               }
+
+               spkt->len -= sizeof spkt->dstid + sizeof spkt->srcid;
+               if(to != myself) {
+                       send_sptps_data_priv(to, n, 0, spkt->data, spkt->len);
+                       return;
+               }
+       } else {
+               direct = true;
+               from = n;
+       }
+
+       if(!receive_udppacket(from, &pkt))
                return;
 
        n->sock = ls - listen_socket;
-       if(direct && sockaddrcmp(&from, &n->address))
-               update_node_udp(n, &from);
+       if(direct && sockaddrcmp(&addr, &n->address))
+               update_node_udp(n, &addr);
 }
 
 void handle_device_data(void *data, int flags) {