blob: 9bb249f1b60d3f2ba7ad793cc4f2612c1efac0b4 [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 */
Benny Prijonob01897b2007-03-18 17:39:27 +000019#include <pjnath.h>
Benny Prijono91169ac2007-02-22 02:09:23 +000020#include <pjlib-util.h>
21#include <pjlib.h>
Benny Prijonod0a35852007-02-23 01:07:54 +000022
Benny Prijono91169ac2007-02-22 02:09:23 +000023
24#define THIS_FILE "client_main.c"
Benny Prijonod5971742007-03-10 23:15:36 +000025#define LOCAL_PORT 1998
Benny Prijonoe4c40202007-03-10 11:19:11 +000026#define BANDWIDTH 64 /* -1 to disable */
Benny Prijonod5971742007-03-10 23:15:36 +000027#define LIFETIME 600 /* -1 to disable */
Benny Prijonoe4c40202007-03-10 11:19:11 +000028#define REQ_TRANSPORT -1 /* 0: udp, 1: tcp, -1: disable */
29#define REQ_PORT_PROPS -1 /* -1 to disable */
30#define REQ_IP NULL /* IP address string */
31
Benny Prijonod0a35852007-02-23 01:07:54 +000032
Benny Prijono4df62612007-03-01 23:39:08 +000033static struct global
34{
35 pj_stun_endpoint *endpt;
36 pj_pool_t *pool;
37 pj_caching_pool cp;
38 pj_timer_heap_t *th;
39 pj_stun_session *sess;
Benny Prijono4df62612007-03-01 23:39:08 +000040 pj_sock_t sock;
41 pj_thread_t *thread;
42 pj_bool_t quit;
Benny Prijonoe4c40202007-03-10 11:19:11 +000043 pj_sockaddr_in peer_addr;
Benny Prijonod5971742007-03-10 23:15:36 +000044 pj_sockaddr_in srv_addr;
45 pj_sockaddr_in relay_addr;
46 char data_buf[256];
47 char *data;
Benny Prijono4df62612007-03-01 23:39:08 +000048} g;
49
50static struct options
51{
Benny Prijonoe4c40202007-03-10 11:19:11 +000052 char *srv_addr;
53 char *srv_port;
Benny Prijono4df62612007-03-01 23:39:08 +000054 char *realm;
55 char *user_name;
56 char *password;
Benny Prijonocc8febb2007-03-03 02:16:36 +000057 char *nonce;
Benny Prijonod5971742007-03-10 23:15:36 +000058 char *peer_addr;
Benny Prijono4df62612007-03-01 23:39:08 +000059 pj_bool_t use_fingerprint;
60} o;
61
Benny Prijonod0a35852007-02-23 01:07:54 +000062
Benny Prijonod5971742007-03-10 23:15:36 +000063static pj_status_t parse_addr(const char *input, pj_sockaddr_in *addr);
64
65
Benny Prijonod0a35852007-02-23 01:07:54 +000066static my_perror(const char *title, pj_status_t status)
67{
68 char errmsg[PJ_ERR_MSG_SIZE];
69 pj_strerror(status, errmsg, sizeof(errmsg));
70
71 PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg));
72}
73
Benny Prijono4df62612007-03-01 23:39:08 +000074static pj_status_t on_send_msg(pj_stun_session *sess,
Benny Prijonod0a35852007-02-23 01:07:54 +000075 const void *pkt,
76 pj_size_t pkt_size,
Benny Prijonoe4c40202007-03-10 11:19:11 +000077 const pj_sockaddr_t *srv_addr,
Benny Prijono4df62612007-03-01 23:39:08 +000078 unsigned addr_len)
Benny Prijonod0a35852007-02-23 01:07:54 +000079{
Benny Prijonod0a35852007-02-23 01:07:54 +000080 pj_ssize_t len;
81 pj_status_t status;
82
Benny Prijonod0a35852007-02-23 01:07:54 +000083 len = pkt_size;
Benny Prijonoe4c40202007-03-10 11:19:11 +000084 status = pj_sock_sendto(g.sock, pkt, &len, 0, srv_addr, addr_len);
Benny Prijonod0a35852007-02-23 01:07:54 +000085
86 if (status != PJ_SUCCESS)
87 my_perror("Error sending packet", status);
88
89 return status;
90}
91
Benny Prijono4df62612007-03-01 23:39:08 +000092static void on_request_complete(pj_stun_session *sess,
93 pj_status_t status,
94 pj_stun_tx_data *tdata,
95 const pj_stun_msg *response)
Benny Prijonod0a35852007-02-23 01:07:54 +000096{
Benny Prijono4df62612007-03-01 23:39:08 +000097 if (status == PJ_SUCCESS) {
Benny Prijonod5971742007-03-10 23:15:36 +000098 switch (response->hdr.type) {
99 case PJ_STUN_ALLOCATE_RESPONSE:
100 {
101 pj_stun_relay_addr_attr *ar;
102
103 ar = (pj_stun_relay_addr_attr*)
104 pj_stun_msg_find_attr(response,
105 PJ_STUN_ATTR_RELAY_ADDR, 0);
106 if (ar) {
107 pj_memcpy(&g.relay_addr, &ar->addr.ipv4,
108 sizeof(pj_sockaddr_in));
109 PJ_LOG(3,(THIS_FILE, "Relay address is %s:%d",
110 pj_inet_ntoa(g.relay_addr.sin_addr),
111 (int)pj_ntohs(g.relay_addr.sin_port)));
112 } else {
113 pj_memset(&g.relay_addr, 0, sizeof(g.relay_addr));
114 }
115 }
116 break;
117 }
Benny Prijono4df62612007-03-01 23:39:08 +0000118 } else {
119 my_perror("Client transaction error", status);
120 }
Benny Prijonod0a35852007-02-23 01:07:54 +0000121}
122
Benny Prijono4df62612007-03-01 23:39:08 +0000123static int worker_thread(void *unused)
Benny Prijonod0a35852007-02-23 01:07:54 +0000124{
Benny Prijono4df62612007-03-01 23:39:08 +0000125 PJ_UNUSED_ARG(unused);
Benny Prijonod0a35852007-02-23 01:07:54 +0000126
Benny Prijono4df62612007-03-01 23:39:08 +0000127 while (!g.quit) {
128 pj_time_val timeout = {0, 50};
129 pj_fd_set_t readset;
Benny Prijonod0a35852007-02-23 01:07:54 +0000130 int n;
Benny Prijonod0a35852007-02-23 01:07:54 +0000131
Benny Prijono4df62612007-03-01 23:39:08 +0000132 pj_timer_heap_poll(g.th, NULL);
Benny Prijonod0a35852007-02-23 01:07:54 +0000133
Benny Prijono4df62612007-03-01 23:39:08 +0000134 PJ_FD_ZERO(&readset);
135 PJ_FD_SET(g.sock, &readset);
Benny Prijonod0a35852007-02-23 01:07:54 +0000136
Benny Prijono4df62612007-03-01 23:39:08 +0000137 n = pj_sock_select(g.sock+1, &readset, NULL, NULL, &timeout);
138 if (n > 0) {
139 if (PJ_FD_ISSET(g.sock, &readset)) {
140 char buffer[512];
141 pj_ssize_t len;
142 pj_sockaddr_in addr;
143 int addrlen;
144 pj_status_t rc;
Benny Prijonod0a35852007-02-23 01:07:54 +0000145
Benny Prijono4df62612007-03-01 23:39:08 +0000146 len = sizeof(buffer);
147 addrlen = sizeof(addr);
148 rc = pj_sock_recvfrom(g.sock, buffer, &len, 0, &addr, &addrlen);
Benny Prijonod5971742007-03-10 23:15:36 +0000149 if (rc != PJ_SUCCESS || len <= 0)
150 continue;
151
152 if (pj_stun_msg_check(buffer, len, PJ_STUN_IS_DATAGRAM)==PJ_SUCCESS) {
Benny Prijono4df62612007-03-01 23:39:08 +0000153 rc = pj_stun_session_on_rx_pkt(g.sess, buffer, len,
Benny Prijonod5971742007-03-10 23:15:36 +0000154 0,
Benny Prijono4df62612007-03-01 23:39:08 +0000155 NULL, &addr, addrlen);
156 if (rc != PJ_SUCCESS)
157 my_perror("Error processing packet", rc);
Benny Prijonod5971742007-03-10 23:15:36 +0000158
159 } else {
160 buffer[len] = '\0';
161 PJ_LOG(3,(THIS_FILE, "Received data: %s", (char*)buffer));
Benny Prijono4df62612007-03-01 23:39:08 +0000162 }
Benny Prijonod0a35852007-02-23 01:07:54 +0000163 }
Benny Prijono4df62612007-03-01 23:39:08 +0000164 } else if (n < 0)
165 pj_thread_sleep(50);
Benny Prijonod0a35852007-02-23 01:07:54 +0000166 }
167
Benny Prijonod0a35852007-02-23 01:07:54 +0000168 return 0;
169}
170
Benny Prijono4df62612007-03-01 23:39:08 +0000171static int init()
172{
173 pj_sockaddr_in addr;
174 pj_stun_session_cb stun_cb;
Benny Prijonod5971742007-03-10 23:15:36 +0000175 int len;
Benny Prijono4df62612007-03-01 23:39:08 +0000176 pj_status_t status;
177
178 g.sock = PJ_INVALID_SOCKET;
179
180 status = pj_init();
181 status = pjlib_util_init();
182
183 pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0);
184
Benny Prijonoe4c40202007-03-10 11:19:11 +0000185 if (o.srv_addr) {
Benny Prijono4df62612007-03-01 23:39:08 +0000186 pj_str_t s;
187 pj_uint16_t port;
188
Benny Prijonoe4c40202007-03-10 11:19:11 +0000189 if (o.srv_port)
190 port = (pj_uint16_t) atoi(o.srv_port);
Benny Prijono4df62612007-03-01 23:39:08 +0000191 else
192 port = PJ_STUN_PORT;
193
Benny Prijonoe4c40202007-03-10 11:19:11 +0000194 status = pj_sockaddr_in_init(&g.srv_addr, pj_cstr(&s, o.srv_addr), port);
Benny Prijono4df62612007-03-01 23:39:08 +0000195 if (status != PJ_SUCCESS) {
196 my_perror("Invalid address", status);
197 return status;
198 }
199
Benny Prijonoe4c40202007-03-10 11:19:11 +0000200 printf("Destination address set to %s:%d\n", o.srv_addr, (int)port);
Benny Prijono4df62612007-03-01 23:39:08 +0000201 } else {
202 printf("Error: address must be specified\n");
203 return PJ_EINVAL;
204 }
205
206 g.pool = pj_pool_create(&g.cp.factory, NULL, 1000, 1000, NULL);
207
208 status = pj_timer_heap_create(g.pool, 1000, &g.th);
209 pj_assert(status == PJ_SUCCESS);
210
211 status = pj_stun_endpoint_create(&g.cp.factory, 0, NULL, g.th, &g.endpt);
212 pj_assert(status == PJ_SUCCESS);
213
214 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &g.sock);
215 pj_assert(status == PJ_SUCCESS);
216
217 status = pj_sockaddr_in_init(&addr, NULL, 0);
218 pj_assert(status == PJ_SUCCESS);
219
Benny Prijonod5971742007-03-10 23:15:36 +0000220 addr.sin_port = pj_htons((pj_uint16_t)LOCAL_PORT);
221 status = pj_sock_bind(g.sock, &addr, sizeof(addr));
222 pj_assert(status == PJ_SUCCESS);
223
224 len = sizeof(addr);
225 status = pj_sock_getsockname(g.sock, &addr, &len);
226 pj_assert(status == PJ_SUCCESS);
227
228 PJ_LOG(3,(THIS_FILE, "Listening on port %d", (int)pj_ntohs(addr.sin_port)));
229
230 pj_memcpy(&g.peer_addr, &addr, sizeof(pj_sockaddr_in));
231 if (g.peer_addr.sin_addr.s_addr == 0)
232 pj_gethostip(&g.peer_addr.sin_addr);
233
Benny Prijono4df62612007-03-01 23:39:08 +0000234 pj_memset(&stun_cb, 0, sizeof(stun_cb));
235 stun_cb.on_send_msg = &on_send_msg;
236 stun_cb.on_request_complete = &on_request_complete;
237
Benny Prijonocc8febb2007-03-03 02:16:36 +0000238 status = pj_stun_session_create(g.endpt, NULL, &stun_cb,
239 o.use_fingerprint!=0, &g.sess);
Benny Prijono4df62612007-03-01 23:39:08 +0000240 pj_assert(status == PJ_SUCCESS);
241
Benny Prijonocc8febb2007-03-03 02:16:36 +0000242 if (o.user_name) {
243 pj_stun_auth_cred cred;
Benny Prijono4df62612007-03-01 23:39:08 +0000244
Benny Prijonocc8febb2007-03-03 02:16:36 +0000245 pj_bzero(&cred, sizeof(cred));
Benny Prijono4df62612007-03-01 23:39:08 +0000246
Benny Prijonocc8febb2007-03-03 02:16:36 +0000247 cred.type = PJ_STUN_AUTH_CRED_STATIC;
248 cred.data.static_cred.realm = pj_str(o.realm);
249 cred.data.static_cred.username = pj_str(o.user_name);
250 cred.data.static_cred.data_type = 0;
251 cred.data.static_cred.data = pj_str(o.password);
252 cred.data.static_cred.nonce = pj_str(o.nonce);
253
254 pj_stun_session_set_credential(g.sess, &cred);
255 puts("Session credential set");
Benny Prijono4df62612007-03-01 23:39:08 +0000256 } else {
257 puts("Credential not set");
258 }
259
Benny Prijonod5971742007-03-10 23:15:36 +0000260 if (o.peer_addr) {
261 if (parse_addr(o.peer_addr, &g.peer_addr)!=PJ_SUCCESS)
262 return -1;
263 }
264
Benny Prijono4df62612007-03-01 23:39:08 +0000265 status = pj_thread_create(g.pool, "stun", &worker_thread, NULL,
266 0, 0, &g.thread);
267 if (status != PJ_SUCCESS)
268 return status;
269
270 return PJ_SUCCESS;
271}
272
273
274static int shutdown()
275{
276 if (g.thread) {
277 g.quit = 1;
278 pj_thread_join(g.thread);
279 pj_thread_destroy(g.thread);
280 g.thread = NULL;
281 }
282 if (g.sess)
283 pj_stun_session_destroy(g.sess);
284 if (g.endpt)
285 pj_stun_endpoint_destroy(g.endpt);
286 if (g.sock != PJ_INVALID_SOCKET)
287 pj_sock_close(g.sock);
288 if (g.th)
289 pj_timer_heap_destroy(g.th);
290 if (g.pool)
291 pj_pool_release(g.pool);
292
293 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
294 pj_caching_pool_destroy(&g.cp);
295
296 return PJ_SUCCESS;
297}
298
Benny Prijono62923f22007-03-09 23:25:11 +0000299static void send_bind_request(void)
300{
301 pj_stun_tx_data *tdata;
302 pj_status_t rc;
303
304 rc = pj_stun_session_create_req(g.sess, PJ_STUN_BINDING_REQUEST, &tdata);
305 pj_assert(rc == PJ_SUCCESS);
306
307 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000308 &g.srv_addr, sizeof(g.srv_addr),
Benny Prijono62923f22007-03-09 23:25:11 +0000309 tdata);
310 if (rc != PJ_SUCCESS)
311 my_perror("Error sending STUN request", rc);
312}
313
Benny Prijonoe4c40202007-03-10 11:19:11 +0000314static void send_allocate_request(pj_bool_t allocate)
Benny Prijono62923f22007-03-09 23:25:11 +0000315{
Benny Prijonoe4c40202007-03-10 11:19:11 +0000316 pj_stun_tx_data *tdata;
317 pj_status_t rc;
318
319 rc = pj_stun_session_create_req(g.sess, PJ_STUN_ALLOCATE_REQUEST, &tdata);
320 pj_assert(rc == PJ_SUCCESS);
321
322
323 if (BANDWIDTH != -1) {
324 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
325 PJ_STUN_ATTR_BANDWIDTH, BANDWIDTH);
326 }
327
328 if (!allocate) {
329 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
330 PJ_STUN_ATTR_LIFETIME, 0);
331
332 } else {
333 if (LIFETIME != -1) {
334 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
335 PJ_STUN_ATTR_LIFETIME, LIFETIME);
336 }
337
338 if (REQ_TRANSPORT != -1) {
339 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
340 PJ_STUN_ATTR_REQ_TRANSPORT, REQ_TRANSPORT);
341 }
342
343 if (REQ_PORT_PROPS != -1) {
344 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
345 PJ_STUN_ATTR_REQ_PORT_PROPS, REQ_PORT_PROPS);
346 }
347
348 if (REQ_IP != NULL) {
349 pj_sockaddr_in addr;
350 pj_str_t tmp;
351
352 pj_sockaddr_in_init(&addr, pj_cstr(&tmp, REQ_IP), 0);
Benny Prijonod5971742007-03-10 23:15:36 +0000353 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000354 PJ_STUN_ATTR_REQ_IP, PJ_FALSE,
355 &addr, sizeof(addr));
356 }
357 }
358
359 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
360 &g.srv_addr, sizeof(g.srv_addr),
361 tdata);
362 pj_assert(rc == PJ_SUCCESS);
Benny Prijono62923f22007-03-09 23:25:11 +0000363}
364
Benny Prijonoe4c40202007-03-10 11:19:11 +0000365static void send_sad_request(pj_bool_t set)
Benny Prijono62923f22007-03-09 23:25:11 +0000366{
Benny Prijonoe4c40202007-03-10 11:19:11 +0000367 pj_stun_tx_data *tdata;
368 pj_status_t rc;
369
370 if (g.peer_addr.sin_addr.s_addr == 0 ||
371 g.peer_addr.sin_port == 0)
372 {
373 puts("Error: peer address is not set");
374 return;
375 }
376
Benny Prijonod5971742007-03-10 23:15:36 +0000377 rc = pj_stun_session_create_req(g.sess, PJ_STUN_SET_ACTIVE_DESTINATION_REQUEST, &tdata);
Benny Prijonoe4c40202007-03-10 11:19:11 +0000378 pj_assert(rc == PJ_SUCCESS);
379
380 if (set) {
Benny Prijonod5971742007-03-10 23:15:36 +0000381 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000382 PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
383 &g.peer_addr, sizeof(g.peer_addr));
384 }
385
386 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
387 &g.srv_addr, sizeof(g.srv_addr),
388 tdata);
389 pj_assert(rc == PJ_SUCCESS);
Benny Prijono62923f22007-03-09 23:25:11 +0000390}
391
392static void send_send_ind(void)
393{
Benny Prijonod5971742007-03-10 23:15:36 +0000394 pj_stun_tx_data *tdata;
395 int len;
396 pj_status_t rc;
397
398 if (g.peer_addr.sin_addr.s_addr == 0 ||
399 g.peer_addr.sin_port == 0)
400 {
401 puts("Error: peer address is not set");
402 return;
403 }
404
405 len = strlen(g.data);
406 if (len==0) {
407 puts("Error: data is not set");
408 return;
409 }
410
411 rc = pj_stun_session_create_ind(g.sess, PJ_STUN_SEND_INDICATION, &tdata);
412 pj_assert(rc == PJ_SUCCESS);
413
414 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
415 PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
416 &g.peer_addr, sizeof(g.peer_addr));
417 pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
418 PJ_STUN_ATTR_DATA, g.data, len);
419
420 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
421 &g.srv_addr, sizeof(g.srv_addr),
422 tdata);
423 pj_assert(rc == PJ_SUCCESS);
424
Benny Prijono62923f22007-03-09 23:25:11 +0000425}
426
Benny Prijonod5971742007-03-10 23:15:36 +0000427static void send_raw_data_to_srv(void)
Benny Prijono62923f22007-03-09 23:25:11 +0000428{
Benny Prijonod5971742007-03-10 23:15:36 +0000429 pj_ssize_t len;
430
431 if (g.srv_addr.sin_addr.s_addr == 0 ||
432 g.srv_addr.sin_port == 0)
433 {
434 puts("Error: server address is not set");
435 return;
436 }
437
438 len = strlen(g.data);
439 if (len==0) {
440 puts("Error: data is not set");
441 return;
442 }
443
444 len = strlen(g.data);
445 pj_sock_sendto(g.sock, g.data, &len, 0, &g.srv_addr, sizeof(g.srv_addr));
446}
447
448static void send_raw_data_to_relay(void)
449{
450 pj_ssize_t len;
451
452 if (g.relay_addr.sin_addr.s_addr == 0 ||
453 g.relay_addr.sin_port == 0)
454 {
455 puts("Error: relay address is not set");
456 return;
457 }
458
459 len = strlen(g.data);
460 if (len==0) {
461 puts("Error: data is not set");
462 return;
463 }
464
465 len = strlen(g.data);
466 pj_sock_sendto(g.sock, g.data, &len, 0, &g.relay_addr, sizeof(g.relay_addr));
467}
468
469static pj_status_t parse_addr(const char *input,
470 pj_sockaddr_in *addr)
471{
472 const char *pos;
473 pj_str_t ip;
474 pj_uint16_t port;
475 pj_sockaddr_in tmp_addr;
476
477 pos = pj_ansi_strchr(input, ':');
478 if (pos==NULL) {
479 puts("Invalid format");
480 return -1;
481 }
482
483 ip.ptr = (char*)input;
484 ip.slen = pos - input;
485 port = (pj_uint16_t)atoi(pos+1);
486
487 if (port==0) {
488 puts("Invalid port");
489 return -1;
490 }
491
492 if (pj_sockaddr_in_init(&tmp_addr, &ip, port)!=PJ_SUCCESS) {
493 puts("Invalid address");
494 return -1;
495 }
496
497 pj_memcpy(addr, &tmp_addr, sizeof(tmp_addr));
498
499 return PJ_SUCCESS;
Benny Prijono62923f22007-03-09 23:25:11 +0000500}
501
Benny Prijonoe4c40202007-03-10 11:19:11 +0000502static void set_peer_addr(void)
503{
Benny Prijonod5971742007-03-10 23:15:36 +0000504 char addr[64];
Benny Prijonoe4c40202007-03-10 11:19:11 +0000505
506 printf("Current peer address: %s:%d\n",
507 pj_inet_ntoa(g.peer_addr.sin_addr),
508 pj_ntohs(g.peer_addr.sin_port));
509
510 printf("Input peer address in IP:PORT format: ");
511 fflush(stdout);
Benny Prijonod5971742007-03-10 23:15:36 +0000512 gets(addr);
Benny Prijonoe4c40202007-03-10 11:19:11 +0000513
Benny Prijonod5971742007-03-10 23:15:36 +0000514 if (parse_addr(addr, &g.peer_addr) != PJ_SUCCESS) {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000515 return;
516 }
517
Benny Prijonoe4c40202007-03-10 11:19:11 +0000518}
519
Benny Prijono4df62612007-03-01 23:39:08 +0000520static void menu(void)
521{
522 puts("Menu:");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000523 printf(" pr Set peer address (currently %s:%d)\n",
524 pj_inet_ntoa(g.peer_addr.sin_addr), pj_ntohs(g.peer_addr.sin_port));
Benny Prijonod5971742007-03-10 23:15:36 +0000525 printf(" dt Set data (currently \"%s\")\n", g.data);
Benny Prijono62923f22007-03-09 23:25:11 +0000526 puts(" br Send Bind request");
527 puts(" ar Send Allocate request");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000528 puts(" dr Send de-Allocate request");
Benny Prijonod5971742007-03-10 23:15:36 +0000529 puts(" sr Send Set Active Destination request");
530 puts(" cr Send clear Active Destination request");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000531 puts(" si Send data with Send Indication");
Benny Prijonod5971742007-03-10 23:15:36 +0000532 puts(" rw Send raw data to TURN server");
533 puts(" rW Send raw data to relay address");
Benny Prijono62923f22007-03-09 23:25:11 +0000534 puts(" q Quit");
Benny Prijono4df62612007-03-01 23:39:08 +0000535 puts("");
536 printf("Choice: ");
537}
538
Benny Prijonoe4c40202007-03-10 11:19:11 +0000539
Benny Prijono4df62612007-03-01 23:39:08 +0000540static void console_main(void)
541{
542 while (!g.quit) {
543 char input[10];
544
545 menu();
546
547 fgets(input, sizeof(input), stdin);
548
Benny Prijonod5971742007-03-10 23:15:36 +0000549 if (0) {
550
551 } else if (input[0]=='d' && input[1]=='t') {
552 printf("Input data: ");
553 gets(g.data);
554
555 } else if (input[0]=='p' && input[1]=='r') {
556 set_peer_addr();
557
558 } else if (input[0]=='b' && input[1]=='r') {
Benny Prijono62923f22007-03-09 23:25:11 +0000559 send_bind_request();
560
561 } else if (input[0]=='a' && input[1]=='r') {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000562 send_allocate_request(PJ_TRUE);
563
564 } else if (input[0]=='d' && input[1]=='r') {
565 send_allocate_request(PJ_FALSE);
Benny Prijono62923f22007-03-09 23:25:11 +0000566
567 } else if (input[0]=='s' && input[1]=='r') {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000568 send_sad_request(PJ_TRUE);
569
570 } else if (input[0]=='c' && input[1]=='r') {
571 send_sad_request(PJ_FALSE);
Benny Prijono62923f22007-03-09 23:25:11 +0000572
573 } else if (input[0]=='s' && input[1]=='i') {
574 send_send_ind();
575
576 } else if (input[0]=='r' && input[1]=='w') {
Benny Prijonod5971742007-03-10 23:15:36 +0000577 send_raw_data_to_srv();
Benny Prijono62923f22007-03-09 23:25:11 +0000578
Benny Prijonod5971742007-03-10 23:15:36 +0000579 } else if (input[0]=='r' && input[1]=='W') {
580 send_raw_data_to_relay();
Benny Prijonoe4c40202007-03-10 11:19:11 +0000581
Benny Prijono62923f22007-03-09 23:25:11 +0000582 } else if (input[0]=='q') {
Benny Prijono4df62612007-03-01 23:39:08 +0000583 g.quit = 1;
Benny Prijono4df62612007-03-01 23:39:08 +0000584 }
585 }
586}
587
588
589static void usage(void)
590{
591 puts("Usage: pjstun_client TARGET [OPTIONS]");
592 puts("");
593 puts("where TARGET is \"host[:port]\"");
594 puts("");
595 puts("and OPTIONS:");
596 puts(" --realm, -r Set realm of the credential");
597 puts(" --username, -u Set username of the credential");
598 puts(" --password, -p Set password of the credential");
Benny Prijonocc8febb2007-03-03 02:16:36 +0000599 puts(" --nonce, -N Set NONCE");
Benny Prijono4df62612007-03-01 23:39:08 +0000600 puts(" --fingerprint, -F Use fingerprint for outgoing requests");
Benny Prijonod5971742007-03-10 23:15:36 +0000601 puts(" --peer, -P Set peer address (address is in HOST:PORT format)");
602 puts(" --data, -D Set data");
Benny Prijono4df62612007-03-01 23:39:08 +0000603 puts(" --help, -h");
604}
605
606int main(int argc, char *argv[])
607{
608 struct pj_getopt_option long_options[] = {
609 { "realm", 1, 0, 'r'},
610 { "username", 1, 0, 'u'},
611 { "password", 1, 0, 'p'},
Benny Prijonocc8febb2007-03-03 02:16:36 +0000612 { "nonce", 1, 0, 'N'},
Benny Prijono4df62612007-03-01 23:39:08 +0000613 { "fingerprint",0, 0, 'F'},
Benny Prijonod5971742007-03-10 23:15:36 +0000614 { "peer", 1, 0, 'P'},
615 { "data", 1, 0, 'D'},
Benny Prijono4df62612007-03-01 23:39:08 +0000616 { "help", 0, 0, 'h'}
617 };
618 int c, opt_id;
619 char *pos;
620 pj_status_t status;
621
Benny Prijonod5971742007-03-10 23:15:36 +0000622 g.data = g.data_buf;
623
Benny Prijono4df62612007-03-01 23:39:08 +0000624 while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
625 switch (c) {
626 case 'r':
627 o.realm = pj_optarg;
628 break;
629 case 'u':
630 o.user_name = pj_optarg;
631 break;
632 case 'p':
633 o.password = pj_optarg;
634 break;
Benny Prijonocc8febb2007-03-03 02:16:36 +0000635 case 'N':
636 o.nonce = pj_optarg;
637 break;
Benny Prijono4df62612007-03-01 23:39:08 +0000638 case 'h':
639 usage();
640 return 0;
641 case 'F':
642 o.use_fingerprint = PJ_TRUE;
643 break;
Benny Prijonod5971742007-03-10 23:15:36 +0000644 case 'P':
645 o.peer_addr = pj_optarg;
646 break;
647 case 'D':
648 g.data = pj_optarg;
649 break;
650
Benny Prijono4df62612007-03-01 23:39:08 +0000651 default:
652 printf("Argument \"%s\" is not valid. Use -h to see help",
653 argv[pj_optind]);
654 return 1;
655 }
656 }
657
658 if (pj_optind == argc) {
659 puts("Error: TARGET is needed");
660 return 1;
661 }
662
663 if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000664 o.srv_addr = argv[pj_optind];
Benny Prijono4df62612007-03-01 23:39:08 +0000665 *pos = '\0';
Benny Prijonoe4c40202007-03-10 11:19:11 +0000666 o.srv_port = pos+1;
Benny Prijono4df62612007-03-01 23:39:08 +0000667 } else {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000668 o.srv_addr = argv[pj_optind];
Benny Prijono4df62612007-03-01 23:39:08 +0000669 }
670
671 status = init();
672 if (status != PJ_SUCCESS)
673 goto on_return;
674
675 console_main();
676
677on_return:
678 shutdown();
679 return status ? 1 : 0;
680}
Benny Prijono91169ac2007-02-22 02:09:23 +0000681