~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjsip-apps/src/samples/icedemo.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: icedemo.c 4217 2012-07-27 17:24:12Z nanang $ */
 
2
/* 
 
3
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 
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 <stdio.h>
 
20
#include <stdlib.h>
 
21
#include <pjlib.h>
 
22
#include <pjlib-util.h>
 
23
#include <pjnath.h>
 
24
 
 
25
 
 
26
#define THIS_FILE   "icedemo.c"
 
27
 
 
28
/* For this demo app, configure longer STUN keep-alive time
 
29
 * so that it does't clutter the screen output.
 
30
 */
 
31
#define KA_INTERVAL 300
 
32
 
 
33
 
 
34
/* This is our global variables */
 
35
static struct app_t
 
36
{
 
37
    /* Command line options are stored here */
 
38
    struct options
 
39
    {
 
40
        unsigned    comp_cnt;
 
41
        pj_str_t    ns;
 
42
        int         max_host;
 
43
        pj_bool_t   regular;
 
44
        pj_str_t    stun_srv;
 
45
        pj_str_t    turn_srv;
 
46
        pj_bool_t   turn_tcp;
 
47
        pj_str_t    turn_username;
 
48
        pj_str_t    turn_password;
 
49
        pj_bool_t   turn_fingerprint;
 
50
        const char *log_file;
 
51
    } opt;
 
52
 
 
53
    /* Our global variables */
 
54
    pj_caching_pool      cp;
 
55
    pj_pool_t           *pool;
 
56
    pj_thread_t         *thread;
 
57
    pj_bool_t            thread_quit_flag;
 
58
    pj_ice_strans_cfg    ice_cfg;
 
59
    pj_ice_strans       *icest;
 
60
    FILE                *log_fhnd;
 
61
 
 
62
    /* Variables to store parsed remote ICE info */
 
63
    struct rem_info
 
64
    {
 
65
        char             ufrag[80];
 
66
        char             pwd[80];
 
67
        unsigned         comp_cnt;
 
68
        pj_sockaddr      def_addr[PJ_ICE_MAX_COMP];
 
69
        unsigned         cand_cnt;
 
70
        pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];
 
71
    } rem;
 
72
 
 
73
} icedemo;
 
74
 
 
75
/* Utility to display error messages */
 
76
static void icedemo_perror(const char *title, pj_status_t status)
 
77
{
 
78
    char errmsg[PJ_ERR_MSG_SIZE];
 
79
 
 
80
    pj_strerror(status, errmsg, sizeof(errmsg));
 
81
    PJ_LOG(1,(THIS_FILE, "%s: %s", title, errmsg));
 
82
}
 
83
 
 
84
/* Utility: display error message and exit application (usually
 
85
 * because of fatal error.
 
86
 */
 
87
static void err_exit(const char *title, pj_status_t status)
 
88
{
 
89
    if (status != PJ_SUCCESS) {
 
90
        icedemo_perror(title, status);
 
91
    }
 
92
    PJ_LOG(3,(THIS_FILE, "Shutting down.."));
 
93
 
 
94
    if (icedemo.icest)
 
95
        pj_ice_strans_destroy(icedemo.icest);
 
96
    
 
97
    pj_thread_sleep(500);
 
98
 
 
99
    icedemo.thread_quit_flag = PJ_TRUE;
 
100
    if (icedemo.thread) {
 
101
        pj_thread_join(icedemo.thread);
 
102
        pj_thread_destroy(icedemo.thread);
 
103
    }
 
104
 
 
105
    if (icedemo.ice_cfg.stun_cfg.ioqueue)
 
106
        pj_ioqueue_destroy(icedemo.ice_cfg.stun_cfg.ioqueue);
 
107
 
 
108
    if (icedemo.ice_cfg.stun_cfg.timer_heap)
 
109
        pj_timer_heap_destroy(icedemo.ice_cfg.stun_cfg.timer_heap);
 
110
 
 
111
    pj_caching_pool_destroy(&icedemo.cp);
 
112
 
 
113
    pj_shutdown();
 
114
 
 
115
    if (icedemo.log_fhnd) {
 
116
        fclose(icedemo.log_fhnd);
 
117
        icedemo.log_fhnd = NULL;
 
118
    }
 
119
 
 
120
    exit(status != PJ_SUCCESS);
 
121
}
 
122
 
 
123
#define CHECK(expr)     status=expr; \
 
124
                        if (status!=PJ_SUCCESS) { \
 
125
                            err_exit(#expr, status); \
 
126
                        }
 
127
 
 
128
/*
 
129
 * This function checks for events from both timer and ioqueue (for
 
130
 * network events). It is invoked by the worker thread.
 
131
 */
 
132
static pj_status_t handle_events(unsigned max_msec, unsigned *p_count)
 
133
{
 
134
    enum { MAX_NET_EVENTS = 1 };
 
135
    pj_time_val max_timeout = {0, 0};
 
136
    pj_time_val timeout = { 0, 0};
 
137
    unsigned count = 0, net_event_count = 0;
 
138
    int c;
 
139
 
 
140
    max_timeout.msec = max_msec;
 
141
 
 
142
    /* Poll the timer to run it and also to retrieve the earliest entry. */
 
143
    timeout.sec = timeout.msec = 0;
 
144
    c = pj_timer_heap_poll( icedemo.ice_cfg.stun_cfg.timer_heap, &timeout );
 
145
    if (c > 0)
 
146
        count += c;
 
147
 
 
148
    /* timer_heap_poll should never ever returns negative value, or otherwise
 
149
     * ioqueue_poll() will block forever!
 
150
     */
 
151
    pj_assert(timeout.sec >= 0 && timeout.msec >= 0);
 
152
    if (timeout.msec >= 1000) timeout.msec = 999;
 
153
 
 
154
    /* compare the value with the timeout to wait from timer, and use the 
 
155
     * minimum value. 
 
156
    */
 
157
    if (PJ_TIME_VAL_GT(timeout, max_timeout))
 
158
        timeout = max_timeout;
 
159
 
 
160
    /* Poll ioqueue. 
 
161
     * Repeat polling the ioqueue while we have immediate events, because
 
162
     * timer heap may process more than one events, so if we only process
 
163
     * one network events at a time (such as when IOCP backend is used),
 
164
     * the ioqueue may have trouble keeping up with the request rate.
 
165
     *
 
166
     * For example, for each send() request, one network event will be
 
167
     *   reported by ioqueue for the send() completion. If we don't poll
 
168
     *   the ioqueue often enough, the send() completion will not be
 
169
     *   reported in timely manner.
 
170
     */
 
171
    do {
 
172
        c = pj_ioqueue_poll( icedemo.ice_cfg.stun_cfg.ioqueue, &timeout);
 
173
        if (c < 0) {
 
174
            pj_status_t err = pj_get_netos_error();
 
175
            pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout));
 
176
            if (p_count)
 
177
                *p_count = count;
 
178
            return err;
 
179
        } else if (c == 0) {
 
180
            break;
 
181
        } else {
 
182
            net_event_count += c;
 
183
            timeout.sec = timeout.msec = 0;
 
184
        }
 
185
    } while (c > 0 && net_event_count < MAX_NET_EVENTS);
 
