blob: f412a4ceeaacb097190cab7df40c3530545a2568 [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 Prijonod0a35852007-02-23 01:07:54 +000024
Benny Prijono4df62612007-03-01 23:39:08 +000025static struct global
26{
27 pj_stun_endpoint *endpt;
28 pj_pool_t *pool;
29 pj_caching_pool cp;
30 pj_timer_heap_t *th;
31 pj_stun_session *sess;
Benny Prijono4df62612007-03-01 23:39:08 +000032 pj_sock_t sock;
33 pj_thread_t *thread;
34 pj_bool_t quit;
35
36 pj_sockaddr_in dst_addr; /**< destination addr */
37
38} g;
39
40static struct options
41{
42 char *dst_addr;
43 char *dst_port;
44 char *realm;
45 char *user_name;
46 char *password;
Benny Prijonocc8febb2007-03-03 02:16:36 +000047 char *nonce;
Benny Prijono4df62612007-03-01 23:39:08 +000048 pj_bool_t use_fingerprint;
49} o;
50
Benny Prijonod0a35852007-02-23 01:07:54 +000051
52static my_perror(const char *title, pj_status_t status)
53{
54 char errmsg[PJ_ERR_MSG_SIZE];
55 pj_strerror(status, errmsg, sizeof(errmsg));
56
57 PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg));
58}
59
Benny Prijono4df62612007-03-01 23:39:08 +000060static pj_status_t on_send_msg(pj_stun_session *sess,
Benny Prijonod0a35852007-02-23 01:07:54 +000061 const void *pkt,
62 pj_size_t pkt_size,
Benny Prijono4df62612007-03-01 23:39:08 +000063 const pj_sockaddr_t *dst_addr,
64 unsigned addr_len)
Benny Prijonod0a35852007-02-23 01:07:54 +000065{
Benny Prijonod0a35852007-02-23 01:07:54 +000066 pj_ssize_t len;
67 pj_status_t status;
68
Benny Prijonod0a35852007-02-23 01:07:54 +000069 len = pkt_size;
Benny Prijono4df62612007-03-01 23:39:08 +000070 status = pj_sock_sendto(g.sock, pkt, &len, 0, dst_addr, addr_len);
Benny Prijonod0a35852007-02-23 01:07:54 +000071
72 if (status != PJ_SUCCESS)
73 my_perror("Error sending packet", status);
74
75 return status;
76}
77
Benny Prijono4df62612007-03-01 23:39:08 +000078static void on_request_complete(pj_stun_session *sess,
79 pj_status_t status,
80 pj_stun_tx_data *tdata,
81 const pj_stun_msg *response)
Benny Prijonod0a35852007-02-23 01:07:54 +000082{
Benny Prijono4df62612007-03-01 23:39:08 +000083 if (status == PJ_SUCCESS) {
84 puts("Client transaction completes");
85 } else {
86 my_perror("Client transaction error", status);
87 }
Benny Prijonod0a35852007-02-23 01:07:54 +000088}
89
Benny Prijono4df62612007-03-01 23:39:08 +000090static int worker_thread(void *unused)
Benny Prijonod0a35852007-02-23 01:07:54 +000091{
Benny Prijono4df62612007-03-01 23:39:08 +000092 PJ_UNUSED_ARG(unused);
Benny Prijonod0a35852007-02-23 01:07:54 +000093
Benny Prijono4df62612007-03-01 23:39:08 +000094 while (!g.quit) {
95 pj_time_val timeout = {0, 50};
96 pj_fd_set_t readset;
Benny Prijonod0a35852007-02-23 01:07:54 +000097 int n;
Benny Prijonod0a35852007-02-23 01:07:54 +000098
Benny Prijono4df62612007-03-01 23:39:08 +000099 pj_timer_heap_poll(g.th, NULL);
Benny Prijonod0a35852007-02-23 01:07:54 +0000100
Benny Prijono4df62612007-03-01 23:39:08 +0000101 PJ_FD_ZERO(&readset);
102 PJ_FD_SET(g.sock, &readset);
Benny Prijonod0a35852007-02-23 01:07:54 +0000103
Benny Prijono4df62612007-03-01 23:39:08 +0000104 n = pj_sock_select(g.sock+1, &readset, NULL, NULL, &timeout);
105 if (n > 0) {
106 if (PJ_FD_ISSET(g.sock, &readset)) {
107 char buffer[512];
108 pj_ssize_t len;
109 pj_sockaddr_in addr;
110 int addrlen;
111 pj_status_t rc;
Benny Prijonod0a35852007-02-23 01:07:54 +0000112
Benny Prijono4df62612007-03-01 23:39:08 +0000113 len = sizeof(buffer);
114 addrlen = sizeof(addr);
115 rc = pj_sock_recvfrom(g.sock, buffer, &len, 0, &addr, &addrlen);
116 if (rc == PJ_SUCCESS && len > 0) {
117 rc = pj_stun_session_on_rx_pkt(g.sess, buffer, len,
118 PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET,
119 NULL, &addr, addrlen);
120 if (rc != PJ_SUCCESS)
121 my_perror("Error processing packet", rc);
122 }
Benny Prijonod0a35852007-02-23 01:07:54 +0000123 }
Benny Prijono4df62612007-03-01 23:39:08 +0000124 } else if (n < 0)
125 pj_thread_sleep(50);
Benny Prijonod0a35852007-02-23 01:07:54 +0000126 }
127
Benny Prijonod0a35852007-02-23 01:07:54 +0000128 return 0;
129}
130
Benny Prijono4df62612007-03-01 23:39:08 +0000131static int init()
132{
133 pj_sockaddr_in addr;
134 pj_stun_session_cb stun_cb;
135 pj_status_t status;
136
137 g.sock = PJ_INVALID_SOCKET;
138
139 status = pj_init();
140 status = pjlib_util_init();
141
142 pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0);
143
144 if (o.dst_addr) {
145 pj_str_t s;
146 pj_uint16_t port;
147
148 if (o.dst_port)
149 port = (pj_uint16_t) atoi(o.dst_port);
150 else
151 port = PJ_STUN_PORT;
152
153 status = pj_sockaddr_in_init(&g.dst_addr, pj_cstr(&s, o.dst_addr), port);
154 if (status != PJ_SUCCESS) {
155 my_perror("Invalid address", status);
156 return status;
157 }
158
159 printf("Destination address set to %s:%d\n", o.dst_addr, (int)port);
160 } else {
161 printf("Error: address must be specified\n");
162 return PJ_EINVAL;
163 }
164
165 g.pool = pj_pool_create(&g.cp.factory, NULL, 1000, 1000, NULL);
166
167 status = pj_timer_heap_create(g.pool, 1000, &g.th);
168 pj_assert(status == PJ_SUCCESS);
169
170 status = pj_stun_endpoint_create(&g.cp.factory, 0, NULL, g.th, &g.endpt);
171 pj_assert(status == PJ_SUCCESS);
172
173 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &g.sock);
174 pj_assert(status == PJ_SUCCESS);
175
176 status = pj_sockaddr_in_init(&addr, NULL, 0);
177 pj_assert(status == PJ_SUCCESS);
178
179 pj_memset(&stun_cb, 0, sizeof(stun_cb));
180 stun_cb.on_send_msg = &on_send_msg;
181 stun_cb.on_request_complete = &on_request_complete;
182
Benny Prijonocc8febb2007-03-03 02:16:36 +0000183 status = pj_stun_session_create(g.endpt, NULL, &stun_cb,
184 o.use_fingerprint!=0, &g.sess);
Benny Prijono4df62612007-03-01 23:39:08 +0000185 pj_assert(status == PJ_SUCCESS);
186
Benny Prijonocc8febb2007-03-03 02:16:36 +0000187 if (o.user_name) {
188 pj_stun_auth_cred cred;
Benny Prijono4df62612007-03-01 23:39:08 +0000189
Benny Prijonocc8febb2007-03-03 02:16:36 +0000190 pj_bzero(&cred, sizeof(cred));
Benny Prijono4df62612007-03-01 23:39:08 +0000191
Benny Prijonocc8febb2007-03-03 02:16:36 +0000192 cred.type = PJ_STUN_AUTH_CRED_STATIC;
193 cred.data.static_cred.realm = pj_str(o.realm);
194 cred.data.static_cred.username = pj_str(o.user_name);
195 cred.data.static_cred.data_type = 0;
196 cred.data.static_cred.data = pj_str(o.password);
197 cred.data.static_cred.nonce = pj_str(o.nonce);
198
199 pj_stun_session_set_credential(g.sess, &cred);
200 puts("Session credential set");
Benny Prijono4df62612007-03-01 23:39:08 +0000201 } else {
202 puts("Credential not set");
203 }
204
Benny Prijono4df62612007-03-01 23:39:08 +0000205 status = pj_thread_create(g.pool, "stun", &worker_thread, NULL,
206 0, 0, &g.thread);
207 if (status != PJ_SUCCESS)
208 return status;
209
210 return PJ_SUCCESS;
211}
212
213
214static int shutdown()
215{
216 if (g.thread) {
217 g.quit = 1;
218 pj_thread_join(g.thread);
219 pj_thread_destroy(g.thread);
220 g.thread = NULL;
221 }
222 if (g.sess)
223 pj_stun_session_destroy(g.sess);
224 if (g.endpt)
225 pj_stun_endpoint_destroy(g.endpt);
226 if (g.sock != PJ_INVALID_SOCKET)
227 pj_sock_close(g.sock);
228 if (g.th)
229 pj_timer_heap_destroy(g.th);
230 if (g.pool)
231 pj_pool_release(g.pool);
232
233 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
234 pj_caching_pool_destroy(&g.cp);
235
236 return PJ_SUCCESS;
237}
238
Benny Prijono62923f22007-03-09 23:25:11 +0000239static void send_bind_request(void)
240{
241 pj_stun_tx_data *tdata;
242 pj_status_t rc;
243
244 rc = pj_stun_session_create_req(g.sess, PJ_STUN_BINDING_REQUEST, &tdata);
245 pj_assert(rc == PJ_SUCCESS);
246
247 rc = pj_stun_session_send_msg(g.sess, PJ_FALSE,
248 &g.dst_addr, sizeof(g.dst_addr),
249 tdata);
250 if (rc != PJ_SUCCESS)
251 my_perror("Error sending STUN request", rc);
252}
253
254static void send_allocate_request(void)
255{
256}
257
258static void send_sad_request(void)
259{
260}
261
262static void send_send_ind(void)
263{
264}
265
266static void send_raw_data(void)
267{
268}
269
Benny Prijono4df62612007-03-01 23:39:08 +0000270static void menu(void)
271{
272 puts("Menu:");
Benny Prijono62923f22007-03-09 23:25:11 +0000273 puts(" br Send Bind request");
274 puts(" ar Send Allocate request");
275 puts(" sr Send Set Active Indication request");
276 puts(" si Send Send Indication");
277 puts(" rw Send raw data");
278 puts(" q Quit");
Benny Prijono4df62612007-03-01 23:39:08 +0000279 puts("");
280 printf("Choice: ");
281}
282
283static void console_main(void)
284{
285 while (!g.quit) {
286 char input[10];
287
288 menu();
289
290 fgets(input, sizeof(input), stdin);
291
Benny Prijono62923f22007-03-09 23:25:11 +0000292 if (input[0]=='b' && input[1]=='r') {
293 send_bind_request();
294
295 } else if (input[0]=='a' && input[1]=='r') {
296 send_allocate_request();
297
298 } else if (input[0]=='s' && input[1]=='r') {
299 send_sad_request();
300
301 } else if (input[0]=='s' && input[1]=='i') {
302 send_send_ind();
303
304 } else if (input[0]=='r' && input[1]=='w') {
305 send_raw_data();
306
307 } else if (input[0]=='q') {
Benny Prijono4df62612007-03-01 23:39:08 +0000308 g.quit = 1;
Benny Prijono4df62612007-03-01 23:39:08 +0000309 }
310 }
311}
312
313
314static void usage(void)
315{
316 puts("Usage: pjstun_client TARGET [OPTIONS]");
317 puts("");
318 puts("where TARGET is \"host[:port]\"");
319 puts("");
320 puts("and OPTIONS:");
321 puts(" --realm, -r Set realm of the credential");
322 puts(" --username, -u Set username of the credential");
323 puts(" --password, -p Set password of the credential");
Benny Prijonocc8febb2007-03-03 02:16:36 +0000324 puts(" --nonce, -N Set NONCE");
Benny Prijono4df62612007-03-01 23:39:08 +0000325 puts(" --fingerprint, -F Use fingerprint for outgoing requests");
326 puts(" --help, -h");
327}
328
329int main(int argc, char *argv[])
330{
331 struct pj_getopt_option long_options[] = {
332 { "realm", 1, 0, 'r'},
333 { "username", 1, 0, 'u'},
334 { "password", 1, 0, 'p'},
Benny Prijonocc8febb2007-03-03 02:16:36 +0000335 { "nonce", 1, 0, 'N'},
Benny Prijono4df62612007-03-01 23:39:08 +0000336 { "fingerprint",0, 0, 'F'},
337 { "help", 0, 0, 'h'}
338 };
339 int c, opt_id;
340 char *pos;
341 pj_status_t status;
342
343 while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
344 switch (c) {
345 case 'r':
346 o.realm = pj_optarg;
347 break;
348 case 'u':
349 o.user_name = pj_optarg;
350 break;
351 case 'p':
352 o.password = pj_optarg;
353 break;
Benny Prijonocc8febb2007-03-03 02:16:36 +0000354 case 'N':
355 o.nonce = pj_optarg;
356 break;
Benny Prijono4df62612007-03-01 23:39:08 +0000357 case 'h':
358 usage();
359 return 0;
360 case 'F':
361 o.use_fingerprint = PJ_TRUE;
362 break;
363 default:
364 printf("Argument \"%s\" is not valid. Use -h to see help",
365 argv[pj_optind]);
366 return 1;
367 }
368 }
369
370 if (pj_optind == argc) {
371 puts("Error: TARGET is needed");
372 return 1;
373 }
374
375 if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
376 o.dst_addr = argv[pj_optind];
377 *pos = '\0';
378 o.dst_port = pos+1;
379 } else {
380 o.dst_addr = argv[pj_optind];
381 }
382
383 status = init();
384 if (status != PJ_SUCCESS)
385 goto on_return;
386
387 console_main();
388
389on_return:
390 shutdown();
391 return status ? 1 : 0;
392}
Benny Prijono91169ac2007-02-22 02:09:23 +0000393