blob: 4f82f743c7a2c09d07d966a2c11eb47abde8cd25 [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;
32 unsigned sess_options;
33 pj_sock_t sock;
34 pj_thread_t *thread;
35 pj_bool_t quit;
36
37 pj_sockaddr_in dst_addr; /**< destination addr */
38
39} g;
40
41static struct options
42{
43 char *dst_addr;
44 char *dst_port;
45 char *realm;
46 char *user_name;
47 char *password;
48 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
183 status = pj_stun_session_create(g.endpt, NULL, &stun_cb, &g.sess);
184 pj_assert(status == PJ_SUCCESS);
185
186 if (o.realm) {
187 pj_str_t r, u, p;
188
189 if (o.user_name == NULL) {
190 printf("error: username must be specified\n");
191 return PJ_EINVAL;
192 }
193 if (o.password == NULL)
194 o.password = "";
195 g.sess_options = PJ_STUN_USE_LONG_TERM_CRED;
196 pj_stun_session_set_long_term_credential(g.sess, pj_cstr(&r, o.realm),
197 pj_cstr(&u, o.user_name),
198 pj_cstr(&p, o.password));
199 puts("Using long term credential");
200 } else if (o.user_name) {
201 pj_str_t u, p;
202
203 if (o.password == NULL)
204 o.password = "";
205 g.sess_options = PJ_STUN_USE_SHORT_TERM_CRED;
206 pj_stun_session_set_short_term_credential(g.sess,
207 pj_cstr(&u, o.user_name),
208 pj_cstr(&p, o.password));
209 puts("Using short term credential");
210 } else {
211 puts("Credential not set");
212 }
213
214 if (o.use_fingerprint)
215 g.sess_options |= PJ_STUN_USE_FINGERPRINT;
216
217 status = pj_thread_create(g.pool, "stun", &worker_thread, NULL,
218 0, 0, &g.thread);
219 if (status != PJ_SUCCESS)
220 return status;
221
222 return PJ_SUCCESS;
223}
224
225
226static int shutdown()
227{
228 if (g.thread) {
229 g.quit = 1;
230 pj_thread_join(g.thread);
231 pj_thread_destroy(g.thread);
232 g.thread = NULL;
233 }
234 if (g.sess)
235 pj_stun_session_destroy(g.sess);
236 if (g.endpt)
237 pj_stun_endpoint_destroy(g.endpt);
238 if (g.sock != PJ_INVALID_SOCKET)
239 pj_sock_close(g.sock);
240 if (g.th)
241 pj_timer_heap_destroy(g.th);
242 if (g.pool)
243 pj_pool_release(g.pool);
244
245 pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
246 pj_caching_pool_destroy(&g.cp);
247
248 return PJ_SUCCESS;
249}
250
251static void menu(void)
252{
253 puts("Menu:");
254 puts(" b Send Bind request");
255 puts(" q Quit");
256 puts("");
257 printf("Choice: ");
258}
259
260static void console_main(void)
261{
262 while (!g.quit) {
263 char input[10];
264
265 menu();
266
267 fgets(input, sizeof(input), stdin);
268
269 switch (input[0]) {
270 case 'b':
271 {
272 pj_stun_tx_data *tdata;
273 pj_status_t rc;
274
275 rc = pj_stun_session_create_bind_req(g.sess, &tdata);
276 pj_assert(rc == PJ_SUCCESS);
277
278 rc = pj_stun_session_send_msg(g.sess, g.sess_options,
279 &g.dst_addr, sizeof(g.dst_addr),
280 tdata);
281 if (rc != PJ_SUCCESS)
282 my_perror("Error sending STUN request", rc);
283 }
284 break;
285 case 'q':
286 g.quit = 1;
287 break;
288 default:
289 break;
290 }
291 }
292}
293
294
295static void usage(void)
296{
297 puts("Usage: pjstun_client TARGET [OPTIONS]");
298 puts("");
299 puts("where TARGET is \"host[:port]\"");
300 puts("");
301 puts("and OPTIONS:");
302 puts(" --realm, -r Set realm of the credential");
303 puts(" --username, -u Set username of the credential");
304 puts(" --password, -p Set password of the credential");
305 puts(" --fingerprint, -F Use fingerprint for outgoing requests");
306 puts(" --help, -h");
307}
308
309int main(int argc, char *argv[])
310{
311 struct pj_getopt_option long_options[] = {
312 { "realm", 1, 0, 'r'},
313 { "username", 1, 0, 'u'},
314 { "password", 1, 0, 'p'},
315 { "fingerprint",0, 0, 'F'},
316 { "help", 0, 0, 'h'}
317 };
318 int c, opt_id;
319 char *pos;
320 pj_status_t status;
321
322 while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
323 switch (c) {
324 case 'r':
325 o.realm = pj_optarg;
326 break;
327 case 'u':
328 o.user_name = pj_optarg;
329 break;
330 case 'p':
331 o.password = pj_optarg;
332 break;
333 case 'h':
334 usage();
335 return 0;
336 case 'F':
337 o.use_fingerprint = PJ_TRUE;
338 break;
339 default:
340 printf("Argument \"%s\" is not valid. Use -h to see help",
341 argv[pj_optind]);
342 return 1;
343 }
344 }
345
346 if (pj_optind == argc) {
347 puts("Error: TARGET is needed");
348 return 1;
349 }
350
351 if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
352 o.dst_addr = argv[pj_optind];
353 *pos = '\0';
354 o.dst_port = pos+1;
355 } else {
356 o.dst_addr = argv[pj_optind];
357 }
358
359 status = init();
360 if (status != PJ_SUCCESS)
361 goto on_return;
362
363 console_main();
364
365on_return:
366 shutdown();
367 return status ? 1 : 0;
368}
Benny Prijono91169ac2007-02-22 02:09:23 +0000369