~ubuntu-branches/ubuntu/saucy/sflphone/saucy-proposed

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2013-06-30 11:40:56 UTC
  • mfrom: (20.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20130630114056-i0rz9ibang07g7qr
Tags: 1.2.3-2
* changeset_r92d62cfc54732bbbcfff2b1d36c096b120b981a5.diff 
  - fixes automatic endian detection 
* Update Vcs: fixes vcs-field-not-canonical

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: icedemo.c 3603 2011-07-07 01:53:35Z bennylp $ */
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
 
        status = pj_ice_strans_enum_cands(icedemo.icest, comp+1,
618
 
                                          &cand_cnt, cand);
619
 
        if (status != PJ_SUCCESS)
620
 
            return -status;
621
 
 
622
 
        /* And encode the candidates as SDP */
623
 
        for (j=0; j<cand_cnt; ++j) {
624
 
            printed = print_cand(p, maxlen - (p-buffer), &cand[j]);
625
 
            if (printed < 0)
626
 
                return -PJ_ETOOSMALL;
627
 
            p += printed;
628
 
        }
629
 
    }
630
 
 
631
 
    if (p == buffer+maxlen)
632
 
        return -PJ_ETOOSMALL;
633
 
 
634
 
    *p = '\0';
635
 
    return p - buffer;
636
 
}
637
 
 
638
 
 
639
 
/*
640
 
 * Show information contained in the ICE stream transport. This is
641
 
 * invoked from the menu.
642
 
 */
643
 
static void icedemo_show_ice(void)
644
 
{
645
 
    static char buffer[1000];
646
 
    int len;
647
 
 
648
 
    if (icedemo.icest == NULL) {
649
 
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
650
 
        return;
651
 
    }
652
 
 
653
 
    puts("General info");
654
 
    puts("---------------");
655
 
    printf("Component count    : %d\n", icedemo.opt.comp_cnt);
656
 
    printf("Status             : ");
657
 
    if (pj_ice_strans_sess_is_complete(icedemo.icest))
658
 
        puts("negotiation complete");
659
 
    else if (pj_ice_strans_sess_is_running(icedemo.icest))
660
 
        puts("negotiation is in progress");
661
 
    else if (pj_ice_strans_has_sess(icedemo.icest))
662
 
        puts("session ready");
663
 
    else
664
 
        puts("session not created");
665
 
 
666
 
    if (!pj_ice_strans_has_sess(icedemo.icest)) {
667
 
        puts("Create the session first to see more info");
668
 
        return;
669
 
    }
670
 
 
671
 
    printf("Negotiated comp_cnt: %d\n", 
672
 
           pj_ice_strans_get_running_comp_cnt(icedemo.icest));
673
 
    printf("Role               : %s\n",
674
 
           pj_ice_strans_get_role(icedemo.icest)==PJ_ICE_SESS_ROLE_CONTROLLED ?
675
 
           "controlled" : "controlling");
676
 
 
677
 
    len = encode_session(buffer, sizeof(buffer));
678
 
    if (len < 0)
679
 
        err_exit("not enough buffer to show ICE status", -len);
680
 
 
681
 
    puts("");
682
 
    printf("Local SDP (paste this to remote host):\n"
683
 
           "--------------------------------------\n"
684
 
           "%s\n", buffer);
685
 
 
686
 
 
687
 
    puts("");
688
 
    puts("Remote info:\n"
689
 
         "----------------------");
690
 
    if (icedemo.rem.cand_cnt==0) {
691
 
        puts("No remote info yet");
692
 
    } else {
693
 
        unsigned i;
694
 
 
695
 
        printf("Remote ufrag       : %s\n", icedemo.rem.ufrag);
696
 
        printf("Remote password    : %s\n", icedemo.rem.pwd);
697
 
        printf("Remote cand. cnt.  : %d\n", icedemo.rem.cand_cnt);
698
 
 
699
 
        for (i=0; i<icedemo.rem.cand_cnt; ++i) {
700
 
            len = print_cand(buffer, sizeof(buffer), &icedemo.rem.cand[i]);
701
 
            if (len < 0)
702
 
                err_exit("not enough buffer to show ICE status", -len);
703
 
 
704
 
            printf("  %s", buffer);
705
 
        }
706
 
    }
707
 
}
708
 
 
709
 
 
710
 