186
 
 
187
    count += net_event_count;
 
188
    if (p_count)
 
189
        *p_count = count;
 
190
 
 
191
    return PJ_SUCCESS;
 
192
 
 
193
}
 
194
 
 
195
/*
 
196
 * This is the worker thread that polls event in the background.
 
197
 */
 
198
static int icedemo_worker_thread(void *unused)
 
199
{
 
200
    PJ_UNUSED_ARG(unused);
 
201
 
 
202
    while (!icedemo.thread_quit_flag) {
 
203
        handle_events(500, NULL);
 
204
    }
 
205
 
 
206
    return 0;
 
207
}
 
208
 
 
209
/*
 
210
 * This is the callback that is registered to the ICE stream transport to
 
211
 * receive notification about incoming data. By "data" it means application
 
212
 * data such as RTP/RTCP, and not packets that belong to ICE signaling (such
 
213
 * as STUN connectivity checks or TURN signaling).
 
214
 */
 
215
static void cb_on_rx_data(pj_ice_strans *ice_st,
 
216
                          unsigned comp_id, 
 
217
                          void *pkt, pj_size_t size,
 
218
                          const pj_sockaddr_t *src_addr,
 
219
                          unsigned src_addr_len)
 
220
{
 
221
    char ipstr[PJ_INET6_ADDRSTRLEN+10];
 
222
 
 
223
    PJ_UNUSED_ARG(ice_st);
 
224
    PJ_UNUSED_ARG(src_addr_len);
 
225
    PJ_UNUSED_ARG(pkt);
 
226
 
 
227
    // Don't do this! It will ruin the packet buffer in case TCP is used!
 
228
    //((char*)pkt)[size] = '\0';
 
229
 
 
230
    PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: \"%.*s\"",
 
231
              comp_id, size,
 
232
              pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3),
 
233
              (unsigned)size,
 
234
              (char*)pkt));
 
235
}
 
236
 
 
237
/*
 
238
 * This is the callback that is registered to the ICE stream transport to
 
239
 * receive notification about ICE state progression.
 
240
 */
 
241
static void cb_on_ice_complete(pj_ice_strans *ice_st, 
 
242
                               pj_ice_strans_op op,
 
243
                               pj_status_t status)
 
244
{
 
245
    const char *opname = 
 
246
        (op==PJ_ICE_STRANS_OP_INIT? "initialization" :
 
247
            (op==PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op"));
 
248
 
 
249
    if (status == PJ_SUCCESS) {
 
250
        PJ_LOG(3,(THIS_FILE, "ICE %s successful", opname));
 
251
    } else {
 
252
        char errmsg[PJ_ERR_MSG_SIZE];
 
253
 
 
254
        pj_strerror(status, errmsg, sizeof(errmsg));
 
255
        PJ_LOG(1,(THIS_FILE, "ICE %s failed: %s", opname, errmsg));
 
256
        pj_ice_strans_destroy(ice_st);
 
257
        icedemo.icest = NULL;
 
258
    }
 
259
}
 
260
 
 
261
/* log callback to write to file */
 
262
static void log_func(int level, const char *data, int len)
 
263
{
 
264
    pj_log_write(level, data, len);
 
265
    if (icedemo.log_fhnd) {
 
266
        if (fwrite(data, len, 1, icedemo.log_fhnd) != 1)
 
267
            return;
 
268
    }
 
269
}
 
270
 
 
271
/*
 
272
 * This is the main application initialization function. It is called
 
273
 * once (and only once) during application initialization sequence by 
 
274
 * main().
 
275
 */
 
276
static pj_status_t icedemo_init(void)
 
277
{
 
278
    pj_status_t status;
 
279
 
 
280
    if (icedemo.opt.log_file) {
 
281
        icedemo.log_fhnd = fopen(icedemo.opt.log_file, "a");
 
282
        pj_log_set_log_func(&log_func);
 
283
    }
 
284
 
 
285
    /* Initialize the libraries before anything else */
 
286
    CHECK( pj_init() );
 
287
    CHECK( pjlib_util_init() );
 
288
    CHECK( pjnath_init() );
 
289
 
 
290
    /* Must create pool factory, where memory allocations come from */
 
291
    pj_caching_pool_init(&icedemo.cp, NULL, 0);
 
292
 
 
293
    /* Init our ICE settings with null values */
 
294
    pj_ice_strans_cfg_default(&icedemo.ice_cfg);
 
295
 
 
296
    icedemo.ice_cfg.stun_cfg.pf = &icedemo.cp.factory;
 
297
 
 
298
    /* Create application memory pool */
 
299
    icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 
 
300
                                  512, 512, NULL);
 
301
 
 
302
    /* Create timer heap for timer stuff */
 
303
    CHECK( pj_timer_heap_create(icedemo.pool, 100, 
 
304
                                &icedemo.ice_cfg.stun_cfg.timer_heap) );
 
305
 
 
306
    /* and create ioqueue for network I/O stuff */
 
307
    CHECK( pj_ioqueue_create(icedemo.pool, 16, 
 
308
                             &icedemo.ice_cfg.stun_cfg.ioqueue) );
 
309
 
 
310
    /* something must poll the timer heap and ioqueue, 
 
311
     * unless we're on Symbian where the timer heap and ioqueue run
 
312
     * on themselves.
 
313
     */
 
314
    CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread,
 
315
                            NULL, 0, 0, &icedemo.thread) );
 
316
 
 
317
    icedemo.ice_cfg.af = pj_AF_INET();
 
318
 
 
319
    /* Create DNS resolver if nameserver is set */
 
320
    if (icedemo.opt.ns.slen) {
 
321
        CHECK( pj_dns_resolver_create(&icedemo.cp.factory, 
 
322
                                      "resolver", 
 
323
                                      0, 
 
324
                                      icedemo.ice_cfg.stun_cfg.timer_heap,
 
325
                                      icedemo.ice_cfg.stun_cfg.ioqueue, 
 
326
                                      &icedemo.ice_cfg.resolver) );
 
327
 
 
328
        CHECK( pj_dns_resolver_set_ns(icedemo.ice_cfg.resolver, 1, 
 
329
                                      &icedemo.opt.ns, NULL) );
 
330
    }
 
