blob: 5159ee78c1326651cacd3e3c20cc1938bdc20635 [file] [log] [blame]
James Kuszmaul871d0712021-01-17 11:30:43 -08001/**
2 * @file tcpconn.c ICE handling of TCP-connections
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_tcp.h>
15#include <re_udp.h>
16#include <re_stun.h>
17#include <re_ice.h>
18#include <re_shim.h>
19#include <re_trice.h>
20#include "trice.h"
21
22
23#define DEBUG_MODULE "tcpconn"
24#define DEBUG_LEVEL 5
25#include <re_dbg.h>
26
27
28/* `mb' contains a complete frame */
29static bool shim_frame_handler(struct mbuf *mb, void *arg)
30{
31 struct ice_tcpconn *conn = arg;
32
33 return conn->frameh(conn->icem, conn->tc, &conn->paddr, mb, conn->arg);
34}
35
36
37static void tcp_estab_handler(void *arg)
38{
39 struct ice_tcpconn *conn = arg;
40 struct trice *icem = conn->icem;
41 struct le *le;
42 int err;
43
44 conn->estab = true;
45
46 trice_printf(icem, "TCP established (local=%J <---> peer=%J)\n",
47 &conn->laddr, &conn->paddr);
48
49 err = shim_insert(&conn->shim, conn->tc, conn->layer,
50 shim_frame_handler, conn);
51 if (err)
52 goto out;
53
54 if (!icem->checklist)
55 goto out;
56
57 /* check all pending CONNCHECKs for TCP */
58 le = icem->checklist->conncheckl.head;
59 while (le) {
60 struct ice_conncheck *cc = le->data;
61 struct ice_candpair *pair = cc->pair;
62 le = le->next;
63
64 if (pair->state == ICE_CANDPAIR_INPROGRESS &&
65 pair->lcand->attr.compid == conn->compid &&
66 pair->lcand->attr.proto == IPPROTO_TCP &&
67 sa_cmp(&pair->lcand->attr.addr, &conn->laddr, SA_ADDR) &&
68 sa_cmp(&pair->rcand->attr.addr, &conn->paddr, SA_ALL)) {
69
70 trice_printf(icem,
71 " estab: sending pending check"
72 " from %j to %J\n",
73 &pair->lcand->attr.addr,
74 &pair->rcand->attr.addr);
75
76 /* todo: */
77 pair->conn = mem_ref(conn);
78
79 err = trice_conncheck_stun_request(icem->checklist, cc,
80 pair, conn->tc,
81 cc->use_cand);
82 if (err) {
83 DEBUG_WARNING("stun_request error (%m)\n",
84 err);
85 }
86 }
87 }
88
89 out:
90 if (err) {
91 DEBUG_WARNING("estab: errors (%m)\n", err);
92 }
93}
94
95
96/* todo: re-connect if estab and active (with a timer) */
97static void tcp_close_handler(int err, void *arg)
98{
99 struct ice_tcpconn *conn = arg;
100 struct trice *icem = conn->icem;
101 struct le *le;
102
103 trice_printf(conn->icem, "TCP-connection [%J -> %J] closed (%m)\n",
104 &conn->laddr, &conn->paddr, err);
105
106 err = err ? err : ECONNRESET;
107
108 /* note: helper must be closed before tc */
109 conn->shim = mem_deref(conn->shim);
110 conn->tc = mem_deref(conn->tc);
111
112 /* todo: iterate through conncheckl and cancel all checks
113 * that are using this conn
114 */
115
116 le = conn->icem->checkl.head;
117 while (le) {
118 struct ice_candpair *pair = le->data;
119
120 le = le->next;
121
122 if (pair->lcand->attr.compid == conn->compid &&
123 pair->lcand->attr.proto == IPPROTO_TCP &&
124 sa_cmp(&pair->rcand->attr.addr, &conn->paddr, SA_ALL)) {
125
126 trice_candpair_failed(pair, err, 0);
127
128 if (icem->checklist) {
129 icem->checklist->failh(err, 0,
130 pair,
131 icem->checklist->arg);
132 }
133 }
134 }
135
136 mem_deref(conn);
137}
138
139
140static void conn_destructor(void *arg)
141{
142 struct ice_tcpconn *conn = arg;
143
144 list_unlink(&conn->le);
145 mem_deref(conn->shim);
146 mem_deref(conn->tc);
147}
148
149
150/* ts: only for accept */
151int trice_conn_alloc(struct list *connl, struct trice *icem, unsigned compid,
152 bool active, const struct sa *laddr, const struct sa *peer,
153 struct tcp_sock *ts, int layer,
154 tcpconn_frame_h *frameh, void *arg)
155{
156 struct ice_tcpconn *conn;
157 int err = 0;
158
159 if (!connl || !icem || !laddr || !peer || !frameh)
160 return EINVAL;
161
162 conn = mem_zalloc(sizeof(*conn), conn_destructor);
163 if (!conn)
164 return ENOMEM;
165
166 conn->icem = icem;
167 conn->active = active;
168 conn->paddr = *peer;
169 conn->compid = compid;
170 conn->layer = layer;
171 conn->frameh = frameh;
172 conn->arg = arg;
173
174 if (active) {
175
176 trice_printf(conn->icem, "<%p> TCP connecting"
177 " [laddr=%J paddr=%J] ..\n",
178 icem, laddr, peer);
179
180 /* This connection is opened from the local candidate of the
181 pair to the remote candidate of the pair.
182 */
183 err = tcp_conn_alloc(&conn->tc, peer, tcp_estab_handler,
184 NULL, tcp_close_handler,
185 conn);
186 if (err) {
187 DEBUG_WARNING("tcp_conn_alloc [peer=%J] (%m)\n",
188 peer, err);
189 goto out;
190 }
191
192 err = tcp_conn_bind(conn->tc, laddr);
193 if (err) {
194 DEBUG_WARNING("tcp_conn_bind [laddr=%J paddr=%J]"
195 " (%m)\n",
196 laddr, peer, err);
197 goto out;
198 }
199
200 err = tcp_conn_connect(conn->tc, peer);
201 if (err) {
202 /* NOTE: this happens sometimes on OSX when
203 * setting up two S-O connections
204 */
205 if (err == EADDRINUSE) {
206 re_printf("EADDRINUSE\n");
207 err = 0;
208 }
209 else {
210 DEBUG_NOTICE("tcp_conn_connect [peer=%J]"
211 " (%d/%m)\n",
212 peer, err, err);
213 goto out;
214 }
215 }
216 }
217 else {
218 err = tcp_accept(&conn->tc, ts, tcp_estab_handler,
219 NULL, tcp_close_handler, conn);
220 if (err) {
221 tcp_reject(ts);
222 goto out;
223 }
224 }
225
226 err = tcp_conn_local_get(conn->tc, &conn->laddr);
227 if (err)
228 goto out;
229
230 list_append(connl, &conn->le, conn);
231
232 out:
233 if (err)
234 mem_deref(conn);
235
236 return err;
237}
238
239
240/* NOTE: laddr matching is SA_ADDR only */
241struct ice_tcpconn *trice_conn_find(struct list *connl, unsigned compid,
242 const struct sa *laddr,
243 const struct sa *peer)
244{
245 struct le *le;
246
247 for (le = list_head(connl); le; le = le->next) {
248
249 struct ice_tcpconn *conn = le->data;
250
251 if (compid != conn->compid)
252 continue;
253
254 /* NOTE: only for established */
255 if (!conn->estab)
256 continue;
257
258 if (sa_cmp(laddr, &conn->laddr, SA_ADDR) &&
259 sa_cmp(peer, &conn->paddr, SA_ALL))
260 return conn;
261 }
262
263 return NULL;
264}
265
266
267int trice_conn_debug(struct re_printf *pf, const struct ice_tcpconn *conn)
268{
269 int err;
270
271 if (!conn)
272 return 0;
273
274 err = re_hprintf(pf, "... {%u} [%s|%5s] %J - %J "
275 " (usage = %u) ",
276 conn->compid,
277 conn->active ? "Active" : "Passive",
278 conn->estab ? "ESTAB" : " ",
279 &conn->laddr, &conn->paddr,
280 mem_nrefs(conn)-1);
281
282 if (conn->shim)
283 err |= shim_debug(pf, conn->shim);
284
285 return err;
286}