/*
711
 
 * Input and parse SDP from the remote (containing remote's ICE information) 
712
 
 * and save it to global variables.
713
 
 */
714
 
static void icedemo_input_remote(void)
715
 
{
716
 
    char linebuf[80];
717
 
    unsigned media_cnt = 0;
718
 
    unsigned comp0_port = 0;
719
 
    char     comp0_addr[80];
720
 
    pj_bool_t done = PJ_FALSE;
721
 
 
722
 
    puts("Paste SDP from remote host, end with empty line");
723
 
 
724
 
    reset_rem_info();
725
 
 
726
 
    comp0_addr[0] = '\0';
727
 
 
728
 
    while (!done) {
729
 
        int len;
730
 
        char *line;
731
 
 
732
 
        printf(">");
733
 
        if (stdout) fflush(stdout);
734
 
 
735
 
        if (fgets(linebuf, sizeof(linebuf), stdin)==NULL)
736
 
            break;
737
 
 
738
 
        len = strlen(linebuf);
739
 
        while (len && (linebuf[len-1] == '\r' || linebuf[len-1] == '\n'))
740
 
            linebuf[--len] = '\0';
741
 
 
742
 
        line = linebuf;
743
 
        while (len && pj_isspace(*line))
744
 
            ++line, --len;
745
 
 
746
 
        if (len==0)
747
 
            break;
748
 
 
749
 
        /* Ignore subsequent media descriptors */
750
 
        if (media_cnt > 1)
751
 
            continue;
752
 
 
753
 
        switch (line[0]) {
754
 
        case 'm':
755
 
            {
756
 
                int cnt;
757
 
                char media[32], portstr[32];
758
 
 
759
 
                ++media_cnt;
760
 
                if (media_cnt > 1) {
761
 
                    puts("Media line ignored");
762
 
                    break;
763
 
                }
764
 
 
765
 
                cnt = sscanf(line+2, "%s %s RTP/", media, portstr);
766
 
                if (cnt != 2) {
767
 
                    PJ_LOG(1,(THIS_FILE, "Error parsing media line"));
768
 
                    goto on_error;
769
 
                }
770
 
 
771
 
                comp0_port = atoi(portstr);
772
 
                
773
 
            }
774
 
            break;
775
 
        case 'c':
776
 
            {
777
 
                int cnt;
778
 
                char c[32], net[32], ip[80];
779
 
                
780
 
                cnt = sscanf(line+2, "%s %s %s", c, net, ip);
781
 
                if (cnt != 3) {
782
 
                    PJ_LOG(1,(THIS_FILE, "Error parsing connection line"));
783
 
                    goto on_error;
784
 
                }
785
 
 
786
 
                strcpy(comp0_addr, ip);
787
 
            }
788
 
            break;
789
 
        case 'a':
790
 
            {
791
 
                char *attr = strtok(line+2, ": \t\r\n");
792
 
                if (strcmp(attr, "ice-ufrag")==0) {
793
 
                    strcpy(icedemo.rem.ufrag, attr+strlen(attr)+1);
794
 
                } else if (strcmp(attr, "ice-pwd")==0) {
795
 
                    strcpy(icedemo.rem.pwd, attr+strlen(attr)+1);
796
 
                } else if (strcmp(attr, "rtcp")==0) {
797
 
                    char *val = attr+strlen(attr)+1;
798
 
                    int af, cnt;
799
 
                    int port;
800
 
                    char net[32], ip[64];
801
 
                    pj_str_t tmp_addr;
802
 
                    pj_status_t status;
803
 
 
804
 
                    cnt = sscanf(val, "%d IN %s %s", &port, net, ip);
805
 
                    if (cnt != 3) {
806
 
                        PJ_LOG(1,(THIS_FILE, "Error parsing rtcp attribute"));
807
 
                        goto on_error;
808
 
                    }
809
 
 
810
 
                    if (strchr(ip, ':'))
811
 
                        af = pj_AF_INET6();
812
 
                    else
813
 
                        af = pj_AF_INET();
814
 
 
815
 
                    pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0);
816
 
                    tmp_addr = pj_str(ip);
817
 
                    status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1],
818
 
                                                      &tmp_addr);
819
 
                    if (status != PJ_SUCCESS) {
820
 
                        PJ_LOG(1,(THIS_FILE, "Invalid IP address"));
821
 
                        goto on_error;
822
 
                    }
823
 
                    pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port);
824
 
 
825
 
                } else if (strcmp(attr, "candidate")==0) {
826
 
                    char *sdpcand = attr+strlen(attr)+1;
827
 
                    int af, cnt;
828
 
                    char foundation[32], transport[12], ipaddr[80], type[32];
829
 
                    pj_str_t tmpaddr;
830
 
                    int comp_id, prio, port;
831
 
                    pj_ice_sess_cand *cand;
832
 
                    pj_status_t status;
833
 
 
834
 
                    cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s",
835
 
                                 foundation,
836
 
                                 &comp_id,
837
 
                                 transport,
838
 
                                 &prio,
839
 
                                 ipaddr,
840
 
                                 &port,
841
 
                                 type);
