2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 2016 GNUnet e.V.
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.
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.
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/>.
19 * This code provides some support for doing STUN transactions. We
20 * receive the simplest possible packet as the STUN server and try
21 * to respond properly.
23 * All STUN packets start with a simple header made of a type,
24 * length (excluding the header) and a 16-byte random transaction id.
25 * Following the header we may have zero or more attributes, each
26 * structured as a type, length and a value (whose format depends
27 * on the type, but often contains addresses).
28 * Of course all fields are in network format.
30 * This code was based on ministun.c.
32 * @file nat/gnunet-service-nat_stun.c
33 * @brief Functions for STUN functionality
34 * @author Bruno Souza Cabral
38 #include "gnunet_util_lib.h"
41 #define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__)
45 * Context for #stun_get_mapped().
46 * Used to store state across processing attributes.
55 * Extract the STUN_MAPPED_ADDRESS from the stun response.
56 * This is used as a callback for stun_handle_response
57 * when called from stun_request.
59 * @param[out] st pointer where we will set the type
60 * @param attr received stun attribute
61 * @param magic Magic cookie
62 * @param[out] arg pointer to a sockaddr_in where we will set the reported IP and port
63 * @return #GNUNET_OK if @a arg was initialized
66 stun_get_mapped (struct StunState *st,
67 const struct stun_attr *attr,
69 struct sockaddr_in *arg)
71 const struct stun_addr *returned_addr;
72 struct sockaddr_in *sa = (struct sockaddr_in *) arg;
73 uint16_t type = ntohs (attr->attr);
77 case STUN_MAPPED_ADDRESS:
78 if ( (st->attr == STUN_XOR_MAPPED_ADDRESS) ||
79 (st->attr == STUN_MS_XOR_MAPPED_ADDRESS) )
83 case STUN_MS_XOR_MAPPED_ADDRESS:
84 if (st->attr == STUN_XOR_MAPPED_ADDRESS)
87 case STUN_XOR_MAPPED_ADDRESS:
93 if (ntohs (attr->len) < sizeof (struct stun_addr))
95 returned_addr = (const struct stun_addr *)(attr + 1);
96 if (AF_INET != returned_addr->family)
99 sa->sin_family = AF_INET;
100 sa->sin_port = returned_addr->port ^ htons (ntohl(magic) >> 16);
101 sa->sin_addr.s_addr = returned_addr->addr ^ magic;
107 * Handle an incoming STUN response. Do some basic sanity checks on
108 * packet size and content, try to extract information.
109 * At the moment this only processes BIND requests,
110 * and returns the externally visible address of the original
113 * @param data the packet
114 * @param len the length of the packet in @a data
115 * @param[out] arg sockaddr_in where we will set our discovered address
116 * @return #GNUNET_OK on success,
117 * #GNUNET_NO if the packet is invalid (not a stun packet)
120 GNUNET_NAT_stun_handle_packet_ (const void *data,
122 struct sockaddr_in *arg)
124 const struct stun_header *hdr;
125 const struct stun_attr *attr;
127 uint32_t advertised_message_size;
128 uint32_t message_magic_cookie;
129 int ret = GNUNET_SYSERR;
131 /* On entry, 'len' is the length of the UDP payload. After the
132 * initial checks it becomes the size of unprocessed options,
133 * while 'data' is advanced accordingly.
135 if (len < sizeof(struct stun_header))
137 LOG (GNUNET_ERROR_TYPE_DEBUG,
138 "Packet too short to be a STUN packet\n");
142 /* Skip header as it is already in hdr */
143 len -= sizeof(struct stun_header);
144 data += sizeof(struct stun_header);
146 /* len as advertised in the message */
147 advertised_message_size = ntohs (hdr->msglen);
148 message_magic_cookie = ntohl (hdr->magic);
149 /* Compare if the cookie match */
150 if (STUN_MAGIC_COOKIE != message_magic_cookie)
152 LOG (GNUNET_ERROR_TYPE_DEBUG,
153 "Invalid magic cookie for STUN packet\n");
157 LOG (GNUNET_ERROR_TYPE_INFO,
158 "STUN Packet, msg %s (%04x), length: %d\n",
159 stun_msg2str (ntohs (hdr->msgtype)),
160 ntohs (hdr->msgtype),
161 advertised_message_size);
162 if (advertised_message_size > len)
164 LOG (GNUNET_ERROR_TYPE_INFO,
165 "Scrambled STUN packet length (got %d, expecting %d)\n",
166 advertised_message_size,
170 len = advertised_message_size;
171 memset (&st, 0, sizeof(st));
175 if (len < sizeof (struct stun_attr))
177 LOG (GNUNET_ERROR_TYPE_INFO,
178 "Attribute too short (got %d, expecting %d)\n",
180 (int) sizeof (struct stun_attr));
183 attr = (const struct stun_attr *) data;
185 /* compute total attribute length */
186 advertised_message_size = ntohs (attr->len) + sizeof (struct stun_attr);
188 /* Check if we still have space in our buffer */
189 if (advertised_message_size > len)
191 LOG (GNUNET_ERROR_TYPE_INFO,
192 "Inconsistent attribute (length %d exceeds remaining msg len %d)\n",
193 advertised_message_size,
198 stun_get_mapped (&st,
203 data += advertised_message_size;
204 len -= advertised_message_size;
209 /* end of gnunet-service-nat_stun.c */