331
 
 
332
    /* -= Start initializing ICE stream transport config =- */
 
333
 
 
334
    /* Maximum number of host candidates */
 
335
    if (icedemo.opt.max_host != -1)
 
336
        icedemo.ice_cfg.stun.max_host_cands = icedemo.opt.max_host;
 
337
 
 
338
    /* Nomination strategy */
 
339
    if (icedemo.opt.regular)
 
340
        icedemo.ice_cfg.opt.aggressive = PJ_FALSE;
 
341
    else
 
342
        icedemo.ice_cfg.opt.aggressive = PJ_TRUE;
 
343
 
 
344
    /* Configure STUN/srflx candidate resolution */
 
345
    if (icedemo.opt.stun_srv.slen) {
 
346
        char *pos;
 
347
 
 
348
        /* Command line option may contain port number */
 
349
        if ((pos=pj_strchr(&icedemo.opt.stun_srv, ':')) != NULL) {
 
350
            icedemo.ice_cfg.stun.server.ptr = icedemo.opt.stun_srv.ptr;
 
351
            icedemo.ice_cfg.stun.server.slen = (pos - icedemo.opt.stun_srv.ptr);
 
352
 
 
353
            icedemo.ice_cfg.stun.port = (pj_uint16_t)atoi(pos+1);
 
354
        } else {
 
355
            icedemo.ice_cfg.stun.server = icedemo.opt.stun_srv;
 
356
            icedemo.ice_cfg.stun.port = PJ_STUN_PORT;
 
357
        }
 
358
 
 
359
        /* For this demo app, configure longer STUN keep-alive time
 
360
         * so that it does't clutter the screen output.
 
361
         */
 
362
        icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL;
 
363
    }
 
364
 
 
365
    /* Configure TURN candidate */
 
366
    if (icedemo.opt.turn_srv.slen) {
 
367
        char *pos;
 
368
 
 
369
        /* Command line option may contain port number */
 
370
        if ((pos=pj_strchr(&icedemo.opt.turn_srv, ':')) != NULL) {
 
371
            icedemo.ice_cfg.turn.server.ptr = icedemo.opt.turn_srv.ptr;
 
372
            icedemo.ice_cfg.turn.server.slen = (pos - icedemo.opt.turn_srv.ptr);
 
373
 
 
374
            icedemo.ice_cfg.turn.port = (pj_uint16_t)atoi(pos+1);
 
375
        } else {
 
376
            icedemo.ice_cfg.turn.server = icedemo.opt.turn_srv;
 
377
            icedemo.ice_cfg.turn.port = PJ_STUN_PORT;
 
378
        }
 
379
 
 
380
        /* TURN credential */
 
381
        icedemo.ice_cfg.turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
 
382
        icedemo.ice_cfg.turn.auth_cred.data.static_cred.username = icedemo.opt.turn_username;
 
383
        icedemo.ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
 
384
        icedemo.ice_cfg.turn.auth_cred.data.static_cred.data = icedemo.opt.turn_password;
 
385
 
 
386
        /* Connection type to TURN server */
 
387
        if (icedemo.opt.turn_tcp)
 
388
            icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_TCP;
 
389
        else
 
390
            icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP;
 
391
 
 
392
        /* For this demo app, configure longer keep-alive time
 
393
         * so that it does't clutter the screen output.
 
394
         */
 
395
        icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL;
 
396
    }
 
397
 
 
398
    /* -= That's it for now, initialization is complete =- */
 
399
    return PJ_SUCCESS;
 
400
}
 
401
 
 
402
 
 
403
/*
 
404
 * Create ICE stream transport instance, invoked from the menu.
 
405
 */
 
406
static void icedemo_create_instance(void)
 
407
{
 
408
    pj_ice_strans_cb icecb;
 
409
    pj_status_t status;
 
410
 
 
411
    if (icedemo.icest != NULL) {
 
412
        puts("ICE instance already created, destroy it first");
 
413
        return;
 
414
    }
 
415
 
 
416
    /* init the callback */
 
417
    pj_bzero(&icecb, sizeof(icecb));
 
418
    icecb.on_rx_data = cb_on_rx_data;
 
419
    icecb.on_ice_complete = cb_on_ice_complete;
 
420
 
 
421
    /* create the instance */
 
422
    status = pj_ice_strans_create("icedemo",                /* object name  */
 
423
                                &icedemo.ice_cfg,           /* settings     */
 
424
                                icedemo.opt.comp_cnt,       /* comp_cnt     */
 
425
                                NULL,                       /* user data    */
 
426
                                &icecb,                     /* callback     */
 
427
                                &icedemo.icest)             /* instance ptr */
 
428
                                ;
 
429
    if (status != PJ_SUCCESS)
 
430
        icedemo_perror("error creating ice", status);
 
431
    else
 
432
        PJ_LOG(3,(THIS_FILE, "ICE instance successfully created"));
 
433
}
 
434
 
 
435
/* Utility to nullify parsed remote info */
 
436
static void reset_rem_info(void)
 
437
{
 
438
    pj_bzero(&icedemo.rem, sizeof(icedemo.rem));
 
439
}
 
440
 
 
441
 
 
442
/*
 
443
 * Destroy ICE stream transport instance, invoked from the menu.
 
444
 */
 
445
static void icedemo_destroy_instance(void)
 
446
{
 
447
    if (icedemo.icest == NULL) {
 
448
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
 
449
        return;
 
450
    }
 
451
 
 
452
    pj_ice_strans_destroy(icedemo.icest);
 
453
    icedemo.icest = NULL;
 
454
 
 
455
    reset_rem_info();
 
456
 
 
457
    PJ_LOG(3,(THIS_FILE, "ICE instance destroyed"));
 
458
}
 
459
 
 
460
 
 
461
/*
 
462
 * Create ICE session, invoked from the menu.
 
463
 */
 
464
static void icedemo_init_session(unsigned rolechar)
 
465
{
 
466
    pj_ice_sess_role role = (pj_tolower((pj_uint8_t)rolechar)=='o' ? 
 
467
                                PJ_ICE_SESS_ROLE_CONTROLLING : 
 
468
                                PJ_ICE_SESS_ROLE_CONTROLLED);
 
469
    pj_status_t status;
 
470
 
 
471
    if (icedemo.icest == NULL) {
 
472
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
 
473
        return;
 
474
    }
 
475
 
 
476
    if (pj_ice_strans_has_sess(icedemo.icest)) {
 
477
        PJ_LOG(1,(THIS_FILE, "Error: Session already created"));
 
478
        return;
 
479
    }
 
480
 
 
481
    status = pj_ice_strans_init_ice(icedemo.icest, role, NULL, NULL);
 
482
    if (status != PJ_SUCCESS)
 
483
        icedemo_perror("error creating session", status);
 
484
    else
 
485
        PJ_LOG(3,(THIS_FILE, "ICE session created"));
 
486
 
 
487
    reset_rem_info();
 
488
}
 
