blob: f7337a4d9f39b02c1df546f7414ab987bbdf8e80 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file stun/keepalive.c STUN usage for NAT Keepalives
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <re_types.h>
7#include <re_mem.h>
8#include <re_mbuf.h>
9#include <re_list.h>
10#include <re_tmr.h>
11#include <re_sa.h>
12#include <re_udp.h>
13#include <re_stun.h>
14
15
16#define DEBUG_MODULE "keepalive"
17#define DEBUG_LEVEL 5
18#include <re_dbg.h>
19
20
21/** Defines a STUN Keepalive session */
22struct stun_keepalive {
23 struct stun_ctrans *ct; /**< STUN client transaction */
24 struct stun *stun; /**< STUN instance */
25 struct udp_helper *uh;
26 int proto;
27 void *sock;
28 struct sa dst;
29 struct tmr tmr; /**< Refresh timer */
30 uint32_t interval; /**< Refresh interval in seconds */
31 stun_mapped_addr_h *mah; /**< Mapped address handler */
32 void *arg; /**< Handler argument */
33 struct sa map; /**< Mapped IP address and port */
34 struct sa xormap; /**< XOR-Mapped IP address and port */
35 struct sa curmap; /**< Currently mapped IP address and port */
36};
37
38static void timeout(void *arg);
39
40
41static void keepalive_destructor(void *data)
42{
43 struct stun_keepalive *ska = data;
44
45 tmr_cancel(&ska->tmr);
46
47 mem_deref(ska->ct);
48 mem_deref(ska->uh);
49 mem_deref(ska->sock);
50 mem_deref(ska->stun);
51}
52
53
54static void call_handler(struct stun_keepalive *ska, int err,
55 const struct sa *map)
56{
57 if (ska->mah)
58 ska->mah(err, map, ska->arg);
59}
60
61
62static void stun_response_handler(int err, uint16_t scode, const char *reason,
63 const struct stun_msg *msg, void *arg)
64{
65 struct stun_keepalive *ska = arg;
66 struct stun_attr *attr;
67 (void)reason;
68
69 /* Restart timer */
70 if (ska->interval > 0)
71 tmr_start(&ska->tmr, ska->interval*1000, timeout, ska);
72
73 if (err || scode) {
74 /* Clear current mapped addr to force new notification */
75 sa_set_in(&ska->curmap, 0, 0);
76
77 goto out;
78 }
79
80 attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
81 if (!attr)
82 attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);
83
84 if (!attr) {
85 err = ENOENT;
86 goto out;
87 }
88
89 if (!sa_cmp(&ska->curmap, &attr->v.sa, SA_ALL)) {
90 ska->curmap = attr->v.sa;
91 call_handler(ska, 0, &ska->curmap);
92 }
93
94 out:
95 if (err)
96 call_handler(ska, err, NULL);
97}
98
99
100static void timeout(void *arg)
101{
102 struct stun_keepalive *ska = arg;
103 int err;
104
105 if (ska->ct)
106 ska->ct = mem_deref(ska->ct);
107
108 err = stun_request(&ska->ct, ska->stun, ska->proto, ska->sock,
109 &ska->dst, 0, STUN_METHOD_BINDING, NULL, 0, false,
110 stun_response_handler, ska, 1,
111 STUN_ATTR_SOFTWARE, stun_software);
112 if (0 == err)
113 return;
114
115 /* Restart timer */
116 if (ska->interval > 0)
117 tmr_start(&ska->tmr, ska->interval*1000, timeout, ska);
118
119 /* Error */
120 call_handler(ska, err, NULL);
121}
122
123
124static bool udp_recv_handler(struct sa *src, struct mbuf *mb, void *arg)
125{
126 struct stun_keepalive *ska = arg;
127 struct stun_unknown_attr ua;
128 struct stun_msg *msg;
129 size_t pos = mb->pos;
130 bool hdld;
131
132 if (!sa_cmp(&ska->dst, src, SA_ALL))
133 return false;
134
135 if (stun_msg_decode(&msg, mb, &ua))
136 return false;
137
138 if (stun_msg_method(msg) != STUN_METHOD_BINDING) {
139 hdld = false;
140 mb->pos = pos;
141 goto out;
142 }
143
144 switch (stun_msg_class(msg)) {
145
146 case STUN_CLASS_ERROR_RESP:
147 case STUN_CLASS_SUCCESS_RESP:
148 (void)stun_ctrans_recv(ska->stun, msg, &ua);
149 hdld = true;
150 break;
151
152 default:
153 hdld = false;
154 mb->pos = pos;
155 break;
156 }
157
158 out:
159 mem_deref(msg);
160
161 return hdld;
162}
163
164
165/**
166 * Allocate a new STUN keepalive session
167 *
168 * @param skap Pointer to keepalive object
169 * @param proto Transport protocol
170 * @param sock Socket
171 * @param layer Protocol layer
172 * @param dst Destination address
173 * @param conf Configuration
174 * @param mah Mapped address handler
175 * @param arg Handler argument
176 *
177 * @return 0 if success, otherwise errorcode
178 */
179int stun_keepalive_alloc(struct stun_keepalive **skap,
180 int proto, void *sock, int layer,
181 const struct sa *dst, const struct stun_conf *conf,
182 stun_mapped_addr_h *mah, void *arg)
183{
184 struct stun_keepalive *ska;
185 int err;
186
187 if (!skap)
188 return EINVAL;
189
190 ska = mem_zalloc(sizeof(*ska), keepalive_destructor);
191 if (!ska)
192 return ENOMEM;
193
194 err = stun_alloc(&ska->stun, conf, NULL, NULL);
195 if (err)
196 goto out;
197
198 tmr_init(&ska->tmr);
199
200 ska->proto = proto;
201 ska->sock = mem_ref(sock);
202 ska->mah = mah;
203 ska->arg = arg;
204
205 if (dst)
206 ska->dst = *dst;
207
208 switch (proto) {
209
210 case IPPROTO_UDP:
211 err = udp_register_helper(&ska->uh, sock, layer,
212 NULL, udp_recv_handler, ska);
213 break;
214
215 default:
216 err = 0;
217 break;
218 }
219
220 out:
221 if (err)
222 mem_deref(ska);
223 else
224 *skap = ska;
225
226 return err;
227}
228
229
230/**
231 * Enable or disable keepalive timer
232 *
233 * @param ska Keepalive object
234 * @param interval Interval in seconds (0 to disable)
235 *
236 * @return 0 if success, otherwise errorcode
237 */
238void stun_keepalive_enable(struct stun_keepalive *ska, uint32_t interval)
239{
240 if (!ska)
241 return;
242
243 ska->interval = interval;
244
245 tmr_cancel(&ska->tmr);
246 if (interval > 0)
247 tmr_start(&ska->tmr, 1, timeout, ska);
248}