blob: 7d6b8ebc2b9e0985e6b4272b258626e003ec362a [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{
Benny Prijono48a49272007-03-21 09:31:01 +000035 pj_stun_config stun_config;
Benny Prijono4df62612007-03-01 23:39:08 +000036 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) {
Benny Prijono48a49272007-03-21 09:31:01 +0000107 pj_memcpy(&g.relay_addr, &ar->sockaddr.ipv4,
Benny Prijonod5971742007-03-10 23:15:36 +0000108 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
Benny Prijono48a49272007-03-21 09:31:01 +0000211 pj_stun_config_init(&g.stun_config, &g.cp.factory, 0, NULL, g.th);
Benny Prijono4df62612007-03-01 23:39:08 +0000212 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 Prijono48a49272007-03-21 09:31:01 +0000238 status = pj_stun_session_create(&g.stun_config, NULL, &stun_cb,
Benny Prijonocc8febb2007-03-03 02:16:36 +0000239 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);
Benny Prijono4df62612007-03-01 23:39:08 +0000284 if (g.sock != PJ_INVALID_SOCKET)
285 pj_sock_close(g.sock);
286 if (g.th)
287 pj_timer_heap_destroy(g.th);
288 if (g.pool)
289 pj_pool_release(g.pool);
290
291 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
292 pj_caching_pool_destroy(&g.cp);
293
294 return PJ_SUCCESS;
295}
296
Benny Prijono62923f22007-03-09 23:25:11 +0000297static void send_bind_request(void)
298{
299 pj_stun_tx_data *tdata;
300 pj_status_t rc;
301
302 rc = pj_stun_session_create_req(g.sess, PJ_STUN_BINDING_REQUEST, &tdata);
303 pj_assert(rc == PJ_SUCCESS);
304
305 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000306 &g.srv_addr, sizeof(g.srv_addr),
Benny Prijono62923f22007-03-09 23:25:11 +0000307 tdata);
308 if (rc != PJ_SUCCESS)
309 my_perror("Error sending STUN request", rc);
310}
311
Benny Prijonoe4c40202007-03-10 11:19:11 +0000312static void send_allocate_request(pj_bool_t allocate)
Benny Prijono62923f22007-03-09 23:25:11 +0000313{
Benny Prijonoe4c40202007-03-10 11:19:11 +0000314 pj_stun_tx_data *tdata;
315 pj_status_t rc;
316
317 rc = pj_stun_session_create_req(g.sess, PJ_STUN_ALLOCATE_REQUEST, &tdata);
318 pj_assert(rc == PJ_SUCCESS);
319
320
321 if (BANDWIDTH != -1) {
322 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
323 PJ_STUN_ATTR_BANDWIDTH, BANDWIDTH);
324 }
325
326 if (!allocate) {
327 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
328 PJ_STUN_ATTR_LIFETIME, 0);
329
330 } else {
331 if (LIFETIME != -1) {
332 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
333 PJ_STUN_ATTR_LIFETIME, LIFETIME);
334 }
335
336 if (REQ_TRANSPORT != -1) {
337 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
338 PJ_STUN_ATTR_REQ_TRANSPORT, REQ_TRANSPORT);
339 }
340
341 if (REQ_PORT_PROPS != -1) {
342 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
343 PJ_STUN_ATTR_REQ_PORT_PROPS, REQ_PORT_PROPS);
344 }
345
346 if (REQ_IP != NULL) {
347 pj_sockaddr_in addr;
348 pj_str_t tmp;
349
350 pj_sockaddr_in_init(&addr, pj_cstr(&tmp, REQ_IP), 0);
Benny Prijonod5971742007-03-10 23:15:36 +0000351 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000352 PJ_STUN_ATTR_REQ_IP, PJ_FALSE,
353 &addr, sizeof(addr));
354 }
355 }
356
357 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
358 &g.srv_addr, sizeof(g.srv_addr),
359 tdata);
360 pj_assert(rc == PJ_SUCCESS);
Benny Prijono62923f22007-03-09 23:25:11 +0000361}
362
Benny Prijonoe4c40202007-03-10 11:19:11 +0000363static void send_sad_request(pj_bool_t set)
Benny Prijono62923f22007-03-09 23:25:11 +0000364{
Benny Prijonoe4c40202007-03-10 11:19:11 +0000365 pj_stun_tx_data *tdata;
366 pj_status_t rc;
367
368 if (g.peer_addr.sin_addr.s_addr == 0 ||
369 g.peer_addr.sin_port == 0)
370 {
371 puts("Error: peer address is not set");
372 return;
373 }
374
Benny Prijonod5971742007-03-10 23:15:36 +0000375 rc = pj_stun_session_create_req(g.sess, PJ_STUN_SET_ACTIVE_DESTINATION_REQUEST, &tdata);
Benny Prijonoe4c40202007-03-10 11:19:11 +0000376 pj_assert(rc == PJ_SUCCESS);
377
378 if (set) {
Benny Prijonod5971742007-03-10 23:15:36 +0000379 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
Benny Prijonoe4c40202007-03-10 11:19:11 +0000380 PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
381 &g.peer_addr, sizeof(g.peer_addr));
382 }
383
384 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
385 &g.srv_addr, sizeof(g.srv_addr),
386 tdata);
387 pj_assert(rc == PJ_SUCCESS);
Benny Prijono62923f22007-03-09 23:25:11 +0000388}
389
390static void send_send_ind(void)
391{
Benny Prijonod5971742007-03-10 23:15:36 +0000392 pj_stun_tx_data *tdata;
393 int len;
394 pj_status_t rc;
395
396 if (g.peer_addr.sin_addr.s_addr == 0 ||
397 g.peer_addr.sin_port == 0)
398 {
399 puts("Error: peer address is not set");
400 return;
401 }
402
403 len = strlen(g.data);
404 if (len==0) {
405 puts("Error: data is not set");
406 return;
407 }
408
409 rc = pj_stun_session_create_ind(g.sess, PJ_STUN_SEND_INDICATION, &tdata);
410 pj_assert(rc == PJ_SUCCESS);
411
412 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
413 PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
414 &g.peer_addr, sizeof(g.peer_addr));
415 pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
416 PJ_STUN_ATTR_DATA, g.data, len);
417
418 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
419 &g.srv_addr, sizeof(g.srv_addr),
420 tdata);
421 pj_assert(rc == PJ_SUCCESS);
422
Benny Prijono62923f22007-03-09 23:25:11 +0000423}
424
Benny Prijonod5971742007-03-10 23:15:36 +0000425static void send_raw_data_to_srv(void)
Benny Prijono62923f22007-03-09 23:25:11 +0000426{
Benny Prijonod5971742007-03-10 23:15:36 +0000427 pj_ssize_t len;
428
429 if (g.srv_addr.sin_addr.s_addr == 0 ||
430 g.srv_addr.sin_port == 0)
431 {
432 puts("Error: server address is not set");
433 return;
434 }
435
436 len = strlen(g.data);
437 if (len==0) {
438 puts("Error: data is not set");
439 return;
440 }
441
442 len = strlen(g.data);
443 pj_sock_sendto(g.sock, g.data, &len, 0, &g.srv_addr, sizeof(g.srv_addr));
444}
445
446static void send_raw_data_to_relay(void)
447{
448 pj_ssize_t len;
449
450 if (g.relay_addr.sin_addr.s_addr == 0 ||
451 g.relay_addr.sin_port == 0)
452 {
453 puts("Error: relay address is not set");
454 return;
455 }
456
457 len = strlen(g.data);
458 if (len==0) {
459 puts("Error: data is not set");
460 return;
461 }
462
463 len = strlen(g.data);
464 pj_sock_sendto(g.sock, g.data, &len, 0, &g.relay_addr, sizeof(g.relay_addr));
465}
466
467static pj_status_t parse_addr(const char *input,
468 pj_sockaddr_in *addr)
469{
470 const char *pos;
471 pj_str_t ip;
472 pj_uint16_t port;
473 pj_sockaddr_in tmp_addr;
474
475 pos = pj_ansi_strchr(input, ':');
476 if (pos==NULL) {
477 puts("Invalid format");
478 return -1;
479 }
480
481 ip.ptr = (char*)input;
482 ip.slen = pos - input;
483 port = (pj_uint16_t)atoi(pos+1);
484
485 if (port==0) {
486 puts("Invalid port");
487 return -1;
488 }
489
490 if (pj_sockaddr_in_init(&tmp_addr, &ip, port)!=PJ_SUCCESS) {
491 puts("Invalid address");
492 return -1;
493 }
494
495 pj_memcpy(addr, &tmp_addr, sizeof(tmp_addr));
496
497 return PJ_SUCCESS;
Benny Prijono62923f22007-03-09 23:25:11 +0000498}
499
Benny Prijonoe4c40202007-03-10 11:19:11 +0000500static void set_peer_addr(void)
501{
Benny Prijonod5971742007-03-10 23:15:36 +0000502 char addr[64];
Benny Prijonoe4c40202007-03-10 11:19:11 +0000503
504 printf("Current peer address: %s:%d\n",
505 pj_inet_ntoa(g.peer_addr.sin_addr),
506 pj_ntohs(g.peer_addr.sin_port));
507
508 printf("Input peer address in IP:PORT format: ");
509 fflush(stdout);
Benny Prijonod5971742007-03-10 23:15:36 +0000510 gets(addr);
Benny Prijonoe4c40202007-03-10 11:19:11 +0000511
Benny Prijonod5971742007-03-10 23:15:36 +0000512 if (parse_addr(addr, &g.peer_addr) != PJ_SUCCESS) {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000513 return;
514 }
515
Benny Prijonoe4c40202007-03-10 11:19:11 +0000516}
517
Benny Prijono4df62612007-03-01 23:39:08 +0000518static void menu(void)
519{
520 puts("Menu:");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000521 printf(" pr Set peer address (currently %s:%d)\n",
522 pj_inet_ntoa(g.peer_addr.sin_addr), pj_ntohs(g.peer_addr.sin_port));
Benny Prijonod5971742007-03-10 23:15:36 +0000523 printf(" dt Set data (currently \"%s\")\n", g.data);
Benny Prijono62923f22007-03-09 23:25:11 +0000524 puts(" br Send Bind request");
525 puts(" ar Send Allocate request");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000526 puts(" dr Send de-Allocate request");
Benny Prijonod5971742007-03-10 23:15:36 +0000527 puts(" sr Send Set Active Destination request");
528 puts(" cr Send clear Active Destination request");
Benny Prijonoe4c40202007-03-10 11:19:11 +0000529 puts(" si Send data with Send Indication");
Benny Prijonod5971742007-03-10 23:15:36 +0000530 puts(" rw Send raw data to TURN server");
531 puts(" rW Send raw data to relay address");
Benny Prijono62923f22007-03-09 23:25:11 +0000532 puts(" q Quit");
Benny Prijono4df62612007-03-01 23:39:08 +0000533 puts("");
534 printf("Choice: ");
535}
536
Benny Prijonoe4c40202007-03-10 11:19:11 +0000537
Benny Prijono4df62612007-03-01 23:39:08 +0000538static void console_main(void)
539{
540 while (!g.quit) {
541 char input[10];
542
543 menu();
544
545 fgets(input, sizeof(input), stdin);
546
Benny Prijonod5971742007-03-10 23:15:36 +0000547 if (0) {
548
549 } else if (input[0]=='d' && input[1]=='t') {
550 printf("Input data: ");
551 gets(g.data);
552
553 } else if (input[0]=='p' && input[1]=='r') {
554 set_peer_addr();
555
556 } else if (input[0]=='b' && input[1]=='r') {
Benny Prijono62923f22007-03-09 23:25:11 +0000557 send_bind_request();
558
559 } else if (input[0]=='a' && input[1]=='r') {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000560 send_allocate_request(PJ_TRUE);
561
562 } else if (input[0]=='d' && input[1]=='r') {
563 send_allocate_request(PJ_FALSE);
Benny Prijono62923f22007-03-09 23:25:11 +0000564
565 } else if (input[0]=='s' && input[1]=='r') {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000566 send_sad_request(PJ_TRUE);
567
568 } else if (input[0]=='c' && input[1]=='r') {
569 send_sad_request(PJ_FALSE);
Benny Prijono62923f22007-03-09 23:25:11 +0000570
571 } else if (input[0]=='s' && input[1]=='i') {
572 send_send_ind();
573
574 } else if (input[0]=='r' && input[1]=='w') {
Benny Prijonod5971742007-03-10 23:15:36 +0000575 send_raw_data_to_srv();
Benny Prijono62923f22007-03-09 23:25:11 +0000576
Benny Prijonod5971742007-03-10 23:15:36 +0000577 } else if (input[0]=='r' && input[1]=='W') {
578 send_raw_data_to_relay();
Benny Prijonoe4c40202007-03-10 11:19:11 +0000579
Benny Prijono62923f22007-03-09 23:25:11 +0000580 } else if (input[0]=='q') {
Benny Prijono4df62612007-03-01 23:39:08 +0000581 g.quit = 1;
Benny Prijono4df62612007-03-01 23:39:08 +0000582 }
583 }
584}
585
586
587static void usage(void)
588{
589 puts("Usage: pjstun_client TARGET [OPTIONS]");
590 puts("");
591 puts("where TARGET is \"host[:port]\"");
592 puts("");
593 puts("and OPTIONS:");
594 puts(" --realm, -r Set realm of the credential");
595 puts(" --username, -u Set username of the credential");
596 puts(" --password, -p Set password of the credential");
Benny Prijonocc8febb2007-03-03 02:16:36 +0000597 puts(" --nonce, -N Set NONCE");
Benny Prijono4df62612007-03-01 23:39:08 +0000598 puts(" --fingerprint, -F Use fingerprint for outgoing requests");
Benny Prijonod5971742007-03-10 23:15:36 +0000599 puts(" --peer, -P Set peer address (address is in HOST:PORT format)");
600 puts(" --data, -D Set data");
Benny Prijono4df62612007-03-01 23:39:08 +0000601 puts(" --help, -h");
602}
603
604int main(int argc, char *argv[])
605{
606 struct pj_getopt_option long_options[] = {
607 { "realm", 1, 0, 'r'},
608 { "username", 1, 0, 'u'},
609 { "password", 1, 0, 'p'},
Benny Prijonocc8febb2007-03-03 02:16:36 +0000610 { "nonce", 1, 0, 'N'},
Benny Prijono4df62612007-03-01 23:39:08 +0000611 { "fingerprint",0, 0, 'F'},
Benny Prijonod5971742007-03-10 23:15:36 +0000612 { "peer", 1, 0, 'P'},
613 { "data", 1, 0, 'D'},
Benny Prijono4df62612007-03-01 23:39:08 +0000614 { "help", 0, 0, 'h'}
615 };
616 int c, opt_id;
617 char *pos;
618 pj_status_t status;
619
Benny Prijonod5971742007-03-10 23:15:36 +0000620 g.data = g.data_buf;
621
Benny Prijono4df62612007-03-01 23:39:08 +0000622 while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
623 switch (c) {
624 case 'r':
625 o.realm = pj_optarg;
626 break;
627 case 'u':
628 o.user_name = pj_optarg;
629 break;
630 case 'p':
631 o.password = pj_optarg;
632 break;
Benny Prijonocc8febb2007-03-03 02:16:36 +0000633 case 'N':
634 o.nonce = pj_optarg;
635 break;
Benny Prijono4df62612007-03-01 23:39:08 +0000636 case 'h':
637 usage();
638 return 0;
639 case 'F':
640 o.use_fingerprint = PJ_TRUE;
641 break;
Benny Prijonod5971742007-03-10 23:15:36 +0000642 case 'P':
643 o.peer_addr = pj_optarg;
644 break;
645 case 'D':
646 g.data = pj_optarg;
647 break;
648
Benny Prijono4df62612007-03-01 23:39:08 +0000649 default:
650 printf("Argument \"%s\" is not valid. Use -h to see help",
651 argv[pj_optind]);
652 return 1;
653 }
654 }
655
656 if (pj_optind == argc) {
657 puts("Error: TARGET is needed");
658 return 1;
659 }
660
661 if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000662 o.srv_addr = argv[pj_optind];
Benny Prijono4df62612007-03-01 23:39:08 +0000663 *pos = '\0';
Benny Prijonoe4c40202007-03-10 11:19:11 +0000664 o.srv_port = pos+1;
Benny Prijono4df62612007-03-01 23:39:08 +0000665 } else {
Benny Prijonoe4c40202007-03-10 11:19:11 +0000666 o.srv_addr = argv[pj_optind];
Benny Prijono4df62612007-03-01 23:39:08 +0000667 }
668
669 status = init();
670 if (status != PJ_SUCCESS)
671 goto on_return;
672
673 console_main();
674
675on_return:
676 shutdown();
677 return status ? 1 : 0;
678}
Benny Prijono91169ac2007-02-22 02:09:23 +0000679