489
 
 
490
 
 
491
/*
 
492
 * Stop/destroy ICE session, invoked from the menu.
 
493
 */
 
494
static void icedemo_stop_session(void)
 
495
{
 
496
    pj_status_t status;
 
497
 
 
498
    if (icedemo.icest == NULL) {
 
499
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
 
500
        return;
 
501
    }
 
502
 
 
503
    if (!pj_ice_strans_has_sess(icedemo.icest)) {
 
504
        PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
 
505
        return;
 
506
    }
 
507
 
 
508
    status = pj_ice_strans_stop_ice(icedemo.icest);
 
509
    if (status != PJ_SUCCESS)
 
510
        icedemo_perror("error stopping session", status);
 
511
    else
 
512
        PJ_LOG(3,(THIS_FILE, "ICE session stopped"));
 
513
 
 
514
    reset_rem_info();
 
515
}
 
516
 
 
517
#define PRINT(fmt, arg0, arg1, arg2, arg3, arg4, arg5)      \
 
518
        printed = pj_ansi_snprintf(p, maxlen - (p-buffer),  \
 
519
                                   fmt, arg0, arg1, arg2, arg3, arg4, arg5); \
 
520
        if (printed <= 0) return -PJ_ETOOSMALL; \
 
521
        p += printed
 
522
 
 
523
 
 
524
/* Utility to create a=candidate SDP attribute */
 
525
static int print_cand(char buffer[], unsigned maxlen,
 
526
                      const pj_ice_sess_cand *cand)
 
527
{
 
528
    char ipaddr[PJ_INET6_ADDRSTRLEN];
 
529
    char *p = buffer;
 
530
    int printed;
 
531
 
 
532
    PRINT("a=candidate:%.*s %u UDP %u %s %u typ ",
 
533
          (int)cand->foundation.slen,
 
534
          cand->foundation.ptr,
 
535
          (unsigned)cand->comp_id,
 
536
          cand->prio,
 
537
          pj_sockaddr_print(&cand->addr, ipaddr, 
 
538
                            sizeof(ipaddr), 0),
 
539
          (unsigned)pj_sockaddr_get_port(&cand->addr));
 
540
 
 
541
    PRINT("%s\n",
 
542
          pj_ice_get_cand_type_name(cand->type),
 
543
          0, 0, 0, 0, 0);
 
544
 
 
545
    if (p == buffer+maxlen)
 
546
        return -PJ_ETOOSMALL;
 
547
 
 
548
    *p = '\0';
 
549
 
 
550
    return p-buffer;
 
551
}
 
552
 
 
553
/* 
 
554
 * Encode ICE information in SDP.
 
555
 */
 
556
static int encode_session(char buffer[], unsigned maxlen)
 
557
{
 
558
    char *p = buffer;
 
559
    unsigned comp;
 
560
    int printed;
 
561
    pj_str_t local_ufrag, local_pwd;
 
562
    pj_status_t status;
 
563
 
 
564
    /* Write "dummy" SDP v=, o=, s=, and t= lines */
 
565
    PRINT("v=0\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n", 
 
566
          0, 0, 0, 0, 0, 0);
 
567
 
 
568
    /* Get ufrag and pwd from current session */
 
569
    pj_ice_strans_get_ufrag_pwd(icedemo.icest, &local_ufrag, &local_pwd,
 
570
                                NULL, NULL);
 
571
 
 
572
    /* Write the a=ice-ufrag and a=ice-pwd attributes */
 
573
    PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n",
 
574
           (int)local_ufrag.slen,
 
575
           local_ufrag.ptr,
 
576
           (int)local_pwd.slen,
 
577
           local_pwd.ptr, 
 
578
           0, 0);
 
579
 
 
580
    /* Write each component */
 
581
    for (comp=0; comp<icedemo.opt.comp_cnt; ++comp) {
 
582
        unsigned j, cand_cnt;
 
583
        pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];
 
584
        char ipaddr[PJ_INET6_ADDRSTRLEN];
 
585
 
 
586
        /* Get default candidate for the component */
 
587
        status = pj_ice_strans_get_def_cand(icedemo.icest, comp+1, &cand[0]);
 
588
        if (status != PJ_SUCCESS)
 
589
            return -status;
 
590
 
 
591
        /* Write the default address */
 
592
        if (comp==0) {
 
593
            /* For component 1, default address is in m= and c= lines */
 
594
            PRINT("m=audio %d RTP/AVP 0\n"
 
595
                  "c=IN IP4 %s\n",
 
596
                  (int)pj_sockaddr_get_port(&cand[0].addr),
 
597
                  pj_sockaddr_print(&cand[0].addr, ipaddr,
 
598
                                    sizeof(ipaddr), 0),
 
599
                  0, 0, 0, 0);
 
600
        } else if (comp==1) {
 
601
            /* For component 2, default address is in a=rtcp line */
 
602
            PRINT("a=rtcp:%d IN IP4 %s\n",
 
603
                  (int)pj_sockaddr_get_port(&cand[0].addr),
 
604
                  pj_sockaddr_print(&cand[0].addr, ipaddr,
 
605
                                    sizeof(ipaddr), 0),
 
606
                  0, 0, 0, 0);
 
607
        } else {
 
608
            /* For other components, we'll just invent this.. */
 
609
            PRINT("a=Xice-defcand:%d IN IP4 %s\n",
 
610
                  (int)pj_sockaddr_get_port(&cand[0].addr),
 
611
                  pj_sockaddr_print(&cand[0].addr, ipaddr,
 
612
                                    sizeof(ipaddr), 0),
 
613
                  0, 0, 0, 0);
 
614
        }
 
615
 
 
616
        /* Enumerate all candidates for this component */
 
617
        cand_cnt = PJ_ARRAY_SIZE(cand);
 
618
        status = pj_ice_strans_enum_cands(icedemo.icest, comp+1,
 
619
                                          &cand_cnt, cand);
 
620
        if (status != PJ_SUCCESS)
 
621
            return -status;
 
622
 
 
623
        /* And encode the candidates as SDP */
 
624
        for (j=0; j<cand_cnt; ++j) {
 
625
            printed = print_cand(p, maxlen - (p-buffer), &cand[j]);
 
626
            if (printed < 0)
 
627
                return -PJ_ETOOSMALL;
 
628
            p += printed;
 
629
        }
 
630
    }
 
631
 
 
632
    if (p == buffer+maxlen)
 
633
        return -PJ_ETOOSMALL;
 
