1 /**
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 
20 enum {
21 	PERM_LIFETIME = 300,
22 	PERM_REFRESH = 250,
23 };
24 
25 
26 struct 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 
38 static int createperm_request(struct perm *perm, bool reset_ls);
39 
40 
41 static 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 
51 static 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 
59 static 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 
66 static 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 
78 static 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 
119 static 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  */
148 int 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 
179 int turnc_perm_hash_alloc(struct hash **ht, uint32_t bsize)
180 {
181 	return hash_alloc(ht, bsize);
182 }
183