842
 
                    if (cnt != 7) {
843
 
                        PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line"));
844
 
                        goto on_error;
845
 
                    }
846
 
 
847
 
                    cand = &icedemo.rem.cand[icedemo.rem.cand_cnt];
848
 
                    pj_bzero(cand, sizeof(*cand));
849
 
                    
850
 
                    if (strcmp(type, "host")==0)
851
 
                        cand->type = PJ_ICE_CAND_TYPE_HOST;
852
 
                    else if (strcmp(type, "srflx")==0)
853
 
                        cand->type = PJ_ICE_CAND_TYPE_SRFLX;
854
 
                    else if (strcmp(type, "relay")==0)
855
 
                        cand->type = PJ_ICE_CAND_TYPE_RELAYED;
856
 
                    else {
857
 
                        PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", 
858
 
                                   type));
859
 
                        goto on_error;
860
 
                    }
861
 
 
862
 
                    cand->comp_id = (pj_uint8_t)comp_id;
863
 
                    pj_strdup2(icedemo.pool, &cand->foundation, foundation);
864
 
                    cand->prio = prio;
865
 
                    
866
 
                    if (strchr(ipaddr, ':'))
867
 
                        af = pj_AF_INET6();
868
 
                    else
869
 
                        af = pj_AF_INET();
870
 
 
871
 
                    tmpaddr = pj_str(ipaddr);
872
 
                    pj_sockaddr_init(af, &cand->addr, NULL, 0);
873
 
                    status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr);
874
 
                    if (status != PJ_SUCCESS) {
875
 
                        PJ_LOG(1,(THIS_FILE, "Error: invalid IP address '%s'",
876
 
                                  ipaddr));
877
 
                        goto on_error;
878
 
                    }
879
 
 
880
 
                    pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port);
881
 
 
882
 
                    ++icedemo.rem.cand_cnt;
883
 
 
884
 
                    if (cand->comp_id > icedemo.rem.comp_cnt)
885
 
                        icedemo.rem.comp_cnt = cand->comp_id;
886
 
                }
887
 
            }
888
 
            break;
889
 
        }
890
 
    }
891
 
 
892
 
    if (icedemo.rem.cand_cnt==0 ||
893
 
        icedemo.rem.ufrag[0]==0 ||
894
 
        icedemo.rem.pwd[0]==0 ||
895
 
        icedemo.rem.comp_cnt == 0)
896
 
    {
897
 
        PJ_LOG(1, (THIS_FILE, "Error: not enough info"));
898
 
        goto on_error;
899
 
    }
