blob: b1005305bd3495a39b9fec650adaf464d24f13c8 [file] [log] [blame]
Benny Prijono5ac0bd72008-03-13 15:11:29 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjnath.h>
20#include <pjlib-util.h>
21#include <pjlib.h>
22
23
24#define THIS_FILE "client_main.c"
25#define LOCAL_PORT 1998
26#define BANDWIDTH 64 /* -1 to disable */
27#define LIFETIME 600 /* -1 to disable */
28#define REQ_TRANSPORT -1 /* 0: udp, 1: tcp, -1: disable */
29#define REQ_PORT_PROPS -1 /* -1 to disable */
30#define REQ_IP 0 /* IP address string */
31
32//#define OPTIONS PJ_STUN_NO_AUTHENTICATE
33#define OPTIONS 0
34
35
36struct peer
37{
Benny Prijono3fdf3f62008-06-21 10:18:49 +000038 pj_stun_sock *stun_sock;
39 pj_sockaddr mapped_addr;
Benny Prijono5ac0bd72008-03-13 15:11:29 +000040};
41
42
43static struct global
44{
45 pj_caching_pool cp;
46 pj_pool_t *pool;
47 pj_stun_config stun_config;
48 pj_thread_t *thread;
49 pj_bool_t quit;
50
Benny Prijonobf7d2222008-10-02 17:54:40 +000051 pj_dns_resolver *resolver;
52
Benny Prijono879ad1a2008-04-09 09:38:12 +000053 pj_turn_sock *relay;
Benny Prijono5ac0bd72008-03-13 15:11:29 +000054 pj_sockaddr relay_addr;
55
56 struct peer peer[2];
57} g;
58
59static struct options
60{
Benny Prijono879ad1a2008-04-09 09:38:12 +000061 pj_bool_t use_tcp;
62 char *srv_addr;
63 char *srv_port;
64 char *realm;
65 char *user_name;
66 char *password;
67 pj_bool_t use_fingerprint;
Benny Prijono3fdf3f62008-06-21 10:18:49 +000068 char *stun_server;
Benny Prijonobf7d2222008-10-02 17:54:40 +000069 char *nameserver;
Benny Prijono5ac0bd72008-03-13 15:11:29 +000070} o;
71
72
73static int worker_thread(void *unused);
Benny Prijono879ad1a2008-04-09 09:38:12 +000074static void turn_on_rx_data(pj_turn_sock *relay,
Benny Prijonoff1df042008-06-06 14:47:10 +000075 void *pkt,
Benny Prijono5ac0bd72008-03-13 15:11:29 +000076 unsigned pkt_len,
77 const pj_sockaddr_t *peer_addr,
78 unsigned addr_len);
Benny Prijono879ad1a2008-04-09 09:38:12 +000079static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state,
Benny Prijono5ac0bd72008-03-13 15:11:29 +000080 pj_turn_state_t new_state);
Benny Prijono3fdf3f62008-06-21 10:18:49 +000081static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock,
82 pj_stun_sock_op op,
83 pj_status_t status);
84static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock,
85 void *pkt,
86 unsigned pkt_len,
87 const pj_sockaddr_t *src_addr,
88 unsigned addr_len);
Benny Prijono5ac0bd72008-03-13 15:11:29 +000089
90
91static void my_perror(const char *title, pj_status_t status)
92{
93 char errmsg[PJ_ERR_MSG_SIZE];
94 pj_strerror(status, errmsg, sizeof(errmsg));
95
96 PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg));
97}
98
99#define CHECK(expr) status=expr; \
100 if (status!=PJ_SUCCESS) { \
101 my_perror(#expr, status); \
102 return status; \
103 }
104
105static int init()
106{
107 int i;
108 pj_status_t status;
109
110 CHECK( pj_init() );
111 CHECK( pjlib_util_init() );
112 CHECK( pjnath_init() );
113
114 /* Check that server is specified */
115 if (!o.srv_addr) {
116 printf("Error: server must be specified\n");
117 return PJ_EINVAL;
118 }
119
120 pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0);
121
122 g.pool = pj_pool_create(&g.cp.factory, "main", 1000, 1000, NULL);
123
124 /* Init global STUN config */
125 pj_stun_config_init(&g.stun_config, &g.cp.factory, 0, NULL, NULL);
126
127 /* Create global timer heap */
128 CHECK( pj_timer_heap_create(g.pool, 1000, &g.stun_config.timer_heap) );
129
130 /* Create global ioqueue */
131 CHECK( pj_ioqueue_create(g.pool, 16, &g.stun_config.ioqueue) );
132
133 /*
134 * Create peers
135 */
Benny Prijono24a21852008-04-14 04:04:30 +0000136 for (i=0; i<(int)PJ_ARRAY_SIZE(g.peer); ++i) {
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000137 pj_stun_sock_cb stun_sock_cb;
138 char name[] = "peer0";
Benny Prijono1222ef62008-07-12 10:08:37 +0000139 pj_uint16_t port;
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000140 pj_str_t server;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000141
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000142 pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
143 stun_sock_cb.on_rx_data = &stun_sock_on_rx_data;
144 stun_sock_cb.on_status = &stun_sock_on_status;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000145
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000146 g.peer[i].mapped_addr.addr.sa_family = pj_AF_INET();
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000147
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000148 name[strlen(name)-1] = '0'+i;
149 status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(),
150 &stun_sock_cb, NULL,
151 &g.peer[i], &g.peer[i].stun_sock);
152 if (status != PJ_SUCCESS) {
153 my_perror("pj_stun_sock_create()", status);
154 return status;
155 }
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000156
Benny Prijono1222ef62008-07-12 10:08:37 +0000157 if (o.stun_server) {
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000158 server = pj_str(o.stun_server);
Benny Prijono1222ef62008-07-12 10:08:37 +0000159 port = PJ_STUN_PORT;
160 } else {
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000161 server = pj_str(o.srv_addr);
Benny Prijono1222ef62008-07-12 10:08:37 +0000162 port = (pj_uint16_t)(o.srv_port?atoi(o.srv_port):PJ_STUN_PORT);
163 }
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000164 status = pj_stun_sock_start(g.peer[i].stun_sock, &server,
Benny Prijono1222ef62008-07-12 10:08:37 +0000165 port, NULL);
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000166 if (status != PJ_SUCCESS) {
167 my_perror("pj_stun_sock_start()", status);
168 return status;
169 }
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000170 }
171
172 /* Start the worker thread */
173 CHECK( pj_thread_create(g.pool, "stun", &worker_thread, NULL, 0, 0, &g.thread) );
174
175
176 return PJ_SUCCESS;
177}
178
179
180static int shutdown()
181{
182 unsigned i;
183
184 if (g.thread) {
185 g.quit = 1;
186 pj_thread_join(g.thread);
187 pj_thread_destroy(g.thread);
188 g.thread = NULL;
189 }
Benny Prijono879ad1a2008-04-09 09:38:12 +0000190 if (g.relay) {
191 pj_turn_sock_destroy(g.relay);
192 g.relay = NULL;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000193 }
194 for (i=0; i<PJ_ARRAY_SIZE(g.peer); ++i) {
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000195 if (g.peer[i].stun_sock) {
196 pj_stun_sock_destroy(g.peer[i].stun_sock);
197 g.peer[i].stun_sock = NULL;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000198 }
199 }
200 if (g.stun_config.timer_heap) {
201 pj_timer_heap_destroy(g.stun_config.timer_heap);
202 g.stun_config.timer_heap = NULL;
203 }
204 if (g.stun_config.ioqueue) {
205 pj_ioqueue_destroy(g.stun_config.ioqueue);
206 g.stun_config.ioqueue = NULL;
207 }
208 if (g.pool) {
209 pj_pool_release(g.pool);
210 g.pool = NULL;
211 }
212 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
213 pj_caching_pool_destroy(&g.cp);
214
215 return PJ_SUCCESS;
216}
217
218
219static int worker_thread(void *unused)
220{
221 PJ_UNUSED_ARG(unused);
222
223 while (!g.quit) {
224 const pj_time_val delay = {0, 10};
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000225
226 /* Poll ioqueue for the TURN client */
227 pj_ioqueue_poll(g.stun_config.ioqueue, &delay);
228
229 /* Poll the timer heap */
230 pj_timer_heap_poll(g.stun_config.timer_heap, NULL);
231
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000232 }
233
234 return 0;
235}
236
237static pj_status_t create_relay(void)
238{
Benny Prijono879ad1a2008-04-09 09:38:12 +0000239 pj_turn_sock_cb rel_cb;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000240 pj_stun_auth_cred cred;
241 pj_str_t srv;
242 pj_status_t status;
243
Benny Prijono879ad1a2008-04-09 09:38:12 +0000244 if (g.relay) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000245 PJ_LOG(1,(THIS_FILE, "Relay already created"));
246 return -1;
247 }
248
Benny Prijonobf7d2222008-10-02 17:54:40 +0000249 /* Create DNS resolver if configured */
250 if (o.nameserver) {
251 pj_str_t ns = pj_str(o.nameserver);
252
253 status = pj_dns_resolver_create(&g.cp.factory, "resolver", 0,
254 g.stun_config.timer_heap,
255 g.stun_config.ioqueue, &g.resolver);
256 if (status != PJ_SUCCESS) {
257 PJ_LOG(1,(THIS_FILE, "Error creating resolver (err=%d)", status));
258 return status;
259 }
260
261 status = pj_dns_resolver_set_ns(g.resolver, 1, &ns, NULL);
262 if (status != PJ_SUCCESS) {
263 PJ_LOG(1,(THIS_FILE, "Error configuring nameserver (err=%d)", status));
264 return status;
265 }
266 }
267
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000268 pj_bzero(&rel_cb, sizeof(rel_cb));
269 rel_cb.on_rx_data = &turn_on_rx_data;
270 rel_cb.on_state = &turn_on_state;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000271 CHECK( pj_turn_sock_create(&g.stun_config, pj_AF_INET(),
272 (o.use_tcp? PJ_TURN_TP_TCP : PJ_TURN_TP_UDP),
273 &rel_cb, 0,
274 NULL, &g.relay) );
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000275
276 if (o.user_name) {
277 pj_bzero(&cred, sizeof(cred));
278 cred.type = PJ_STUN_AUTH_CRED_STATIC;
279 cred.data.static_cred.realm = pj_str(o.realm);
280 cred.data.static_cred.username = pj_str(o.user_name);
Benny Prijono24a21852008-04-14 04:04:30 +0000281 cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000282 cred.data.static_cred.data = pj_str(o.password);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000283 //cred.data.static_cred.nonce = pj_str(o.nonce);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000284 } else {
285 PJ_LOG(2,(THIS_FILE, "Warning: no credential is set"));
286 }
287
288 srv = pj_str(o.srv_addr);
Benny Prijonoff1df042008-06-06 14:47:10 +0000289 CHECK(pj_turn_sock_alloc(g.relay, /* the relay */
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000290 &srv, /* srv addr */
291 (o.srv_port?atoi(o.srv_port):PJ_STUN_PORT),/* def port */
Benny Prijonobf7d2222008-10-02 17:54:40 +0000292 g.resolver, /* resolver */
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000293 (o.user_name?&cred:NULL), /* credential */
294 NULL) /* alloc param */
295 );
296
297 return PJ_SUCCESS;
298}
299
300static void destroy_relay(void)
301{
Benny Prijono879ad1a2008-04-09 09:38:12 +0000302 if (g.relay) {
303 pj_turn_sock_destroy(g.relay);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000304 }
305}
306
307
Benny Prijono879ad1a2008-04-09 09:38:12 +0000308static void turn_on_rx_data(pj_turn_sock *relay,
Benny Prijonoff1df042008-06-06 14:47:10 +0000309 void *pkt,
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000310 unsigned pkt_len,
311 const pj_sockaddr_t *peer_addr,
312 unsigned addr_len)
313{
314 char addrinfo[80];
315
316 pj_sockaddr_print(peer_addr, addrinfo, sizeof(addrinfo), 3);
317
318 PJ_LOG(3,(THIS_FILE, "Client received %d bytes data from %s: %.*s",
319 pkt_len, addrinfo, pkt_len, pkt));
320}
321
322
Benny Prijono879ad1a2008-04-09 09:38:12 +0000323static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state,
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000324 pj_turn_state_t new_state)
325{
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000326 PJ_LOG(3,(THIS_FILE, "State %s --> %s", pj_turn_state_name(old_state),
327 pj_turn_state_name(new_state)));
328
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000329 if (new_state == PJ_TURN_STATE_READY) {
330 pj_turn_session_info info;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000331 pj_turn_sock_get_info(relay, &info);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000332 pj_memcpy(&g.relay_addr, &info.relay_addr, sizeof(pj_sockaddr));
Benny Prijono879ad1a2008-04-09 09:38:12 +0000333 } else if (new_state > PJ_TURN_STATE_READY && g.relay) {
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000334 PJ_LOG(3,(THIS_FILE, "Relay shutting down.."));
Benny Prijono879ad1a2008-04-09 09:38:12 +0000335 g.relay = NULL;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000336 }
337}
338
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000339static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock,
340 pj_stun_sock_op op,
341 pj_status_t status)
342{
343 struct peer *peer = (struct peer*) pj_stun_sock_get_user_data(stun_sock);
344
345 if (status == PJ_SUCCESS) {
346 PJ_LOG(4,(THIS_FILE, "peer%d: %s success", peer-g.peer,
347 pj_stun_sock_op_name(op)));
348 } else {
349 char errmsg[PJ_ERR_MSG_SIZE];
350 pj_strerror(status, errmsg, sizeof(errmsg));
351 PJ_LOG(1,(THIS_FILE, "peer%d: %s error: %s", peer-g.peer,
352 pj_stun_sock_op_name(op), errmsg));
353 return PJ_FALSE;
354 }
355
356 if (op==PJ_STUN_SOCK_BINDING_OP || op==PJ_STUN_SOCK_KEEP_ALIVE_OP) {
357 pj_stun_sock_info info;
358 int cmp;
359
360 pj_stun_sock_get_info(stun_sock, &info);
361 cmp = pj_sockaddr_cmp(&info.mapped_addr, &peer->mapped_addr);
362
363 if (cmp) {
364 char straddr[PJ_INET6_ADDRSTRLEN+10];
365
366 pj_sockaddr_cp(&peer->mapped_addr, &info.mapped_addr);
367 pj_sockaddr_print(&peer->mapped_addr, straddr, sizeof(straddr), 3);
368 PJ_LOG(3,(THIS_FILE, "peer%d: STUN mapped address is %s",
369 peer-g.peer, straddr));
370 }
371 }
372
373 return PJ_TRUE;
374}
375
376static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock,
377 void *pkt,
378 unsigned pkt_len,
379 const pj_sockaddr_t *src_addr,
380 unsigned addr_len)
381{
382 struct peer *peer = (struct peer*) pj_stun_sock_get_user_data(stun_sock);
383 char straddr[PJ_INET6_ADDRSTRLEN+10];
384
385 ((char*)pkt)[pkt_len] = '\0';
386
387 pj_sockaddr_print(src_addr, straddr, sizeof(straddr), 3);
388 PJ_LOG(3,(THIS_FILE, "peer%d: received %d bytes data from %s: %s",
389 peer-g.peer, pkt_len, straddr, (char*)pkt));
390
391 return PJ_TRUE;
392}
393
394
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000395static void menu(void)
396{
397 pj_turn_session_info info;
398 char client_state[20], relay_addr[80], peer0_addr[80], peer1_addr[80];
399
Benny Prijono879ad1a2008-04-09 09:38:12 +0000400 if (g.relay) {
401 pj_turn_sock_get_info(g.relay, &info);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000402 strcpy(client_state, pj_turn_state_name(info.state));
Benny Prijono17d10b52008-03-14 17:56:11 +0000403 if (info.state >= PJ_TURN_STATE_READY)
404 pj_sockaddr_print(&info.relay_addr, relay_addr, sizeof(relay_addr), 3);
405 else
406 strcpy(relay_addr, "0.0.0.0:0");
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000407 } else {
408 strcpy(client_state, "NULL");
409 strcpy(relay_addr, "0.0.0.0:0");
410 }
411
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000412 pj_sockaddr_print(&g.peer[0].mapped_addr, peer0_addr, sizeof(peer0_addr), 3);
413 pj_sockaddr_print(&g.peer[1].mapped_addr, peer1_addr, sizeof(peer1_addr), 3);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000414
415
416 puts("\n");
417 puts("+====================================================================+");
418 puts("| CLIENT | PEER-0 |");
419 puts("| | |");
Benny Prijono17d10b52008-03-14 17:56:11 +0000420 printf("| State : %-12s | Address: %-21s |\n",
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000421 client_state, peer0_addr);
Benny Prijono17d10b52008-03-14 17:56:11 +0000422 printf("| Relay addr: %-21s | |\n",
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000423 relay_addr);
424 puts("| | 0 Send data to relay address |");
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000425 puts("| a Allocate relay +--------------------------------+ ");
426 puts("| s,ss Send data to peer 0/1 | PEER-1 |");
427 puts("| b,bb BindChannel to peer 0/1 | |");
428 printf("| x Delete allocation | Address: %-21s |\n",
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000429 peer1_addr);
430 puts("+-----------------------------------+ |");
Benny Prijono879ad1a2008-04-09 09:38:12 +0000431 puts("| q Quit d Dump | 1 Send data to relay adderss |");
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000432 puts("+-----------------------------------+--------------------------------+");
433 printf(">>> ");
434 fflush(stdout);
435}
436
437
438static void console_main(void)
439{
440 while (!g.quit) {
441 char input[32];
442 struct peer *peer;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000443 pj_status_t status;
444
445 menu();
446
447 fgets(input, sizeof(input), stdin);
448
449 switch (input[0]) {
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000450 case 'a':
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000451 create_relay();
452 break;
Benny Prijonof279d672008-04-01 18:23:56 +0000453 case 'd':
454 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
455 break;
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000456 case 's':
Benny Prijono879ad1a2008-04-09 09:38:12 +0000457 if (g.relay == NULL) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000458 puts("Error: no relay");
459 continue;
460 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000461 if (input[1]!='s')
462 peer = &g.peer[0];
463 else
464 peer = &g.peer[1];
465
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000466 strcpy(input, "Hello from client");
Benny Prijono879ad1a2008-04-09 09:38:12 +0000467 status = pj_turn_sock_sendto(g.relay, (const pj_uint8_t*)input,
Benny Prijono68854002008-03-20 19:21:27 +0000468 strlen(input)+1,
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000469 &peer->mapped_addr,
470 pj_sockaddr_get_len(&peer->mapped_addr));
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000471 if (status != PJ_SUCCESS)
472 my_perror("turn_udp_sendto() failed", status);
473 break;
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000474 case 'b':
Benny Prijono879ad1a2008-04-09 09:38:12 +0000475 if (g.relay == NULL) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000476 puts("Error: no relay");
477 continue;
478 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000479 if (input[1]!='b')
480 peer = &g.peer[0];
481 else
482 peer = &g.peer[1];
483
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000484 status = pj_turn_sock_bind_channel(g.relay, &peer->mapped_addr,
485 pj_sockaddr_get_len(&peer->mapped_addr));
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000486 if (status != PJ_SUCCESS)
487 my_perror("turn_udp_bind_channel() failed", status);
488 break;
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000489 case 'x':
Benny Prijono879ad1a2008-04-09 09:38:12 +0000490 if (g.relay == NULL) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000491 puts("Error: no relay");
492 continue;
493 }
494 destroy_relay();
495 break;
496 case '0':
497 case '1':
Benny Prijono879ad1a2008-04-09 09:38:12 +0000498 if (g.relay == NULL) {
Benny Prijono68854002008-03-20 19:21:27 +0000499 puts("No relay");
500 break;
501 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000502 peer = &g.peer[input[0]-'0'];
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000503 sprintf(input, "Hello from peer%d", input[0]-'0');
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000504 pj_stun_sock_sendto(peer->stun_sock, NULL, input, strlen(input)+1, 0,
505 &g.relay_addr, pj_sockaddr_get_len(&g.relay_addr));
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000506 break;
507 case 'q':
508 g.quit = PJ_TRUE;
509 break;
510 }
511 }
512}
513
514
515static void usage(void)
516{
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000517 puts("Usage: pjturn_client TURN-SERVER [OPTIONS]");
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000518 puts("");
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000519 puts("where TURN-SERVER is \"host[:port]\"");
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000520 puts("");
521 puts("and OPTIONS:");
Benny Prijono879ad1a2008-04-09 09:38:12 +0000522 puts(" --tcp, -T Use TCP to connect to TURN server");
523 puts(" --realm, -r REALM Set realm of the credential to REALM");
524 puts(" --username, -u UID Set username of the credential to UID");
525 puts(" --password, -p PASSWD Set password of the credential to PASSWD");
526 puts(" --fingerprint, -F Use fingerprint for outgoing requests");
Benny Prijonobf7d2222008-10-02 17:54:40 +0000527 puts(" --stun-srv, -S NAME Use this STUN srv instead of TURN for Binding discovery");
528 puts(" --nameserver, -N IP Activate DNS SRV, use this DNS server");
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000529 puts(" --help, -h");
530}
531
532int main(int argc, char *argv[])
533{
534 struct pj_getopt_option long_options[] = {
535 { "realm", 1, 0, 'r'},
536 { "username", 1, 0, 'u'},
537 { "password", 1, 0, 'p'},
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000538 { "fingerprint",0, 0, 'F'},
Benny Prijono879ad1a2008-04-09 09:38:12 +0000539 { "tcp", 0, 0, 'T'},
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000540 { "help", 0, 0, 'h'},
Benny Prijonobf7d2222008-10-02 17:54:40 +0000541 { "stun-srv", 1, 0, 'S'},
542 { "nameserver", 1, 0, 'N'}
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000543 };
544 int c, opt_id;
545 char *pos;
546 pj_status_t status;
547
Benny Prijonobf7d2222008-10-02 17:54:40 +0000548 while((c=pj_getopt_long(argc,argv, "r:u:p:S:N:hFT", long_options, &opt_id))!=-1) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000549 switch (c) {
550 case 'r':
551 o.realm = pj_optarg;
552 break;
553 case 'u':
554 o.user_name = pj_optarg;
555 break;
556 case 'p':
557 o.password = pj_optarg;
558 break;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000559 case 'h':
560 usage();
561 return 0;
562 case 'F':
563 o.use_fingerprint = PJ_TRUE;
564 break;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000565 case 'T':
566 o.use_tcp = PJ_TRUE;
567 break;
Benny Prijono3fdf3f62008-06-21 10:18:49 +0000568 case 'S':
569 o.stun_server = pj_optarg;
570 break;
Benny Prijonobf7d2222008-10-02 17:54:40 +0000571 case 'N':
572 o.nameserver = pj_optarg;
573 break;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000574 default:
575 printf("Argument \"%s\" is not valid. Use -h to see help",
576 argv[pj_optind]);
577 return 1;
578 }
579 }
580
581 if (pj_optind == argc) {
582 puts("Error: TARGET is needed");
Benny Prijono68854002008-03-20 19:21:27 +0000583 usage();
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000584 return 1;
585 }
586
587 if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
588 o.srv_addr = argv[pj_optind];
589 *pos = '\0';
590 o.srv_port = pos+1;
591 } else {
592 o.srv_addr = argv[pj_optind];
593 }
594
595 if ((status=init()) != 0)
596 goto on_return;
597
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000598 //if ((status=create_relay()) != 0)
599 // goto on_return;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000600
601 console_main();
602
603on_return:
604 shutdown();
605 return status ? 1 : 0;
606}
607