blob: c5c73441c1743bf206b89392f5e63c425f8b1153 [file] [log] [blame]
Benny Prijono91169ac2007-02-22 02:09:23 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2005 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 <pjlib-util.h>
20#include <pjlib.h>
Benny Prijonod0a35852007-02-23 01:07:54 +000021
Benny Prijono91169ac2007-02-22 02:09:23 +000022
23#define THIS_FILE "client_main.c"
Benny Prijonoe4c40202007-03-10 11:19:11 +000024#define BANDWIDTH 64 /* -1 to disable */
25#define LIFETIME 30 /* -1 to disable */
26#define REQ_TRANSPORT -1 /* 0: udp, 1: tcp, -1: disable */
27#define REQ_PORT_PROPS -1 /* -1 to disable */
28#define REQ_IP NULL /* IP address string */
29
Benny Prijonod0a35852007-02-23 01:07:54 +000030
Benny Prijono4df62612007-03-01 23:39:08 +000031static struct global
32{
33 pj_stun_endpoint *endpt;
34 pj_pool_t *pool;
35 pj_caching_pool cp;
36 pj_timer_heap_t *th;
37 pj_stun_session *sess;
Benny Prijono4df62612007-03-01 23:39:08 +000038 pj_sock_t sock;
39 pj_thread_t *thread;
40 pj_bool_t quit;
Benny Prijonoe4c40202007-03-10 11:19:11 +000041 pj_sockaddr_in peer_addr;
42 pj_sockaddr_in srv_addr; /**< server addr */
Benny Prijono4df62612007-03-01 23:39:08 +000043
44} g;
45
46static struct options
47{
Benny Prijonoe4c40202007-03-10 11:19:11 +000048 char *srv_addr;
49 char *srv_port;
Benny Prijono4df62612007-03-01 23:39:08 +000050 char *realm;
51 char *user_name;
52 char *password;
Benny Prijonocc8febb2007-03-03 02:16:36 +000053 char *nonce;
Benny Prijono4df62612007-03-01 23:39:08 +000054 pj_bool_t use_fingerprint;
55} o;
56
Benny Prijonod0a35852007-02-23 01:07:54 +000057
58static my_perror(const char *title, pj_status_t status)
59{
60 char errmsg[PJ_ERR_MSG_SIZE];
61 pj_strerror(status, errmsg, sizeof(errmsg));
62
63 PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg));
64}
65
Benny Prijono4df62612007-03-01 23:39:08 +000066static pj_status_t on_send_msg(pj_stun_session *sess,
Benny Prijonod0a35852007-02-23 01:07:54 +000067 const void *pkt,
68 pj_size_t pkt_size,
Benny Prijonoe4c40202007-03-10 11:19:11 +000069 const pj_sockaddr_t *srv_addr,
Benny Prijono4df62612007-03-01 23:39:08 +000070 unsigned addr_len)
Benny Prijonod0a35852007-02-23 01:07:54 +000071{
Benny Prijonod0a35852007-02-23 01:07:54 +000072 pj_ssize_t len;
73 pj_status_t status;
74
Benny Prijonod0a35852007-02-23 01:07:54 +000075 len = pkt_size;
Benny Prijonoe4c40202007-03-10 11:19:11 +000076 status = pj_sock_sendto(g.sock, pkt, &len, 0, srv_addr, addr_len);
Benny Prijonod0a35852007-02-23 01:07:54 +000077
78 if (status != PJ_SUCCESS)
79 my_perror("Error sending packet", status);
80
81 return status;
82}
83
Benny Prijono4df62612007-03-01 23:39:08 +000084static void on_request_complete(pj_stun_session *sess,
85 pj_status_t status,
86 pj_stun_tx_data *tdata,
87 const pj_stun_msg *response)
Benny Prijonod0a35852007-02-23 01:07:54 +000088{
Benny Prijono4df62612007-03-01 23:39:08 +000089 if (status == PJ_SUCCESS) {
90 puts("Client transaction completes");
91 } else {
92 my_perror("Client transaction error", status);
93 }
Benny Prijonod0a35852007-02-23 01:07:54 +000094}
95
Benny Prijono4df62612007-03-01 23:39:08 +000096static int worker_thread(void *unused)
Benny Prijonod0a35852007-02-23 01:07:54 +000097{
Benny Prijono4df62612007-03-01 23:39:08 +000098 PJ_UNUSED_ARG(unused);
Benny Prijonod0a35852007-02-23 01:07:54 +000099
Benny Prijono4df62612007-03-01 23:39:08 +0000100 while (!g.quit) {
101 pj_time_val timeout = {0, 50};
102 pj_fd_set_t readset;
Benny Prijonod0a35852007-02-23 01:07:54 +0000103 int n;
Benny Prijonod0a35852007-02-23 01:07:54 +0000104
Benny Prijono4df62612007-03-01 23:39:08 +0000105 pj_timer_heap_poll(g.th, NULL);
Benny Prijonod0a35852007-02-23 01:07:54 +0000106
Benny Prijono4df62612007-03-01 23:39:08 +0000107 PJ_FD_ZERO(&readset);
108 PJ_FD_SET(g.sock, &readset);
Benny Prijonod0a35852007-02-23 01:07:54 +0000109
Benny Prijono4df62612007-03-01 23:39:08 +0000110 n = pj_sock_select(g.sock+1, &readset, NULL, NULL, &timeout);
111 if (n > 0) {
112 if (PJ_FD_ISSET(g.sock, &readset)) {
113 char buffer[512];
114 pj_ssize_t len;
115 pj_sockaddr_in addr;
116 int addrlen;
117 pj_status_t rc;
Benny Prijonod0a35852007-02-23 01:07:54 +0000118
Benny Prijono4df62612007-03-01 23:39:08 +0000119 len = sizeof(buffer);
120 addrlen = sizeof(addr);
121 rc = pj_sock_recvfrom(g.sock, buffer, &len, 0, &addr, &addrlen);
122 if (rc == PJ_SUCCESS && len > 0) {
123 rc = pj_stun_session_on_rx_pkt(g.sess, buffer, len,
124 PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET,
125 NULL, &addr, addrlen);
126 if (rc != PJ_SUCCESS)
127 my_perror("Error processing packet", rc);
128 }
Benny Prijonod0a35852007-02-23 01:07:54 +0000129 }
Benny Prijono4df62612007-03-01 23:39:08 +0000130 } else if (n < 0)
131 pj_thread_sleep(50);
Benny Prijonod0a35852007-02-23 01:07:54 +0000132 }
133
Benny Prijonod0a35852007-02-23 01:07:54 +0000134 return 0;
135}
136
Benny Prijono4df62612007-03-01 23:39:08 +0000137static int init()
138{
139 pj_sockaddr_in addr;
140 pj_stun_session_cb stun_cb;
141 pj_status_t status;
142
143 g.sock = PJ_INVALID_SOCKET;
144
145 status = pj_init();
146 status = pjlib_util_init();
147
148 pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0);
149
Benny Prijonoe4c40202007-03-10 11:19:11 +0000150 if (o.srv_addr) {
Benny Prijono4df62612007-03-01 23:39:08 +0000151 pj_str_t s;
152 pj_uint16_t port;
153
Benny Prijonoe4c40202007-03-10 11:19:11 +0000154 if (o.srv_port)
155 port = (pj_uint16_t) atoi(o.srv_port);
Benny Prijono4df62612007-03-01 23:39:08 +0000156 else
157 port = PJ_STUN_PORT;
158
Benny Prijonoe4c40202007-03-10 11:19:11 +0000159 status = pj_sockaddr_in_init(&g.srv_addr, pj_cstr(&s, o.srv_addr), port);
Benny Prijono4df62612007-03-01 23:39:08 +0000160 if (status != PJ_SUCCESS) {
161 my_perror("Invalid address", status);
162 return status;
163 }
164
Benny Prijonoe4c40202007-03-10 11:19:11 +0000165 printf("Destination address set to %s:%d\n", o.srv_addr, (int)port);
Benny Prijono4df62612007-03-01 23:39:08 +0000166 } else {
167 printf("Error: address must be specified\n");
168 return PJ_EINVAL;
169 }
170
171 g.pool = pj_pool_create(&g.cp.factory, NULL, 1000, 1000, NULL);
172
173 status = pj_timer_heap_create(g.pool, 1000, &g.th);
174 pj_assert(status == PJ_SUCCESS);
175
176 status = pj_stun_endpoint_create(&g.cp.factory, 0, NULL, g.th, &g.endpt);
177 pj_assert(status == PJ_SUCCESS);
178
179 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &g.sock);
180 pj_assert(status == PJ_SUCCESS);
181
182 status = pj_sockaddr_in_init(&addr, NULL, 0);
183 pj_assert(status == PJ_SUCCESS);
184
185 pj_memset(&stun_cb, 0, sizeof(stun_cb));
186 stun_cb.on_send_msg = &on_send_msg;
187 stun_cb.on_request_complete = &on_request_complete;
188
Benny Prijonocc8febb2007-03-03 02:16:36 +0000189 status = pj_stun_session_create(g.endpt, NULL, &stun_cb,
190 o.use_fingerprint!=0, &g.sess);
Benny Prijono4df62612007-03-01 23:39:08 +0000191 pj_assert(status == PJ_SUCCESS);
192
Benny Prijonocc8febb2007-03-03 02:16:36 +0000193 if (o.user_name) {
194 pj_stun_auth_cred cred;
Benny Prijono4df62612007-03-01 23:39:08 +0000195
Benny Prijonocc8febb2007-03-03 02:16:36 +0000196 pj_bzero(&cred, sizeof(cred));
Benny Prijono4df62612007-03-01 23:39:08 +0000197
Benny Prijonocc8febb2007-03-03 02:16:36 +0000198 cred.type = PJ_STUN_AUTH_CRED_STATIC;
199 cred.data.static_cred.realm = pj_str(o.realm);
200 cred.data.static_cred.username = pj_str(o.user_name);
201 cred.data.static_cred.data_type = 0;
202 cred.data.static_cred.data = pj_str(o.password);
203 cred.data.static_cred.nonce = pj_str(o.nonce);
204
205 pj_stun_session_set_credential(g.sess, &cred);
206 puts("Session credential set");
Benny Prijono4df62612007-03-01 23:39:08 +0000207 } else {
208 puts("Credential not set");
209 }
210
Benny Prijono4df62612007-03-01 23:39:08 +0000211 status = pj_thread_create(g.pool, "stun", &worker_thread, NULL,
212 0, 0, &g.thread);
213 if (status != PJ_SUCCESS)
214 return status;
215
216 return PJ_SUCCESS;
217}
218
219
220static int shutdown()
221{
222 if (g.thread) {
223 g.quit = 1;
224 pj_thread_join(g.thread);
225 pj_thread_destroy(g.thread);
226 g.thread = NULL;
227 }
228 if (g.sess)
229 pj_stun_session_destroy(g.sess);
230 if (g.endpt)
231 pj_stun_endpoint_destroy(g.endpt);
232 if (g.sock != PJ_INVALID_SOCKET)
233 pj_sock_close(g.sock);
234 if (g.th)
235 pj_timer_heap_destroy(g.th);
236 if (g.pool)
237 pj_pool_release(g.pool);
238
239 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
240 pj_caching_pool_destroy(&g.cp);
241
242 return PJ_SUCCESS;
243}
244
Benny Prijono62923f22007-03-09 23:25:11 +0000245static void send_bind_request(void)
246{
247 pj_stun_tx_data *tdata;
248 pj_status_t rc;
249
250 rc = pj_stun_session_create_req(g.sess, PJ_STUN_BINDING_REQUEST, &tdata);
251 pj_assert(rc == PJ_SUCCESS);
252
253 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000254 &g.srv_addr, sizeof(g.srv_addr),
Benny Prijono62923f22007-03-09 23:25:11 +0000255 tdata);
256 if (rc != PJ_SUCCESS)
257 my_perror("Error sending STUN request", rc);
258}
259
Benny Prijonoe4c40202007-03-10 11:19:11 +0000260static void send_allocate_request(pj_bool_t allocate)
Benny Prijono62923f22007-03-09 23:25:11 +0000261{
Benny Prijonoe4c40202007-03-10 11:19:11 +0000262 pj_stun_tx_data *tdata;
263 pj_status_t rc;
264
265 rc = pj_stun_session_create_req(g.sess, PJ_STUN_ALLOCATE_REQUEST, &tdata);
266 pj_assert(rc == PJ_SUCCESS);
267
268
269 if (BANDWIDTH != -1) {
270 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
271 PJ_STUN_ATTR_BANDWIDTH, BANDWIDTH);
272 }
273
274 if (!allocate) {
275 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
276 PJ_STUN_ATTR_LIFETIME, 0);
277
278 } else {
279 if (LIFETIME != -1) {
280 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
281 PJ_STUN_ATTR_LIFETIME, LIFETIME);
282 }
283
284 if (REQ_TRANSPORT != -1) {
285 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
286 PJ_STUN_ATTR_REQ_TRANSPORT, REQ_TRANSPORT);
287 }
288
289 if (REQ_PORT_PROPS != -1) {
290 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
291 PJ_STUN_ATTR_REQ_PORT_PROPS, REQ_PORT_PROPS);
292 }
293
294 if (REQ_IP != NULL) {
295 pj_sockaddr_in addr;
296 pj_str_t tmp;
297
298 pj_sockaddr_in_init(&addr, pj_cstr(&tmp, REQ_IP), 0);
299 pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg,
300 PJ_STUN_ATTR_REQ_IP, PJ_FALSE,
301 &addr, sizeof(addr));
302 }
303 }
304
305 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
306 &g.srv_addr, sizeof(g.srv_addr),
307 tdata);
308 pj_assert(rc == PJ_SUCCESS);
Benny Prijono62923f22007-03-09 23:25:11 +0000309}
310
Benny Prijonoe4c40202007-03-10 11:19:11 +0000311static void send_sad_request(pj_bool_t set)
Benny Prijono62923f22007-03-09 23:25:11 +0000312{
Benny Prijonoe4c40202007-03-10 11:19:11 +0000313 pj_stun_tx_data *tdata;
314 pj_status_t rc;
315
316 if (g.peer_addr.sin_addr.s_addr == 0 ||
317 g.peer_addr.sin_port == 0)
318 {
319 puts("Error: peer address is not set");
320 return;
321 }
322
323 rc = pj_stun_session_create_req(g.sess, PJ_STUN_ALLOCATE_REQUEST, &tdata);
324 pj_assert(rc == PJ_SUCCESS);
325
326 if (set) {
327 pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg,
328 PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
329 &g.peer_addr, sizeof(g.peer_addr));
330 }
331
332 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
333 &g.srv_addr, sizeof(g.srv_addr),
334 tdata);
335 pj_assert(rc == PJ_SUCCESS);
Benny Prijono62923f22007-03-09 23:25:11 +0000336}
337
338static void send_send_ind(void)
339{
340}
341
342static void send_raw_data(void)
343{
344}
345
Benny Prijonoe4c40202007-03-10 11:19:11 +0000346static void set_peer_addr(void)
347{
348 char ip_addr[64];
349 pj_str_t tmp;
350 pj_sockaddr_in addr;
351 int port;
352
353 printf("Current peer address: %s:%d\n",
354 pj_inet_ntoa(g.peer_addr.sin_addr),
355 pj_ntohs(g.peer_addr.sin_port));
356
357 printf("Input peer address in IP:PORT format: ");
358 fflush(stdout);
359
360 if (scanf("%s:%d", ip_addr, &port) != 2) {
361 puts("Error.");
362 return;
363 }
364
365 if (pj_sockaddr_in_init(&addr, pj_cstr(&tmp,ip_addr), (pj_uint16_t)port) != PJ_SUCCESS) {
366 puts("Error: invalid address");
367 return;
368 }
369
370 g.peer_addr = addr;
371}
372
Benny Prijono4df62612007-03-01 23:39:08 +0000373static void menu(void)
374{
375 puts("Menu:");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000376 printf(" pr Set peer address (currently %s:%d)\n",
377 pj_inet_ntoa(g.peer_addr.sin_addr), pj_ntohs(g.peer_addr.sin_port));
378 puts("");
Benny Prijono62923f22007-03-09 23:25:11 +0000379 puts(" br Send Bind request");
380 puts(" ar Send Allocate request");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000381 puts(" dr Send de-Allocate request");
Benny Prijono62923f22007-03-09 23:25:11 +0000382 puts(" sr Send Set Active Indication request");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000383 puts(" cr Send clear Active Indication request");
384 puts(" si Send data with Send Indication");
Benny Prijono62923f22007-03-09 23:25:11 +0000385 puts(" rw Send raw data");
386 puts(" q Quit");
Benny Prijono4df62612007-03-01 23:39:08 +0000387 puts("");
388 printf("Choice: ");
389}
390
Benny Prijonoe4c40202007-03-10 11:19:11 +0000391
Benny Prijono4df62612007-03-01 23:39:08 +0000392static void console_main(void)
393{
394 while (!g.quit) {
395 char input[10];
396
397 menu();
398
399 fgets(input, sizeof(input), stdin);
400
Benny Prijono62923f22007-03-09 23:25:11 +0000401 if (input[0]=='b' && input[1]=='r') {
402 send_bind_request();
403
404 } else if (input[0]=='a' && input[1]=='r') {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000405 send_allocate_request(PJ_TRUE);
406
407 } else if (input[0]=='d' && input[1]=='r') {
408 send_allocate_request(PJ_FALSE);
Benny Prijono62923f22007-03-09 23:25:11 +0000409
410 } else if (input[0]=='s' && input[1]=='r') {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000411 send_sad_request(PJ_TRUE);
412
413 } else if (input[0]=='c' && input[1]=='r') {
414 send_sad_request(PJ_FALSE);
Benny Prijono62923f22007-03-09 23:25:11 +0000415
416 } else if (input[0]=='s' && input[1]=='i') {
417 send_send_ind();
418
419 } else if (input[0]=='r' && input[1]=='w') {
420 send_raw_data();
421
Benny Prijonoe4c40202007-03-10 11:19:11 +0000422 } else if (input[0]=='p' && input[1]=='r') {
423 set_peer_addr();
424
Benny Prijono62923f22007-03-09 23:25:11 +0000425 } else if (input[0]=='q') {
Benny Prijono4df62612007-03-01 23:39:08 +0000426 g.quit = 1;
Benny Prijono4df62612007-03-01 23:39:08 +0000427 }
428 }
429}
430
431
432static void usage(void)
433{
434 puts("Usage: pjstun_client TARGET [OPTIONS]");
435 puts("");
436 puts("where TARGET is \"host[:port]\"");
437 puts("");
438 puts("and OPTIONS:");
439 puts(" --realm, -r Set realm of the credential");
440 puts(" --username, -u Set username of the credential");
441 puts(" --password, -p Set password of the credential");
Benny Prijonocc8febb2007-03-03 02:16:36 +0000442 puts(" --nonce, -N Set NONCE");
Benny Prijono4df62612007-03-01 23:39:08 +0000443 puts(" --fingerprint, -F Use fingerprint for outgoing requests");
444 puts(" --help, -h");
445}
446
447int main(int argc, char *argv[])
448{
449 struct pj_getopt_option long_options[] = {
450 { "realm", 1, 0, 'r'},
451 { "username", 1, 0, 'u'},
452 { "password", 1, 0, 'p'},
Benny Prijonocc8febb2007-03-03 02:16:36 +0000453 { "nonce", 1, 0, 'N'},
Benny Prijono4df62612007-03-01 23:39:08 +0000454 { "fingerprint",0, 0, 'F'},
455 { "help", 0, 0, 'h'}
456 };
457 int c, opt_id;
458 char *pos;
459 pj_status_t status;
460
461 while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
462 switch (c) {
463 case 'r':
464 o.realm = pj_optarg;
465 break;
466 case 'u':
467 o.user_name = pj_optarg;
468 break;
469 case 'p':
470 o.password = pj_optarg;
471 break;
Benny Prijonocc8febb2007-03-03 02:16:36 +0000472 case 'N':
473 o.nonce = pj_optarg;
474 break;
Benny Prijono4df62612007-03-01 23:39:08 +0000475 case 'h':
476 usage();
477 return 0;
478 case 'F':
479 o.use_fingerprint = PJ_TRUE;
480 break;
481 default:
482 printf("Argument \"%s\" is not valid. Use -h to see help",
483 argv[pj_optind]);
484 return 1;
485 }
486 }
487
488 if (pj_optind == argc) {
489 puts("Error: TARGET is needed");
490 return 1;
491 }
492
493 if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000494 o.srv_addr = argv[pj_optind];
Benny Prijono4df62612007-03-01 23:39:08 +0000495 *pos = '\0';
Benny Prijonoe4c40202007-03-10 11:19:11 +0000496 o.srv_port = pos+1;
Benny Prijono4df62612007-03-01 23:39:08 +0000497 } else {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000498 o.srv_addr = argv[pj_optind];
Benny Prijono4df62612007-03-01 23:39:08 +0000499 }
500
501 status = init();
502 if (status != PJ_SUCCESS)
503 goto on_return;
504
505 console_main();
506
507on_return:
508 shutdown();
509 return status ? 1 : 0;
510}
Benny Prijono91169ac2007-02-22 02:09:23 +0000511