900
 
 
901
 
    if (comp0_port==0 || comp0_addr[0]=='\0') {
902
 
        PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found"));
903
 
        goto on_error;
904
 
    } else {
905
 
        int af;
906
 
        pj_str_t tmp_addr;
907
 
        pj_status_t status;
908
 
 
909
 
        if (strchr(comp0_addr, ':'))
910
 
            af = pj_AF_INET6();
911
 
        else
912
 
            af = pj_AF_INET();
913
 
 
914
 
        pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0);
915
 
        tmp_addr = pj_str(comp0_addr);
916
 
        status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0],
917
 
                                          &tmp_addr);
918
 
        if (status != PJ_SUCCESS) {
919
 
            PJ_LOG(1,(THIS_FILE, "Invalid IP address in c= line"));
920
 
            goto on_error;
921
 
        }
922
 
        pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port);
923
 
    }
924
 
 
925
 
    PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", 
926
 
               icedemo.rem.cand_cnt));
927
 
    return;
928
 
 
929
 
on_error:
930
 
    reset_rem_info();
931
 
}
932
 
 
933
 
 
934
 
/*
935
 
 * Start ICE negotiation! This function is invoked from the menu.
936
 
 */
937
 
static void icedemo_start_nego(void)
938
 
{
939
 
    pj_str_t rufrag, rpwd;
940
 
    pj_status_t status;
941
 
 
942
 
    if (icedemo.icest == NULL) {
943
 
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
944
 
        return;
945
 
    }
946
 
 
947
 
    if (!pj_ice_strans_has_sess(icedemo.icest)) {
948
 
        PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
949
 
        return;
950
 
    }
951
 
 
952
 
    if (icedemo.rem.cand_cnt == 0) {
953
 
        PJ_LOG(1,(THIS_FILE, "Error: No remote info, input remote info first"));
954
 
        return;
955
 
    }
956
 
 
957
 
    PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation.."));
958
 
 
959
 
    status = pj_ice_strans_start_ice(icedemo.icest, 
960
 
                                     pj_cstr(&rufrag, icedemo.rem.ufrag),
961
 
                                     pj_cstr(&rpwd, icedemo.rem.pwd),
962
 
                                     icedemo.rem.cand_cnt,
963
 
                                     icedemo.rem.cand);
964
 
    if (status != PJ_SUCCESS)
965
 
        icedemo_perror("Error starting ICE", status);
966
 
    else
967
 
        PJ_LOG(3,(THIS_FILE, "ICE negotiation started"));
968
 
}
969
 
 
970
 
 
971
 
/*
972
 
 * Send application data to remote agent.
973
 
 */
974
 
static void icedemo_send_data(unsigned comp_id, const char *data)
975
 
{
976
 
    pj_status_t status;
977
 
 
978
 
    if (icedemo.icest == NULL) {
979
 
        PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
980
 
        return;
981
 
    }
982
 
 
983
 
    if (!pj_ice_strans_has_sess(icedemo.icest)) {
984
 
        PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
985
 
        return;
986
 
    }
987
 
 
988
 
    /*
989
 
    if (!pj_ice_strans_sess_is_complete(icedemo.icest)) {
990
 
        PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress"));
991
 
        return;
992
 
    }
993
 
    */
994
 
 
995
 
    if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
996
 
        PJ_LOG(1,(THIS_FILE, "Error: invalid component ID"));
997
 
        return;
998
 
    }
999
 
 
1000
 
    status = pj_ice_strans_sendto(icedemo.icest, comp_id, data, strlen(data),
1001
 
                                  &icedemo.rem.def_addr[comp_id-1],
1002
 
                                  pj_sockaddr_get_len(&icedemo.rem.def_addr[comp_id-1]));
1003
 
    if (status != PJ_SUCCESS)
1004
 
        icedemo_perror("Error sending data", status);
1005
 
    else
1006
 
        PJ_LOG(3,(THIS_FILE, "Data sent"));
1007
 
}
1008
 
 
1009
 
 
1010
 
/*
1011
 
 * Display help for the menu.
1012
 
 */
1013
 
static void icedemo_help_menu(void)
1014
 
