blob: 39efa890f39bc9fe9c713897ddc95fccee832863 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file keepalive_udp.c SIP UDP Keepalive
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6
7#include <string.h>
8#include <re_types.h>
9#include <re_mem.h>
10#include <re_mbuf.h>
11#include <re_sa.h>
12#include <re_list.h>
13#include <re_hash.h>
14#include <re_fmt.h>
15#include <re_uri.h>
16#include <re_sys.h>
17#include <re_tmr.h>
18#include <re_udp.h>
19#include <re_stun.h>
20#include <re_msg.h>
21#include <re_sip.h>
22#include "sip.h"
23
24
25enum {
26 UDP_KEEPALIVE_INTVAL = 29,
27};
28
29
30struct sip_udpconn {
31 struct le he;
32 struct list kal;
33 struct tmr tmr_ka;
34 struct sa maddr;
35 struct sa paddr;
36 struct udp_sock *us;
37 struct stun_ctrans *ct;
38 struct stun *stun;
39 uint32_t ka_interval;
40};
41
42
43static void udpconn_keepalive_handler(void *arg);
44
45
46static void destructor(void *arg)
47{
48 struct sip_udpconn *uc = arg;
49
50 list_flush(&uc->kal);
51 hash_unlink(&uc->he);
52 tmr_cancel(&uc->tmr_ka);
53 mem_deref(uc->ct);
54 mem_deref(uc->us);
55 mem_deref(uc->stun);
56}
57
58
59static void udpconn_close(struct sip_udpconn *uc, int err)
60{
61 sip_keepalive_signal(&uc->kal, err);
62 hash_unlink(&uc->he);
63 tmr_cancel(&uc->tmr_ka);
64 uc->ct = mem_deref(uc->ct);
65 uc->us = mem_deref(uc->us);
66 uc->stun = mem_deref(uc->stun);
67}
68
69
70static void stun_response_handler(int err, uint16_t scode, const char *reason,
71 const struct stun_msg *msg, void *arg)
72{
73 struct sip_udpconn *uc = arg;
74 struct stun_attr *attr;
75 (void)reason;
76
77 if (err || scode) {
78 err = err ? err : EPROTO;
79 goto out;
80 }
81
82 attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
83 if (!attr) {
84 attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);
85 if (!attr) {
86 err = EPROTO;
87 goto out;
88 }
89 }
90
91 if (!sa_isset(&uc->maddr, SA_ALL)) {
92 uc->maddr = attr->v.sa;
93 }
94 else if (!sa_cmp(&uc->maddr, &attr->v.sa, SA_ALL)) {
95 err = ENOTCONN;
96 goto out;
97 }
98
99 out:
100 if (err) {
101 udpconn_close(uc, err);
102 mem_deref(uc);
103 }
104 else {
105 tmr_start(&uc->tmr_ka, sip_keepalive_wait(uc->ka_interval),
106 udpconn_keepalive_handler, uc);
107 }
108}
109
110
111static void udpconn_keepalive_handler(void *arg)
112{
113 struct sip_udpconn *uc = arg;
114 int err;
115
116 if (!uc->kal.head) {
117 /* no need for us anymore */
118 udpconn_close(uc, 0);
119 mem_deref(uc);
120 return;
121 }
122
123 err = stun_request(&uc->ct, uc->stun, IPPROTO_UDP, uc->us,
124 &uc->paddr, 0, STUN_METHOD_BINDING, NULL, 0,
125 false, stun_response_handler, uc, 1,
126 STUN_ATTR_SOFTWARE, stun_software);
127 if (err) {
128 udpconn_close(uc, err);
129 mem_deref(uc);
130 }
131}
132
133
134static struct sip_udpconn *udpconn_find(struct sip *sip, struct udp_sock *us,
135 const struct sa *paddr)
136{
137 struct le *le;
138
139 le = list_head(hash_list(sip->ht_udpconn, sa_hash(paddr, SA_ALL)));
140
141 for (; le; le = le->next) {
142
143 struct sip_udpconn *uc = le->data;
144
145 if (!sa_cmp(&uc->paddr, paddr, SA_ALL))
146 continue;
147
148 if (uc->us != us)
149 continue;
150
151 return uc;
152 }
153
154 return NULL;
155}
156
157
158int sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip,
159 struct udp_sock *us, const struct sa *paddr,
160 uint32_t interval)
161{
162 struct sip_udpconn *uc;
163
164 if (!ka || !sip || !us || !paddr)
165 return EINVAL;
166
167 uc = udpconn_find(sip, us, paddr);
168 if (!uc) {
169 uc = mem_zalloc(sizeof(*uc), destructor);
170 if (!uc)
171 return ENOMEM;
172
173 hash_append(sip->ht_udpconn, sa_hash(paddr, SA_ALL),
174 &uc->he, uc);
175
176 uc->paddr = *paddr;
177 uc->stun = mem_ref(sip->stun);
178 uc->us = mem_ref(us);
179 uc->ka_interval = interval ? interval : UDP_KEEPALIVE_INTVAL;
180
181 /* learn mapped address immediately */
182 tmr_start(&uc->tmr_ka, 0, udpconn_keepalive_handler, uc);
183 }
184
185 list_append(&uc->kal, &ka->le, ka);
186
187 return 0;
188}