634
 
 
635
    *p = '\0';
 
636
    return p - buffer;
 
637
}
 
638
 
 
639
 
 
640
/*
 
641
 * Show information contained in the ICE stream transport. This is
 
642
 * invoked from the menu.
 
643
 */
 
644
static void icedemo_show_ice(void)
 
645
{
 
646
    static char buffer[1000];
 
647
    int len;
 
648
 
 
649
    if (icedemo.icest == NULL) {
 
650
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
 
651
        return;
 
652
    }
 
653
 
 
654
    puts("General info");
 
655
    puts("---------------");
 
656
    printf("Component count    : %d\n", icedemo.opt.comp_cnt);
 
657
    printf("Status             : ");
 
658
    if (pj_ice_strans_sess_is_complete(icedemo.icest))
 
659
        puts("negotiation complete");
 
660
    else if (pj_ice_strans_sess_is_running(icedemo.icest))
 
661
        puts("negotiation is in progress");
 
662
    else if (pj_ice_strans_has_sess(icedemo.icest))
 
663
        puts("session ready");
 
664
    else
 
665
        puts("session not created");
 
666
 
 
667
    if (!pj_ice_strans_has_sess(icedemo.icest)) {
 
668
        puts("Create the session first to see more info");
 
669
        return;
 
670
    }
 
671
 
 
672
    printf("Negotiated comp_cnt: %d\n", 
 
673
           pj_ice_strans_get_running_comp_cnt(icedemo.icest));
 
674
    printf("Role               : %s\n",
 
675
           pj_ice_strans_get_role(icedemo.icest)==PJ_ICE_SESS_ROLE_CONTROLLED ?
 
676
           "controlled" : "controlling");
 
677
 
 
678
    len = encode_session(buffer, sizeof(buffer));
 
679
    if (len < 0)
 
680
        err_exit("not enough buffer to show ICE status", -len);
 
681
 
 
682
    puts("");
 
683
    printf("Local SDP (paste this to remote host):\n"
 
684
           "--------------------------------------\n"
 
685
           "%s\n", buffer);
 
686
 
 
687
 
 
688
    puts("");
 
689
    puts("Remote info:\n"
 
690
         "----------------------");
 
691
    if (icedemo.rem.cand_cnt==0) {
 
692
        puts("No remote info yet");
 
693
    } else {
 
694
        unsigned i;
 
695
 
 
696
        printf("Remote ufrag       : %s\n", icedemo.rem.ufrag);
 
697
        printf("Remote password    : %s\n", icedemo.rem.pwd);
 
698
        printf("Remote cand. cnt.  : %d\n", icedemo.rem.cand_cnt);
 
699
 
 
700
        for (i=0; i<icedemo.rem.cand_cnt; ++i) {
 
701
            len = print_cand(buffer, sizeof(buffer), &icedemo.rem.cand[i]);
 
702
            if (len < 0)
 
703
                err_exit("not enough buffer to show ICE status", -len);
 
704
 
 
705
            printf("  %s", buffer);
 
706
        }
 
707
    }
 
708
}
 
709
 
 
710
 
 
711
/*
 
712
 * Input and parse SDP from the remote (containing remote's ICE information) 
 
713
 * and save it to global variables.
 
714
 */
 
715
static void icedemo_input_remote(void)
 
716
{
 
717
    char linebuf[80];
 
718
    unsigned media_cnt = 0;
 
719
    unsigned comp0_port = 0;
 
720
    char     comp0_addr[80];
 
721
    pj_bool_t done = PJ_FALSE;
 
722
 
 
723
    puts("Paste SDP from remote host, end with empty line");
 
724
 
 
725
    reset_rem_info();
 
726
 
 
727
    comp0_addr[0] = '\0';
 
728
 
 
729
    while (!done) {
 
730
        int len;
 
731
        char *line;
 
732
 
 
733
        printf(">");
 
734
        if (stdout) fflush(stdout);
 
735
 
 
736
        if (fgets(linebuf, sizeof(linebuf), stdin)==NULL)
 
737
            break;
 
738
 
 
739
        len = strlen(linebuf);
 
740
        while (len && (linebuf[len-1] == '\r' || linebuf[len-1] == '\n'))
 
741
            linebuf[--len] = '\0';
 
742
 
 
743
        line = linebuf;
 
744
        while (len && pj_isspace(*line))
 
745
            ++line, --len;
 
746
 
 
747
        if (len==0)
 
748
            break;
 
749
 
 
750
        /* Ignore subsequent media descriptors */
 
751
        if (media_cnt > 1)
 
752
            continue;
 
753
 
 
754
        switch (line[0]) {
 
755
        case 'm':
 
756
            {
 
757
                int cnt;
 
758
                char media[32], portstr[32];
 
759
 
 
760
                ++media_cnt;
 
761
                if (media_cnt > 1) {
 
762
                    puts("Media line ignored");
 
763
                    break;
 
764
                }
 
765
 
 
766
                cnt = sscanf(line+2, "%s %s RTP/", media, portstr);
 
767
                if (cnt != 2) {
 
768
                    PJ_LOG(1,(THIS_FILE, "Error parsing media line"));
 
769
                    goto on_error;
 
770
                }
 
771
 
 
772
                comp0_port = atoi(portstr);
 
773
                
 
774
            }
 
775
            break;
 
776
        case 'c':
 
777
            {
 
778
                int cnt;
 
779
                char c[32], net[32], ip[80];
 
780
                
 
781
                cnt = sscanf(line+2, "%s %s %s", c, net, ip);
 
782
                if (cnt != 3) {
 
783
                    PJ_LOG(1,(THIS_FILE, "Error parsing connection line"));
 
784
                    goto on_error;
 
785
                }
 
786
 
 
787
                strcpy(comp0_addr, ip);
 
788
            }
 
789
            break;
 
790
        case 'a':
 
791
            {
 
792
                char *attr = strtok(line+2, ": \t\r\n");
 
793
                if (strcmp(attr, "ice-ufrag")==0) {
 
794
                    strcpy(icedemo.rem.ufrag, attr+strlen(attr)+1);
 
795
                } else if (strcmp(attr, "ice-pwd")==0) {
 
796
                    strcpy(icedemo.rem.pwd, attr+strlen(attr)+1);
 
797
                } else if (strcmp(attr, "rtcp")==0) {
 
798
                    char *val = attr+strlen(attr)+1;
 
799
                    int af, cnt;
 
800
                    int port;
 
801
                    char net[32], ip[64];
 
802
                    pj_str_t tmp_addr;
 
803
                    pj_status_t status;
 
804
 
 
805
                    cnt = sscanf(val, "%d IN %s %s", &port, net, ip);
 
806
                    if (cnt != 3) {
 
807
                        PJ_LOG(1,(THIS_FILE, "Error parsing rtcp attribute"));
 
808
                        goto on_error;
 
809
                    }
 
810
 
 
811
                    if (strchr(ip, ':'))
 
812
                        af = pj_AF_INET6();
 
813
                    else
 
814
                        af = pj_AF_INET();
 
815
 
 
816
                    pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0);
 
817
                    tmp_addr = pj_str(ip);
 
818
                    status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1],
 
819
                                                      &tmp_addr);
 
820
                    if (status != PJ_SUCCESS) {
 
821
                        PJ_LOG(1,(THIS_FILE, "Invalid IP address"));
 
822
                        goto on_error;
 
823
                    }
 
824
                    pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port);
 
825
 
 
826
                } else if (strcmp(attr, "candidate")==0) {
 
827
                    char *sdpcand = attr+strlen(attr)+1;
 
828
                    int af, cnt;
 
829
                    char foundation[32], transport[12], ipaddr[80], type[32];
 
830
                    pj_str_t tmpaddr;
 
831
                    int comp_id, prio, port;
 
832
                    pj_ice_sess_cand *cand;
 
833
                    pj_status_t status;
 
834
 
 
835
                    cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s",
 
836
                                 foundation,
 
837
                                 &comp_id,
 
838
                                 transport,
 
839
                                 &prio,
 
840
                                 ipaddr,
 
841
                                 &port,
 
842
                                 type);
 
