a0e52f4ac3238afcb29f5cdab3957dd381a6d96f
[oweals/tinc.git] / src / protocol_subnet.c
1 /*
2     protocol_subnet.c -- handle the meta-protocol, subnets
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2009 Guus Sliepen <guus@tinc-vpn.org>
5                   2009      Michael Tokarev <mjt@tls.msk.ru>
6
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "system.h"
23
24 #include "conf.h"
25 #include "connection.h"
26 #include "logger.h"
27 #include "net.h"
28 #include "netutl.h"
29 #include "node.h"
30 #include "protocol.h"
31 #include "subnet.h"
32 #include "utils.h"
33 #include "xalloc.h"
34
35 bool send_add_subnet(connection_t *c, const subnet_t *subnet)
36 {
37         char netstr[MAXNETSTR];
38
39         cp();
40
41         if(!net2str(netstr, sizeof netstr, subnet))
42                 return false;
43
44         return send_request(c, "%d %x %s %s", ADD_SUBNET, rand(), subnet->owner->name, netstr);
45 }
46
47 bool add_subnet_h(connection_t *c)
48 {
49         char subnetstr[MAX_STRING_SIZE];
50         char name[MAX_STRING_SIZE];
51         node_t *owner;
52         subnet_t s = {0}, *new;
53
54         cp();
55
56         if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
57                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_SUBNET", c->name,
58                            c->hostname);
59                 return false;
60         }
61
62         /* Check if owner name is valid */
63
64         if(!check_id(name)) {
65                 logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name,
66                            c->hostname, _("invalid name"));
67                 return false;
68         }
69
70         /* Check if subnet string is valid */
71
72         if(!str2net(&s, subnetstr)) {
73                 logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name,
74                            c->hostname, _("invalid subnet string"));
75                 return false;
76         }
77
78         if(seen_request(c->buffer))
79                 return true;
80
81         /* Check if the owner of the new subnet is in the connection list */
82
83         owner = lookup_node(name);
84
85         if(tunnelserver && owner != myself && owner != c->node) {
86                 /* in case of tunnelserver, ignore indirect subnet registrations */
87                 ifdebug(PROTOCOL) logger(LOG_WARNING, _("Ignoring indirect %s from %s (%s) for %s"),
88                                    "ADD_SUBNET", c->name, c->hostname, subnetstr);
89                 return true;
90         }
91
92         if(!owner) {
93                 owner = new_node();
94                 owner->name = xstrdup(name);
95                 node_add(owner);
96         }
97
98         /* Check if we already know this subnet */
99
100         if(lookup_subnet(owner, &s))
101                 return true;
102
103         /* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
104
105         if(owner == myself) {
106                 ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for ourself"),
107                                    "ADD_SUBNET", c->name, c->hostname);
108                 s.owner = myself;
109                 send_del_subnet(c, &s);
110                 return true;
111         }
112
113         /* In tunnel server mode, check if the subnet matches one in the config file of this node */
114
115         if(tunnelserver) {
116                 config_t *cfg;
117                 subnet_t *allowed;
118
119                 for(cfg = lookup_config(c->config_tree, "Subnet"); cfg; cfg = lookup_config_next(c->config_tree, cfg)) {
120                         if(!get_config_subnet(cfg, &allowed))
121                                 return false;
122
123                         if(!subnet_compare(&s, allowed))
124                                 break;
125
126                         free_subnet(allowed);
127                 }
128
129                 if(!cfg) {
130                         logger(LOG_WARNING, _("Unauthorized %s from %s (%s) for %s"),
131                                 "ADD_SUBNET", c->name, c->hostname, subnetstr);
132                         return false;
133                 }
134
135                 free_subnet(allowed);
136         }
137
138         /* If everything is correct, add the subnet to the list of the owner */
139
140         *(new = new_subnet()) = s;
141         subnet_add(owner, new);
142
143         if(owner->status.reachable)
144                 subnet_update(owner, new, true);
145
146         /* Tell the rest */
147
148         if(!tunnelserver)
149                 forward_request(c);
150
151         return true;
152 }
153
154 bool send_del_subnet(connection_t *c, const subnet_t *s)
155 {
156         char netstr[MAXNETSTR];
157
158         cp();
159
160         if(!net2str(netstr, sizeof netstr, s))
161                 return false;
162
163         return send_request(c, "%d %x %s %s", DEL_SUBNET, rand(), s->owner->name, netstr);
164 }
165
166 bool del_subnet_h(connection_t *c)
167 {
168         char subnetstr[MAX_STRING_SIZE];
169         char name[MAX_STRING_SIZE];
170         node_t *owner;
171         subnet_t s = {0}, *find;
172
173         cp();
174
175         if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
176                 logger(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_SUBNET", c->name,
177                            c->hostname);
178                 return false;
179         }
180
181         /* Check if owner name is valid */
182
183         if(!check_id(name)) {
184                 logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name,
185                            c->hostname, _("invalid name"));
186                 return false;
187         }
188
189         /* Check if subnet string is valid */
190
191         if(!str2net(&s, subnetstr)) {
192                 logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name,
193                            c->hostname, _("invalid subnet string"));
194                 return false;
195         }
196
197         if(seen_request(c->buffer))
198                 return true;
199
200         /* Check if the owner of the subnet being deleted is in the connection list */
201
202         owner = lookup_node(name);
203
204         if(tunnelserver && owner != myself && owner != c->node) {
205                 /* in case of tunnelserver, ignore indirect subnet deletion */
206                 ifdebug(PROTOCOL) logger(LOG_WARNING, _("Ignoring indirect %s from %s (%s) for %s"),
207                                   "DEL_SUBNET", c->name, c->hostname, subnetstr);
208                 return true;
209         }
210
211         if(!owner) {
212                 ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
213                                    "DEL_SUBNET", c->name, c->hostname, name);
214                 return true;
215         }
216
217         /* If everything is correct, delete the subnet from the list of the owner */
218
219         s.owner = owner;
220
221         find = lookup_subnet(owner, &s);
222
223         if(!find) {
224                 ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which does not appear in his subnet tree"),
225                                    "DEL_SUBNET", c->name, c->hostname, name);
226                 return true;
227         }
228
229         /* If we are the owner of this subnet, retaliate with an ADD_SUBNET */
230
231         if(owner == myself) {
232                 ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for ourself"),
233                                    "DEL_SUBNET", c->name, c->hostname);
234                 send_add_subnet(c, find);
235                 return true;
236         }
237
238         /* Tell the rest */
239
240         if(!tunnelserver)
241                 forward_request(c);
242
243         /* Finally, delete it. */
244
245         if(owner->status.reachable)
246                 subnet_update(owner, find, false);
247
248         subnet_del(owner, find);
249
250         return true;
251 }