{
1015
 
    puts("");
1016
 
    puts("-= Help on using ICE and this icedemo program =-");
1017
 
    puts("");
1018
 
    puts("This application demonstrates how to use ICE in pjnath without having\n"
1019
 
         "to use the SIP protocol. To use this application, you will need to run\n"
1020
 
         "two instances of this application, to simulate two ICE agents.\n");
1021
 
 
1022
 
    puts("Basic ICE flow:\n"
1023
 
         " create instance [menu \"c\"]\n"
1024
 
         " repeat these steps as wanted:\n"
1025
 
         "   - init session as offerer or answerer [menu \"i\"]\n"
1026
 
         "   - display our SDP [menu \"s\"]\n"
1027
 
         "   - \"send\" our SDP from the \"show\" output above to remote, by\n"
1028
 
         "     copy-pasting the SDP to the other icedemo application\n"
1029
 
         "   - parse remote SDP, by pasting SDP generated by the other icedemo\n"
1030
 
         "     instance [menu \"r\"]\n"
1031
 
         "   - begin ICE negotiation in our end [menu \"b\"], and \n"
1032
 
         "   - immediately begin ICE negotiation in the other icedemo instance\n"
1033
 
         "   - ICE negotiation will run, and result will be printed to screen\n"
1034
 
         "   - send application data to remote [menu \"x\"]\n"
1035
 
         "   - end/stop ICE session [menu \"e\"]\n"
1036
 
         " destroy instance [menu \"d\"]\n"
1037
 
         "");
1038
 
 
1039
 
    puts("");
1040
 
    puts("This concludes the help screen.");
1041
 
    puts("");
1042
 
}
1043
 
 
1044
 
 
1045
 
/*
1046
 
 * Display console menu
1047
 
 */
1048
 
static void icedemo_print_menu(void)
1049
 
{
1050
 
    puts("");
1051
 
    puts("+----------------------------------------------------------------------+");
1052
 
    puts("|                    M E N U                                           |");
1053
 
    puts("+---+------------------------------------------------------------------+");
1054
 
    puts("| c | create           Create the instance                             |");
1055
 
    puts("| d | destroy          Destroy the instance                            |");
1056
 
    puts("| i | init o|a         Initialize ICE session as offerer or answerer   |");
1057
 
    puts("| e | stop             End/stop ICE session                            |");
1058
 
    puts("| s | show             Display local ICE info                          |");
1059
 
    puts("| r | remote           Input remote ICE info                           |");
1060
 
    puts("| b | start            Begin ICE negotiation                           |");
1061
 
    puts("| x | send <compid> .. Send data to remote                             |");
1062
 
    puts("+---+------------------------------------------------------------------+");
1063
 
    puts("| h |  help            * Help! *                                       |");
1064
 
    puts("| q |  quit            Quit                                            |");
1065
 
    puts("+----------------------------------------------------------------------+");
1066
 
}
1067
 
 
1068
 
 
1069
 
/*
1070
 
 * Main console loop.
1071
 
 */
1072
 
static void icedemo_console(void)
1073
 
