blob: be65b516e5f9b4292df4c35c23b507fc325ead06 [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 Prijonod5971742007-03-10 23:15:36 +000024#define LOCAL_PORT 1998
Benny Prijonoe4c40202007-03-10 11:19:11 +000025#define BANDWIDTH 64 /* -1 to disable */
Benny Prijonod5971742007-03-10 23:15:36 +000026#define LIFETIME 600 /* -1 to disable */
Benny Prijonoe4c40202007-03-10 11:19:11 +000027#define REQ_TRANSPORT -1 /* 0: udp, 1: tcp, -1: disable */
28#define REQ_PORT_PROPS -1 /* -1 to disable */
29#define REQ_IP NULL /* IP address string */
30
Benny Prijonod0a35852007-02-23 01:07:54 +000031
Benny Prijono4df62612007-03-01 23:39:08 +000032static struct global
33{
34 pj_stun_endpoint *endpt;
35 pj_pool_t *pool;
36 pj_caching_pool cp;
37 pj_timer_heap_t *th;
38 pj_stun_session *sess;
Benny Prijono4df62612007-03-01 23:39:08 +000039 pj_sock_t sock;
40 pj_thread_t *thread;
41 pj_bool_t quit;
Benny Prijonoe4c40202007-03-10 11:19:11 +000042 pj_sockaddr_in peer_addr;
Benny Prijonod5971742007-03-10 23:15:36 +000043 pj_sockaddr_in srv_addr;
44 pj_sockaddr_in relay_addr;
45 char data_buf[256];
46 char *data;
Benny Prijono4df62612007-03-01 23:39:08 +000047} g;
48
49static struct options
50{
Benny Prijonoe4c40202007-03-10 11:19:11 +000051 char *srv_addr;
52 char *srv_port;
Benny Prijono4df62612007-03-01 23:39:08 +000053 char *realm;
54 char *user_name;
55 char *password;
Benny Prijonocc8febb2007-03-03 02:16:36 +000056 char *nonce;
Benny Prijonod5971742007-03-10 23:15:36 +000057 char *peer_addr;
Benny Prijono4df62612007-03-01 23:39:08 +000058 pj_bool_t use_fingerprint;
59} o;
60
Benny Prijonod0a35852007-02-23 01:07:54 +000061
Benny Prijonod5971742007-03-10 23:15:36 +000062static pj_status_t parse_addr(const char *input, pj_sockaddr_in *addr);
63
64
Benny Prijonod0a35852007-02-23 01:07:54 +000065static my_perror(const char *title, pj_status_t status)
66{
67 char errmsg[PJ_ERR_MSG_SIZE];
68 pj_strerror(status, errmsg, sizeof(errmsg));
69
70 PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg));
71}
72
Benny Prijono4df62612007-03-01 23:39:08 +000073static pj_status_t on_send_msg(pj_stun_session *sess,
Benny Prijonod0a35852007-02-23 01:07:54 +000074 const void *pkt,
75 pj_size_t pkt_size,
Benny Prijonoe4c40202007-03-10 11:19:11 +000076 const pj_sockaddr_t *srv_addr,
Benny Prijono4df62612007-03-01 23:39:08 +000077 unsigned addr_len)
Benny Prijonod0a35852007-02-23 01:07:54 +000078{
Benny Prijonod0a35852007-02-23 01:07:54 +000079 pj_ssize_t len;
80 pj_status_t status;
81
Benny Prijonod0a35852007-02-23 01:07:54 +000082 len = pkt_size;
Benny Prijonoe4c40202007-03-10 11:19:11 +000083 status = pj_sock_sendto(g.sock, pkt, &len, 0, srv_addr, addr_len);
Benny Prijonod0a35852007-02-23 01:07:54 +000084
85 if (status != PJ_SUCCESS)
86 my_perror("Error sending packet", status);
87
88 return status;
89}
90
Benny Prijono4df62612007-03-01 23:39:08 +000091static void on_request_complete(pj_stun_session *sess,
92 pj_status_t status,
93 pj_stun_tx_data *tdata,
94 const pj_stun_msg *response)
Benny Prijonod0a35852007-02-23 01:07:54 +000095{
Benny Prijono4df62612007-03-01 23:39:08 +000096 if (status == PJ_SUCCESS) {
Benny Prijonod5971742007-03-10 23:15:36 +000097 switch (response->hdr.type) {
98 case PJ_STUN_ALLOCATE_RESPONSE:
99 {
100 pj_stun_relay_addr_attr *ar;
101
102 ar = (pj_stun_relay_addr_attr*)
103 pj_stun_msg_find_attr(response,
104 PJ_STUN_ATTR_RELAY_ADDR, 0);
105 if (ar) {
106 pj_memcpy(&g.relay_addr, &ar->addr.ipv4,
107 sizeof(pj_sockaddr_in));
108 PJ_LOG(3,(THIS_FILE, "Relay address is %s:%d",
109 pj_inet_ntoa(g.relay_addr.sin_addr),
110 (int)pj_ntohs(g.relay_addr.sin_port)));
111 } else {
112 pj_memset(&g.relay_addr, 0, sizeof(g.relay_addr));
113 }
114 }
115 break;
116 }
Benny Prijono4df62612007-03-01 23:39:08 +0000117 } else {
118 my_perror("Client transaction error", status);
119 }
Benny Prijonod0a35852007-02-23 01:07:54 +0000120}
121
Benny Prijono4df62612007-03-01 23:39:08 +0000122static int worker_thread(void *unused)
Benny Prijonod0a35852007-02-23 01:07:54 +0000123{
Benny Prijono4df62612007-03-01 23:39:08 +0000124 PJ_UNUSED_ARG(unused);
Benny Prijonod0a35852007-02-23 01:07:54 +0000125
Benny Prijono4df62612007-03-01 23:39:08 +0000126 while (!g.quit) {
127 pj_time_val timeout = {0, 50};
128 pj_fd_set_t readset;
Benny Prijonod0a35852007-02-23 01:07:54 +0000129 int n;
Benny Prijonod0a35852007-02-23 01:07:54 +0000130
Benny Prijono4df62612007-03-01 23:39:08 +0000131 pj_timer_heap_poll(g.th, NULL);
Benny Prijonod0a35852007-02-23 01:07:54 +0000132
Benny Prijono4df62612007-03-01 23:39:08 +0000133 PJ_FD_ZERO(&readset);
134 PJ_FD_SET(g.sock, &readset);
Benny Prijonod0a35852007-02-23 01:07:54 +0000135
Benny Prijono4df62612007-03-01 23:39:08 +0000136 n = pj_sock_select(g.sock+1, &readset, NULL, NULL, &timeout);
137 if (n > 0) {
138 if (PJ_FD_ISSET(g.sock, &readset)) {
139 char buffer[512];
140 pj_ssize_t len;
141 pj_sockaddr_in addr;
142 int addrlen;
143 pj_status_t rc;
Benny Prijonod0a35852007-02-23 01:07:54 +0000144
Benny Prijono4df62612007-03-01 23:39:08 +0000145 len = sizeof(buffer);
146 addrlen = sizeof(addr);
147 rc = pj_sock_recvfrom(g.sock, buffer, &len, 0, &addr, &addrlen);
Benny Prijonod5971742007-03-10 23:15:36 +0000148 if (rc != PJ_SUCCESS || len <= 0)
149 continue;
150
151 if (pj_stun_msg_check(buffer, len, PJ_STUN_IS_DATAGRAM)==PJ_SUCCESS) {
Benny Prijono4df62612007-03-01 23:39:08 +0000152 rc = pj_stun_session_on_rx_pkt(g.sess, buffer, len,
Benny Prijonod5971742007-03-10 23:15:36 +0000153 0,
Benny Prijono4df62612007-03-01 23:39:08 +0000154 NULL, &addr, addrlen);
155 if (rc != PJ_SUCCESS)
156 my_perror("Error processing packet", rc);
Benny Prijonod5971742007-03-10 23:15:36 +0000157
158 } else {
159 buffer[len] = '\0';
160 PJ_LOG(3,(THIS_FILE, "Received data: %s", (char*)buffer));
Benny Prijono4df62612007-03-01 23:39:08 +0000161 }
Benny Prijonod0a35852007-02-23 01:07:54 +0000162 }
Benny Prijono4df62612007-03-01 23:39:08 +0000163 } else if (n < 0)
164 pj_thread_sleep(50);
Benny Prijonod0a35852007-02-23 01:07:54 +0000165 }
166
Benny Prijonod0a35852007-02-23 01:07:54 +0000167 return 0;
168}
169
Benny Prijono4df62612007-03-01 23:39:08 +0000170static int init()
171{
172 pj_sockaddr_in addr;
173 pj_stun_session_cb stun_cb;
Benny Prijonod5971742007-03-10 23:15:36 +0000174 int len;
Benny Prijono4df62612007-03-01 23:39:08 +0000175 pj_status_t status;
176
177 g.sock = PJ_INVALID_SOCKET;
178
179 status = pj_init();
180 status = pjlib_util_init();
181
182 pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0);
183
Benny Prijonoe4c40202007-03-10 11:19:11 +0000184 if (o.srv_addr) {
Benny Prijono4df62612007-03-01 23:39:08 +0000185 pj_str_t s;
186 pj_uint16_t port;
187
Benny Prijonoe4c40202007-03-10 11:19:11 +0000188 if (o.srv_port)
189 port = (pj_uint16_t) atoi(o.srv_port);
Benny Prijono4df62612007-03-01 23:39:08 +0000190 else
191 port = PJ_STUN_PORT;
192
Benny Prijonoe4c40202007-03-10 11:19:11 +0000193 status = pj_sockaddr_in_init(&g.srv_addr, pj_cstr(&s, o.srv_addr), port);
Benny Prijono4df62612007-03-01 23:39:08 +0000194 if (status != PJ_SUCCESS) {
195 my_perror("Invalid address", status);
196 return status;
197 }
198
Benny Prijonoe4c40202007-03-10 11:19:11 +0000199 printf("Destination address set to %s:%d\n", o.srv_addr, (int)port);
Benny Prijono4df62612007-03-01 23:39:08 +0000200 } else {
201 printf("Error: address must be specified\n");
202 return PJ_EINVAL;
203 }
204
205 g.pool = pj_pool_create(&g.cp.factory, NULL, 1000, 1000, NULL);
206
207 status = pj_timer_heap_create(g.pool, 1000, &g.th);
208 pj_assert(status == PJ_SUCCESS);
209
210 status = pj_stun_endpoint_create(&g.cp.factory, 0, NULL, g.th, &g.endpt);
211 pj_assert(status == PJ_SUCCESS);
212
213 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &g.sock);
214 pj_assert(status == PJ_SUCCESS);
215
216 status = pj_sockaddr_in_init(&addr, NULL, 0);
217 pj_assert(status == PJ_SUCCESS);
218
Benny Prijonod5971742007-03-10 23:15:36 +0000219 addr.sin_port = pj_htons((pj_uint16_t)LOCAL_PORT);
220 status = pj_sock_bind(g.sock, &addr, sizeof(addr));
221 pj_assert(status == PJ_SUCCESS);
222
223 len = sizeof(addr);
224 status = pj_sock_getsockname(g.sock, &addr, &len);
225 pj_assert(status == PJ_SUCCESS);
226
227 PJ_LOG(3,(THIS_FILE, "Listening on port %d", (int)pj_ntohs(addr.sin_port)));
228
229 pj_memcpy(&g.peer_addr, &addr, sizeof(pj_sockaddr_in));
230 if (g.peer_addr.sin_addr.s_addr == 0)
231 pj_gethostip(&g.peer_addr.sin_addr);
232
Benny Prijono4df62612007-03-01 23:39:08 +0000233 pj_memset(&stun_cb, 0, sizeof(stun_cb));
234 stun_cb.on_send_msg = &on_send_msg;
235 stun_cb.on_request_complete = &on_request_complete;
236
Benny Prijonocc8febb2007-03-03 02:16:36 +0000237 status = pj_stun_session_create(g.endpt, NULL, &stun_cb,
238 o.use_fingerprint!=0, &g.sess);
Benny Prijono4df62612007-03-01 23:39:08 +0000239 pj_assert(status == PJ_SUCCESS);
240
Benny Prijonocc8febb2007-03-03 02:16:36 +0000241 if (o.user_name) {
242 pj_stun_auth_cred cred;
Benny Prijono4df62612007-03-01 23:39:08 +0000243
Benny Prijonocc8febb2007-03-03 02:16:36 +0000244 pj_bzero(&cred, sizeof(cred));
Benny Prijono4df62612007-03-01 23:39:08 +0000245
Benny Prijonocc8febb2007-03-03 02:16:36 +0000246 cred.type = PJ_STUN_AUTH_CRED_STATIC;
247 cred.data.static_cred.realm = pj_str(o.realm);
248 cred.data.static_cred.username = pj_str(o.user_name);
249 cred.data.static_cred.data_type = 0;
250 cred.data.static_cred.data = pj_str(o.password);
251 cred.data.static_cred.nonce = pj_str(o.nonce);
252
253 pj_stun_session_set_credential(g.sess, &cred);
254 puts("Session credential set");
Benny Prijono4df62612007-03-01 23:39:08 +0000255 } else {
256 puts("Credential not set");
257 }
258
Benny Prijonod5971742007-03-10 23:15:36 +0000259 if (o.peer_addr) {
260 if (parse_addr(o.peer_addr, &g.peer_addr)!=PJ_SUCCESS)
261 return -1;
262 }
263
Benny Prijono4df62612007-03-01 23:39:08 +0000264 status = pj_thread_create(g.pool, "stun", &worker_thread, NULL,
265 0, 0, &g.thread);
266 if (status != PJ_SUCCESS)
267 return status;
268
269 return PJ_SUCCESS;
270}
271
272
273static int shutdown()
274{
275 if (g.thread) {
276 g.quit = 1;
277 pj_thread_join(g.thread);
278 pj_thread_destroy(g.thread);
279 g.thread = NULL;
280 }
281 if (g.sess)
282 pj_stun_session_destroy(g.sess);
283 if (g.endpt)
284 pj_stun_endpoint_destroy(g.endpt);
285 if (g.sock != PJ_INVALID_SOCKET)
286 pj_sock_close(g.sock);
287 if (g.th)
288 pj_timer_heap_destroy(g.th);
289 if (g.pool)
290 pj_pool_release(g.pool);
291
292 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
293 pj_caching_pool_destroy(&g.cp);
294
295 return PJ_SUCCESS;
296}
297
Benny Prijono62923f22007-03-09 23:25:11 +0000298static void send_bind_request(void)
299{
300 pj_stun_tx_data *tdata;
301 pj_status_t rc;
302
303 rc = pj_stun_session_create_req(g.sess, PJ_STUN_BINDING_REQUEST, &tdata);
304 pj_assert(rc == PJ_SUCCESS);
305
306 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000307 &g.srv_addr, sizeof(g.srv_addr),
Benny Prijono62923f22007-03-09 23:25:11 +0000308 tdata);
309 if (rc != PJ_SUCCESS)
310 my_perror("Error sending STUN request", rc);
311}
312
Benny Prijonoe4c40202007-03-10 11:19:11 +0000313static void send_allocate_request(pj_bool_t allocate)
Benny Prijono62923f22007-03-09 23:25:11 +0000314{
Benny Prijonoe4c40202007-03-10 11:19:11 +0000315 pj_stun_tx_data *tdata;
316 pj_status_t rc;
317
318 rc = pj_stun_session_create_req(g.sess, PJ_STUN_ALLOCATE_REQUEST, &tdata);
319 pj_assert(rc == PJ_SUCCESS);
320
321
322 if (BANDWIDTH != -1) {
323 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
324 PJ_STUN_ATTR_BANDWIDTH, BANDWIDTH);
325 }
326
327 if (!allocate) {
328 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
329 PJ_STUN_ATTR_LIFETIME, 0);
330
331 } else {
332 if (LIFETIME != -1) {
333 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
334 PJ_STUN_ATTR_LIFETIME, LIFETIME);
335 }
336
337 if (REQ_TRANSPORT != -1) {
338 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
339 PJ_STUN_ATTR_REQ_TRANSPORT, REQ_TRANSPORT);
340 }
341
342 if (REQ_PORT_PROPS != -1) {
343 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
344 PJ_STUN_ATTR_REQ_PORT_PROPS, REQ_PORT_PROPS);
345 }
346
347 if (REQ_IP != NULL) {
348 pj_sockaddr_in addr;
349 pj_str_t tmp;
350
351 pj_sockaddr_in_init(&addr, pj_cstr(&tmp, REQ_IP), 0);
Benny Prijonod5971742007-03-10 23:15:36 +0000352 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000353 PJ_STUN_ATTR_REQ_IP, PJ_FALSE,
354 &addr, sizeof(addr));
355 }
356 }
357
358 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
359 &g.srv_addr, sizeof(g.srv_addr),
360 tdata);
361 pj_assert(rc == PJ_SUCCESS);
Benny Prijono62923f22007-03-09 23:25:11 +0000362}
363
Benny Prijonoe4c40202007-03-10 11:19:11 +0000364static void send_sad_request(pj_bool_t set)
Benny Prijono62923f22007-03-09 23:25:11 +0000365{
Benny Prijonoe4c40202007-03-10 11:19:11 +0000366 pj_stun_tx_data *tdata;
367 pj_status_t rc;
368
369 if (g.peer_addr.sin_addr.s_addr == 0 ||
370 g.peer_addr.sin_port == 0)
371 {
372 puts("Error: peer address is not set");
373 return;
374 }
375
Benny Prijonod5971742007-03-10 23:15:36 +0000376 rc = pj_stun_session_create_req(g.sess, PJ_STUN_SET_ACTIVE_DESTINATION_REQUEST, &tdata);
Benny Prijonoe4c40202007-03-10 11:19:11 +0000377 pj_assert(rc == PJ_SUCCESS);
378
379 if (set) {
Benny Prijonod5971742007-03-10 23:15:36 +0000380 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000381 PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
382 &g.peer_addr, sizeof(g.peer_addr));
383 }
384
385 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
386 &g.srv_addr, sizeof(g.srv_addr),
387 tdata);
388 pj_assert(rc == PJ_SUCCESS);
Benny Prijono62923f22007-03-09 23:25:11 +0000389}
390
391static void send_send_ind(void)
392{
Benny Prijonod5971742007-03-10 23:15:36 +0000393 pj_stun_tx_data *tdata;
394 int len;
395 pj_status_t rc;
396
397 if (g.peer_addr.sin_addr.s_addr == 0 ||
398 g.peer_addr.sin_port == 0)
399 {
400 puts("Error: peer address is not set");
401 return;
402 }
403
404 len = strlen(g.data);
405 if (len==0) {
406 puts("Error: data is not set");
407 return;
408 }
409
410 rc = pj_stun_session_create_ind(g.sess, PJ_STUN_SEND_INDICATION, &tdata);
411 pj_assert(rc == PJ_SUCCESS);
412
413 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
414 PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
415 &g.peer_addr, sizeof(g.peer_addr));
416 pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
417 PJ_STUN_ATTR_DATA, g.data, len);
418
419 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
420 &g.srv_addr, sizeof(g.srv_addr),
421 tdata);
422 pj_assert(rc == PJ_SUCCESS);
423
Benny Prijono62923f22007-03-09 23:25:11 +0000424}
425
Benny Prijonod5971742007-03-10 23:15:36 +0000426static void send_raw_data_to_srv(void)
Benny Prijono62923f22007-03-09 23:25:11 +0000427{
Benny Prijonod5971742007-03-10 23:15:36 +0000428 pj_ssize_t len;
429
430 if (g.srv_addr.sin_addr.s_addr == 0 ||
431 g.srv_addr.sin_port == 0)
432 {
433 puts("Error: server address is not set");
434 return;
435 }
436
437 len = strlen(g.data);
438 if (len==0) {
439 puts("Error: data is not set");
440 return;
441 }
442
443 len = strlen(g.data);
444 pj_sock_sendto(g.sock, g.data, &len, 0, &g.srv_addr, sizeof(g.srv_addr));
445}
446
447static void send_raw_data_to_relay(void)
448{
449 pj_ssize_t len;
450
451 if (g.relay_addr.sin_addr.s_addr == 0 ||
452 g.relay_addr.sin_port == 0)
453 {
454 puts("Error: relay address is not set");
455 return;
456 }
457
458 len = strlen(g.data);
459 if (len==0) {
460 puts("Error: data is not set");
461 return;
462 }
463
464 len = strlen(g.data);
465 pj_sock_sendto(g.sock, g.data, &len, 0, &g.relay_addr, sizeof(g.relay_addr));
466}
467
468static pj_status_t parse_addr(const char *input,
469 pj_sockaddr_in *addr)
470{
471 const char *pos;
472 pj_str_t ip;
473 pj_uint16_t port;
474 pj_sockaddr_in tmp_addr;
475
476 pos = pj_ansi_strchr(input, ':');
477 if (pos==NULL) {
478 puts("Invalid format");
479 return -1;
480 }
481
482 ip.ptr = (char*)input;
483 ip.slen = pos - input;
484 port = (pj_uint16_t)atoi(pos+1);
485
486 if (port==0) {
487 puts("Invalid port");
488 return -1;
489 }
490
491 if (pj_sockaddr_in_init(&tmp_addr, &ip, port)!=PJ_SUCCESS) {
492 puts("Invalid address");
493 return -1;
494 }
495
496 pj_memcpy(addr, &tmp_addr, sizeof(tmp_addr));
497
498 return PJ_SUCCESS;
Benny Prijono62923f22007-03-09 23:25:11 +0000499}
500
Benny Prijonoe4c40202007-03-10 11:19:11 +0000501static void set_peer_addr(void)
502{
Benny Prijonod5971742007-03-10 23:15:36 +0000503 char addr[64];
Benny Prijonoe4c40202007-03-10 11:19:11 +0000504
505 printf("Current peer address: %s:%d\n",
506 pj_inet_ntoa(g.peer_addr.sin_addr),
507 pj_ntohs(g.peer_addr.sin_port));
508
509 printf("Input peer address in IP:PORT format: ");
510 fflush(stdout);
Benny Prijonod5971742007-03-10 23:15:36 +0000511 gets(addr);
Benny Prijonoe4c40202007-03-10 11:19:11 +0000512
Benny Prijonod5971742007-03-10 23:15:36 +0000513 if (parse_addr(addr, &g.peer_addr) != PJ_SUCCESS) {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000514 return;
515 }
516
Benny Prijonoe4c40202007-03-10 11:19:11 +0000517}
518
Benny Prijono4df62612007-03-01 23:39:08 +0000519static void menu(void)
520{
521 puts("Menu:");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000522 printf(" pr Set peer address (currently %s:%d)\n",
523 pj_inet_ntoa(g.peer_addr.sin_addr), pj_ntohs(g.peer_addr.sin_port));
Benny Prijonod5971742007-03-10 23:15:36 +0000524 printf(" dt Set data (currently \"%s\")\n", g.data);
Benny Prijono62923f22007-03-09 23:25:11 +0000525 puts(" br Send Bind request");
526 puts(" ar Send Allocate request");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000527 puts(" dr Send de-Allocate request");
Benny Prijonod5971742007-03-10 23:15:36 +0000528 puts(" sr Send Set Active Destination request");
529 puts(" cr Send clear Active Destination request");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000530 puts(" si Send data with Send Indication");
Benny Prijonod5971742007-03-10 23:15:36 +0000531 puts(" rw Send raw data to TURN server");
532 puts(" rW Send raw data to relay address");
Benny Prijono62923f22007-03-09 23:25:11 +0000533 puts(" q Quit");
Benny Prijono4df62612007-03-01 23:39:08 +0000534 puts("");
535 printf("Choice: ");
536}
537
Benny Prijonoe4c40202007-03-10 11:19:11 +0000538
Benny Prijono4df62612007-03-01 23:39:08 +0000539static void console_main(void)
540{
541 while (!g.quit) {
542 char input[10];
543
544 menu();
545
546 fgets(input, sizeof(input), stdin);
547
Benny Prijonod5971742007-03-10 23:15:36 +0000548 if (0) {
549
550 } else if (input[0]=='d' && input[1]=='t') {
551 printf("Input data: ");
552 gets(g.data);
553
554 } else if (input[0]=='p' && input[1]=='r') {
555 set_peer_addr();
556
557 } else if (input[0]=='b' && input[1]=='r') {
Benny Prijono62923f22007-03-09 23:25:11 +0000558 send_bind_request();
559
560 } else if (input[0]=='a' && input[1]=='r') {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000561 send_allocate_request(PJ_TRUE);
562
563 } else if (input[0]=='d' && input[1]=='r') {
564 send_allocate_request(PJ_FALSE);
Benny Prijono62923f22007-03-09 23:25:11 +0000565
566 } else if (input[0]=='s' && input[1]=='r') {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000567 send_sad_request(PJ_TRUE);
568
569 } else if (input[0]=='c' && input[1]=='r') {
570 send_sad_request(PJ_FALSE);
Benny Prijono62923f22007-03-09 23:25:11 +0000571
572 } else if (input[0]=='s' && input[1]=='i') {
573 send_send_ind();
574
575 } else if (input[0]=='r' && input[1]=='w') {
Benny Prijonod5971742007-03-10 23:15:36 +0000576 send_raw_data_to_srv();
Benny Prijono62923f22007-03-09 23:25:11 +0000577
Benny Prijonod5971742007-03-10 23:15:36 +0000578 } else if (input[0]=='r' && input[1]=='W') {
579 send_raw_data_to_relay();
Benny Prijonoe4c40202007-03-10 11:19:11 +0000580
Benny Prijono62923f22007-03-09 23:25:11 +0000581 } else if (input[0]=='q') {
Benny Prijono4df62612007-03-01 23:39:08 +0000582 g.quit = 1;
Benny Prijono4df62612007-03-01 23:39:08 +0000583 }
584 }
585}
586
587
588static void usage(void)
589{
590 puts("Usage: pjstun_client TARGET [OPTIONS]");
591 puts("");
592 puts("where TARGET is \"host[:port]\"");
593 puts("");
594 puts("and OPTIONS:");
595 puts(" --realm, -r Set realm of the credential");
596 puts(" --username, -u Set username of the credential");
597 puts(" --password, -p Set password of the credential");
Benny Prijonocc8febb2007-03-03 02:16:36 +0000598 puts(" --nonce, -N Set NONCE");
Benny Prijono4df62612007-03-01 23:39:08 +0000599 puts(" --fingerprint, -F Use fingerprint for outgoing requests");
Benny Prijonod5971742007-03-10 23:15:36 +0000600 puts(" --peer, -P Set peer address (address is in HOST:PORT format)");
601 puts(" --data, -D Set data");
Benny Prijono4df62612007-03-01 23:39:08 +0000602 puts(" --help, -h");
603}
604
605int main(int argc, char *argv[])
606{
607 struct pj_getopt_option long_options[] = {
608 { "realm", 1, 0, 'r'},
609 { "username", 1, 0, 'u'},
610 { "password", 1, 0, 'p'},
Benny Prijonocc8febb2007-03-03 02:16:36 +0000611 { "nonce", 1, 0, 'N'},
Benny Prijono4df62612007-03-01 23:39:08 +0000612 { "fingerprint",0, 0, 'F'},
Benny Prijonod5971742007-03-10 23:15:36 +0000613 { "peer", 1, 0, 'P'},
614 { "data", 1, 0, 'D'},
Benny Prijono4df62612007-03-01 23:39:08 +0000615 { "help", 0, 0, 'h'}
616 };
617 int c, opt_id;
618 char *pos;
619 pj_status_t status;
620
Benny Prijonod5971742007-03-10 23:15:36 +0000621 g.data = g.data_buf;
622
Benny Prijono4df62612007-03-01 23:39:08 +0000623 while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
624 switch (c) {
625 case 'r':
626 o.realm = pj_optarg;
627 break;
628 case 'u':
629 o.user_name = pj_optarg;
630 break;
631 case 'p':
632 o.password = pj_optarg;
633 break;
Benny Prijonocc8febb2007-03-03 02:16:36 +0000634 case 'N':
635 o.nonce = pj_optarg;
636 break;
Benny Prijono4df62612007-03-01 23:39:08 +0000637 case 'h':
638 usage();
639 return 0;
640 case 'F':
641 o.use_fingerprint = PJ_TRUE;
642 break;
Benny Prijonod5971742007-03-10 23:15:36 +0000643 case 'P':
644 o.peer_addr = pj_optarg;
645 break;
646 case 'D':
647 g.data = pj_optarg;
648 break;
649
Benny Prijono4df62612007-03-01 23:39:08 +0000650 default:
651 printf("Argument \"%s\" is not valid. Use -h to see help",
652 argv[pj_optind]);
653 return 1;
654 }
655 }
656
657 if (pj_optind == argc) {
658 puts("Error: TARGET is needed");
659 return 1;
660 }
661
662 if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000663 o.srv_addr = argv[pj_optind];
Benny Prijono4df62612007-03-01 23:39:08 +0000664 *pos = '\0';
Benny Prijonoe4c40202007-03-10 11:19:11 +0000665 o.srv_port = pos+1;
Benny Prijono4df62612007-03-01 23:39:08 +0000666 } else {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000667 o.srv_addr = argv[pj_optind];
Benny Prijono4df62612007-03-01 23:39:08 +0000668 }
669
670 status = init();
671 if (status != PJ_SUCCESS)
672 goto on_return;
673
674 console_main();
675
676on_return:
677 shutdown();
678 return status ? 1 : 0;
679}
Benny Prijono91169ac2007-02-22 02:09:23 +0000680