843
                    if (cnt != 7) {
 
844
                        PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line"));
 
845
                        goto on_error;
 
846
                    }
 
847
 
 
848
                    cand = &icedemo.rem.cand[icedemo.rem.cand_cnt];
 
849
                    pj_bzero(cand, sizeof(*cand));
 
850
                    
 
851
                    if (strcmp(type, "host")==0)
 
852
                        cand->type = PJ_ICE_CAND_TYPE_HOST;
 
853
                    else if (strcmp(type, "srflx")==0)
 
854
                        cand->type = PJ_ICE_CAND_TYPE_SRFLX;
 
855
                    else if (strcmp(type, "relay")==0)
 
856
                        cand->type = PJ_ICE_CAND_TYPE_RELAYED;
 
857
                    else {
 
858
                        PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", 
 
859
                                   type));
 
860
                        goto on_error;
 
861
                    }
 
862
 
 
863
                    cand->comp_id = (pj_uint8_t)comp_id;
 
864
                    pj_strdup2(icedemo.pool, &cand->foundation, foundation);
 
865
                    cand->prio = prio;
 
866
                    
 
867
                    if (strchr(ipaddr, ':'))
 
868
                        af = pj_AF_INET6();
 
869
                    else
 
870
                        af = pj_AF_INET();
 
871
 
 
872
                    tmpaddr = pj_str(ipaddr);
 
873
                    pj_sockaddr_init(af, &cand->addr, NULL, 0);
 
874
                    status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr);
 
875
                    if (status != PJ_SUCCESS) {
 
876
                        PJ_LOG(1,(THIS_FILE, "Error: invalid IP address '%s'",
 
877
                                  ipaddr));
 
878
                        goto on_error;
 
879
                    }
 
880
 
 
881
                    pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port);
 
882
 
 
883
                    ++icedemo.rem.cand_cnt;
 
884
 
 
885
                    if (cand->comp_id > icedemo.rem.comp_cnt)
 
886
                        icedemo.rem.comp_cnt = cand->comp_id;
 
887
                }
 
888
            }
 
889
            break;
 
890
        }
 
891
    }
 
892
 
 
893
    if (icedemo.rem.cand_cnt==0 ||
 
894
        icedemo.rem.ufrag[0]==0 ||
 
895
        icedemo.rem.pwd[0]==0 ||
 
896
        icedemo.rem.comp_cnt == 0)
 
897
    {
 
898
        PJ_LOG(1, (THIS_FILE, "Error: not enough info"));
 
899
        goto on_error;
 
900
    }
 
901
 
 
902
    if (comp0_port==0 || comp0_addr[0]=='\0') {
 
903
        PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found"));
 
904
        goto on_error;
 
905
    } else {
 
906
        int af;
 
907
        pj_str_t tmp_addr;
 
908
        pj_status_t status;
 
909
 
 
910
        if (strchr(comp0_addr, ':'))
 
911
            af = pj_AF_INET6();
 
912
        else
 
913
            af = pj_AF_INET();
 
914
 
 
915
        pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0);
 
916
        tmp_addr = pj_str(comp0_addr);
 
917
        status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0],
 
918
                                          &tmp_addr);
 
919
        if (status != PJ_SUCCESS) {
 
920
            PJ_LOG(1,(THIS_FILE, "Invalid IP address in c= line"));
 
921
            goto on_error;
 
922
        }
 
923
        pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port);
 
924
    }
 
925
 
 
926
    PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", 
 
927
               icedemo.rem.cand_cnt));
 
928
    return;
 
929
 
 
930
on_error:
 
931
    reset_rem_info();
 
932
}
 
933
 
 
934
 
 
935
/*
 
936
 * Start ICE negotiation! This function is invoked from the menu.
 
937
 */
 
938
static void icedemo_start_nego(void)
 
939
{
 
940
    pj_str_t rufrag, rpwd;
 
941
    pj_status_t status;
 
942
 
 
943
    if (icedemo.icest == NULL) {
 
944
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
 
945
        return;
 
946
    }
 
947
 
 
948
    if (!pj_ice_strans_has_sess(icedemo.icest)) {
 
949
        PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
 
950
        return;
 
951
    }
 
952
 
 
953
    if (icedemo.rem.cand_cnt == 0) {
 
954
        PJ_LOG(1,(THIS_FILE, "Error: No remote info, input remote info first"));
 
955
        return;
 
956
    }
 
957
 
 
958
    PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation.."));
 
959
 
 
960
    status = pj_ice_strans_start_ice(icedemo.icest, 
 
961
                                     pj_cstr(&rufrag, icedemo.rem.ufrag),
 
962
                                     pj_cstr(&rpwd, icedemo.rem.pwd),
 
963
                                     icedemo.rem.cand_cnt,
 
964
                                     icedemo.rem.cand);
 
965
    if (status != PJ_SUCCESS)
 
966
        icedemo_perror("Error starting ICE", status);
 
967
    else
 
968
        PJ_LOG(3,(THIS_FILE, "ICE negotiation started"));
 
969
}
 
970
 
 
971
 
 
972
/*
 
973
 * Send application data to remote agent.
 
974
 */
 
975
static void icedemo_send_data(unsigned comp_id, const char *data)
 
