blob: c80650ad935f7ddb6d50c969401fab8d098363a7 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file perm.c TURN permission handling
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_hash.h>
11#include <re_tmr.h>
12#include <re_sa.h>
13#include <re_md5.h>
14#include <re_udp.h>
15#include <re_stun.h>
16#include <re_turn.h>
17#include "turnc.h"
18
19
20enum {
21 PERM_LIFETIME = 300,
22 PERM_REFRESH = 250,
23};
24
25
26struct perm {
27 struct le he;
28 struct loop_state ls;
29 struct sa peer;
30 struct tmr tmr;
31 struct turnc *turnc;
32 struct stun_ctrans *ct;
33 turnc_perm_h *ph;
34 void *arg;
35};
36
37
38static int createperm_request(struct perm *perm, bool reset_ls);
39
40
41static void destructor(void *arg)
42{
43 struct perm *perm = arg;
44
45 tmr_cancel(&perm->tmr);
46 mem_deref(perm->ct);
47 hash_unlink(&perm->he);
48}
49
50
51static bool hash_cmp_handler(struct le *le, void *arg)
52{
53 const struct perm *perm = le->data;
54
55 return sa_cmp(&perm->peer, arg, SA_ADDR);
56}
57
58
59static struct perm *perm_find(const struct turnc *turnc, const struct sa *peer)
60{
61 return list_ledata(hash_lookup(turnc->perms, sa_hash(peer, SA_ADDR),
62 hash_cmp_handler, (void *)peer));
63}
64
65
66static void timeout(void *arg)
67{
68 struct perm *perm = arg;
69 int err;
70
71 err = createperm_request(perm, true);
72 if (err)
73 perm->turnc->th(err, 0, NULL, NULL, NULL, NULL,
74 perm->turnc->arg);
75}
76
77
78static void createperm_resp_handler(int err, uint16_t scode,
79 const char *reason,
80 const struct stun_msg *msg, void *arg)
81{
82 struct perm *perm = arg;
83
84 if (err || turnc_request_loops(&perm->ls, scode))
85 goto out;
86
87 switch (scode) {
88
89 case 0:
90 tmr_start(&perm->tmr, PERM_REFRESH * 1000, timeout, perm);
91 if (perm->ph) {
92 perm->ph(perm->arg);
93 perm->ph = NULL;
94 perm->arg = NULL;
95 }
96 return;
97
98 case 401:
99 case 438:
100 err = turnc_keygen(perm->turnc, msg);
101 if (err)
102 break;
103
104 err = createperm_request(perm, false);
105 if (err)
106 break;
107
108 return;
109
110 default:
111 break;
112 }
113
114 out:
115 perm->turnc->th(err, scode, reason, NULL, NULL, msg, perm->turnc->arg);
116}
117
118
119static int createperm_request(struct perm *perm, bool reset_ls)
120{
121 struct turnc *t = perm->turnc;
122
123 if (reset_ls)
124 turnc_loopstate_reset(&perm->ls);
125
126 return stun_request(&perm->ct, t->stun, t->proto, t->sock, &t->srv, 0,
127 STUN_METHOD_CREATEPERM,
128 t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash),
129 false, createperm_resp_handler, perm, 5,
130 STUN_ATTR_XOR_PEER_ADDR, &perm->peer,
131 STUN_ATTR_USERNAME, t->realm ? t->username : NULL,
132 STUN_ATTR_REALM, t->realm,
133 STUN_ATTR_NONCE, t->nonce,
134 STUN_ATTR_SOFTWARE, stun_software);
135}
136
137
138/**
139 * Add TURN Permission for a peer
140 *
141 * @param turnc TURN Client
142 * @param peer Peer IP-address
143 * @param ph Permission handler
144 * @param arg Handler argument
145 *
146 * @return 0 if success, otherwise errorcode
147 */
148int turnc_add_perm(struct turnc *turnc, const struct sa *peer,
149 turnc_perm_h *ph, void *arg)
150{
151 struct perm *perm;
152 int err;
153
154 if (!turnc || !peer)
155 return EINVAL;
156
157 if (perm_find(turnc, peer))
158 return 0;
159
160 perm = mem_zalloc(sizeof(*perm), destructor);
161 if (!perm)
162 return ENOMEM;
163
164 hash_append(turnc->perms, sa_hash(peer, SA_ADDR), &perm->he, perm);
165 tmr_init(&perm->tmr);
166 perm->peer = *peer;
167 perm->turnc = turnc;
168 perm->ph = ph;
169 perm->arg = arg;
170
171 err = createperm_request(perm, true);
172 if (err)
173 mem_deref(perm);
174
175 return err;
176}
177
178
179int turnc_perm_hash_alloc(struct hash **ht, uint32_t bsize)
180{
181 return hash_alloc(ht, bsize);
182}