blob: 0ec611603a287bc9a78622a5fd1f386619cf6f8e [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{
38 pj_sock_t sock;
39 pj_sockaddr addr;
40};
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 Prijono879ad1a2008-04-09 09:38:12 +000051 pj_turn_sock *relay;
Benny Prijono5ac0bd72008-03-13 15:11:29 +000052 pj_sockaddr relay_addr;
53
54 struct peer peer[2];
55} g;
56
57static struct options
58{
Benny Prijono879ad1a2008-04-09 09:38:12 +000059 pj_bool_t use_tcp;
60 char *srv_addr;
61 char *srv_port;
62 char *realm;
63 char *user_name;
64 char *password;
65 pj_bool_t use_fingerprint;
Benny Prijono5ac0bd72008-03-13 15:11:29 +000066} o;
67
68
69static int worker_thread(void *unused);
Benny Prijono879ad1a2008-04-09 09:38:12 +000070static void turn_on_rx_data(pj_turn_sock *relay,
Benny Prijono5ac0bd72008-03-13 15:11:29 +000071 const pj_uint8_t *pkt,
72 unsigned pkt_len,
73 const pj_sockaddr_t *peer_addr,
74 unsigned addr_len);
Benny Prijono879ad1a2008-04-09 09:38:12 +000075static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state,
Benny Prijono5ac0bd72008-03-13 15:11:29 +000076 pj_turn_state_t new_state);
77
78
79static void my_perror(const char *title, pj_status_t status)
80{
81 char errmsg[PJ_ERR_MSG_SIZE];
82 pj_strerror(status, errmsg, sizeof(errmsg));
83
84 PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg));
85}
86
87#define CHECK(expr) status=expr; \
88 if (status!=PJ_SUCCESS) { \
89 my_perror(#expr, status); \
90 return status; \
91 }
92
93static int init()
94{
95 int i;
96 pj_status_t status;
97
98 CHECK( pj_init() );
99 CHECK( pjlib_util_init() );
100 CHECK( pjnath_init() );
101
102 /* Check that server is specified */
103 if (!o.srv_addr) {
104 printf("Error: server must be specified\n");
105 return PJ_EINVAL;
106 }
107
108 pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0);
109
110 g.pool = pj_pool_create(&g.cp.factory, "main", 1000, 1000, NULL);
111
112 /* Init global STUN config */
113 pj_stun_config_init(&g.stun_config, &g.cp.factory, 0, NULL, NULL);
114
115 /* Create global timer heap */
116 CHECK( pj_timer_heap_create(g.pool, 1000, &g.stun_config.timer_heap) );
117
118 /* Create global ioqueue */
119 CHECK( pj_ioqueue_create(g.pool, 16, &g.stun_config.ioqueue) );
120
121 /*
122 * Create peers
123 */
124 for (i=0; i<PJ_ARRAY_SIZE(g.peer); ++i) {
125 int len;
126 pj_sockaddr addr;
127 pj_uint16_t port;
128
129 CHECK( pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &g.peer[i].sock) );
130 CHECK( pj_sock_bind_in(g.peer[i].sock, 0, 0) );
131
132 len = sizeof(addr);
133 CHECK( pj_sock_getsockname(g.peer[i].sock, &addr, &len) );
Benny Prijono17d10b52008-03-14 17:56:11 +0000134 port = pj_sockaddr_get_port(&addr);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000135
136 CHECK( pj_gethostip(pj_AF_INET(), &g.peer[i].addr) );
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000137 pj_sockaddr_set_port(&g.peer[i].addr, port);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000138
139 }
140
141 /* Start the worker thread */
142 CHECK( pj_thread_create(g.pool, "stun", &worker_thread, NULL, 0, 0, &g.thread) );
143
144
145 return PJ_SUCCESS;
146}
147
148
149static int shutdown()
150{
151 unsigned i;
152
153 if (g.thread) {
154 g.quit = 1;
155 pj_thread_join(g.thread);
156 pj_thread_destroy(g.thread);
157 g.thread = NULL;
158 }
Benny Prijono879ad1a2008-04-09 09:38:12 +0000159 if (g.relay) {
160 pj_turn_sock_destroy(g.relay);
161 g.relay = NULL;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000162 }
163 for (i=0; i<PJ_ARRAY_SIZE(g.peer); ++i) {
164 if (g.peer[i].sock) {
165 pj_sock_close(g.peer[i].sock);
166 g.peer[i].sock = 0;
167 }
168 }
169 if (g.stun_config.timer_heap) {
170 pj_timer_heap_destroy(g.stun_config.timer_heap);
171 g.stun_config.timer_heap = NULL;
172 }
173 if (g.stun_config.ioqueue) {
174 pj_ioqueue_destroy(g.stun_config.ioqueue);
175 g.stun_config.ioqueue = NULL;
176 }
177 if (g.pool) {
178 pj_pool_release(g.pool);
179 g.pool = NULL;
180 }
181 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
182 pj_caching_pool_destroy(&g.cp);
183
184 return PJ_SUCCESS;
185}
186
187
188static int worker_thread(void *unused)
189{
190 PJ_UNUSED_ARG(unused);
191
192 while (!g.quit) {
193 const pj_time_val delay = {0, 10};
Benny Prijono1add4d52008-03-22 09:33:26 +0000194 int i, n=0;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000195 pj_fd_set_t readset;
196
197 /* Poll ioqueue for the TURN client */
198 pj_ioqueue_poll(g.stun_config.ioqueue, &delay);
199
200 /* Poll the timer heap */
201 pj_timer_heap_poll(g.stun_config.timer_heap, NULL);
202
203 /* Poll peer sockets */
204 PJ_FD_ZERO(&readset);
205 for (i=0; i<PJ_ARRAY_SIZE(g.peer); ++i) {
206 PJ_FD_SET(g.peer[i].sock, &readset);
Benny Prijono1add4d52008-03-22 09:33:26 +0000207 if (g.peer[i].sock > n)
208 n = g.peer[i].sock;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000209 }
210
Benny Prijono1add4d52008-03-22 09:33:26 +0000211 if (pj_sock_select(n+1, &readset, NULL, NULL, &delay) <= 0)
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000212 continue;
213
214 /* Handle incoming data on peer socket */
215 for (i=0; i<PJ_ARRAY_SIZE(g.peer); ++i) {
216 char buf[128];
217 pj_ssize_t len;
218 pj_sockaddr src_addr;
219 int src_addr_len;
220 pj_status_t status;
221
222 if (!PJ_FD_ISSET(g.peer[i].sock, &readset))
223 continue;
224
225 len = sizeof(buf);
226 src_addr_len = sizeof(src_addr);
227
228 status = pj_sock_recvfrom(g.peer[i].sock, buf, &len, 0,
229 &src_addr, &src_addr_len);
230 if (status != PJ_SUCCESS) {
231 my_perror("recvfrom error", status);
232 } else {
233 char addrinfo[80];
234 pj_sockaddr_print(&src_addr, addrinfo, sizeof(addrinfo), 3);
235 PJ_LOG(3,(THIS_FILE, "Peer%d received %d bytes from %s: %.*s",
236 i, len, addrinfo, len, buf));
237 }
238 }
239 }
240
241 return 0;
242}
243
244static pj_status_t create_relay(void)
245{
Benny Prijono879ad1a2008-04-09 09:38:12 +0000246 pj_turn_sock_cb rel_cb;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000247 pj_stun_auth_cred cred;
248 pj_str_t srv;
249 pj_status_t status;
250
Benny Prijono879ad1a2008-04-09 09:38:12 +0000251 if (g.relay) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000252 PJ_LOG(1,(THIS_FILE, "Relay already created"));
253 return -1;
254 }
255
256 pj_bzero(&rel_cb, sizeof(rel_cb));
257 rel_cb.on_rx_data = &turn_on_rx_data;
258 rel_cb.on_state = &turn_on_state;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000259 CHECK( pj_turn_sock_create(&g.stun_config, pj_AF_INET(),
260 (o.use_tcp? PJ_TURN_TP_TCP : PJ_TURN_TP_UDP),
261 &rel_cb, 0,
262 NULL, &g.relay) );
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000263
264 if (o.user_name) {
265 pj_bzero(&cred, sizeof(cred));
266 cred.type = PJ_STUN_AUTH_CRED_STATIC;
267 cred.data.static_cred.realm = pj_str(o.realm);
268 cred.data.static_cred.username = pj_str(o.user_name);
269 cred.data.static_cred.data_type = 0;
270 cred.data.static_cred.data = pj_str(o.password);
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000271 //cred.data.static_cred.nonce = pj_str(o.nonce);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000272 } else {
273 PJ_LOG(2,(THIS_FILE, "Warning: no credential is set"));
274 }
275
276 srv = pj_str(o.srv_addr);
Benny Prijono879ad1a2008-04-09 09:38:12 +0000277 CHECK( pj_turn_sock_init(g.relay, /* the relay */
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000278 &srv, /* srv addr */
279 (o.srv_port?atoi(o.srv_port):PJ_STUN_PORT),/* def port */
280 NULL, /* resolver */
281 (o.user_name?&cred:NULL), /* credential */
282 NULL) /* alloc param */
283 );
284
285 return PJ_SUCCESS;
286}
287
288static void destroy_relay(void)
289{
Benny Prijono879ad1a2008-04-09 09:38:12 +0000290 if (g.relay) {
291 pj_turn_sock_destroy(g.relay);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000292 }
293}
294
295
Benny Prijono879ad1a2008-04-09 09:38:12 +0000296static void turn_on_rx_data(pj_turn_sock *relay,
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000297 const pj_uint8_t *pkt,
298 unsigned pkt_len,
299 const pj_sockaddr_t *peer_addr,
300 unsigned addr_len)
301{
302 char addrinfo[80];
303
304 pj_sockaddr_print(peer_addr, addrinfo, sizeof(addrinfo), 3);
305
306 PJ_LOG(3,(THIS_FILE, "Client received %d bytes data from %s: %.*s",
307 pkt_len, addrinfo, pkt_len, pkt));
308}
309
310
Benny Prijono879ad1a2008-04-09 09:38:12 +0000311static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state,
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000312 pj_turn_state_t new_state)
313{
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000314 PJ_LOG(3,(THIS_FILE, "State %s --> %s", pj_turn_state_name(old_state),
315 pj_turn_state_name(new_state)));
316
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000317 if (new_state == PJ_TURN_STATE_READY) {
318 pj_turn_session_info info;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000319 pj_turn_sock_get_info(relay, &info);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000320 pj_memcpy(&g.relay_addr, &info.relay_addr, sizeof(pj_sockaddr));
Benny Prijono879ad1a2008-04-09 09:38:12 +0000321 } else if (new_state > PJ_TURN_STATE_READY && g.relay) {
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000322 PJ_LOG(3,(THIS_FILE, "Relay shutting down.."));
Benny Prijono879ad1a2008-04-09 09:38:12 +0000323 g.relay = NULL;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000324 }
325}
326
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000327static void menu(void)
328{
329 pj_turn_session_info info;
330 char client_state[20], relay_addr[80], peer0_addr[80], peer1_addr[80];
331
Benny Prijono879ad1a2008-04-09 09:38:12 +0000332 if (g.relay) {
333 pj_turn_sock_get_info(g.relay, &info);
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000334 strcpy(client_state, pj_turn_state_name(info.state));
Benny Prijono17d10b52008-03-14 17:56:11 +0000335 if (info.state >= PJ_TURN_STATE_READY)
336 pj_sockaddr_print(&info.relay_addr, relay_addr, sizeof(relay_addr), 3);
337 else
338 strcpy(relay_addr, "0.0.0.0:0");
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000339 } else {
340 strcpy(client_state, "NULL");
341 strcpy(relay_addr, "0.0.0.0:0");
342 }
343
344 pj_sockaddr_print(&g.peer[0].addr, peer0_addr, sizeof(peer0_addr), 3);
345 pj_sockaddr_print(&g.peer[1].addr, peer1_addr, sizeof(peer1_addr), 3);
346
347
348 puts("\n");
349 puts("+====================================================================+");
350 puts("| CLIENT | PEER-0 |");
351 puts("| | |");
Benny Prijono17d10b52008-03-14 17:56:11 +0000352 printf("| State : %-12s | Address: %-21s |\n",
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000353 client_state, peer0_addr);
Benny Prijono17d10b52008-03-14 17:56:11 +0000354 printf("| Relay addr: %-21s | |\n",
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000355 relay_addr);
356 puts("| | 0 Send data to relay address |");
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000357 puts("| a Allocate relay +--------------------------------+ ");
358 puts("| s,ss Send data to peer 0/1 | PEER-1 |");
359 puts("| b,bb BindChannel to peer 0/1 | |");
360 printf("| x Delete allocation | Address: %-21s |\n",
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000361 peer1_addr);
362 puts("+-----------------------------------+ |");
Benny Prijono879ad1a2008-04-09 09:38:12 +0000363 puts("| q Quit d Dump | 1 Send data to relay adderss |");
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000364 puts("+-----------------------------------+--------------------------------+");
365 printf(">>> ");
366 fflush(stdout);
367}
368
369
370static void console_main(void)
371{
372 while (!g.quit) {
373 char input[32];
374 struct peer *peer;
375 pj_ssize_t len;
376 pj_status_t status;
377
378 menu();
379
380 fgets(input, sizeof(input), stdin);
381
382 switch (input[0]) {
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000383 case 'a':
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000384 create_relay();
385 break;
Benny Prijonof279d672008-04-01 18:23:56 +0000386 case 'd':
387 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
388 break;
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000389 case 's':
Benny Prijono879ad1a2008-04-09 09:38:12 +0000390 if (g.relay == NULL) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000391 puts("Error: no relay");
392 continue;
393 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000394 if (input[1]!='s')
395 peer = &g.peer[0];
396 else
397 peer = &g.peer[1];
398
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000399 strcpy(input, "Hello from client");
Benny Prijono879ad1a2008-04-09 09:38:12 +0000400 status = pj_turn_sock_sendto(g.relay, (const pj_uint8_t*)input,
Benny Prijono68854002008-03-20 19:21:27 +0000401 strlen(input)+1,
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000402 &peer->addr,
403 pj_sockaddr_get_len(&peer->addr));
404 if (status != PJ_SUCCESS)
405 my_perror("turn_udp_sendto() failed", status);
406 break;
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000407 case 'b':
Benny Prijono879ad1a2008-04-09 09:38:12 +0000408 if (g.relay == NULL) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000409 puts("Error: no relay");
410 continue;
411 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000412 if (input[1]!='b')
413 peer = &g.peer[0];
414 else
415 peer = &g.peer[1];
416
Benny Prijono879ad1a2008-04-09 09:38:12 +0000417 status = pj_turn_sock_bind_channel(g.relay, &peer->addr,
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000418 pj_sockaddr_get_len(&peer->addr));
419 if (status != PJ_SUCCESS)
420 my_perror("turn_udp_bind_channel() failed", status);
421 break;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000422 case 'd':
423 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
424 break;
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000425 case 'x':
Benny Prijono879ad1a2008-04-09 09:38:12 +0000426 if (g.relay == NULL) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000427 puts("Error: no relay");
428 continue;
429 }
430 destroy_relay();
431 break;
432 case '0':
433 case '1':
Benny Prijono879ad1a2008-04-09 09:38:12 +0000434 if (g.relay == NULL) {
Benny Prijono68854002008-03-20 19:21:27 +0000435 puts("No relay");
436 break;
437 }
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000438 peer = &g.peer[input[0]-'0'];
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000439 sprintf(input, "Hello from peer%d", input[0]-'0');
440 len = strlen(input)+1;
441 pj_sock_sendto(peer->sock, input, &len, 0, &g.relay_addr,
442 pj_sockaddr_get_len(&g.relay_addr));
443 break;
444 case 'q':
445 g.quit = PJ_TRUE;
446 break;
447 }
448 }
449}
450
451
452static void usage(void)
453{
454 puts("Usage: pjturn_client TARGET [OPTIONS]");
455 puts("");
456 puts("where TARGET is \"host[:port]\"");
457 puts("");
458 puts("and OPTIONS:");
Benny Prijono879ad1a2008-04-09 09:38:12 +0000459 puts(" --tcp, -T Use TCP to connect to TURN server");
460 puts(" --realm, -r REALM Set realm of the credential to REALM");
461 puts(" --username, -u UID Set username of the credential to UID");
462 puts(" --password, -p PASSWD Set password of the credential to PASSWD");
463 puts(" --fingerprint, -F Use fingerprint for outgoing requests");
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000464 puts(" --help, -h");
465}
466
467int main(int argc, char *argv[])
468{
469 struct pj_getopt_option long_options[] = {
470 { "realm", 1, 0, 'r'},
471 { "username", 1, 0, 'u'},
472 { "password", 1, 0, 'p'},
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000473 { "fingerprint",0, 0, 'F'},
Benny Prijono879ad1a2008-04-09 09:38:12 +0000474 { "tcp", 0, 0, 'T'},
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000475 { "help", 0, 0, 'h'}
476 };
477 int c, opt_id;
478 char *pos;
479 pj_status_t status;
480
Benny Prijono879ad1a2008-04-09 09:38:12 +0000481 while((c=pj_getopt_long(argc,argv, "r:u:p:N:hFT", long_options, &opt_id))!=-1) {
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000482 switch (c) {
483 case 'r':
484 o.realm = pj_optarg;
485 break;
486 case 'u':
487 o.user_name = pj_optarg;
488 break;
489 case 'p':
490 o.password = pj_optarg;
491 break;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000492 case 'h':
493 usage();
494 return 0;
495 case 'F':
496 o.use_fingerprint = PJ_TRUE;
497 break;
Benny Prijono879ad1a2008-04-09 09:38:12 +0000498 case 'T':
499 o.use_tcp = PJ_TRUE;
500 break;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000501
502 default:
503 printf("Argument \"%s\" is not valid. Use -h to see help",
504 argv[pj_optind]);
505 return 1;
506 }
507 }
508
509 if (pj_optind == argc) {
510 puts("Error: TARGET is needed");
Benny Prijono68854002008-03-20 19:21:27 +0000511 usage();
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000512 return 1;
513 }
514
515 if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
516 o.srv_addr = argv[pj_optind];
517 *pos = '\0';
518 o.srv_port = pos+1;
519 } else {
520 o.srv_addr = argv[pj_optind];
521 }
522
523 if ((status=init()) != 0)
524 goto on_return;
525
Benny Prijono9e6d60a2008-03-20 16:32:06 +0000526 //if ((status=create_relay()) != 0)
527 // goto on_return;
Benny Prijono5ac0bd72008-03-13 15:11:29 +0000528
529 console_main();
530
531on_return:
532 shutdown();
533 return status ? 1 : 0;
534}
535