976
{
 
977
    pj_status_t status;
 
978
 
 
979
    if (icedemo.icest == NULL) {
 
980
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
 
981
        return;
 
982
    }
 
983
 
 
984
    if (!pj_ice_strans_has_sess(icedemo.icest)) {
 
985
        PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
 
986
        return;
 
987
    }
 
988
 
 
989
    /*
 
990
    if (!pj_ice_strans_sess_is_complete(icedemo.icest)) {
 
991
        PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress"));
 
992
        return;
 
993
    }
 
994
    */
 
995
 
 
996
    if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
 
997
        PJ_LOG(1,(THIS_FILE, "Error: invalid component ID"));
 
998
        return;
 
999
    }
 
1000
 
 
1001
    status = pj_ice_strans_sendto(icedemo.icest, comp_id, data, strlen(data),
 
1002
                                  &icedemo.rem.def_addr[comp_id-1],
 
1003
                                  pj_sockaddr_get_len(&icedemo.rem.def_addr[comp_id-1]));
 
1004
    if (status != PJ_SUCCESS)
 
1005
        icedemo_perror("Error sending data", status);
 
1006
    else
 
1007
        PJ_LOG(3,(THIS_FILE, "Data sent"));
 
1008
}
 
1009
 
 
1010
 
 
1011
/*
 
1012
 * Display help for the menu.
 
1013
 */
 
1014
static void icedemo_help_menu(void)
 
1015
{
 
1016
    puts("");
 
1017
    puts("-= Help on using ICE and this icedemo program =-");
 
1018
    puts("");
 
1019
    puts("This application demonstrates how to use ICE in pjnath without having\n"
 
1020
         "to use the SIP protocol. To use this application, you will need to run\n"
 
1021
         "two instances of this application, to simulate two ICE agents.\n");
 
1022
 
 
1023
    puts("Basic ICE flow:\n"
 
1024
         " create instance [menu \"c\"]\n"
 
1025
         " repeat these steps as wanted:\n"
 
1026
         "   - init session as offerer or answerer [menu \"i\"]\n"
 
1027
         "   - display our SDP [menu \"s\"]\n"
 
1028
         "   - \"send\" our SDP from the \"show\" output above to remote, by\n"
 
1029
         "     copy-pasting the SDP to the other icedemo application\n"
 
1030
         "   - parse remote SDP, by pasting SDP generated by the other icedemo\n"
 
1031
         "     instance [menu \"r\"]\n"
 
1032
         "   - begin ICE negotiation in our end [menu \"b\"], and \n"
 
1033
         "   - immediately begin ICE negotiation in the other icedemo instance\n"
 
1034
         "   - ICE negotiation will run, and result will be printed to screen\n"
 
1035
         "   - send application data to remote [menu \"x\"]\n"
 
1036
         "   - end/stop ICE session [menu \"e\"]\n"
 
1037
         " destroy instance [menu \"d\"]\n"
 
1038
         "");
 
1039
 
 
1040
    puts("");
 
1041
    puts("This concludes the help screen.");
 
1042
    puts("");
 
1043
}
 
1044
 
 
1045
 
 
1046
/*
 
1047
 * Display console menu
 
1048
 */
 
1049
static void icedemo_print_menu(void)
 
1050
{
 
1051
    puts("");
 
1052
    puts("+----------------------------------------------------------------------+");
 
1053
    puts("|                    M E N U                                           |");
 
1054
    puts("+---+------------------------------------------------------------------+");
 
1055
    puts("| c | create           Create the instance                             |");
 
1056
    puts("| d | destroy          Destroy the instance                            |");
 
1057
    puts("| i | init o|a         Initialize ICE session as offerer or answerer   |");
 
1058
    puts("| e | stop             End/stop ICE session                            |");
 
1059
    puts("| s | show             Display local ICE info                          |");
 
1060
    puts("| r | remote           Input remote ICE info                           |");
 
1061
    puts("| b | start            Begin ICE negotiation                           |");
 
1062
    puts("| x | send <compid> .. Send data to remote                             |");
 
1063
    puts("+---+------------------------------------------------------------------+");
 
1064
    puts("| h |  help            * Help! *                                       |");
 
1065
    puts("| q |  quit            Quit                                            |");
 
1066
    puts("+----------------------------------------------------------------------+");
 
1067
}
 
1068
 
 
1069
 
 
1070
/*
 
1071
 * Main console loop.
 
1072
 */
 
1073
static void icedemo_console(void)
 
1074
{
 
1075
    pj_bool_t app_quit = PJ_FALSE;
 
1076
 
 
1077
    while (!app_quit) {
 
1078
        char input[80], *cmd;
 
1079
        const char *SEP = " \t\r\n";
 
1080
        int len;
 
1081
 
 
1082
        icedemo_print_menu();
 
1083
 
 
1084
        printf("Input: ");
 
1085
        if (stdout) fflush(stdout);
 
1086
 
 
1087
        pj_bzero(input, sizeof(input));
 
1088
        if (fgets(input, sizeof(input), stdin) == NULL)
 
1089
            break;
 
1090
 
 
1091
        len = strlen(input);
 
1092
        while (len && (input[len-1]=='\r' || input[len-1]=='\n'))
 
1093
            input[--len] = '\0';
 
1094
 
 
1095
        cmd = strtok(input, SEP);
 
1096
        if (!cmd)
 
1097
            continue;
 
1098
 
 
1099
        if (strcmp(cmd, "create")==0 || strcmp(cmd, "c")==0) {
 
1100
 
 
1101
            icedemo_create_instance();
 
1102
 
 
1103
        } else if (strcmp(cmd, "destroy")==0 || strcmp(cmd, "d")==0) {
 
1104
 
 
1105
            icedemo_destroy_instance();
 
1106
 
 
1107
        } else if (strcmp(cmd, "init")==0 || strcmp(cmd, "i")==0) {
 
1108
 
 
1109
            char *role = strtok(NULL, SEP);
 
1110
            if (role)
 
1111
                icedemo_init_session(*role);
 
1112
            else
 
1113
                puts("error: Role required");
 
1114
 
 
1115
        } else if (strcmp(cmd, "stop")==0 || strcmp(cmd, "e")==0) {
 
1116
 
 
1117
            icedemo_stop_session();
 
1118
 
 
1119
        } else if (strcmp(cmd, "show")==0 || strcmp(cmd, "s")==0) {
 
1120
 
 
1121
            icedemo_show_ice();
 
1122
 
 
1123
        } else if (strcmp(cmd, "remote")==0 || strcmp(cmd, "r")==0) {
 
1124
 
 
1125
            icedemo_input_remote();
 
1126
 
 
1127
        } else if (strcmp(cmd, "start")==0 || strcmp(cmd, "b")==0) {
 
1128
 
 
1129
            icedemo_start_nego();
 
1130
 
 
1131
        } else if (strcmp(cmd, "send")==0 || strcmp(cmd, "x")==0) {
 
1132
 
 
1133
            char *comp = strtok(NULL, SEP);
 
1134
 
 
1135
            if (!comp) {
 
1136
                PJ_LOG(1,(THIS_FILE, "Error: component ID required"));
 
1137
            } else {
 
1138
                char *data = comp + strlen(comp) + 1;
 
1139
                if (!data)
 
1140
                    data = "";
 
1141
                icedemo_send_data(atoi(comp), data);
 
1142
            }
 
1143
 
 
1144
        } else if (strcmp(cmd, "help")==0 || strcmp(cmd, "h")==0) {
 
1145
 
 
1146
            icedemo_help_menu();
 
1147
 
 
1148
        } else if (strcmp(cmd, "quit")==0 || strcmp(cmd, "q")==0) {
 
1149
 
 
1150
            app_quit = PJ_TRUE;
 
1151
 
 
1152
        } else {
 
1153
 
 
1154
            printf("Invalid command '%s'\n", cmd);
 
1155
 
 
1156
        }
 
1157
    }
 
1158
}
 
