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 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.
16 * This code provides some support for doing STUN transactions. We
17 * receive the simplest possible packet as the STUN server and try
18 * to respond properly.
20 * All STUN packets start with a simple header made of a type,
21 * length (excluding the header) and a 16-byte random transaction id.
22 * Following the header we may have zero or more attributes, each
23 * structured as a type, length and a value (whose format depends
24 * on the type, but often contains addresses).
25 * Of course all fields are in network format.
27 * This code was based on ministun.c.
29 * @file nat/gnunet-service-nat_stun.c
30 * @brief Functions for STUN functionality
31 * @author Bruno Souza Cabral
35 #include "gnunet_util_lib.h"
38 #define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__)
42 * Context for #stun_get_mapped().
43 * Used to store state across processing attributes.
52 * Extract the STUN_MAPPED_ADDRESS from the stun response.
53 * This is used as a callback for stun_handle_response
54 * when called from stun_request.
56 * @param[out] st pointer where we will set the type
57 * @param attr received stun attribute
58 * @param magic Magic cookie
59 * @param[out] arg pointer to a sockaddr_in where we will set the reported IP and port
60 * @return #GNUNET_OK if @a arg was initialized
63 stun_get_mapped (struct StunState *st,
64 const struct stun_attr *attr,
66 struct sockaddr_in *arg)
68 const struct stun_addr *returned_addr;
69 struct sockaddr_in *sa = (struct sockaddr_in *) arg;
70 uint16_t type = ntohs (attr->attr);
74 case STUN_MAPPED_ADDRESS:
75 if ( (st->attr == STUN_XOR_MAPPED_ADDRESS) ||
76 (st->attr == STUN_MS_XOR_MAPPED_ADDRESS) )
80 case STUN_MS_XOR_MAPPED_ADDRESS:
81 if (st->attr == STUN_XOR_MAPPED_ADDRESS)
84 case STUN_XOR_MAPPED_ADDRESS:
90 if (ntohs (attr->len) < sizeof (struct stun_addr))
92 returned_addr = (const struct stun_addr *)(attr + 1);
93 if (AF_INET != returned_addr->family)
96 sa->sin_family = AF_INET;
97 sa->sin_port = returned_addr->port ^ htons (ntohl(magic) >> 16);
98 sa->sin_addr.s_addr = returned_addr->addr ^ magic;
104 * Handle an incoming STUN response. Do some basic sanity checks on
105 * packet size and content, try to extract information.
106 * At the moment this only processes BIND requests,
107 * and returns the externally visible address of the original
110 * @param data the packet
111 * @param len the length of the packet in @a data
112 * @param[out] arg sockaddr_in where we will set our discovered address
113 * @return #GNUNET_OK on success,
114 * #GNUNET_NO if the packet is invalid (not a stun packet)
117 GNUNET_NAT_stun_handle_packet_ (const void *data,
119 struct sockaddr_in *arg)
121 const struct stun_header *hdr;
122 const struct stun_attr *attr;
124 uint32_t advertised_message_size;
125 uint32_t message_magic_cookie;
126 int ret = GNUNET_SYSERR;
128 /* On entry, 'len' is the length of the UDP payload. After the
129 * initial checks it becomes the size of unprocessed options,
130 * while 'data' is advanced accordingly.
132 if (len < sizeof(struct stun_header))
134 LOG (GNUNET_ERROR_TYPE_DEBUG,
135 "Packet too short to be a STUN packet\n");
139 /* Skip header as it is already in hdr */
140 len -= sizeof(struct stun_header);
141 data += sizeof(struct stun_header);
143 /* len as advertised in the message */
144 advertised_message_size = ntohs (hdr->msglen);
145 message_magic_cookie = ntohl (hdr->magic);
146 /* Compare if the cookie match */
147 if (STUN_MAGIC_COOKIE != message_magic_cookie)
149 LOG (GNUNET_ERROR_TYPE_DEBUG,
150 "Invalid magic cookie for STUN packet\n");
154 LOG (GNUNET_ERROR_TYPE_INFO,
155 "STUN Packet, msg %s (%04x), length: %d\n",
156 stun_msg2str (ntohs (hdr->msgtype)),
157 ntohs (hdr->msgtype),
158 advertised_message_size);
159 if (advertised_message_size > len)
161 LOG (GNUNET_ERROR_TYPE_INFO,
162 "Scrambled STUN packet length (got %d, expecting %d)\n",
163 advertised_message_size,
167 len = advertised_message_size;
168 memset (&st, 0, sizeof(st));
172 if (len < sizeof (struct stun_attr))
174 LOG (GNUNET_ERROR_TYPE_INFO,
175 "Attribute too short (got %d, expecting %d)\n",
177 (int) sizeof (struct stun_attr));
180 attr = (const struct stun_attr *) data;
182 /* compute total attribute length */
183 advertised_message_size = ntohs (attr->len) + sizeof (struct stun_attr);
185 /* Check if we still have space in our buffer */
186 if (advertised_message_size > len)
188 LOG (GNUNET_ERROR_TYPE_INFO,
189 "Inconsistent attribute (length %d exceeds remaining msg len %d)\n",
190 advertised_message_size,
195 stun_get_mapped (&st,
200 data += advertised_message_size;
201 len -= advertised_message_size;
206 /* end of gnunet-service-nat_stun.c */