blob: b32785cb4d62c88b263ac7ef69917b71794c0597 [file] [log] [blame]
James Kuszmaul871d0712021-01-17 11:30:43 -08001/**
2 * @file stunsrv.c Basic STUN Server for Connectivity checks
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_fmt.h>
9#include <re_mem.h>
10#include <re_mbuf.h>
11#include <re_list.h>
12#include <re_tmr.h>
13#include <re_sa.h>
14#include <re_stun.h>
15#include <re_ice.h>
16#include <re_udp.h>
17#include <re_tcp.h>
18#include <re_sys.h>
19#include <re_trice.h>
20#include "trice.h"
21
22
23#define DEBUG_MODULE "stunsrv"
24#define DEBUG_LEVEL 5
25#include <re_dbg.h>
26
27
28static const char *sw = "ice stunsrv v" VERSION " (" ARCH "/" OS ")";
29
30
31/*
32 * NOTE about TCP-candidates:
33 *
34 * Note that STUN requests received on a passive TCP candidate
35 * will typically produce a remote peer reflexive candidate.
36 */
37static int handle_stun_full(struct trice *icem, struct ice_lcand *lcand,
38 void *sock, const struct sa *src,
39 uint32_t prio, bool use_cand)
40{
41 struct ice_candpair *pair = NULL;
42 struct ice_rcand *rcand;
43 enum ice_tcptype tcptype_rev;
44 int err = 0;
45
46 trice_tracef(icem, 36,
47 "[%u] STUNSRV: Rx Binding Request [%H <--- %J] %s\n",
48 lcand->attr.compid,
49 trice_cand_print, lcand,
50 src,
51 use_cand ? "[USE]" : "");
52
53 tcptype_rev = ice_tcptype_reverse(lcand->attr.tcptype);
54
55 rcand = trice_rcand_find(icem, lcand->attr.compid,
56 lcand->attr.proto, src);
57 if (!rcand) {
58
59 if (icem->conf.enable_prflx) {
60
61 err = trice_rcand_add(&rcand, icem,
62 lcand->attr.compid,
63 "444", lcand->attr.proto, prio,
64 src, ICE_CAND_TYPE_PRFLX,
65 tcptype_rev);
66 if (err)
67 return err;
68
69 trice_printf(icem, "{%u} added PRFLX "
70 "remote candidate (%H)\n",
71 lcand->attr.compid,
72 trice_cand_print, rcand);
73 }
74 }
75
76 /* already valid, skip */
77 pair = trice_candpair_find(&icem->validl, lcand, rcand);
78 if (pair)
79 goto out;
80
81 /* note: the candidate-pair can exist in either list */
82 pair = trice_candpair_find(&icem->checkl, lcand, rcand);
83 if (!pair) {
84 if (icem->conf.enable_prflx) {
85 DEBUG_WARNING("{%u} candidate pair not found:"
86 " source=%J\n",
87 lcand->attr.compid, src);
88 }
89 goto out;
90 }
91
92 /* 7.2.1.5. Updating the Nominated Flag */
93 if (use_cand) {
94 if (icem->lrole == ICE_ROLE_CONTROLLED) {
95
96 pair->nominated = true;
97 }
98 }
99
100 out:
101 /*
102 send a triggered request
103 */
104 if (pair && use_cand) {
105
106 if (icem->checklist && !pair->trigged) {
107
108 err = trice_conncheck_trigged(icem, pair,
109 sock, use_cand);
110 if (err) {
111 DEBUG_WARNING("ice_checklist_stun_request"
112 " failed (%m)\n",
113 err);
114 }
115 pair->trigged = true;
116 }
117 }
118
119 return 0;
120}
121
122
123static int stunsrv_ereply(struct trice *icem, struct ice_lcand *lcand,
124 void *sock, const struct sa *src,
125 size_t presz, const struct stun_msg *req,
126 uint16_t scode, const char *reason)
127{
128 DEBUG_WARNING("[%H] replying error to %J (%u %s)\n",
129 trice_cand_print, lcand,
130 src,
131 scode, reason);
132
133 trice_tracef(icem, 31,
134 "[%u] STUNSRV: Tx error [%J <--- %H] (%u %s)\n",
135 lcand->attr.compid,
136 src,
137 trice_cand_print, lcand,
138 scode, reason);
139
140 return stun_ereply(lcand->attr.proto, sock, src, presz, req,
141 scode, reason,
142 (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 1,
143 STUN_ATTR_SOFTWARE, icem->sw ? icem->sw : sw);
144}
145
146
147int trice_stund_recv(struct trice *icem, struct ice_lcand *lcand,
148 void *sock, const struct sa *src,
149 struct stun_msg *req, size_t presz)
150{
151 struct stun_attr *attr;
152 struct pl lu, ru;
153 int err;
154
155 /* RFC 5389: Fingerprint errors are silently discarded */
156 err = stun_msg_chk_fingerprint(req);
157 if (err)
158 return err;
159
160 err = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd));
161 if (err) {
162 DEBUG_WARNING("message-integrity failed (src=%J)\n", src);
163 if (err == EBADMSG)
164 goto unauth;
165 else
166 goto badmsg;
167 }
168
169 attr = stun_msg_attr(req, STUN_ATTR_USERNAME);
170 if (!attr)
171 goto badmsg;
172
173 err = re_regex(attr->v.username, strlen(attr->v.username),
174 "[^:]+:[^]+", &lu, &ru);
175 if (err) {
176 DEBUG_WARNING("could not parse USERNAME attribute (%s)\n",
177 attr->v.username);
178 goto unauth;
179 }
180 if (pl_strcmp(&lu, icem->lufrag)) {
181 DEBUG_WARNING("local ufrag err (expected %s, actual %r)\n",
182 icem->lufrag, &lu);
183 goto unauth;
184 }
185 if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag)) {
186 DEBUG_WARNING("remote ufrag err (expected %s, actual %r)\n",
187 icem->rufrag, &ru);
188 goto unauth;
189 }
190
191 if (icem->lrole == ICE_ROLE_UNKNOWN) {
192 err = trice_reqbuf_append(icem, lcand, sock, src, req, presz);
193 if (err) {
194 DEBUG_WARNING("unable to buffer STUN request: %m\n",
195 err);
196 }
197 }
198
199 return trice_stund_recv_role_set(icem, lcand, sock, src, req, presz);
200
201 badmsg:
202 return stunsrv_ereply(icem, lcand, sock, src, presz, req,
203 400, "Bad Request");
204
205 unauth:
206 return stunsrv_ereply(icem, lcand, sock, src, presz, req,
207 401, "Unauthorized");
208}
209
210
211int trice_stund_recv_role_set(struct trice *icem, struct ice_lcand *lcand,
212 void *sock, const struct sa *src,
213 struct stun_msg *req, size_t presz)
214{
215 struct stun_attr *attr;
216 enum ice_role remote_role = ICE_ROLE_UNKNOWN;
217 uint64_t tiebrk = 0;
218 uint32_t prio_prflx;
219 int err;
220 bool use_cand = false;
221
222 attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED);
223 if (attr) {
224 remote_role = ICE_ROLE_CONTROLLED;
225 tiebrk = attr->v.uint64;
226 }
227
228 attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING);
229 if (attr) {
230 remote_role = ICE_ROLE_CONTROLLING;
231 tiebrk = attr->v.uint64;
232 }
233
234 if (remote_role == ICE_ROLE_UNKNOWN)
235 goto badmsg;
236
237 if (remote_role == icem->lrole) {
238 DEBUG_NOTICE("role conflict detected (both %s)\n",
239 ice_role2name(remote_role));
240
241 if (icem->tiebrk >= tiebrk)
242 trice_switch_local_role(icem);
243 else
244 goto conflict;
245 }
246
247 attr = stun_msg_attr(req, STUN_ATTR_PRIORITY);
248 if (attr)
249 prio_prflx = attr->v.uint32;
250 else
251 goto badmsg;
252
253 attr = stun_msg_attr(req, STUN_ATTR_USE_CAND);
254 if (attr)
255 use_cand = true;
256
257 err = handle_stun_full(icem, lcand, sock, src, prio_prflx, use_cand);
258
259 if (err)
260 goto badmsg;
261
262 trice_tracef(icem, 32,
263 "[%u] STUNSRV: Tx success respons [%H ---> %J]\n",
264 lcand->attr.compid,
265 trice_cand_print, lcand, src);
266
267 return stun_reply(lcand->attr.proto, sock, src, presz, req,
268 (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2,
269 STUN_ATTR_XOR_MAPPED_ADDR, src,
270 STUN_ATTR_SOFTWARE, icem->sw ? icem->sw : sw);
271
272
273 badmsg:
274 return stunsrv_ereply(icem, lcand, sock, src, presz, req,
275 400, "Bad Request");
276
277 conflict:
278 return stunsrv_ereply(icem, lcand, sock, src, presz, req,
279 487, "Role Conflict");
280}