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