1
/* $Id: stun_simple_client.c 2394 2008-12-23 17:27:53Z bennylp $ */
3
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
* Additional permission under GNU GPL version 3 section 7:
22
* If you modify this program, or any covered work, by linking or
23
* combining it with the OpenSSL project's OpenSSL library (or a
24
* modified version of that library), containing parts covered by the
25
* terms of the OpenSSL or SSLeay licenses, Teluu Inc. (http://www.teluu.com)
26
* grants you additional permission to convey the resulting work.
27
* Corresponding Source for a non-source form of such a combination
28
* shall include the source code for the parts of OpenSSL used as well
29
* as that of the covered work.
31
#include <pjlib-util/stun_simple.h>
32
#include <pjlib-util/errno.h>
37
#include <pj/sock_select.h>
38
#include <pj/string.h>
41
enum { MAX_REQUEST = 4 };
42
static int stun_timer[] = {1000, 1000, 1000, 1000 };
44
#define THIS_FILE "stun_client.c"
45
#define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
48
PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
49
int sock_cnt, pj_sock_t sock[],
50
const pj_str_t *srv1, int port1,
51
const pj_str_t *srv2, int port2,
52
pj_sockaddr_in mapped_addr[])
54
pj_sockaddr_in srv_addr[2];
55
int i, j, send_cnt = 0;
59
pj_uint32_t mapped_addr;
60
pj_uint32_t mapped_port;
64
pj_size_t out_msg_len;
71
pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
76
/* Allocate client records */
77
rec = (struct query_rec*) pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
84
/* Create the outgoing BIND REQUEST message template */
85
status = pjstun_create_bind_req( pool, &out_msg, &out_msg_len,
86
pj_rand(), pj_rand());
87
if (status != PJ_SUCCESS)
90
/* Resolve servers. */
91
status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
92
if (status != PJ_SUCCESS)
95
status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
96
if (status != PJ_SUCCESS)
99
/* Init mapped addresses to zero */
100
pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
102
/* Main retransmission loop. */
103
for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
104
pj_time_val next_tx, now;
110
/* Send messages to servers that has not given us response. */
111
for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
112
for (j=0; j<2 && status==PJ_SUCCESS; ++j) {
113
pjstun_msg_hdr *msg_hdr = (pjstun_msg_hdr*) out_msg;
116
if (rec[i].srv[j].mapped_port != 0)
119
/* Modify message so that we can distinguish response. */
120
msg_hdr->tsx[2] = pj_htonl(i);
121
msg_hdr->tsx[3] = pj_htonl(j);
124
sent_len = out_msg_len;
125
status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
126
(pj_sockaddr_t*)&srv_addr[j],
127
sizeof(pj_sockaddr_in));
128
if (status == PJ_SUCCESS)
133
/* All requests sent.
134
* The loop below will wait for responses until all responses have
135
* been received (i.e. wait_resp==0) or timeout occurs, which then
136
* we'll go to the next retransmission iteration.
139
/* Calculate time of next retransmission. */
140
pj_gettimeofday(&next_tx);
141
next_tx.sec += (stun_timer[send_cnt]/1000);
142
next_tx.msec += (stun_timer[send_cnt]%1000);
143
pj_time_val_normalize(&next_tx);
145
for (pj_gettimeofday(&now), select_rc=1;
146
status==PJ_SUCCESS && select_rc>=1 && wait_resp>0
147
&& PJ_TIME_VAL_LT(now, next_tx);
148
pj_gettimeofday(&now))
153
PJ_TIME_VAL_SUB(timeout, now);
155
for (i=0; i<sock_cnt; ++i) {
156
PJ_FD_SET(sock[i], &r);
159
select_rc = pj_sock_select(PJ_IOQUEUE_MAX_HANDLES, &r, NULL, NULL, &timeout);
163
for (i=0; i<sock_cnt; ++i) {
164
int sock_idx, srv_idx;
168
int addrlen = sizeof(addr);
169
pjstun_mapped_addr_attr *attr;
172
if (!PJ_FD_ISSET(sock[i], &r))
175
len = sizeof(recv_buf);
176
status = pj_sock_recvfrom( sock[i], recv_buf,
178
(pj_sockaddr_t*)&addr,
181
if (status != PJ_SUCCESS) {
182
char errmsg[PJ_ERR_MSG_SIZE];
184
PJ_LOG(4,(THIS_FILE, "recvfrom() error ignored: %s",
185
pj_strerror(status, errmsg,sizeof(errmsg)).ptr));
187
/* Ignore non-PJ_SUCCESS status.
188
* It possible that other SIP entity is currently
189
* sending SIP request to us, and because SIP message
190
* is larger than STUN, we could get EMSGSIZE when
191
* we call recvfrom().
197
status = pjstun_parse_msg(recv_buf, len, &msg);
198
if (status != PJ_SUCCESS) {
199
char errmsg[PJ_ERR_MSG_SIZE];
201
PJ_LOG(4,(THIS_FILE, "STUN parsing error ignored: %s",
202
pj_strerror(status, errmsg,sizeof(errmsg)).ptr));
204
/* Also ignore non-successful parsing. This may not
205
* be STUN response at all. See the comment above.
211
sock_idx = pj_ntohl(msg.hdr->tsx[2]);
212
srv_idx = pj_ntohl(msg.hdr->tsx[3]);
214
if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
215
status = PJLIB_UTIL_ESTUNININDEX;
219
if (pj_ntohs(msg.hdr->type) != PJSTUN_BINDING_RESPONSE) {
220
status = PJLIB_UTIL_ESTUNNOBINDRES;
224
/* From this part, we consider the packet as a valid STUN
225
* response for our request.
229
if (pjstun_msg_find_attr(&msg, PJSTUN_ATTR_ERROR_CODE) != NULL) {
230
status = PJLIB_UTIL_ESTUNRECVERRATTR;
234
attr = (pjstun_mapped_addr_attr*)
235
pjstun_msg_find_attr(&msg, PJSTUN_ATTR_MAPPED_ADDR);
237
status = PJLIB_UTIL_ESTUNNOMAP;
241
rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
242
rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
246
/* The best scenario is if all requests have been replied.
247
* Then we don't need to go to the next retransmission iteration.
253
for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
254
if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
255
rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
257
mapped_addr[i].sin_family = pj_AF_INET();
258
mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
259
mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
261
if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
262
status = PJLIB_UTIL_ESTUNNOTRESPOND;
266
status = PJLIB_UTIL_ESTUNSYMMETRIC;
271
pj_pool_release(pool);
276
if (pool) pj_pool_release(pool);