2000-09-26 14:06:11 +00:00
|
|
|
/*
|
|
|
|
meta.c -- handle the meta communication
|
2014-12-24 21:15:40 +00:00
|
|
|
Copyright (C) 2000-2014 Guus Sliepen <guus@tinc-vpn.org>,
|
2006-04-26 13:52:58 +00:00
|
|
|
2000-2005 Ivo Timmermans
|
2009-09-25 19:14:56 +00:00
|
|
|
2006 Scott Lamb <slamb@slamb.org>
|
2000-09-26 14:06:11 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
2009-09-24 22:01:00 +00:00
|
|
|
You should have received a copy of the GNU General Public License along
|
|
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2000-09-26 14:06:11 +00:00
|
|
|
*/
|
|
|
|
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "system.h"
|
2000-10-11 10:35:17 +00:00
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
#include "cipher.h"
|
2000-11-20 19:12:17 +00:00
|
|
|
#include "connection.h"
|
2003-07-06 22:11:37 +00:00
|
|
|
#include "logger.h"
|
2003-08-12 14:48:13 +00:00
|
|
|
#include "meta.h"
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "net.h"
|
|
|
|
#include "protocol.h"
|
|
|
|
#include "utils.h"
|
2006-03-19 12:43:28 +00:00
|
|
|
#include "xalloc.h"
|
2000-10-11 10:35:17 +00:00
|
|
|
|
2015-05-17 21:21:11 +00:00
|
|
|
#ifndef MIN
|
|
|
|
#define MIN(x, y) (((x)<(y))?(x):(y))
|
|
|
|
#endif
|
|
|
|
|
2014-12-24 21:15:40 +00:00
|
|
|
bool send_meta_sptps(void *handle, uint8_t type, const void *buffer, size_t length) {
|
2012-02-25 17:25:21 +00:00
|
|
|
connection_t *c = handle;
|
|
|
|
|
|
|
|
if(!c) {
|
2015-06-28 22:45:00 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "send_meta_sptps() called with NULL pointer! [type: %d]", type);
|
2012-02-25 17:25:21 +00:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer_add(&c->outbuf, buffer, length);
|
2012-11-29 11:28:23 +00:00
|
|
|
io_set(&c->io, IO_READ | IO_WRITE);
|
2012-02-25 17:25:21 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-29 17:42:23 +00:00
|
|
|
bool send_meta(connection_t *c, const char *buffer, size_t length) {
|
2009-06-11 16:36:08 +00:00
|
|
|
if(!c) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "send_meta() called with NULL pointer!");
|
2009-06-11 16:36:08 +00:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2015-06-29 17:42:23 +00:00
|
|
|
logger(DEBUG_META, LOG_DEBUG, "Sending %zu bytes of metadata to %s (%s)", length,
|
2007-11-07 02:47:05 +00:00
|
|
|
c->name, c->hostname);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2015-06-29 17:42:23 +00:00
|
|
|
if(c->protocol_minor >= 2) {
|
|
|
|
if (length > UINT16_MAX) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "send_meta() aborting due to length %zu >= %d!", length, UINT16_MAX);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
return sptps_send_record(&c->sptps, 0, buffer, (uint16_t)length);
|
|
|
|
}
|
2012-02-25 17:25:21 +00:00
|
|
|
|
2006-01-19 17:13:18 +00:00
|
|
|
/* Add our data to buffer */
|
2002-09-09 21:25:28 +00:00
|
|
|
if(c->status.encryptout) {
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifdef DISABLE_LEGACY
|
|
|
|
return false;
|
|
|
|
#else
|
2008-12-11 14:44:44 +00:00
|
|
|
size_t outlen = length;
|
2007-05-19 22:23:02 +00:00
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
if(!cipher_encrypt(c->outcipher, buffer, length, buffer_prepare(&c->outbuf, length), &outlen, false) || outlen != length) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting metadata to %s (%s)",
|
2008-12-11 14:44:44 +00:00
|
|
|
c->name, c->hostname);
|
2003-10-10 16:24:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2006-01-19 17:13:18 +00:00
|
|
|
} else {
|
2011-05-14 17:20:56 +00:00
|
|
|
buffer_add(&c->outbuf, buffer, length);
|
2006-01-19 17:13:18 +00:00
|
|
|
}
|
|
|
|
|
2012-11-29 11:28:23 +00:00
|
|
|
io_set(&c->io, IO_READ | IO_WRITE);
|
2011-05-14 17:20:56 +00:00
|
|
|
|
2006-01-19 17:13:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2015-06-29 17:42:23 +00:00
|
|
|
void send_meta_raw(connection_t *c, const char *buffer, size_t length) {
|
Introduce raw TCP SPTPS packet transport.
Currently, SPTPS packets are transported over TCP metaconnections using
extended REQ_KEY requests, in order for the packets to pass through
tinc-1.0 nodes unaltered. Unfortunately, this method presents two
significant downsides:
- An already encrypted SPTPS packet is decrypted and then encrypted
again every time it passes through a node, since it is transported
over the SPTPS channels of the metaconnections. This
double-encryption is unnecessary and wastes CPU cycles.
- More importantly, the only way to transport binary data over
standard metaconnection messages such as REQ_KEY is to encode it
in base64, which has a 33% encoding overhead. This wastes 25% of the
network bandwidth.
This commit introduces a new protocol message, SPTPS_PACKET, which can
be used to transport SPTPS packets over a TCP metaconnection in an
efficient way. The new message is appropriately protected through a
minor protocol version increment, and extended REQ_KEY messages are
still used with nodes that do not support the new message, as well as
for the intial handshake packets, for which efficiency is not a concern.
The way SPTPS_PACKET works is very similar to how the traditional PACKET
message works: after the SPTPS_PACKET message, the raw binary packet is
sent directly over the metaconnection. There is one important
difference, however: in the case of SPTPS_PACKET, the packet is sent
directly over the TCP stream completely bypassing the SPTPS channel of
the metaconnection itself for maximum efficiency. This is secure because
the SPTPS packet that is being sent is already encrypted with an
end-to-end key.
2015-05-10 18:00:03 +00:00
|
|
|
if(!c) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "send_meta() called with NULL pointer!");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2015-06-29 17:42:23 +00:00
|
|
|
logger(DEBUG_META, LOG_DEBUG, "Sending %zu bytes of raw metadata to %s (%s)", length,
|
Introduce raw TCP SPTPS packet transport.
Currently, SPTPS packets are transported over TCP metaconnections using
extended REQ_KEY requests, in order for the packets to pass through
tinc-1.0 nodes unaltered. Unfortunately, this method presents two
significant downsides:
- An already encrypted SPTPS packet is decrypted and then encrypted
again every time it passes through a node, since it is transported
over the SPTPS channels of the metaconnections. This
double-encryption is unnecessary and wastes CPU cycles.
- More importantly, the only way to transport binary data over
standard metaconnection messages such as REQ_KEY is to encode it
in base64, which has a 33% encoding overhead. This wastes 25% of the
network bandwidth.
This commit introduces a new protocol message, SPTPS_PACKET, which can
be used to transport SPTPS packets over a TCP metaconnection in an
efficient way. The new message is appropriately protected through a
minor protocol version increment, and extended REQ_KEY messages are
still used with nodes that do not support the new message, as well as
for the intial handshake packets, for which efficiency is not a concern.
The way SPTPS_PACKET works is very similar to how the traditional PACKET
message works: after the SPTPS_PACKET message, the raw binary packet is
sent directly over the metaconnection. There is one important
difference, however: in the case of SPTPS_PACKET, the packet is sent
directly over the TCP stream completely bypassing the SPTPS channel of
the metaconnection itself for maximum efficiency. This is secure because
the SPTPS packet that is being sent is already encrypted with an
end-to-end key.
2015-05-10 18:00:03 +00:00
|
|
|
c->name, c->hostname);
|
|
|
|
|
|
|
|
buffer_add(&c->outbuf, buffer, length);
|
|
|
|
|
|
|
|
io_set(&c->io, IO_READ | IO_WRITE);
|
|
|
|
}
|
|
|
|
|
2015-06-29 17:42:23 +00:00
|
|
|
void broadcast_meta(connection_t *from, const char *buffer, size_t length) {
|
2012-10-07 22:35:38 +00:00
|
|
|
for list_each(connection_t, c, connection_list)
|
2014-07-12 10:57:03 +00:00
|
|
|
if(c != from && c->edge)
|
2002-09-09 21:25:28 +00:00
|
|
|
send_meta(c, buffer, length);
|
2000-09-26 14:06:11 +00:00
|
|
|
}
|
|
|
|
|
2014-12-24 21:15:40 +00:00
|
|
|
bool receive_meta_sptps(void *handle, uint8_t type, const void *vdata, uint16_t length) {
|
|
|
|
const char *data = vdata;
|
2012-02-25 17:25:21 +00:00
|
|
|
connection_t *c = handle;
|
|
|
|
|
|
|
|
if(!c) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "receive_meta_sptps() called with NULL pointer!");
|
2012-02-25 17:25:21 +00:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(type == SPTPS_HANDSHAKE) {
|
|
|
|
if(c->allow_request == ACK)
|
|
|
|
return send_ack(c);
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!data)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Are we receiving a TCPpacket? */
|
|
|
|
|
|
|
|
if(c->tcplen) {
|
|
|
|
if(length != c->tcplen)
|
|
|
|
return false;
|
|
|
|
receive_tcppacket(c, data, length);
|
|
|
|
c->tcplen = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-11 20:47:13 +00:00
|
|
|
/* Change newline to null byte, just like non-SPTPS requests */
|
|
|
|
|
|
|
|
if(data[length - 1] == '\n')
|
|
|
|
((char *)data)[length - 1] = 0;
|
|
|
|
|
2012-02-25 17:25:21 +00:00
|
|
|
/* Otherwise we are waiting for a request */
|
|
|
|
|
|
|
|
return receive_request(c, data);
|
|
|
|
}
|
|
|
|
|
2007-05-18 10:00:00 +00:00
|
|
|
bool receive_meta(connection_t *c) {
|
2015-06-29 13:53:22 +00:00
|
|
|
ssize_t inlen;
|
2002-09-09 21:25:28 +00:00
|
|
|
char inbuf[MAXBUFSIZE];
|
2007-05-19 22:23:02 +00:00
|
|
|
char *bufp = inbuf, *endp;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
|
|
|
/* Strategy:
|
|
|
|
- Read as much as possible from the TCP socket in one go.
|
|
|
|
- Decrypt it.
|
|
|
|
- Check if a full request is in the input buffer.
|
|
|
|
- If yes, process request and remove it from the buffer,
|
|
|
|
then check again.
|
|
|
|
- If not, keep stuff in buffer and exit.
|
|
|
|
*/
|
|
|
|
|
2011-05-22 11:15:05 +00:00
|
|
|
buffer_compact(&c->inbuf, MAXBUFSIZE);
|
2011-05-22 10:56:51 +00:00
|
|
|
|
|
|
|
if(sizeof inbuf <= c->inbuf.len) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Input buffer full for %s (%s)", c->name, c->hostname);
|
2011-05-22 10:56:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-05-14 17:20:56 +00:00
|
|
|
inlen = recv(c->socket, inbuf, sizeof inbuf - c->inbuf.len, 0);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2007-05-18 11:19:31 +00:00
|
|
|
if(inlen <= 0) {
|
2014-06-26 19:42:40 +00:00
|
|
|
if(!inlen || !sockerrno) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)",
|
2002-09-09 21:25:28 +00:00
|
|
|
c->name, c->hostname);
|
2009-10-24 23:40:07 +00:00
|
|
|
} else if(sockwouldblock(sockerrno))
|
2003-07-22 20:55:21 +00:00
|
|
|
return true;
|
2002-09-09 21:25:28 +00:00
|
|
|
else
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Metadata socket read error for %s (%s): %s",
|
2009-10-24 23:40:07 +00:00
|
|
|
c->name, c->hostname, sockstrerror(sockerrno));
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2007-05-19 22:23:02 +00:00
|
|
|
do {
|
Introduce raw TCP SPTPS packet transport.
Currently, SPTPS packets are transported over TCP metaconnections using
extended REQ_KEY requests, in order for the packets to pass through
tinc-1.0 nodes unaltered. Unfortunately, this method presents two
significant downsides:
- An already encrypted SPTPS packet is decrypted and then encrypted
again every time it passes through a node, since it is transported
over the SPTPS channels of the metaconnections. This
double-encryption is unnecessary and wastes CPU cycles.
- More importantly, the only way to transport binary data over
standard metaconnection messages such as REQ_KEY is to encode it
in base64, which has a 33% encoding overhead. This wastes 25% of the
network bandwidth.
This commit introduces a new protocol message, SPTPS_PACKET, which can
be used to transport SPTPS packets over a TCP metaconnection in an
efficient way. The new message is appropriately protected through a
minor protocol version increment, and extended REQ_KEY messages are
still used with nodes that do not support the new message, as well as
for the intial handshake packets, for which efficiency is not a concern.
The way SPTPS_PACKET works is very similar to how the traditional PACKET
message works: after the SPTPS_PACKET message, the raw binary packet is
sent directly over the metaconnection. There is one important
difference, however: in the case of SPTPS_PACKET, the packet is sent
directly over the TCP stream completely bypassing the SPTPS channel of
the metaconnection itself for maximum efficiency. This is secure because
the SPTPS packet that is being sent is already encrypted with an
end-to-end key.
2015-05-10 18:00:03 +00:00
|
|
|
/* Are we receiving a SPTPS packet? */
|
|
|
|
|
|
|
|
if(c->sptpslen) {
|
|
|
|
int len = MIN(inlen, c->sptpslen - c->inbuf.len);
|
|
|
|
buffer_add(&c->inbuf, bufp, len);
|
|
|
|
|
|
|
|
char *sptpspacket = buffer_read(&c->inbuf, c->sptpslen);
|
|
|
|
if(!sptpspacket)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if(!receive_tcppacket_sptps(c, sptpspacket, c->sptpslen))
|
|
|
|
return false;
|
|
|
|
c->sptpslen = 0;
|
|
|
|
|
|
|
|
bufp += len;
|
|
|
|
inlen -= len;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-05-10 18:28:11 +00:00
|
|
|
if(c->protocol_minor >= 2) {
|
|
|
|
int len = sptps_receive_data(&c->sptps, bufp, inlen);
|
|
|
|
if(!len)
|
|
|
|
return false;
|
|
|
|
bufp += len;
|
|
|
|
inlen -= len;
|
|
|
|
continue;
|
|
|
|
}
|
2012-02-25 17:25:21 +00:00
|
|
|
|
2007-05-19 22:23:02 +00:00
|
|
|
if(!c->status.decryptin) {
|
|
|
|
endp = memchr(bufp, '\n', inlen);
|
|
|
|
if(endp)
|
|
|
|
endp++;
|
|
|
|
else
|
|
|
|
endp = bufp + inlen;
|
|
|
|
|
2011-05-14 17:20:56 +00:00
|
|
|
buffer_add(&c->inbuf, bufp, endp - bufp);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2007-05-19 22:23:02 +00:00
|
|
|
inlen -= endp - bufp;
|
|
|
|
bufp = endp;
|
|
|
|
} else {
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifdef DISABLE_LEGACY
|
|
|
|
return false;
|
|
|
|
#else
|
2008-12-11 14:44:44 +00:00
|
|
|
size_t outlen = inlen;
|
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
if(!cipher_decrypt(c->incipher, bufp, inlen, buffer_prepare(&c->inbuf, inlen), &outlen, false) || inlen != outlen) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting metadata from %s (%s)",
|
2008-12-11 14:44:44 +00:00
|
|
|
c->name, c->hostname);
|
2003-10-10 16:24:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
2007-11-07 02:47:05 +00:00
|
|
|
|
2007-05-19 22:23:02 +00:00
|
|
|
inlen = 0;
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-05-14 17:20:56 +00:00
|
|
|
while(c->inbuf.len) {
|
2007-05-19 22:23:02 +00:00
|
|
|
/* Are we receiving a TCPpacket? */
|
|
|
|
|
|
|
|
if(c->tcplen) {
|
2011-05-14 17:20:56 +00:00
|
|
|
char *tcpbuffer = buffer_read(&c->inbuf, c->tcplen);
|
2013-02-06 14:24:02 +00:00
|
|
|
if(!tcpbuffer)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if(!c->node) {
|
2013-02-07 13:22:28 +00:00
|
|
|
if(c->outgoing && proxytype == PROXY_SOCKS4 && c->allow_request == ID) {
|
2012-06-26 11:24:20 +00:00
|
|
|
if(tcpbuffer[0] == 0 && tcpbuffer[1] == 0x5a) {
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
|
|
|
|
} else {
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy request rejected");
|
|
|
|
return false;
|
|
|
|
}
|
2013-02-07 13:22:28 +00:00
|
|
|
} else if(c->outgoing && proxytype == PROXY_SOCKS5 && c->allow_request == ID) {
|
2013-02-06 14:24:02 +00:00
|
|
|
if(tcpbuffer[0] != 5) {
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_ERR, "Invalid response from proxy server");
|
|
|
|
return false;
|
|
|
|
}
|
2013-02-20 14:35:08 +00:00
|
|
|
if(tcpbuffer[1] == (char)0xff) {
|
2013-02-06 14:24:02 +00:00
|
|
|
logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy request rejected: unsuitable authentication method");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(tcpbuffer[2] != 5) {
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_ERR, "Invalid response from proxy server");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(tcpbuffer[3] == 0) {
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
|
|
|
|
} else {
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request rejected");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_ERR, "c->tcplen set but c->node is NULL!");
|
|
|
|
abort();
|
|
|
|
}
|
2007-05-19 22:23:02 +00:00
|
|
|
} else {
|
2013-02-06 14:24:02 +00:00
|
|
|
if(c->allow_request == ALL) {
|
|
|
|
receive_tcppacket(c, tcpbuffer, c->tcplen);
|
|
|
|
} else {
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_ERR, "Got unauthorized TCP packet from %s (%s)", c->name, c->hostname);
|
|
|
|
return false;
|
|
|
|
}
|
2007-05-19 22:23:02 +00:00
|
|
|
}
|
2013-02-06 14:24:02 +00:00
|
|
|
|
|
|
|
c->tcplen = 0;
|
2007-05-19 22:23:02 +00:00
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2007-05-19 22:23:02 +00:00
|
|
|
/* Otherwise we are waiting for a request */
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2011-05-14 17:20:56 +00:00
|
|
|
char *request = buffer_readline(&c->inbuf);
|
2007-05-19 22:23:02 +00:00
|
|
|
if(request) {
|
2015-06-27 22:39:57 +00:00
|
|
|
bool result = false;
|
|
|
|
result = receive_request(c, request);
|
2008-12-11 14:44:44 +00:00
|
|
|
if(!result)
|
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-05-19 22:23:02 +00:00
|
|
|
} while(inlen);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2003-07-22 20:55:21 +00:00
|
|
|
return true;
|
2000-09-26 14:06:11 +00:00
|
|
|
}
|