{
1074
 
    pj_bool_t app_quit = PJ_FALSE;
1075
 
 
1076
 
    while (!app_quit) {
1077
 
        char input[80], *cmd;
1078
 
        const char *SEP = " \t\r\n";
1079
 
        int len;
1080
 
 
1081
 
        icedemo_print_menu();
1082
 
 
1083
 
        printf("Input: ");
1084
 
        if (stdout) fflush(stdout);
1085
 
 
1086
 
        pj_bzero(input, sizeof(input));
1087
 
        if (fgets(input, sizeof(input), stdin) == NULL)
1088
 
            break;
1089
 
 
1090
 
        len = strlen(input);
1091
 
        while (len && (input[len-1]=='\r' || input[len-1]=='\n'))
1092
 
            input[--len] = '\0';
1093
 
 
1094
 
        cmd = strtok(input, SEP);
1095
 
        if (!cmd)
1096
 
            continue;
1097
 
 
1098
 
        if (strcmp(cmd, "create")==0 || strcmp(cmd, "c")==0) {
1099
 
 
1100
 
            icedemo_create_instance();
1101
 
 
1102
 
        } else if (strcmp(cmd, "destroy")==0 || strcmp(cmd, "d")==0) {
1103
 
 
1104
 
            icedemo_destroy_instance();
1105
 
 
1106
 
        } else if (strcmp(cmd, "init")==0 || strcmp(cmd, "i")==0) {
1107
 
 
1108
 
            char *role = strtok(NULL, SEP);
1109
 
            if (role)
1110
 
                icedemo_init_session(*role);
1111
 
            else
1112
 
                puts("error: Role required");
1113
 
 
1114
 
        } else if (strcmp(cmd, "stop")==0 || strcmp(cmd, "e")==0) {
1115
 
 
1116
 
            icedemo_stop_session();
1117
 
 
1118
 
        } else if (strcmp(cmd, "show")==0 || strcmp(cmd, "s")==0) {
1119
 
 
1120
 
            icedemo_show_ice();
1121
 
 
1122
 
        } else if (strcmp(cmd, "remote")==0 || strcmp(cmd, "r")==0) {
1123
 
 
1124
 
            icedemo_input_remote();
1125
 
 
1126
 
        } else if (strcmp(cmd, "start")==0 || strcmp(cmd, "b")==0) {
1127
 
 
1128
 
            icedemo_start_nego();
1129
 
 
1130
 
        } else if (strcmp(cmd, "send")==0 || strcmp(cmd, "x")==0) {
1131
 
 
1132
 
            char *comp = strtok(NULL, SEP);
1133
 
 
1134
 
            if (!comp) {
1135
 
                PJ_LOG(1,(THIS_FILE, "Error: component ID required"));
1136
 
            } else {
1137
 
                char *data = comp + strlen(comp) + 1;
1138
 
                if (!data)
1139
 
                    data = "";
1140
 
                icedemo_send_data(atoi(comp), data);
1141
 
            }
1142
 
 
1143
 
        } else if (strcmp(cmd, "help")==0 || strcmp(cmd, "h")==0) {
1144
 
 
1145
 
            icedemo_help_menu();
1146
 
 
1147
 
        } else if (strcmp(cmd, "quit")==0 || strcmp(cmd, "q")==0) {
1148
 
 
1149
 
            app_quit = PJ_TRUE;
1150
 
 
1151
 
        } else {
1152
 
 
1153
 
            printf("Invalid command '%s'\n", cmd);
1154
 
 
1155
 
        }
1156
 
    }
1157
 
}
1158
 
 
1159
 
 
1160
 
/*
1161
 
 * Display program usage.
1162
 
 */
1163
 
static void icedemo_usage()
1164
 
{
1165
 
    puts("Usage: icedemo [optons]");
1166
 
    printf("icedemo v%s by pjsip.org\n", pj_get_version());
1167
 
    puts("");
1168
 
    puts("General options:");
1169
 
    puts(" --comp-cnt, -c N          Component count (default=1)");
1170
 
    puts(" --nameserver, -n IP       Configure nameserver to activate DNS SRV");
1171
 
    puts("                           resolution");
1172
 
    puts(" --max-host, -H N          Set max number of host candidates to N");
1173
 
    puts(" --regular, -R             Use regular nomination (default aggressive)");
1174
 
    puts(" --log-file, -L FILE       Save output to log FILE");
1175
 
    puts(" --help, -h                Display this screen.");
1176
 
    puts("");
1177
 
    puts("STUN related options:");
1178
 
    puts(" --stun-srv, -s HOSTDOM    Enable srflx candidate by resolving to STUN server.");
1179
 
    puts("                           HOSTDOM may be a \"host_or_ip[:port]\" or a domain");
1180
 
    puts("                           name if DNS SRV resolution is used.");
1181
 
    puts("");
1182
 
    puts("TURN related options:");
1183
 
    puts(" --turn-srv, -t HOSTDOM    Enable relayed candidate by using this TURN server.");
1184
 
    puts("                           HOSTDOM may be a \"host_or_ip[:port]\" or a domain");
1185
 
    puts("                           name if DNS SRV resolution is used.");
1186
 
    puts(" --turn-tcp, -T            Use TCP to connect to TURN server");
1187
 
    puts(" --turn-username, -u UID   Set TURN username of the credential to UID");
1188
 
    puts(" --turn-password, -p PWD   Set password of the credential to WPWD");
1189
 
    puts(" --turn-fingerprint, -F    Use fingerprint for outgoing TURN requests");
1190
 
    puts("");
1191
 
}
1192
 
 
1193
 
 
1194
 