1159
 
 
1160
 
 
1161
/*
 
1162
 * Display program usage.
 
1163
 */
 
1164
static void icedemo_usage()
 
1165
{
 
1166
    puts("Usage: icedemo [optons]");
 
1167
    printf("icedemo v%s by pjsip.org\n", pj_get_version());
 
1168
    puts("");
 
1169
    puts("General options:");
 
1170
    puts(" --comp-cnt, -c N          Component count (default=1)");
 
1171
    puts(" --nameserver, -n IP       Configure nameserver to activate DNS SRV");
 
1172
    puts("                           resolution");
 
1173
    puts(" --max-host, -H N          Set max number of host candidates to N");
 
1174
    puts(" --regular, -R             Use regular nomination (default aggressive)");
 
1175
    puts(" --log-file, -L FILE       Save output to log FILE");
 
1176
    puts(" --help, -h                Display this screen.");
 
1177
    puts("");
 
1178
    puts("STUN related options:");
 
1179
    puts(" --stun-srv, -s HOSTDOM    Enable srflx candidate by resolving to STUN server.");
 
1180
    puts("                           HOSTDOM may be a \"host_or_ip[:port]\" or a domain");
 
1181
    puts("                           name if DNS SRV resolution is used.");
 
1182
    puts("");
 
1183
    puts("TURN related options:");
 
1184
    puts(" --turn-srv, -t HOSTDOM    Enable relayed candidate by using this TURN server.");
 
1185
    puts("                           HOSTDOM may be a \"host_or_ip[:port]\" or a domain");
 
1186
    puts("                           name if DNS SRV resolution is used.");
 
1187
    puts(" --turn-tcp, -T            Use TCP to connect to TURN server");
 
1188
    puts(" --turn-username, -u UID   Set TURN username of the credential to UID");
 
1189
    puts(" --turn-password, -p PWD   Set password of the credential to WPWD");
 
1190
    puts(" --turn-fingerprint, -F    Use fingerprint for outgoing TURN requests");
 
1191
    puts("");
 
1192
}
 
1193
 
 
1194
 
 
1195
/*
 
1196
 * And here's the main()
 
1197
 */
 
1198
int main(int argc, char *argv[])
 
1199
{
 
1200
    struct pj_getopt_option long_options[] = {
 
1201
        { "comp-cnt",           1, 0, 'c'},
 
1202
        { "nameserver",         1, 0, 'n'},
 
1203
        { "max-host",           1, 0, 'H'},
 
1204
        { "help",               0, 0, 'h'},
 
1205
        { "stun-srv",           1, 0, 's'},
 
1206
        { "turn-srv",           1, 0, 't'},
 
1207
        { "turn-tcp",           0, 0, 'T'},
 
1208
        { "turn-username",      1, 0, 'u'},
 
1209
        { "turn-password",      1, 0, 'p'},
 
1210
        { "turn-fingerprint",   0, 0, 'F'},
 
1211
        { "regular",            0, 0, 'R'},
 
1212
        { "log-file",           1, 0, 'L'},
 
1213
    };
 
1214
    int c, opt_id;
 
1215
    pj_status_t status;
 
1216
 
 
1217
    icedemo.opt.comp_cnt = 1;
 
1218
    icedemo.opt.max_host = -1;
 
1219
 
 
1220
    while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) {
 
1221
        switch (c) {
 
1222
        case 'c':
 
1223
            icedemo.opt.comp_cnt = atoi(pj_optarg);
 
1224
            if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) {
 
1225
                puts("Invalid component count value");
 
1226
                return 1;
 
1227
            }
 
1228
            break;
 
1229
        case 'n':
 
1230
            icedemo.opt.ns = pj_str(pj_optarg);
 
1231
            break;
 
1232
        case 'H':
 
1233
            icedemo.opt.max_host = atoi(pj_optarg);
 
1234
            break;
 
1235
        case 'h':
 
1236
            icedemo_usage();
 
1237
            return 0;
 
1238
        case 's':
 
1239
            icedemo.opt.stun_srv = pj_str(pj_optarg);
 
1240
            break;
 
1241
        case 't':
 
1242
            icedemo.opt.turn_srv = pj_str(pj_optarg);
 
1243
            break;
 
1244
        case 'T':
 
1245
            icedemo.opt.turn_tcp = PJ_TRUE;
 
1246
            break;
 
1247
        case 'u':
 
1248
            icedemo.opt.turn_username = pj_str(pj_optarg);
 
1249
            break;
 
1250
        case 'p':
 
1251
            icedemo.opt.turn_password = pj_str(pj_optarg);
 
1252
            break;
 
1253
        case 'F':
 
1254
            icedemo.opt.turn_fingerprint = PJ_TRUE;
 
1255
            break;
 
1256
        case 'R':
 
1257
            icedemo.opt.regular = PJ_TRUE;
 
1258
            break;
 
1259
        case 'L':
 
1260
            icedemo.opt.log_file = pj_optarg;
 
1261
            break;
 
1262
        default:
 
1263
            printf("Argument \"%s\" is not valid. Use -h to see help",
 
1264
                   argv[pj_optind]);
 
1265
            return 1;
 
1266
        }
 
1267
    }
 
1268
 
 
1269
    status = icedemo_init();
 
1270
    if (status != PJ_SUCCESS)
 
1271
        return 1;
 
1272
 
 
1273
    icedemo_console();
 
1274
 
 
1275
    err_exit("Quitting..", PJ_SUCCESS);
 
1276
    return 0;
 
1277
}