blob: 75d20ee6b52751e184a5c9e189d0c3fd67d10968 [file] [log] [blame]
Benny Prijono4766ffe2005-11-01 17:56:59 +00001/* $Id$
Benny Prijonodd859a62005-11-01 16:42:51 +00002 */
3#include <pj/stun.h>
4#include <pj/pool.h>
5#include <pj/log.h>
6#include <pj/string.h>
7#include <pj/os.h>
8#include <pj/sock_select.h>
9
10enum { MAX_REQUEST = 3 };
11static int stun_timer[] = {1600, 1600, 1600 };
12
13#define THIS_FILE "stunclient"
14#define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
15
16
17PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
18 int sock_cnt, pj_sock_t sock[],
19 const pj_str_t *srv1, int port1,
20 const pj_str_t *srv2, int port2,
21 pj_sockaddr_in mapped_addr[])
22{
23 pj_sockaddr_in srv_addr[2];
24 int i, j, rc, send_cnt = 0;
25 pj_pool_t *pool;
26 struct {
27 struct {
28 pj_uint32_t mapped_addr;
29 pj_uint32_t mapped_port;
30 } srv[2];
31 } *rec;
32 void *out_msg;
33 pj_size_t out_msg_len;
34 int wait_resp = 0;
35 int mapped_status = 0;
36
37 PJ_CHECK_STACK();
38
39 /* Create pool. */
40 pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
41 if (!pool) {
42 mapped_status = PJ_STUN_ERR_MEMORY;
43 return -1;
44 }
45
46 /* Allocate client records */
47 rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
48 if (!rec) {
49 mapped_status = PJ_STUN_ERR_MEMORY;
50 goto on_error;
51 }
52
53 /* Create the outgoing BIND REQUEST message template */
54 rc = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 0, 0);
55 if (rc != 0) {
56 mapped_status = -1;
57 goto on_error;
58 }
59
60 /* Resolve servers. */
61 if (pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1) != 0) {
62 mapped_status = PJ_STUN_ERR_RESOLVE;
63 goto on_error;
64 }
65 if (pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2) != 0) {
66 mapped_status = PJ_STUN_ERR_RESOLVE;
67 goto on_error;
68 }
69
70 /* Init mapped addresses to zero */
71 pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
72
73 /* Main retransmission loop. */
74 for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
75 pj_time_val next_tx, now;
76 pj_fd_set_t r;
77 int select_rc;
78
79 PJ_LOG(4,(THIS_FILE, "STUN retransmit %d, wait_resp=%d",
80 send_cnt, wait_resp));
81
82 PJ_FD_ZERO(&r);
83
84 /* Send messages to servers that has not given us response. */
85 for (i=0; i<sock_cnt && mapped_status==0; ++i) {
86 for (j=0; j<2 && mapped_status==0; ++j) {
87 pj_stun_msg_hdr *msg_hdr = out_msg;
88 pj_ssize_t sent_len;
89
90 if (rec[i].srv[j].mapped_port != 0)
91 continue;
92
93 /* Modify message so that we can distinguish response. */
94 msg_hdr->tsx[2] = pj_htonl(i);
95 msg_hdr->tsx[3] = pj_htonl(j);
96
97 /* Send! */
98 sent_len = out_msg_len;
99 rc = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
100 (pj_sockaddr_t*)&srv_addr[j],
101 sizeof(pj_sockaddr_in));
102 if (sent_len != (int)out_msg_len) {
103 PJ_LOG(4,(THIS_FILE,
104 "Error sending STUN request to %s:%d",
105 LOG_ADDR(srv_addr[j])));
106 mapped_status = PJ_STUN_ERR_TRANSPORT;
107 } else {
108 ++wait_resp;
109 }
110 }
111 }
112
113 /* All requests sent.
114 * The loop below will wait for responses until all responses have
115 * been received (i.e. wait_resp==0) or timeout occurs, which then
116 * we'll go to the next retransmission iteration.
117 */
118
119 /* Calculate time of next retransmission. */
120 pj_gettimeofday(&next_tx);
121 next_tx.sec += (stun_timer[send_cnt]/1000);
122 next_tx.msec += (stun_timer[send_cnt]%1000);
123 pj_time_val_normalize(&next_tx);
124
125 for (pj_gettimeofday(&now), select_rc=1;
126 mapped_status==0 && select_rc==1 && wait_resp>0 && PJ_TIME_VAL_LT(now, next_tx);
127 pj_gettimeofday(&now))
128 {
129 pj_time_val timeout;
130
131 timeout = next_tx;
132 PJ_TIME_VAL_SUB(timeout, now);
133
134 for (i=0; i<sock_cnt; ++i) {
135 PJ_FD_SET(sock[i], &r);
136 }
137
138 select_rc = pj_sock_select(FD_SETSIZE, &r, NULL, NULL, &timeout);
139 if (select_rc < 1)
140 continue;
141
142 for (i=0; i<sock_cnt; ++i) {
143 int sock_idx, srv_idx;
144 pj_ssize_t len;
145 pj_stun_msg msg;
146 pj_sockaddr_in addr;
147 int addrlen = sizeof(addr);
148 pj_stun_mapped_addr_attr *attr;
149 char recv_buf[128];
150
151 if (!PJ_FD_ISSET(sock[i], &r))
152 continue;
153
154 len = sizeof(recv_buf);
155 pj_sock_recvfrom( sock[i], recv_buf,
156 &len, 0,
157 (pj_sockaddr_t*)&addr,
158 &addrlen);
159
160 --wait_resp;
161
162 if (len < 1) {
163 mapped_status = PJ_STUN_ERR_TRANSPORT;
164 continue;
165 }
166
167 if (pj_stun_parse_msg(recv_buf, len, &msg) != 0) {
168 PJ_LOG(4,(THIS_FILE,
169 "Error parsing STUN response from %s:%d",
170 LOG_ADDR(addr)));
171 mapped_status = PJ_STUN_ERR_INVALID_MSG;
172 continue;
173 }
174
175 sock_idx = pj_ntohl(msg.hdr->tsx[2]);
176 srv_idx = pj_ntohl(msg.hdr->tsx[3]);
177
178 if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
179 PJ_LOG(4,(THIS_FILE,
180 "Invalid transaction ID from %s:%d",
181 LOG_ADDR(addr)));
182 mapped_status = PJ_STUN_ERR_INVALID_MSG;
183 continue;
184 }
185
186 if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) {
187 PJ_LOG(4,(THIS_FILE,
188 "Non binding response %d from %s:%d",
189 pj_ntohs(msg.hdr->type), LOG_ADDR(addr)));
190 mapped_status = PJ_STUN_ERR_INVALID_MSG;
191 continue;
192 }
193
194 if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) {
195 PJ_LOG(4,(THIS_FILE,
196 "Got STUN error attribute from %s:%d",
197 LOG_ADDR(addr)));
198 mapped_status = PJ_STUN_ERR_INVALID_MSG;
199 continue;
200 }
201
202 attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR);
203 if (!attr) {
204 PJ_LOG(4,(THIS_FILE,
205 "No mapped address in response from %s:%d",
206 LOG_ADDR(addr)));
207 mapped_status = PJ_STUN_ERR_INVALID_MSG;
208 continue;
209 }
210
211 rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
212 rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
213 }
214 }
215
216 /* The best scenario is if all requests have been replied.
217 * Then we don't need to go to the next retransmission iteration.
218 */
219 if (wait_resp <= 0)
220 break;
221 }
222
223 for (i=0; i<sock_cnt && mapped_status==0; ++i) {
224 if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
225 rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
226 {
227 mapped_addr[i].sin_family = PJ_AF_INET;
228 mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
229 mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
230
231 if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
232 mapped_status = PJ_STUN_ERR_NO_RESPONSE;
233 }
234 } else {
235 mapped_status = PJ_STUN_ERR_SYMETRIC;
236 }
237 }
238
239 pj_pool_release(pool);
240
241 return mapped_status;
242
243on_error:
244 if (pool) pj_pool_release(pool);
245 return -1;
246}
247
248PJ_DEF(const char*) pj_stun_get_err_msg(pj_status_t status)
249{
250 switch (status) {
251 case 0: return "No error";
252 case -1: return "General error";
253 case PJ_STUN_ERR_MEMORY: return "Memory allocation failed";
254 case PJ_STUN_ERR_RESOLVE: return "Invalid IP or unable to resolve STUN server";
255 case PJ_STUN_ERR_TRANSPORT: return "Unable to contact STUN server";
256 case PJ_STUN_ERR_INVALID_MSG: return "Invalid response from STUN server";
257 case PJ_STUN_ERR_NO_RESPONSE: return "No response from STUN server";
258 case PJ_STUN_ERR_SYMETRIC: return "Different mappings are returned from servers";
259 }
260 return "Unknown error";
261}