Home --> Documentations --> PJNATH Reference

ice_demo, an interactive ICE endpoint

This sample demonstrates how to use ICE stream transport without using signaling protocol such as SIP. It provides interactive user interface to create and manage the ICE sessions as well as to exchange SDP with another ice_demo instance.

Features of the demo application:

  • supports host, STUN, and TURN candidates
  • disabling of host candidates
  • DNS SRV resolution for STUN and TURN servers
  • TCP connection to TURN server
  • Optional use of fingerprint for TURN
  • prints and parse SDP containing ICE infos
  • exchange SDP with copy/paste

This file is pjsip-apps/src/samples/icedemo.c

Screenshot on WinXP:

ice_demo.jpg
ice_demo on WinXP
1 /* $Id: icedemo.c 4624 2013-10-21 06:37:30Z ming $ */
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;
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) {
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' ?
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(...) \
518  printed = pj_ansi_snprintf(p, maxlen - (p-buffer), \
519  __VA_ARGS__); \
520  if (printed <= 0 || printed >= (int)(maxlen - (p-buffer))) \
521  return -PJ_ETOOSMALL; \
522  p += printed
523 
524 
525 /* Utility to create a=candidate SDP attribute */
526 static int print_cand(char buffer[], unsigned maxlen,
527  const pj_ice_sess_cand *cand)
528 {
529  char ipaddr[PJ_INET6_ADDRSTRLEN];
530  char *p = buffer;
531  int printed;
532 
533  PRINT("a=candidate:%.*s %u UDP %u %s %u typ ",
534  (int)cand->foundation.slen,
535  cand->foundation.ptr,
536  (unsigned)cand->comp_id,
537  cand->prio,
538  pj_sockaddr_print(&cand->addr, ipaddr,
539  sizeof(ipaddr), 0),
540  (unsigned)pj_sockaddr_get_port(&cand->addr));
541 
542  PRINT("%s\n",
544 
545  if (p == buffer+maxlen)
546  return -PJ_ETOOSMALL;
547 
548  *p = '\0';
549 
550  return (int)(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 
567  /* Get ufrag and pwd from current session */
568  pj_ice_strans_get_ufrag_pwd(icedemo.icest, &local_ufrag, &local_pwd,
569  NULL, NULL);
570 
571  /* Write the a=ice-ufrag and a=ice-pwd attributes */
572  PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n",
573  (int)local_ufrag.slen,
574  local_ufrag.ptr,
575  (int)local_pwd.slen,
576  local_pwd.ptr);
577 
578  /* Write each component */
579  for (comp=0; comp<icedemo.opt.comp_cnt; ++comp) {
580  unsigned j, cand_cnt;
582  char ipaddr[PJ_INET6_ADDRSTRLEN];
583 
584  /* Get default candidate for the component */
585  status = pj_ice_strans_get_def_cand(icedemo.icest, comp+1, &cand[0]);
586  if (status != PJ_SUCCESS)
587  return -status;
588 
589  /* Write the default address */
590  if (comp==0) {
591  /* For component 1, default address is in m= and c= lines */
592  PRINT("m=audio %d RTP/AVP 0\n"
593  "c=IN IP4 %s\n",
594  (int)pj_sockaddr_get_port(&cand[0].addr),
595  pj_sockaddr_print(&cand[0].addr, ipaddr,
596  sizeof(ipaddr), 0));
597  } else if (comp==1) {
598  /* For component 2, default address is in a=rtcp line */
599  PRINT("a=rtcp:%d IN IP4 %s\n",
600  (int)pj_sockaddr_get_port(&cand[0].addr),
601  pj_sockaddr_print(&cand[0].addr, ipaddr,
602  sizeof(ipaddr), 0));
603  } else {
604  /* For other components, we'll just invent this.. */
605  PRINT("a=Xice-defcand:%d IN IP4 %s\n",
606  (int)pj_sockaddr_get_port(&cand[0].addr),
607  pj_sockaddr_print(&cand[0].addr, ipaddr,
608  sizeof(ipaddr), 0));
609  }
610 
611  /* Enumerate all candidates for this component */
612  cand_cnt = PJ_ARRAY_SIZE(cand);
613  status = pj_ice_strans_enum_cands(icedemo.icest, comp+1,
614  &cand_cnt, cand);
615  if (status != PJ_SUCCESS)
616  return -status;
617 
618  /* And encode the candidates as SDP */
619  for (j=0; j<cand_cnt; ++j) {
620  printed = print_cand(p, maxlen - (unsigned)(p-buffer), &cand[j]);
621  if (printed < 0)
622  return -PJ_ETOOSMALL;
623  p += printed;
624  }
625  }
626 
627  if (p == buffer+maxlen)
628  return -PJ_ETOOSMALL;
629 
630  *p = '\0';
631  return (int)(p - buffer);
632 }
633 
634 
635 /*
636  * Show information contained in the ICE stream transport. This is
637  * invoked from the menu.
638  */
639 static void icedemo_show_ice(void)
640 {
641  static char buffer[1000];
642  int len;
643 
644  if (icedemo.icest == NULL) {
645  PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
646  return;
647  }
648 
649  puts("General info");
650  puts("---------------");
651  printf("Component count : %d\n", icedemo.opt.comp_cnt);
652  printf("Status : ");
653  if (pj_ice_strans_sess_is_complete(icedemo.icest))
654  puts("negotiation complete");
655  else if (pj_ice_strans_sess_is_running(icedemo.icest))
656  puts("negotiation is in progress");
657  else if (pj_ice_strans_has_sess(icedemo.icest))
658  puts("session ready");
659  else
660  puts("session not created");
661 
662  if (!pj_ice_strans_has_sess(icedemo.icest)) {
663  puts("Create the session first to see more info");
664  return;
665  }
666 
667  printf("Negotiated comp_cnt: %d\n",
668  pj_ice_strans_get_running_comp_cnt(icedemo.icest));
669  printf("Role : %s\n",
671  "controlled" : "controlling");
672 
673  len = encode_session(buffer, sizeof(buffer));
674  if (len < 0)
675  err_exit("not enough buffer to show ICE status", -len);
676 
677  puts("");
678  printf("Local SDP (paste this to remote host):\n"
679  "--------------------------------------\n"
680  "%s\n", buffer);
681 
682 
683  puts("");
684  puts("Remote info:\n"
685  "----------------------");
686  if (icedemo.rem.cand_cnt==0) {
687  puts("No remote info yet");
688  } else {
689  unsigned i;
690 
691  printf("Remote ufrag : %s\n", icedemo.rem.ufrag);
692  printf("Remote password : %s\n", icedemo.rem.pwd);
693  printf("Remote cand. cnt. : %d\n", icedemo.rem.cand_cnt);
694 
695  for (i=0; i<icedemo.rem.cand_cnt; ++i) {
696  len = print_cand(buffer, sizeof(buffer), &icedemo.rem.cand[i]);
697  if (len < 0)
698  err_exit("not enough buffer to show ICE status", -len);
699 
700  printf(" %s", buffer);
701  }
702  }
703 }
704 
705 
706 /*
707  * Input and parse SDP from the remote (containing remote's ICE information)
708  * and save it to global variables.
709  */
710 static void icedemo_input_remote(void)
711 {
712  char linebuf[80];
713  unsigned media_cnt = 0;
714  unsigned comp0_port = 0;
715  char comp0_addr[80];
716  pj_bool_t done = PJ_FALSE;
717 
718  puts("Paste SDP from remote host, end with empty line");
719 
720  reset_rem_info();
721 
722  comp0_addr[0] = '\0';
723 
724  while (!done) {
725  pj_size_t len;
726  char *line;
727 
728  printf(">");
729  if (stdout) fflush(stdout);
730 
731  if (fgets(linebuf, sizeof(linebuf), stdin)==NULL)
732  break;
733 
734  len = strlen(linebuf);
735  while (len && (linebuf[len-1] == '\r' || linebuf[len-1] == '\n'))
736  linebuf[--len] = '\0';
737 
738  line = linebuf;
739  while (len && pj_isspace(*line))
740  ++line, --len;
741 
742  if (len==0)
743  break;
744 
745  /* Ignore subsequent media descriptors */
746  if (media_cnt > 1)
747  continue;
748 
749  switch (line[0]) {
750  case 'm':
751  {
752  int cnt;
753  char media[32], portstr[32];
754 
755  ++media_cnt;
756  if (media_cnt > 1) {
757  puts("Media line ignored");
758  break;
759  }
760 
761  cnt = sscanf(line+2, "%s %s RTP/", media, portstr);
762  if (cnt != 2) {
763  PJ_LOG(1,(THIS_FILE, "Error parsing media line"));
764  goto on_error;
765  }
766 
767  comp0_port = atoi(portstr);
768 
769  }
770  break;
771  case 'c':
772  {
773  int cnt;
774  char c[32], net[32], ip[80];
775 
776  cnt = sscanf(line+2, "%s %s %s", c, net, ip);
777  if (cnt != 3) {
778  PJ_LOG(1,(THIS_FILE, "Error parsing connection line"));
779  goto on_error;
780  }
781 
782  strcpy(comp0_addr, ip);
783  }
784  break;
785  case 'a':
786  {
787  char *attr = strtok(line+2, ": \t\r\n");
788  if (strcmp(attr, "ice-ufrag")==0) {
789  strcpy(icedemo.rem.ufrag, attr+strlen(attr)+1);
790  } else if (strcmp(attr, "ice-pwd")==0) {
791  strcpy(icedemo.rem.pwd, attr+strlen(attr)+1);
792  } else if (strcmp(attr, "rtcp")==0) {
793  char *val = attr+strlen(attr)+1;
794  int af, cnt;
795  int port;
796  char net[32], ip[64];
797  pj_str_t tmp_addr;
798  pj_status_t status;
799 
800  cnt = sscanf(val, "%d IN %s %s", &port, net, ip);
801  if (cnt != 3) {
802  PJ_LOG(1,(THIS_FILE, "Error parsing rtcp attribute"));
803  goto on_error;
804  }
805 
806  if (strchr(ip, ':'))
807  af = pj_AF_INET6();
808  else
809  af = pj_AF_INET();
810 
811  pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0);
812  tmp_addr = pj_str(ip);
813  status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1],
814  &tmp_addr);
815  if (status != PJ_SUCCESS) {
816  PJ_LOG(1,(THIS_FILE, "Invalid IP address"));
817  goto on_error;
818  }
819  pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port);
820 
821  } else if (strcmp(attr, "candidate")==0) {
822  char *sdpcand = attr+strlen(attr)+1;
823  int af, cnt;
824  char foundation[32], transport[12], ipaddr[80], type[32];
825  pj_str_t tmpaddr;
826  int comp_id, prio, port;
827  pj_ice_sess_cand *cand;
828  pj_status_t status;
829 
830  cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s",
831  foundation,
832  &comp_id,
833  transport,
834  &prio,
835  ipaddr,
836  &port,
837  type);
838  if (cnt != 7) {
839  PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line"));
840  goto on_error;
841  }
842 
843  cand = &icedemo.rem.cand[icedemo.rem.cand_cnt];
844  pj_bzero(cand, sizeof(*cand));
845 
846  if (strcmp(type, "host")==0)
847  cand->type = PJ_ICE_CAND_TYPE_HOST;
848  else if (strcmp(type, "srflx")==0)
850  else if (strcmp(type, "relay")==0)
852  else {
853  PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'",
854  type));
855  goto on_error;
856  }
857 
858  cand->comp_id = (pj_uint8_t)comp_id;
859  pj_strdup2(icedemo.pool, &cand->foundation, foundation);
860  cand->prio = prio;
861 
862  if (strchr(ipaddr, ':'))
863  af = pj_AF_INET6();
864  else
865  af = pj_AF_INET();
866 
867  tmpaddr = pj_str(ipaddr);
868  pj_sockaddr_init(af, &cand->addr, NULL, 0);
869  status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr);
870  if (status != PJ_SUCCESS) {
871  PJ_LOG(1,(THIS_FILE, "Error: invalid IP address '%s'",
872  ipaddr));
873  goto on_error;
874  }
875 
876  pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port);
877 
878  ++icedemo.rem.cand_cnt;
879 
880  if (cand->comp_id > icedemo.rem.comp_cnt)
881  icedemo.rem.comp_cnt = cand->comp_id;
882  }
883  }
884  break;
885  }
886  }
887 
888  if (icedemo.rem.cand_cnt==0 ||
889  icedemo.rem.ufrag[0]==0 ||
890  icedemo.rem.pwd[0]==0 ||
891  icedemo.rem.comp_cnt == 0)
892  {
893  PJ_LOG(1, (THIS_FILE, "Error: not enough info"));
894  goto on_error;
895  }
896 
897  if (comp0_port==0 || comp0_addr[0]=='\0') {
898  PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found"));
899  goto on_error;
900  } else {
901  int af;
902  pj_str_t tmp_addr;
903  pj_status_t status;
904 
905  if (strchr(comp0_addr, ':'))
906  af = pj_AF_INET6();
907  else
908  af = pj_AF_INET();
909 
910  pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0);
911  tmp_addr = pj_str(comp0_addr);
912  status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0],
913  &tmp_addr);
914  if (status != PJ_SUCCESS) {
915  PJ_LOG(1,(THIS_FILE, "Invalid IP address in c= line"));
916  goto on_error;
917  }
918  pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port);
919  }
920 
921  PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added",
922  icedemo.rem.cand_cnt));
923  return;
924 
925 on_error:
926  reset_rem_info();
927 }
928 
929 
930 /*
931  * Start ICE negotiation! This function is invoked from the menu.
932  */
933 static void icedemo_start_nego(void)
934 {
935  pj_str_t rufrag, rpwd;
936  pj_status_t status;
937 
938  if (icedemo.icest == NULL) {
939  PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
940  return;
941  }
942 
943  if (!pj_ice_strans_has_sess(icedemo.icest)) {
944  PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
945  return;
946  }
947 
948  if (icedemo.rem.cand_cnt == 0) {
949  PJ_LOG(1,(THIS_FILE, "Error: No remote info, input remote info first"));
950  return;
951  }
952 
953  PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation.."));
954 
955  status = pj_ice_strans_start_ice(icedemo.icest,
956  pj_cstr(&rufrag, icedemo.rem.ufrag),
957  pj_cstr(&rpwd, icedemo.rem.pwd),
958  icedemo.rem.cand_cnt,
959  icedemo.rem.cand);
960  if (status != PJ_SUCCESS)
961  icedemo_perror("Error starting ICE", status);
962  else
963  PJ_LOG(3,(THIS_FILE, "ICE negotiation started"));
964 }
965 
966 
967 /*
968  * Send application data to remote agent.
969  */
970 static void icedemo_send_data(unsigned comp_id, const char *data)
971 {
972  pj_status_t status;
973 
974  if (icedemo.icest == NULL) {
975  PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
976  return;
977  }
978 
979  if (!pj_ice_strans_has_sess(icedemo.icest)) {
980  PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
981  return;
982  }
983 
984  /*
985  if (!pj_ice_strans_sess_is_complete(icedemo.icest)) {
986  PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress"));
987  return;
988  }
989  */
990 
991  if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
992  PJ_LOG(1,(THIS_FILE, "Error: invalid component ID"));
993  return;
994  }
995 
996  status = pj_ice_strans_sendto(icedemo.icest, comp_id, data, strlen(data),
997  &icedemo.rem.def_addr[comp_id-1],
998  pj_sockaddr_get_len(&icedemo.rem.def_addr[comp_id-1]));
999  if (status != PJ_SUCCESS)
1000  icedemo_perror("Error sending data", status);
1001  else
1002  PJ_LOG(3,(THIS_FILE, "Data sent"));
1003 }
1004 
1005 
1006 /*
1007  * Display help for the menu.
1008  */
1009 static void icedemo_help_menu(void)
1010 {
1011  puts("");
1012  puts("-= Help on using ICE and this icedemo program =-");
1013  puts("");
1014  puts("This application demonstrates how to use ICE in pjnath without having\n"
1015  "to use the SIP protocol. To use this application, you will need to run\n"
1016  "two instances of this application, to simulate two ICE agents.\n");
1017 
1018  puts("Basic ICE flow:\n"
1019  " create instance [menu \"c\"]\n"
1020  " repeat these steps as wanted:\n"
1021  " - init session as offerer or answerer [menu \"i\"]\n"
1022  " - display our SDP [menu \"s\"]\n"
1023  " - \"send\" our SDP from the \"show\" output above to remote, by\n"
1024  " copy-pasting the SDP to the other icedemo application\n"
1025  " - parse remote SDP, by pasting SDP generated by the other icedemo\n"
1026  " instance [menu \"r\"]\n"
1027  " - begin ICE negotiation in our end [menu \"b\"], and \n"
1028  " - immediately begin ICE negotiation in the other icedemo instance\n"
1029  " - ICE negotiation will run, and result will be printed to screen\n"
1030  " - send application data to remote [menu \"x\"]\n"
1031  " - end/stop ICE session [menu \"e\"]\n"
1032  " destroy instance [menu \"d\"]\n"
1033  "");
1034 
1035  puts("");
1036  puts("This concludes the help screen.");
1037  puts("");
1038 }
1039 
1040 
1041 /*
1042  * Display console menu
1043  */
1044 static void icedemo_print_menu(void)
1045 {
1046  puts("");
1047  puts("+----------------------------------------------------------------------+");
1048  puts("| M E N U |");
1049  puts("+---+------------------------------------------------------------------+");
1050  puts("| c | create Create the instance |");
1051  puts("| d | destroy Destroy the instance |");
1052  puts("| i | init o|a Initialize ICE session as offerer or answerer |");
1053  puts("| e | stop End/stop ICE session |");
1054  puts("| s | show Display local ICE info |");
1055  puts("| r | remote Input remote ICE info |");
1056  puts("| b | start Begin ICE negotiation |");
1057  puts("| x | send <compid> .. Send data to remote |");
1058  puts("+---+------------------------------------------------------------------+");
1059  puts("| h | help * Help! * |");
1060  puts("| q | quit Quit |");
1061  puts("+----------------------------------------------------------------------+");
1062 }
1063 
1064 
1065 /*
1066  * Main console loop.
1067  */
1068 static void icedemo_console(void)
1069 {
1070  pj_bool_t app_quit = PJ_FALSE;
1071 
1072  while (!app_quit) {
1073  char input[80], *cmd;
1074  const char *SEP = " \t\r\n";
1075  pj_size_t len;
1076 
1077  icedemo_print_menu();
1078 
1079  printf("Input: ");
1080  if (stdout) fflush(stdout);
1081 
1082  pj_bzero(input, sizeof(input));
1083  if (fgets(input, sizeof(input), stdin) == NULL)
1084  break;
1085 
1086  len = strlen(input);
1087  while (len && (input[len-1]=='\r' || input[len-1]=='\n'))
1088  input[--len] = '\0';
1089 
1090  cmd = strtok(input, SEP);
1091  if (!cmd)
1092  continue;
1093 
1094  if (strcmp(cmd, "create")==0 || strcmp(cmd, "c")==0) {
1095 
1096  icedemo_create_instance();
1097 
1098  } else if (strcmp(cmd, "destroy")==0 || strcmp(cmd, "d")==0) {
1099 
1100  icedemo_destroy_instance();
1101 
1102  } else if (strcmp(cmd, "init")==0 || strcmp(cmd, "i")==0) {
1103 
1104  char *role = strtok(NULL, SEP);
1105  if (role)
1106  icedemo_init_session(*role);
1107  else
1108  puts("error: Role required");
1109 
1110  } else if (strcmp(cmd, "stop")==0 || strcmp(cmd, "e")==0) {
1111 
1112  icedemo_stop_session();
1113 
1114  } else if (strcmp(cmd, "show")==0 || strcmp(cmd, "s")==0) {
1115 
1116  icedemo_show_ice();
1117 
1118  } else if (strcmp(cmd, "remote")==0 || strcmp(cmd, "r")==0) {
1119 
1120  icedemo_input_remote();
1121 
1122  } else if (strcmp(cmd, "start")==0 || strcmp(cmd, "b")==0) {
1123 
1124  icedemo_start_nego();
1125 
1126  } else if (strcmp(cmd, "send")==0 || strcmp(cmd, "x")==0) {
1127 
1128  char *comp = strtok(NULL, SEP);
1129 
1130  if (!comp) {
1131  PJ_LOG(1,(THIS_FILE, "Error: component ID required"));
1132  } else {
1133  char *data = comp + strlen(comp) + 1;
1134  if (!data)
1135  data = "";
1136  icedemo_send_data(atoi(comp), data);
1137  }
1138 
1139  } else if (strcmp(cmd, "help")==0 || strcmp(cmd, "h")==0) {
1140 
1141  icedemo_help_menu();
1142 
1143  } else if (strcmp(cmd, "quit")==0 || strcmp(cmd, "q")==0) {
1144 
1145  app_quit = PJ_TRUE;
1146 
1147  } else {
1148 
1149  printf("Invalid command '%s'\n", cmd);
1150 
1151  }
1152  }
1153 }
1154 
1155 
1156 /*
1157  * Display program usage.
1158  */
1159 static void icedemo_usage()
1160 {
1161  puts("Usage: icedemo [optons]");
1162  printf("icedemo v%s by pjsip.org\n", pj_get_version());
1163  puts("");
1164  puts("General options:");
1165  puts(" --comp-cnt, -c N Component count (default=1)");
1166  puts(" --nameserver, -n IP Configure nameserver to activate DNS SRV");
1167  puts(" resolution");
1168  puts(" --max-host, -H N Set max number of host candidates to N");
1169  puts(" --regular, -R Use regular nomination (default aggressive)");
1170  puts(" --log-file, -L FILE Save output to log FILE");
1171  puts(" --help, -h Display this screen.");
1172  puts("");
1173  puts("STUN related options:");
1174  puts(" --stun-srv, -s HOSTDOM Enable srflx candidate by resolving to STUN server.");
1175  puts(" HOSTDOM may be a \"host_or_ip[:port]\" or a domain");
1176  puts(" name if DNS SRV resolution is used.");
1177  puts("");
1178  puts("TURN related options:");
1179  puts(" --turn-srv, -t HOSTDOM Enable relayed candidate by using this TURN 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(" --turn-tcp, -T Use TCP to connect to TURN server");
1183  puts(" --turn-username, -u UID Set TURN username of the credential to UID");
1184  puts(" --turn-password, -p PWD Set password of the credential to WPWD");
1185  puts(" --turn-fingerprint, -F Use fingerprint for outgoing TURN requests");
1186  puts("");
1187 }
1188 
1189 
1190 /*
1191  * And here's the main()
1192  */
1193 int main(int argc, char *argv[])
1194 {
1195  struct pj_getopt_option long_options[] = {
1196  { "comp-cnt", 1, 0, 'c'},
1197  { "nameserver", 1, 0, 'n'},
1198  { "max-host", 1, 0, 'H'},
1199  { "help", 0, 0, 'h'},
1200  { "stun-srv", 1, 0, 's'},
1201  { "turn-srv", 1, 0, 't'},
1202  { "turn-tcp", 0, 0, 'T'},
1203  { "turn-username", 1, 0, 'u'},
1204  { "turn-password", 1, 0, 'p'},
1205  { "turn-fingerprint", 0, 0, 'F'},
1206  { "regular", 0, 0, 'R'},
1207  { "log-file", 1, 0, 'L'},
1208  };
1209  int c, opt_id;
1210  pj_status_t status;
1211 
1212  icedemo.opt.comp_cnt = 1;
1213  icedemo.opt.max_host = -1;
1214 
1215  while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) {
1216  switch (c) {
1217  case 'c':
1218  icedemo.opt.comp_cnt = atoi(pj_optarg);
1219  if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) {
1220  puts("Invalid component count value");
1221  return 1;
1222  }
1223  break;
1224  case 'n':
1225  icedemo.opt.ns = pj_str(pj_optarg);
1226  break;
1227  case 'H':
1228  icedemo.opt.max_host = atoi(pj_optarg);
1229  break;
1230  case 'h':
1231  icedemo_usage();
1232  return 0;
1233  case 's':
1234  icedemo.opt.stun_srv = pj_str(pj_optarg);
1235  break;
1236  case 't':
1237  icedemo.opt.turn_srv = pj_str(pj_optarg);
1238  break;
1239  case 'T':
1240  icedemo.opt.turn_tcp = PJ_TRUE;
1241  break;
1242  case 'u':
1243  icedemo.opt.turn_username = pj_str(pj_optarg);
1244  break;
1245  case 'p':
1246  icedemo.opt.turn_password = pj_str(pj_optarg);
1247  break;
1248  case 'F':
1249  icedemo.opt.turn_fingerprint = PJ_TRUE;
1250  break;
1251  case 'R':
1252  icedemo.opt.regular = PJ_TRUE;
1253  break;
1254  case 'L':
1255  icedemo.opt.log_file = pj_optarg;
1256  break;
1257  default:
1258  printf("Argument \"%s\" is not valid. Use -h to see help",
1259  argv[pj_optind]);
1260  return 1;
1261  }
1262  }
1263 
1264  status = icedemo_init();
1265  if (status != PJ_SUCCESS)
1266  return 1;
1267 
1268  icedemo_console();
1269 
1270  err_exit("Quitting..", PJ_SUCCESS);
1271  return 0;
1272 }
void pj_log_write(int level, const char *buffer, int len)
void pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg)
pj_ssize_t slen
pj_status_t pj_ioqueue_destroy(pj_ioqueue_t *ioque)
#define PJ_ICE_ST_MAX_CAND
Definition: config.h:244
Definition: ice_session.h:531
pj_status_t pjnath_init(void)
void pj_log_set_log_func(pj_log_func *func)
pj_ice_sess_role
Definition: ice_session.h:521
Definition: turn_session.h:144
char * ptr
int pj_ioqueue_poll(pj_ioqueue_t *ioque, const pj_time_val *timeout)
pj_status_t pj_ice_strans_get_def_cand(pj_ice_strans *ice_st, unsigned comp_id, pj_ice_sess_cand *cand)
int pj_bool_t
pj_ice_cand_type type
Definition: ice_session.h:243
#define PJ_TIME_VAL_GT(t1, t2)
struct pj_thread_t pj_thread_t
pj_status_t pj_ice_strans_sendto(pj_ice_strans *ice_st, unsigned comp_id, const void *data, pj_size_t data_len, const pj_sockaddr_t *dst_addr, int dst_addr_len)
Definition: ice_session.h:238
pj_status_t pj_ice_strans_stop_ice(pj_ice_strans *ice_st)
#define pj_AF_INET()
Definition: ice_session.h:157
Definition: ice_strans.h:143
pj_status_t pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd, pj_ioqueue_t **ioqueue)
pj_status_t pj_ice_strans_enum_cands(pj_ice_strans *ice_st, unsigned comp_id, unsigned *count, pj_ice_sess_cand cand[])
pj_status_t pj_dns_resolver_create(pj_pool_factory *pf, const char *name, unsigned options, pj_timer_heap_t *timer, pj_ioqueue_t *ioqueue, pj_dns_resolver **p_resolver)
Definition: ice_strans.h:123
#define PJ_TIME_VAL_MSEC(t)
Definition: ice_session.h:536
pj_status_t pj_ice_strans_start_ice(pj_ice_strans *ice_st, const pj_str_t *rem_ufrag, const pj_str_t *rem_passwd, unsigned rcand_cnt, const pj_ice_sess_cand rcand[])
#define PJ_LOG(level, arg)
pj_uint8_t comp_id
Definition: ice_session.h:258
pj_uint32_t prio
Definition: ice_session.h:283
int pj_status_t
pj_status_t pj_thread_create(pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **thread)
pj_status_t pj_ice_strans_destroy(pj_ice_strans *ice_st)
Definition: ice_session.h:138
Definition: ice_strans.h:126
void pj_sockaddr_t
int pj_isspace(unsigned char c)
pj_str_t * pj_strdup2(pj_pool_t *pool, pj_str_t *dst, const char *src)
Definition: stun_auth.h:75
Definition: ice_session.h:145
pj_bool_t pj_ice_strans_sess_is_complete(pj_ice_strans *ice_st)
char * pj_strchr(const pj_str_t *str, int chr)
pj_status_t pj_ice_strans_init_ice(pj_ice_strans *ice_st, pj_ice_sess_role role, const pj_str_t *local_ufrag, const pj_str_t *local_passwd)
const char * pj_get_version(void)
void(* on_ice_complete)(pj_ice_strans *ice_st, pj_ice_strans_op op, pj_status_t status)
Definition: ice_strans.h:170
void pj_timer_heap_destroy(pj_timer_heap_t *ht)
pj_status_t pj_ice_strans_create(const char *name, const pj_ice_strans_cfg *cfg, unsigned comp_cnt, void *user_data, const pj_ice_strans_cb *cb, pj_ice_strans **p_ice_st)
#define PJ_ETOOSMALL
struct pj_ice_strans pj_ice_strans
Definition: ice_strans.h:117
void(* on_rx_data)(pj_ice_strans *ice_st, unsigned comp_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len)
Definition: ice_strans.h:157
pj_str_t pj_str(char *str)
pj_status_t pj_sockaddr_set_port(pj_sockaddr *addr, pj_uint16_t hostport)
pj_status_t pj_get_netos_error(void)
void pj_caching_pool_init(pj_caching_pool *ch_pool, const pj_pool_factory_policy *policy, pj_size_t max_capacity)
pj_status_t pj_sockaddr_init(int af, pj_sockaddr *addr, const pj_str_t *cp, pj_uint16_t port)
PJ_BEGIN_DECL pj_status_t pjlib_util_init(void)
char * pj_sockaddr_print(const pj_sockaddr_t *addr, char *buf, int size, unsigned flags)
void pj_bzero(void *dst, pj_size_t size)
#define PJ_STUN_PORT
Definition: config.h:123
pj_str_t pj_strerror(pj_status_t statcode, char *buf, pj_size_t bufsize)
const pj_str_t * pj_cstr(pj_str_t *str, const char *s)
unsigned pj_ice_strans_get_running_comp_cnt(pj_ice_strans *ice_st)
unsigned char pj_uint8_t
#define pj_assert(expr)
pj_ice_strans_op
Definition: ice_strans.h:120
const char * pj_ice_get_cand_type_name(pj_ice_cand_type type)
#define PJ_ARRAY_SIZE(a)
pj_status_t pj_sockaddr_set_str_addr(int af, pj_sockaddr *addr, const pj_str_t *cp)
pj_uint16_t pj_sockaddr_get_port(const pj_sockaddr_t *addr)
pj_bool_t pj_ice_strans_sess_is_running(pj_ice_strans *ice_st)
Definition: turn_session.h:149
pj_status_t pj_dns_resolver_set_ns(pj_dns_resolver *resolver, unsigned count, const pj_str_t servers[], const pj_uint16_t ports[])
unsigned pj_sockaddr_get_len(const pj_sockaddr_t *addr)
#define PJ_ERR_MSG_SIZE
pj_pool_t * pj_pool_create(pj_pool_factory *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback)
pj_status_t pj_thread_join(pj_thread_t *thread)
pj_status_t pj_ice_strans_get_ufrag_pwd(pj_ice_strans *ice_st, pj_str_t *loc_ufrag, pj_str_t *loc_pwd, pj_str_t *rem_ufrag, pj_str_t *rem_pwd)
pj_status_t pj_thread_destroy(pj_thread_t *thread)
void pj_shutdown(void)
Definition: stun_auth.h:96
#define pj_AF_INET6()
pj_ice_sess_role pj_ice_strans_get_role(pj_ice_strans *ice_st)
pj_bool_t pj_ice_strans_has_sess(pj_ice_strans *ice_st)
pj_status_t pj_thread_sleep(unsigned msec)
#define PJ_ICE_MAX_COMP
Definition: config.h:282
pj_str_t foundation
Definition: ice_session.h:276
unsigned pj_timer_heap_poll(pj_timer_heap_t *ht, pj_time_val *next_delay)
unsigned short pj_uint16_t
int pj_tolower(unsigned char c)
pj_status_t pj_init(void)
size_t pj_size_t
pj_status_t pj_timer_heap_create(pj_pool_t *pool, pj_size_t count, pj_timer_heap_t **ht)
pj_sockaddr addr
Definition: ice_session.h:294
#define PJ_UNUSED_ARG(arg)
Definition: ice_strans.h:338
#define PJ_INET6_ADDRSTRLEN
void pj_caching_pool_destroy(pj_caching_pool *ch_pool)

.

 


PJNATH - Open Source NAT traversal helper library supporting STUN, TURN, and ICE
Copyright (C) 2006-2009 Teluu Inc.