Add UDP_INFO protocol message.
[tinc] / src / protocol_misc.c
1 /*
2     protocol_misc.c -- handle the meta-protocol, miscellaneous functions
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2013 Guus Sliepen <guus@tinc-vpn.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "system.h"
22
23 #include "conf.h"
24 #include "connection.h"
25 #include "logger.h"
26 #include "meta.h"
27 #include "net.h"
28 #include "netutl.h"
29 #include "protocol.h"
30 #include "utils.h"
31 #include "xalloc.h"
32
33 int maxoutbufsize = 0;
34
35 /* Status and error notification routines */
36
37 bool send_status(connection_t *c, int statusno, const char *statusstring) {
38         if(!statusstring)
39                 statusstring = "Status";
40
41         return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
42 }
43
44 bool status_h(connection_t *c, const char *request) {
45         int statusno;
46         char statusstring[MAX_STRING_SIZE];
47
48         if(sscanf(request, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) {
49                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "STATUS",
50                            c->name, c->hostname);
51                 return false;
52         }
53
54         logger(DEBUG_STATUS, LOG_NOTICE, "Status message from %s (%s): %d: %s",
55                            c->name, c->hostname, statusno, statusstring);
56
57         return true;
58 }
59
60 bool send_error(connection_t *c, int err, const char *errstring) {
61         if(!errstring)
62                 errstring = "Error";
63
64         return send_request(c, "%d %d %s", ERROR, err, errstring);
65 }
66
67 bool error_h(connection_t *c, const char *request) {
68         int err;
69         char errorstring[MAX_STRING_SIZE];
70
71         if(sscanf(request, "%*d %d " MAX_STRING, &err, errorstring) != 2) {
72                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ERROR",
73                            c->name, c->hostname);
74                 return false;
75         }
76
77         logger(DEBUG_ERROR, LOG_NOTICE, "Error message from %s (%s): %d: %s",
78                            c->name, c->hostname, err, errorstring);
79
80         return false;
81 }
82
83 bool send_termreq(connection_t *c) {
84         return send_request(c, "%d", TERMREQ);
85 }
86
87 bool termreq_h(connection_t *c, const char *request) {
88         return false;
89 }
90
91 bool send_ping(connection_t *c) {
92         c->status.pinged = true;
93         c->last_ping_time = now.tv_sec;
94
95         return send_request(c, "%d", PING);
96 }
97
98 bool ping_h(connection_t *c, const char *request) {
99         return send_pong(c);
100 }
101
102 bool send_pong(connection_t *c) {
103         return send_request(c, "%d", PONG);
104 }
105
106 bool pong_h(connection_t *c, const char *request) {
107         c->status.pinged = false;
108
109         /* Succesful connection, reset timeout if this is an outgoing connection. */
110
111         if(c->outgoing) {
112                 c->outgoing->timeout = 0;
113                 c->outgoing->cfg = NULL;
114                 if(c->outgoing->ai)
115                         freeaddrinfo(c->outgoing->ai);
116                 c->outgoing->ai = NULL;
117                 c->outgoing->aip = NULL;
118         }
119
120         return true;
121 }
122
123 /* Sending and receiving packets via TCP */
124
125 bool send_tcppacket(connection_t *c, const vpn_packet_t *packet) {
126         /* If there already is a lot of data in the outbuf buffer, discard this packet.
127            We use a very simple Random Early Drop algorithm. */
128
129         if(2.0 * c->outbuf.len / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX)
130                 return true;
131
132         if(!send_request(c, "%d %hd", PACKET, packet->len))
133                 return false;
134
135         return send_meta(c, (char *)DATA(packet), packet->len);
136 }
137
138 bool tcppacket_h(connection_t *c, const char *request) {
139         short int len;
140
141         if(sscanf(request, "%*d %hd", &len) != 1) {
142                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "PACKET", c->name,
143                            c->hostname);
144                 return false;
145         }
146
147         /* Set reqlen to len, this will tell receive_meta() that a tcppacket is coming. */
148
149         c->tcplen = len;
150
151         return true;
152 }
153
154 /* Transmitting UDP information */
155
156 bool send_udp_info(node_t *from, node_t *to) {
157         /* If there's a static relay in the path, there's no point in sending the message
158            farther than the static relay. */
159         to = (to->via == myself) ? to->nexthop : to->via;
160
161         /* Skip cases where sending UDP info messages doesn't make sense.
162            This is done here in order to avoid repeating the same logic in multiple callsites. */
163
164         if(to == myself)
165                 return true;
166
167         if(!to->status.reachable)
168                 return true;
169
170         if(from == myself && to->connection)
171                 return true;
172
173         if((myself->options | from->options | to->options) & OPTION_TCPONLY)
174                 return true;
175
176         if((to->nexthop->options >> 24) < 5)
177                 return true;
178
179         char *from_address, *from_port;
180         /* If we're the originator, the address we use is irrelevant
181            because the first intermediate node will ignore it.
182            We use our local address as it somewhat makes sense
183            and it's simpler than introducing an encoding for "null" addresses anyway. */
184         sockaddr2str((from != myself) ? &from->address : &to->nexthop->connection->edge->local_address, &from_address, &from_port);
185
186         bool x = send_request(to->nexthop->connection, "%d %s %s %s %s", UDP_INFO, from->name, to->name, from_address, from_port);
187
188         free(from_address);
189         free(from_port);
190
191         return x;
192 }
193
194 bool udp_info_h(connection_t *c, const char* request) {
195         char from_name[MAX_STRING_SIZE];
196         char to_name[MAX_STRING_SIZE];
197         char from_address[MAX_STRING_SIZE];
198         char from_port[MAX_STRING_SIZE];
199
200         if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING, from_name, to_name, from_address, from_port) != 4) {
201                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "UDP_INFO", c->name, c->hostname);
202                 return false;
203         }
204
205         if(!check_id(from_name) || !check_id(to_name)) {
206                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "UDP_INFO", c->name, c->hostname, "invalid name");
207                 return false;
208         }
209
210         node_t *from = lookup_node(from_name);
211         if(!from) {
212                 logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list", "UDP_INFO", c->name, c->hostname, from_name);
213                 return true;
214         }
215
216         if(from != from->via) {
217                 /* Not supposed to happen, as it means the message wandered past a static relay */
218                 logger(DEBUG_PROTOCOL, LOG_WARNING, "Got UDP info message from %s (%s) which we can't reach directly", from->name, from->hostname);
219                 return true;
220         }
221
222         /* If we have a direct edge to "from", we are in a better position
223            to guess its address than it is itself. */
224         if(!from->connection && !from->status.udp_confirmed) {
225                 sockaddr_t from_addr = str2sockaddr(from_address, from_port);
226                 if(sockaddrcmp(&from_addr, &from->address))
227                         update_node_udp(from, &from_addr);
228         }
229
230         node_t *to = lookup_node(to_name);
231         if(!to) {
232                 logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list", "UDP_INFO", c->name, c->hostname, to_name);
233                 return true;
234         }
235
236         /* Send our own data (which could be what we just received) up the chain. */
237
238         return send_udp_info(from, to);
239 }