/*
1195
 
 * And here's the main()
1196
 
 */
1197
 
int main(int argc, char *argv[])
1198
 
{
1199
 
    struct pj_getopt_option long_options[] = {
1200
 
        { "comp-cnt",           1, 0, 'c'},
1201
 
        { "nameserver",         1, 0, 'n'},
1202
 
        { "max-host",           1, 0, 'H'},
1203
 
        { "help",               0, 0, 'h'},
1204
 
        { "stun-srv",           1, 0, 's'},
1205
 
        { "turn-srv",           1, 0, 't'},
1206
 
        { "turn-tcp",           0, 0, 'T'},
1207
 
        { "turn-username",      1, 0, 'u'},
1208
 
        { "turn-password",      1, 0, 'p'},
1209
 
        { "turn-fingerprint",   0, 0, 'F'},
1210
 
        { "regular",            0, 0, 'R'},
1211
 
        { "log-file",           1, 0, 'L'},
1212
 
    };
1213
 
    int c, opt_id;
1214
 
    pj_status_t status;
1215
 
 
1216
 
    icedemo.opt.comp_cnt = 1;
1217
 
    icedemo.opt.max_host = -1;
1218
 
 
1219
 
    while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) {
1220
 
        switch (c) {
1221
 
        case 'c':
1222
 
            icedemo.opt.comp_cnt = atoi(pj_optarg);
1223
 
            if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) {
1224
 
                puts("Invalid component count value");
1225
 
                return 1;
1226
 
            }
1227
 
            break;
1228
 
        case 'n':
1229
 
            icedemo.opt.ns = pj_str(pj_optarg);
1230
 
            break;
1231
 
        case 'H':
1232
 
            icedemo.opt.max_host = atoi(pj_optarg);
1233
 
            break;
1234
 
        case 'h':
1235
 
            icedemo_usage();
1236
 
            return 0;
1237
 
        case 's':
1238
 
            icedemo.opt.stun_srv = pj_str(pj_optarg);
1239
 
            break;
1240
 
        case 't':
1241
 
            icedemo.opt.turn_srv = pj_str(pj_optarg);
1242
 
            break;
1243
 
        case 'T':
1244
 
            icedemo.opt.turn_tcp = PJ_TRUE;
1245
 
            break;
1246
 
        case 'u':
1247
 
            icedemo.opt.turn_username = pj_str(pj_optarg);
1248
 
            break;
1249
 
        case 'p':
1250
 
            icedemo.opt.turn_password = pj_str(pj_optarg);
1251
 
            break;
1252
 
        case 'F':
1253
 
            icedemo.opt.turn_fingerprint = PJ_TRUE;
1254
 
            break;
1255
 
        case 'R':
1256
 
            icedemo.opt.regular = PJ_TRUE;
1257
 
            break;
1258
 
        case 'L':
1259
 
            icedemo.opt.log_file = pj_optarg;
1260
 
            break;
1261
 
        default:
1262
 
            printf("Argument \"%s\" is not valid. Use -h to see help",
1263
 
                   argv[pj_optind]);
1264
 
            return 1;
1265
 
        }
1266
 
    }
1267
 
 
1268
 
    status = icedemo_init();
1269
 
    if (status != PJ_SUCCESS)
1270
 
        return 1;
1271
 
 
1272
 
    icedemo_console();
1273
 
 
1274
 
    err_exit("Quitting..", PJ_SUCCESS);
1275
 
    return 0;
1276
 
}