use correct plugin name; thanks Corvus Corax
[oweals/gnunet.git] / src / nat / gnunet-service-nat_stun.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2015, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * This code provides some support for doing STUN transactions.  We
22  * receive the simplest possible packet as the STUN server and try
23  * to respond properly.
24  *
25  * All STUN packets start with a simple header made of a type,
26  * length (excluding the header) and a 16-byte random transaction id.
27  * Following the header we may have zero or more attributes, each
28  * structured as a type, length and a value (whose format depends
29  * on the type, but often contains addresses).
30  * Of course all fields are in network format.
31  *
32  * This code was based on ministun.c.
33  *
34  * @file nat/gnunet-service-nat_stun.c
35  * @brief Functions for STUN functionality
36  * @author Bruno Souza Cabral
37  */
38
39 #include "platform.h"
40 #include "gnunet_util_lib.h"
41 #include "nat_stun.h"
42
43 #define LOG(kind, ...) GNUNET_log_from (kind, "stun", __VA_ARGS__)
44
45
46 /**
47  * Context for #stun_get_mapped().
48  * Used to store state across processing attributes.
49  */
50 struct StunState
51 {
52   uint16_t attr;
53 };
54
55
56 /**
57  * Extract the STUN_MAPPED_ADDRESS from the stun response.
58  * This is used as a callback for stun_handle_response
59  * when called from stun_request.
60  *
61  * @param[out] st pointer where we will set the type
62  * @param attr received stun attribute
63  * @param magic Magic cookie
64  * @param[out] arg pointer to a sockaddr_in where we will set the reported IP and port
65  * @return #GNUNET_OK if @a arg was initialized
66  */
67 static int
68 stun_get_mapped (struct StunState *st,
69                  const struct stun_attr *attr,
70                  uint32_t magic,
71                  struct sockaddr_in *arg)
72 {
73   const struct stun_addr *returned_addr;
74   struct sockaddr_in *sa = (struct sockaddr_in *) arg;
75   uint16_t type = ntohs (attr->attr);
76
77   switch (type)
78   {
79   case STUN_MAPPED_ADDRESS:
80     if ((st->attr == STUN_XOR_MAPPED_ADDRESS) ||
81         (st->attr == STUN_MS_XOR_MAPPED_ADDRESS))
82       return GNUNET_NO;
83     magic = 0;
84     break;
85
86   case STUN_MS_XOR_MAPPED_ADDRESS:
87     if (st->attr == STUN_XOR_MAPPED_ADDRESS)
88       return GNUNET_NO;
89     break;
90
91   case STUN_XOR_MAPPED_ADDRESS:
92     break;
93
94   default:
95     return GNUNET_NO;
96   }
97
98   if (ntohs (attr->len) < sizeof(struct stun_addr))
99     return GNUNET_NO;
100   returned_addr = (const struct stun_addr *) (attr + 1);
101   if (AF_INET != returned_addr->family)
102     return GNUNET_NO;
103   st->attr = type;
104   sa->sin_family = AF_INET;
105   sa->sin_port = returned_addr->port ^ htons (ntohl (magic) >> 16);
106   sa->sin_addr.s_addr = returned_addr->addr ^ magic;
107   return GNUNET_OK;
108 }
109
110
111 /**
112  * Handle an incoming STUN response.  Do some basic sanity checks on
113  * packet size and content, try to extract information.
114  * At the moment this only processes BIND requests,
115  * and returns the externally visible address of the original
116  * request.
117  *
118  * @param data the packet
119  * @param len the length of the packet in @a data
120  * @param[out] arg sockaddr_in where we will set our discovered address
121  * @return #GNUNET_OK on success,
122  *         #GNUNET_NO if the packet is invalid (not a stun packet)
123  */
124 int
125 GNUNET_NAT_stun_handle_packet_ (const void *data,
126                                 size_t len,
127                                 struct sockaddr_in *arg)
128 {
129   const struct stun_header *hdr;
130   const struct stun_attr *attr;
131   struct StunState st;
132   uint32_t advertised_message_size;
133   uint32_t message_magic_cookie;
134   int ret = GNUNET_SYSERR;
135
136   /* On entry, 'len' is the length of the UDP payload. After the
137    * initial checks it becomes the size of unprocessed options,
138    * while 'data' is advanced accordingly.
139    */
140   if (len < sizeof(struct stun_header))
141   {
142     LOG (GNUNET_ERROR_TYPE_DEBUG,
143          "Packet too short to be a STUN packet\n");
144     return GNUNET_NO;
145   }
146   hdr = data;
147   /* Skip header as it is already in hdr */
148   len -= sizeof(struct stun_header);
149   data += sizeof(struct stun_header);
150
151   /* len as advertised in the message */
152   advertised_message_size = ntohs (hdr->msglen);
153   message_magic_cookie = ntohl (hdr->magic);
154   /* Compare if the cookie match */
155   if (STUN_MAGIC_COOKIE != message_magic_cookie)
156   {
157     LOG (GNUNET_ERROR_TYPE_DEBUG,
158          "Invalid magic cookie for STUN packet\n");
159     return GNUNET_NO;
160   }
161
162   LOG (GNUNET_ERROR_TYPE_INFO,
163        "STUN Packet, msg %s (%04x), length: %d\n",
164        stun_msg2str (ntohs (hdr->msgtype)),
165        ntohs (hdr->msgtype),
166        advertised_message_size);
167   if (advertised_message_size > len)
168   {
169     LOG (GNUNET_ERROR_TYPE_INFO,
170          "Scrambled STUN packet length (got %d, expecting %d)\n",
171          advertised_message_size,
172          (int) len);
173     return GNUNET_NO;
174   }
175   len = advertised_message_size;
176   memset (&st, 0, sizeof(st));
177
178   while (len > 0)
179   {
180     if (len < sizeof(struct stun_attr))
181     {
182       LOG (GNUNET_ERROR_TYPE_INFO,
183            "Attribute too short (got %d, expecting %d)\n",
184            (int) len,
185            (int) sizeof(struct stun_attr));
186       break;
187     }
188     attr = (const struct stun_attr *) data;
189
190     /* compute total attribute length */
191     advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr);
192
193     /* Check if we still have space in our buffer */
194     if (advertised_message_size > len)
195     {
196       LOG (GNUNET_ERROR_TYPE_INFO,
197            "Inconsistent attribute (length %d exceeds remaining msg len %d)\n",
198            advertised_message_size,
199            (int) len);
200       break;
201     }
202     if (GNUNET_OK ==
203         stun_get_mapped (&st,
204                          attr,
205                          hdr->magic,
206                          arg))
207       ret = GNUNET_OK;
208     data += advertised_message_size;
209     len -= advertised_message_size;
210   }
211   return ret;
212 }
213
214 /* end of gnunet-service-nat_stun.c */