agent.c 62.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * This file is part of the Nice GLib ICE library.
 *
 * (C) 2006, 2007 Collabora Ltd.
 *  Contact: Dafydd Harries
 * (C) 2006, 2007 Nokia Corporation. All rights reserved.
 *  Contact: Kai Vehmanen
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Nice GLib ICE library.
 *
 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
 * Corporation. All Rights Reserved.
 *
 * Contributors:
 *   Dafydd Harries, Collabora Ltd.
26
 *   Kai Vehmanen, Nokia
27
28
29
30
31
32
33
34
35
36
37
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
 * case the provisions of LGPL are applicable instead of those above. If you
 * wish to allow use of your version of this file only under the terms of the
 * LGPL and not to allow others to use your version of this file under the
 * MPL, indicate your decision by deleting the provisions above and replace
 * them with the notice and other provisions required by the LGPL. If you do
 * not delete the provisions above, a recipient may use your version of this
 * file under either the MPL or the LGPL.
 */
Dafydd Harries's avatar
Dafydd Harries committed
38

39
40
41

#ifdef HAVE_CONFIG_H
# include <config.h>
42
43
#else
#define NICEAPI_EXPORT
44
45
#endif

46
47
#include <glib.h>

Dafydd Harries's avatar
Dafydd Harries committed
48
#include <string.h>
49
#include <errno.h>
Dafydd Harries's avatar
Dafydd Harries committed
50

51
52
53
#ifdef G_OS_WIN32
#include <winsock2.h>
#else
54
55
56
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
57
#endif
Dafydd Harries's avatar
Dafydd Harries committed
58

59
60
#include "debug.h"

61
#include "socket.h"
62
#include "stun/usages/turn.h"
63
#include "candidate.h"
64
#include "component.h"
65
66
#include "conncheck.h"
#include "discovery.h"
67
#include "agent.h"
68
#include "agent-priv.h"
69
#include "agent-signals-marshal.h"
Dafydd Harries's avatar
Dafydd Harries committed
70

71
#include "stream.h"
72
#include "interfaces.h"
Dafydd Harries's avatar
Dafydd Harries committed
73

74
75
76
77
/* This is the max size of a UDP packet
 * will it work tcp relaying??
 */
#define MAX_BUFFER_SIZE 65536
78
79
#define DEFAULT_STUN_PORT  3478

80

Dafydd Harries's avatar
Dafydd Harries committed
81
82
83
84
G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT);

enum
{
85
  PROP_COMPATIBILITY = 1,
86
  PROP_MAIN_CONTEXT,
87
  PROP_STUN_SERVER,
88
  PROP_STUN_SERVER_PORT,
89
  PROP_CONTROLLING_MODE,
90
  PROP_FULL_MODE,
91
  PROP_STUN_PACING_TIMER,
92
93
94
95
96
  PROP_MAX_CONNECTIVITY_CHECKS,
  PROP_PROXY_TYPE,
  PROP_PROXY_IP,
  PROP_PROXY_PORT,
  PROP_PROXY_USERNAME,
Youness Alaoui's avatar
Youness Alaoui committed
97
98
99
100
101
  PROP_PROXY_PASSWORD,
#ifdef HAVE_GUPNP
  PROP_UPNP,
  PROP_UPNP_TIMEOUT
#endif
Dafydd Harries's avatar
Dafydd Harries committed
102
103
104
};


105
106
107
enum
{
  SIGNAL_COMPONENT_STATE_CHANGED,
108
  SIGNAL_CANDIDATE_GATHERING_DONE,
109
110
  SIGNAL_NEW_SELECTED_PAIR,
  SIGNAL_NEW_CANDIDATE,
111
  SIGNAL_NEW_REMOTE_CANDIDATE,
112
  SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED,
113
114
115
116
117
  N_SIGNALS,
};

static guint signals[N_SIGNALS];

118
119
static GStaticRecMutex agent_mutex = G_STATIC_REC_MUTEX_INIT;    /* Mutex used for thread-safe lib */

120
121
static gboolean priv_attach_stream_component (NiceAgent *agent,
    Stream *stream,
122
    Component *component);
Youness Alaoui's avatar
Youness Alaoui committed
123
static void priv_detach_stream_component (Stream *stream, Component *component);
124

Youness Alaoui's avatar
Youness Alaoui committed
125
126
static void priv_free_upnp (NiceAgent *agent);

127
128
129
130
131
132
133
134
135
136
137
138
139

void agent_lock (void)
{
  g_static_rec_mutex_lock (&agent_mutex);
}

void agent_unlock (void)
{
  g_static_rec_mutex_unlock (&agent_mutex);
}



140
141
142
StunUsageIceCompatibility
agent_to_ice_compatibility (NiceAgent *agent)
{
143
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
144
145
      STUN_USAGE_ICE_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
146
147
      STUN_USAGE_ICE_COMPATIBILITY_MSN :
      STUN_USAGE_ICE_COMPATIBILITY_DRAFT19;
148
149
150
151
152
153
}


StunUsageTurnCompatibility
agent_to_turn_compatibility (NiceAgent *agent)
{
154
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
155
156
      STUN_USAGE_TURN_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
157
158
      STUN_USAGE_TURN_COMPATIBILITY_MSN :
      agent->compatibility == NICE_COMPATIBILITY_WLM2009 ?
159
160
161
      STUN_USAGE_TURN_COMPATIBILITY_MSN : STUN_USAGE_TURN_COMPATIBILITY_DRAFT9;
}

162
163
164
NiceTurnSocketCompatibility
agent_to_turn_socket_compatibility (NiceAgent *agent)
{
165
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
166
167
168
      NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
      NICE_TURN_SOCKET_COMPATIBILITY_MSN :
169
170
      agent->compatibility == NICE_COMPATIBILITY_WLM2009 ?
      NICE_TURN_SOCKET_COMPATIBILITY_MSN :
171
172
173
      NICE_TURN_SOCKET_COMPATIBILITY_DRAFT9;
}

174
Stream *agent_find_stream (NiceAgent *agent, guint stream_id)
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
{
  GSList *i;

  for (i = agent->streams; i; i = i->next)
    {
      Stream *s = i->data;

      if (s->id == stream_id)
        return s;
    }

  return NULL;
}


190
191
gboolean
agent_find_component (
192
193
194
195
196
197
198
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  Stream **stream,
  Component **component)
{
  Stream *s;
199
  Component *c;
200

201
  s = agent_find_stream (agent, stream_id);
202

203
  if (s == NULL)
204
205
    return FALSE;

206
207
208
209
210
  c = stream_find_component_by_id (s, component_id);

  if (c == NULL)
    return FALSE;

211
212
213
214
  if (stream)
    *stream = s;

  if (component)
215
    *component = c;
216
217
218
219
220

  return TRUE;
}


Dafydd Harries's avatar
Dafydd Harries committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
static void
nice_agent_dispose (GObject *object);

static void
nice_agent_get_property (
  GObject *object,
  guint property_id,
  GValue *value,
  GParamSpec *pspec);

static void
nice_agent_set_property (
  GObject *object,
  guint property_id,
  const GValue *value,
  GParamSpec *pspec);


static void
nice_agent_class_init (NiceAgentClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->get_property = nice_agent_get_property;
  gobject_class->set_property = nice_agent_set_property;
  gobject_class->dispose = nice_agent_dispose;

248
  /* install properties */
249
250
251
252
253
254
  /**
   * NiceAgent:main-context:
   *
   * A GLib main context is needed for all timeouts used by libnice.
   * This is a property being set by the nice_agent_new() call.
   */
255
256
257
258
259
260
261
  g_object_class_install_property (gobject_class, PROP_MAIN_CONTEXT,
      g_param_spec_pointer (
         "main-context",
         "The GMainContext to use for timeouts",
         "The GMainContext to use for timeouts",
         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

262
263
264
265
266
267
268
  /**
   * NiceAgent:compatibility:
   *
   * The Nice agent can work in various compatibility modes depending on
   * what the application/peer needs.
   * <para> See also: #NiceCompatibility</para>
   */
269
270
271
272
273
  g_object_class_install_property (gobject_class, PROP_COMPATIBILITY,
      g_param_spec_uint (
         "compatibility",
         "ICE specification compatibility",
         "The compatibility mode for the agent",
274
275
         NICE_COMPATIBILITY_DRAFT19, NICE_COMPATIBILITY_LAST,
         NICE_COMPATIBILITY_DRAFT19,
276
277
         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

Dafydd Harries's avatar
Dafydd Harries committed
278
  g_object_class_install_property (gobject_class, PROP_STUN_SERVER,
279
      g_param_spec_string (
Dafydd Harries's avatar
Dafydd Harries committed
280
281
282
        "stun-server",
        "STUN server",
        "The STUN server used to obtain server-reflexive candidates",
283
        NULL,
Dafydd Harries's avatar
Dafydd Harries committed
284
285
        G_PARAM_READWRITE));

286
287
288
289
290
291
  g_object_class_install_property (gobject_class, PROP_STUN_SERVER_PORT,
      g_param_spec_uint (
        "stun-server-port",
        "STUN server port",
        "The STUN server used to obtain server-reflexive candidates",
        1, 65536, 
292
	1, /* not a construct property, ignored */
293
294
        G_PARAM_READWRITE));

295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE,
      g_param_spec_boolean (
        "controlling-mode",
        "ICE controlling mode",
        "Whether the agent is in controlling mode",
	FALSE, /* not a construct property, ignored */
        G_PARAM_READWRITE));

   g_object_class_install_property (gobject_class, PROP_FULL_MODE,
      g_param_spec_boolean (
        "full-mode",
        "ICE full mode",
        "Whether agent runs in ICE full mode",
	TRUE, /* use full mode by default */
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

311
312
313
314
315
  g_object_class_install_property (gobject_class, PROP_STUN_PACING_TIMER,
      g_param_spec_uint (
        "stun-pacing-timer",
        "STUN pacing timer",
        "Timer 'Ta' (msecs) used in the IETF ICE specification for pacing candidate gathering and sending of connectivity checks",
Youness Alaoui's avatar
Youness Alaoui committed
316
        1, 0xffffffff,
317
318
319
	NICE_AGENT_TIMER_TA_DEFAULT,
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

320
  /* note: according to spec recommendation in sect 5.7.3 (ID-19) */
321
322
323
324
325
  g_object_class_install_property (gobject_class, PROP_MAX_CONNECTIVITY_CHECKS,
      g_param_spec_uint (
        "max-connectivity-checks",
        "Maximum number of connectivity checks",
        "Upper limit for the total number of connectivity checks performed",
Youness Alaoui's avatar
Youness Alaoui committed
326
        0, 0xffffffff,
327
328
329
	0, /* default set in init */
        G_PARAM_READWRITE));

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
  g_object_class_install_property (gobject_class, PROP_PROXY_IP,
      g_param_spec_string (
        "proxy-ip",
        "Proxy server IP",
        "The proxy server used to bypass a proxy firewall",
        NULL,
        G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_PROXY_PORT,
      g_param_spec_uint (
        "proxy-port",
        "Proxy server port",
        "The Proxy server used to bypass a proxy firewall",
        1, 65536,
	1,
        G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_PROXY_TYPE,
      g_param_spec_uint (
         "proxy-type",
         "Type of proxy to use",
         "The type of proxy set in the proxy-ip property",
         NICE_PROXY_TYPE_NONE, NICE_PROXY_TYPE_LAST,
         NICE_PROXY_TYPE_NONE,
         G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_PROXY_USERNAME,
      g_param_spec_string (
        "proxy-username",
        "Proxy server username",
        "The username used to authenticate with the proxy",
        NULL,
        G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_PROXY_PASSWORD,
      g_param_spec_string (
        "proxy-password",
        "Proxy server password",
        "The password used to authenticate with the proxy",
        NULL,
        G_PARAM_READWRITE));

Youness Alaoui's avatar
Youness Alaoui committed
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
#ifdef HAVE_GUPNP
   g_object_class_install_property (gobject_class, PROP_UPNP,
      g_param_spec_boolean (
        "upnp",
        "Use UPnP",
        "Whether the agent should use UPnP to open a port in the router and "
        "get the external IP",
	TRUE, /* enable UPnP by default */
        G_PARAM_READWRITE| G_PARAM_CONSTRUCT));

  g_object_class_install_property (gobject_class, PROP_UPNP_TIMEOUT,
      g_param_spec_uint (
        "upnp-timeout",
        "Timeout for UPnP discovery",
        "The maximum amount of time to wait for UPnP discovery to finish before "
        "signaling the candidate-gathering-done signal",
        100, 60000,
389
	200,
Youness Alaoui's avatar
Youness Alaoui committed
390
391
392
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
#endif

393
394
  /* install signals */

395
396
397
398
399
400
401
402
  /**
   * NiceAgent::component-state-changed
   * @stream_id: The ID of the stream
   * @component_id: The ID of the component
   * @state: The #NiceComponentState of the component
   *
   * This signal is fired whenever a component's state changes
   */
403
404
405
406
  signals[SIGNAL_COMPONENT_STATE_CHANGED] =
      g_signal_new (
          "component-state-changed",
          G_OBJECT_CLASS_TYPE (klass),
407
          G_SIGNAL_RUN_LAST,
408
409
410
411
412
413
414
415
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT_UINT_UINT,
          G_TYPE_NONE,
          3,
          G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
          G_TYPE_INVALID);
416

417
418
419
420
421
422
423
  /**
   * NiceAgent::candidate-gathering-done:
   * @stream_id: The ID of the stream
   *
   * This signal is fired whenever a stream has finished gathering its
   * candidates after a call to nice_agent_gather_candidates()
   */
424
425
426
427
  signals[SIGNAL_CANDIDATE_GATHERING_DONE] =
      g_signal_new (
          "candidate-gathering-done",
          G_OBJECT_CLASS_TYPE (klass),
428
          G_SIGNAL_RUN_LAST,
429
430
431
          0,
          NULL,
          NULL,
432
          agent_marshal_VOID__UINT,
433
          G_TYPE_NONE,
434
435
          1,
          G_TYPE_UINT, G_TYPE_INVALID);
436

437
438
439
440
441
442
443
444
445
446
  /**
   * NiceAgent::new-selected-pair
   * @stream_id: The ID of the stream
   * @component_id: The ID of the component
   * @lfoundation: The local foundation of the selected candidate pair
   * @rfoundation: The remote foundation of the selected candidate pair
   *
   * This signal is fired once a candidate pair is selected for data transfer for
   * a stream's component
   */
447
448
449
450
  signals[SIGNAL_NEW_SELECTED_PAIR] =
      g_signal_new (
          "new-selected-pair",
          G_OBJECT_CLASS_TYPE (klass),
451
          G_SIGNAL_RUN_LAST,
452
453
454
455
456
457
458
459
460
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT_UINT_STRING_STRING,
          G_TYPE_NONE,
          4,
          G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING,
          G_TYPE_INVALID);

461
462
463
464
465
466
467
468
469
  /**
   * NiceAgent::new-candidate
   * @stream_id: The ID of the stream
   * @component_id: The ID of the component
   * @foundation: The foundation of the new candidate
   *
   * This signal is fired when the agent discovers a new candidate
   * <para> See also: #NiceAgent::candidates-gathering-done </para>
   */
470
471
472
473
  signals[SIGNAL_NEW_CANDIDATE] =
      g_signal_new (
          "new-candidate",
          G_OBJECT_CLASS_TYPE (klass),
474
          G_SIGNAL_RUN_LAST,
475
476
477
478
479
480
481
482
483
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT_UINT_STRING,
          G_TYPE_NONE,
          3,
          G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING,
          G_TYPE_INVALID);

484
485
486
487
488
489
490
491
492
493
  /**
   * NiceAgent::new-remote-candidate
   * @stream_id: The ID of the stream
   * @component_id: The ID of the component
   * @foundation: The foundation of the new candidate
   *
   * This signal is fired when the agent discovers a new remote candidate.
   * This can happen with peer reflexive candidates.
   * <para> See also: #NiceAgent::candidates-gathering-done </para>
   */
494
495
496
497
  signals[SIGNAL_NEW_REMOTE_CANDIDATE] =
      g_signal_new (
          "new-remote-candidate",
          G_OBJECT_CLASS_TYPE (klass),
498
          G_SIGNAL_RUN_LAST,
499
500
501
502
503
504
505
506
507
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT_UINT_STRING,
          G_TYPE_NONE,
          3,
          G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING,
          G_TYPE_INVALID);

508
509
510
511
512
513
514
  /**
   * NiceAgent::initial-binding-request-received
   * @stream_id: The ID of the stream
   *
   * This signal is fired when we received our first binding request from
   * the peer.
   */
515
516
517
518
  signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED] =
      g_signal_new (
          "initial-binding-request-received",
          G_OBJECT_CLASS_TYPE (klass),
519
          G_SIGNAL_RUN_LAST,
520
521
522
523
524
525
526
527
528
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT,
          G_TYPE_NONE,
          1,
          G_TYPE_UINT,
          G_TYPE_INVALID);

529
530
  /* Init debug options depending on env variables */
  nice_debug_init ();
Dafydd Harries's avatar
Dafydd Harries committed
531
532
}

533
534
535
536
static void priv_generate_tie_breaker (NiceAgent *agent) 
{
  nice_rng_generate_bytes (agent->rng, 8, (gchar*)&agent->tie_breaker);
}
Dafydd Harries's avatar
Dafydd Harries committed
537
538
539
540
541
542

static void
nice_agent_init (NiceAgent *agent)
{
  agent->next_candidate_id = 1;
  agent->next_stream_id = 1;
543

544
  /* set defaults; not construct params, so set here */
545
  agent->stun_server_port = DEFAULT_STUN_PORT;
546
  agent->controlling_mode = TRUE;
547
  agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT;
548
549

  agent->discovery_list = NULL;
550
  agent->discovery_unsched_items = 0;
551
552
553
  agent->discovery_timer_source = NULL;
  agent->conncheck_timer_source = NULL;
  agent->keepalive_timer_source = NULL;
554
  agent->refresh_list = NULL;
555
  agent->media_after_tick = FALSE;
556
  agent->software_attribute = NULL;
557

558
  agent->compatibility = NICE_COMPATIBILITY_DRAFT19;
559

560
  stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
561
      STUN_COMPATIBILITY_RFC5389,
562
563
564
      STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
      STUN_AGENT_USAGE_USE_FINGERPRINT);

Dafydd Harries's avatar
Dafydd Harries committed
565
  agent->rng = nice_rng_new ();
566
  priv_generate_tie_breaker (agent);
Dafydd Harries's avatar
Dafydd Harries committed
567
568
569
}


570
NICEAPI_EXPORT NiceAgent *
571
nice_agent_new (GMainContext *ctx, NiceCompatibility compat)
Dafydd Harries's avatar
Dafydd Harries committed
572
{
573
574
  NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
      "compatibility", compat,
575
      "main-context", ctx,
Dafydd Harries's avatar
Dafydd Harries committed
576
      NULL);
577
578

  return agent;
Dafydd Harries's avatar
Dafydd Harries committed
579
580
581
582
583
584
585
586
587
588
589
}


static void
nice_agent_get_property (
  GObject *object,
  guint property_id,
  GValue *value,
  GParamSpec *pspec)
{
  NiceAgent *agent = NICE_AGENT (object);
Dafydd Harries's avatar
Dafydd Harries committed
590

591
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
592

Dafydd Harries's avatar
Dafydd Harries committed
593
594
  switch (property_id)
    {
595
596
597
598
    case PROP_MAIN_CONTEXT:
      g_value_set_pointer (value, agent->main_context);
      break;

599
600
601
602
    case PROP_COMPATIBILITY:
      g_value_set_uint (value, agent->compatibility);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
603
    case PROP_STUN_SERVER:
604
      g_value_set_string (value, agent->stun_server_ip);
605
      break;
606
607
608

    case PROP_STUN_SERVER_PORT:
      g_value_set_uint (value, agent->stun_server_port);
609
      break;
610

611
612
613
614
615
616
617
    case PROP_CONTROLLING_MODE:
      g_value_set_boolean (value, agent->controlling_mode);
      break;

    case PROP_FULL_MODE:
      g_value_set_boolean (value, agent->full_mode);
      break;
Dafydd Harries's avatar
Dafydd Harries committed
618

619
620
621
622
    case PROP_STUN_PACING_TIMER:
      g_value_set_uint (value, agent->timer_ta);
      break;

623
624
625
626
627
    case PROP_MAX_CONNECTIVITY_CHECKS:
      g_value_set_uint (value, agent->max_conn_checks);
      /* XXX: should we prune the list of already existing checks? */
      break;

628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
    case PROP_PROXY_IP:
      g_value_set_string (value, agent->proxy_ip);
      break;

    case PROP_PROXY_PORT:
      g_value_set_uint (value, agent->proxy_port);
      break;

    case PROP_PROXY_TYPE:
      g_value_set_uint (value, agent->proxy_type);
      break;

    case PROP_PROXY_USERNAME:
      g_value_set_string (value, agent->proxy_username);
      break;

    case PROP_PROXY_PASSWORD:
      g_value_set_string (value, agent->proxy_password);
      break;

Youness Alaoui's avatar
Youness Alaoui committed
648
649
650
651
652
653
654
655
656
657
#ifdef HAVE_GUPNP
    case PROP_UPNP:
      g_value_set_boolean (value, agent->upnp_enabled);
      break;

    case PROP_UPNP_TIMEOUT:
      g_value_set_uint (value, agent->upnp_timeout);
      break;
#endif

Dafydd Harries's avatar
Dafydd Harries committed
658
659
660
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
661

662
  agent_unlock();
Dafydd Harries's avatar
Dafydd Harries committed
663
664
665
666
667
668
669
670
671
672
673
674
}


static void
nice_agent_set_property (
  GObject *object,
  guint property_id,
  const GValue *value,
  GParamSpec *pspec)
{
  NiceAgent *agent = NICE_AGENT (object);

675
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
676

Dafydd Harries's avatar
Dafydd Harries committed
677
678
  switch (property_id)
    {
679
680
681
682
    case PROP_MAIN_CONTEXT:
      agent->main_context = g_value_get_pointer (value);
      break;

683
684
    case PROP_COMPATIBILITY:
      agent->compatibility = g_value_get_uint (value);
685
      if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
686
687
688
689
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
Youness Alaoui's avatar
Youness Alaoui committed
690
691
692
      } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
693
694
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_FORCE_VALIDATER);
695
696
697
698
699
700
701
702
703
704
      } else if (agent->compatibility == NICE_COMPATIBILITY_WLM2009) {
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_WLM2009,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_USE_FINGERPRINT);
      } else {
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC5389,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_USE_FINGERPRINT);
705
      }
706
      stun_agent_set_software (&agent->stun_agent, agent->software_attribute);
707

708
709
      break;

Dafydd Harries's avatar
Dafydd Harries committed
710
    case PROP_STUN_SERVER:
711
712
713
714
715
716
717
      agent->stun_server_ip = g_value_dup_string (value);
      break;

    case PROP_STUN_SERVER_PORT:
      agent->stun_server_port = g_value_get_uint (value);
      break;

718
719
720
721
722
723
724
725
    case PROP_CONTROLLING_MODE:
      agent->controlling_mode = g_value_get_boolean (value);
      break;

    case PROP_FULL_MODE:
      agent->full_mode = g_value_get_boolean (value);
      break;

726
727
728
729
    case PROP_STUN_PACING_TIMER:
      agent->timer_ta = g_value_get_uint (value);
      break;

730
731
732
733
    case PROP_MAX_CONNECTIVITY_CHECKS:
      agent->max_conn_checks = g_value_get_uint (value);
      break;

734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
    case PROP_PROXY_IP:
      agent->proxy_ip = g_value_dup_string (value);
      break;

    case PROP_PROXY_PORT:
      agent->proxy_port = g_value_get_uint (value);
      break;

    case PROP_PROXY_TYPE:
      agent->proxy_type = g_value_get_uint (value);
      break;

    case PROP_PROXY_USERNAME:
      agent->proxy_username = g_value_dup_string (value);
      break;

    case PROP_PROXY_PASSWORD:
      agent->proxy_password = g_value_dup_string (value);
      break;

Youness Alaoui's avatar
Youness Alaoui committed
754
755
756
757
758
759
760
761
762
#ifdef HAVE_GUPNP
    case PROP_UPNP_TIMEOUT:
      agent->upnp_timeout = g_value_get_uint (value);
      break;

    case PROP_UPNP:
      agent->upnp_enabled = g_value_get_boolean (value);
      break;
#endif
Dafydd Harries's avatar
Dafydd Harries committed
763
764
765
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
766

767
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
768

Dafydd Harries's avatar
Dafydd Harries committed
769
770
}

771
772
773
774
775
776
777
778
779
780
781
782
void agent_gathering_done (NiceAgent *agent)
{

  GSList *i, *j, *k, *l, *m;

  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;
    for (j = stream->components; j; j = j->next) {
      Component *component = j->data;

      for (k = component->local_candidates; k; k = k->next) {
        NiceCandidate *local_candidate = k->data;
Youness Alaoui's avatar
Youness Alaoui committed
783
784
785
786
787
788
789
790
791
	{
	  gchar tmpbuf[INET6_ADDRSTRLEN];
	  nice_address_to_string (&local_candidate->addr, tmpbuf);
          nice_debug ("Agent %p: gathered local candidate : [%s]:%u"
              " for s%d/c%d. U/P '%s'/'%s'", agent,
              tmpbuf, nice_address_get_port (&local_candidate->addr),
              local_candidate->stream_id, local_candidate->component_id,
              local_candidate->username, local_candidate->password);
	}
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
        for (l = component->remote_candidates; l; l = l->next) {
          NiceCandidate *remote_candidate = l->data;

          for (m = stream->conncheck_list; m; m = m->next) {
            CandidateCheckPair *p = m->data;

            if (p->local == local_candidate && p->remote == remote_candidate)
              break;
          }
          if (m == NULL) {
            conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
          }
        }
      }
    }
  }

809
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
810
811
812
813
  if (agent->discovery_timer_source == NULL &&
      agent->upnp_timer_source == NULL) {
    agent_signal_gathering_done (agent);
  }
814
815
816
#else
  agent_signal_gathering_done (agent);
#endif
817
818
}

819
void agent_signal_gathering_done (NiceAgent *agent)
820
{
821
822
823
824
825
826
  GSList *i;

  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;
    if (stream->gathering) {
      stream->gathering = FALSE;
827
      g_signal_emit (agent, signals[SIGNAL_CANDIDATE_GATHERING_DONE], 0, stream->id);
828
829
    }
  }
830
}
Dafydd Harries's avatar
Dafydd Harries committed
831

832
void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream)
833
{
834
835
836
837
  if (stream->initial_binding_request_received != TRUE) {
    stream->initial_binding_request_received = TRUE;
    g_signal_emit (agent, signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED], 0, stream->id);
  }
838
839
}

840
void agent_signal_new_selected_pair (NiceAgent *agent, guint stream_id, guint component_id, const gchar *local_foundation, const gchar *remote_foundation)
841
842
{
  Component *component;
843
844
  gchar *lf_copy;
  gchar *rf_copy;
845

846
  if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
847
848
    return;

849
850
851
  lf_copy = g_strdup (local_foundation);
  rf_copy = g_strdup (remote_foundation);

852
  if (component->selected_pair.local->type == NICE_CANDIDATE_TYPE_RELAYED) {
853
    nice_turn_socket_set_peer (component->selected_pair.local->sockptr,
854
855
                                   &component->selected_pair.remote->addr);
  }
856
857
858
859
860
861

  g_signal_emit (agent, signals[SIGNAL_NEW_SELECTED_PAIR], 0,
      stream_id, component_id, lf_copy, rf_copy);

  g_free (lf_copy);
  g_free (rf_copy);
862
863
}

864
void agent_signal_new_candidate (NiceAgent *agent, NiceCandidate *candidate)
865
{
866
867
868
  g_signal_emit (agent, signals[SIGNAL_NEW_CANDIDATE], 0,
		 candidate->stream_id,
		 candidate->component_id,
869
		 candidate->foundation);
870
871
}

872
873
874
875
876
877
878
879
void agent_signal_new_remote_candidate (NiceAgent *agent, NiceCandidate *candidate)
{
  g_signal_emit (agent, signals[SIGNAL_NEW_REMOTE_CANDIDATE], 0, 
		 candidate->stream_id, 
		 candidate->component_id, 
		 candidate->foundation);
}

880
void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state)
881
882
883
{
  Component *component;

884
  if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
885
886
    return;

887
  if (component->state != state && state < NICE_COMPONENT_STATE_LAST) {
888
    nice_debug ("Agent %p : stream %u component %u STATE-CHANGE %u -> %u.", agent,
889
	     stream_id, component_id, component->state, state);
890

891
    component->state = state;
892
893

    g_signal_emit (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], 0,
894
		   stream_id, component_id, state);
895
896
897
  }
}

898
899
900
guint64
agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandidate *remote)
{
Olivier Crête's avatar
Olivier Crête committed
901
  if (agent->controlling_mode)
902
    return nice_candidate_pair_priority (local->priority, remote->priority);
Olivier Crête's avatar
Olivier Crête committed
903
904
  else
    return nice_candidate_pair_priority (remote->priority, local->priority);
905
906
}

907
static gboolean
908
priv_add_new_candidate_discovery_stun (NiceAgent *agent,
909
    NiceSocket *socket, NiceAddress server,
910
    Stream *stream, guint component_id)
911
912
913
914
915
916
{
  CandidateDiscovery *cdisco;
  GSList *modified_list;

  /* note: no need to check for redundant candidates, as this is
   *       done later on in the process */
917

918
919
920
  cdisco = g_slice_new0 (CandidateDiscovery);
  if (cdisco) {
    modified_list = g_slist_append (agent->discovery_list, cdisco);
921

922
    if (modified_list) {
923
      cdisco->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
924
      cdisco->nicesock = socket;
925
      cdisco->server = server;
926
927
928
      cdisco->stream = stream;
      cdisco->component = stream_find_component_by_id (stream, component_id);
      cdisco->agent = agent;
929
930
931
      stun_agent_init (&cdisco->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
          STUN_COMPATIBILITY_RFC3489, 0);

932
933
      nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p\n",
          agent, cdisco);
934
935
      agent->discovery_list = modified_list;
      ++agent->discovery_unsched_items;
936
937
938
939
940
941
942
943
    }

    return TRUE;
  }

  return FALSE;
}

944
945
static gboolean
priv_add_new_candidate_discovery_turn (NiceAgent *agent,
946
    NiceSocket *socket, TurnServer *turn,
947
    Stream *stream, guint component_id)
948
949
950
{
  CandidateDiscovery *cdisco;
  GSList *modified_list;
951
  GSList *socket_modified_list;
952
953
954
955
956
957
958
959
960

  /* note: no need to check for redundant candidates, as this is
   *       done later on in the process */

  cdisco = g_slice_new0 (CandidateDiscovery);
  if (cdisco) {
    modified_list = g_slist_append (agent->discovery_list, cdisco);

    if (modified_list) {
961
962
      Component *component = stream_find_component_by_id (stream, component_id);

963
      cdisco->type = NICE_CANDIDATE_TYPE_RELAYED;
964

965
      if (turn->type ==  NICE_RELAY_TYPE_TURN_UDP) {
966
967
968
969
970
971
972
        if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
          NiceAddress addr = socket->addr;
          NiceSocket *new_socket;
          nice_address_set_port (&addr, 0);

          new_socket = nice_udp_bsd_socket_new (&addr);
          if (new_socket) {
973
            _priv_set_socket_tos (agent, new_socket, stream->tos);
974
975
            agent_attach_stream_component_socket (agent, stream,
                component, new_socket);
976
977
            socket_modified_list = g_slist_append (component->sockets, new_socket);
            if (socket_modified_list) {
978
              /* success: store a pointer to the sockaddr */
979
              component->sockets = socket_modified_list;
980
981
982
983
984
985
986
              socket = new_socket;
            } else {
              nice_socket_free (new_socket);
            }
          }
        }
        cdisco->nicesock = socket;
987
      } else {
988
989
990
991
992
993
994
995
996
        NiceAddress proxy_server;
        socket = NULL;

        if (agent->proxy_type != NICE_PROXY_TYPE_NONE &&
            agent->proxy_ip != NULL &&
            nice_address_set_from_string (&proxy_server, agent->proxy_ip)) {
          nice_address_set_port (&proxy_server, agent->proxy_port);
          socket = nice_tcp_bsd_socket_new (agent, component->ctx, &proxy_server);

Youness Alaoui's avatar
Youness Alaoui committed
997
          if (socket) {
998
            _priv_set_socket_tos (agent, socket, stream->tos);
Youness Alaoui's avatar
Youness Alaoui committed
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
            if (agent->proxy_type == NICE_PROXY_TYPE_SOCKS5) {
              socket = nice_socks5_socket_new (socket, &turn->server,
                  agent->proxy_username, agent->proxy_password);
            } else if (agent->proxy_type == NICE_PROXY_TYPE_HTTP){
              socket = nice_http_socket_new (socket, &turn->server,
                  agent->proxy_username, agent->proxy_password);
            } else {
              nice_socket_free (socket);
              socket = NULL;
            }
1009
1010
1011
1012
1013
          }

        }
        if (socket == NULL) {
          socket = nice_tcp_bsd_socket_new (agent, component->ctx, &turn->server);
1014
          _priv_set_socket_tos (agent, socket, stream->tos);
1015
        }
1016
1017
1018
1019
1020
1021
1022
        if (turn->type ==  NICE_RELAY_TYPE_TURN_TLS &&
            agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
          socket = nice_pseudossl_socket_new (agent, socket);
        }
        cdisco->nicesock = nice_tcp_turn_socket_new (agent, socket,
            agent_to_turn_socket_compatibility (agent));

1023
1024
1025
1026
1027
        if (!cdisco->nicesock) {
          agent->discovery_list = g_slist_remove (modified_list, cdisco);
          g_slice_free (CandidateDiscovery, cdisco);
          return FALSE;
        }
Youness Alaoui's avatar
Youness Alaoui committed
1028

1029
        agent_attach_stream_component_socket (agent, stream,
Youness Alaoui's avatar
Youness Alaoui committed
1030
            component, cdisco->nicesock);
1031
1032
1033
1034
1035
        socket_modified_list = g_slist_append (component->sockets, cdisco->nicesock);
        if (socket_modified_list) {
          /* success: store a pointer to the sockaddr */
          component->sockets = socket_modified_list;
        }
1036
1037
1038
1039
      }
      cdisco->turn = turn;
      cdisco->server = turn->server;

1040
1041
1042
1043
      cdisco->stream = stream;
      cdisco->component = stream_find_component_by_id (stream, component_id);
      cdisco->agent = agent;

1044
      if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
1045
        stun_agent_init (&cdisco->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
1046
1047
1048
1049
1050
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
      } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
                 agent->compatibility == NICE_COMPATIBILITY_WLM2009) {
1051
        stun_agent_init (&cdisco->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
1052
1053
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS);
1054
      } else {
1055
        stun_agent_init (&cdisco->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
1056
1057
1058
            STUN_COMPATIBILITY_RFC5389,
            STUN_AGENT_USAGE_ADD_SOFTWARE |
            STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS);
1059
      }
1060
      stun_agent_set_software (&cdisco->stun_agent, agent->software_attribute);
1061

Youness Alaoui's avatar
Youness Alaoui committed
1062
1063
      nice_debug ("Agent %p : Adding new relay-rflx candidate discovery %p\n",
          agent, cdisco);
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
      agent->discovery_list = modified_list;
      ++agent->discovery_unsched_items;
    }

    return TRUE;
  }

  return FALSE;
}

1074
NICEAPI_EXPORT guint
1075
1076
nice_agent_add_stream (
  NiceAgent *agent,
1077
  guint n_components)
Dafydd Harries's avatar
Dafydd Harries committed
1078
1079
{
  Stream *stream;
1080
  GSList *modified_list = NULL;
1081
  guint ret = 0;
1082

1083
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
1084

1085
  stream = stream_new (n_components);
1086
1087
1088
1089
  if (stream) {
    modified_list = g_slist_append (agent->streams, stream);
    if (modified_list) {
      stream->id = agent->next_stream_id++;
1090
      nice_debug ("Agent %p : allocating stream id %u (%p)", agent, stream->id, stream);
1091

1092
      stream_initialize_credentials (stream, agent->rng);
1093
1094
1095
1096
1097
1098

      agent->streams = modified_list;
    }
    else
      stream_free (stream);
  }
1099

1100
1101
  ret = stream->id;

1102
  agent_unlock();
1103
1104
1105
1106
  return ret;
}


1107
1108
NICEAPI_EXPORT gboolean
nice_agent_set_relay_info(NiceAgent *agent,
1109
1110
    guint stream_id, guint component_id,
    const gchar *server_ip, guint server_port,
1111
1112
    const gchar *username, const gchar *password,
    NiceRelayType type)
1113
1114
1115
1116
{

  Component *component = NULL;

1117
1118
1119
1120
1121
1122
  g_return_val_if_fail (server_ip, FALSE);
  g_return_val_if_fail (server_port, FALSE);
  g_return_val_if_fail (username, FALSE);
  g_return_val_if_fail (password, FALSE);
  g_return_val_if_fail (type <= NICE_PROXY_TYPE_LAST, FALSE);

1123
  agent_lock();
1124
1125

  if (agent_find_component (agent, stream_id, component_id, NULL, &component)) {
1126
1127
1128
1129
1130
1131
1132
    TurnServer *turn = g_slice_new0 (TurnServer);
    nice_address_init (&turn->server);

    if (nice_address_set_from_string (&turn->server, server_ip)) {
      nice_address_set_port (&turn->server, server_port);
    } else {
      g_slice_free (TurnServer, turn);
1133
      agent_unlock();
1134
      return FALSE;
1135
1136
    }

1137

1138
1139
1140
    turn->username = g_strdup (username);
    turn->password = g_strdup (password);
    turn->type = type;
1141

Youness Alaoui's avatar
Youness Alaoui committed
1142
1143
1144
    nice_debug ("Agent %p: added relay server [%s]:%d of type %d", agent,
        server_ip, server_port, type);

1145
    component->turn_servers = g_list_append (component->turn_servers, turn);
1146
  }
1147

1148
  agent_unlock();
1149
  return TRUE;
1150
1151
}

Youness Alaoui's avatar
Youness Alaoui committed
1152
1153
1154
1155
1156
1157
1158
#ifdef HAVE_GUPNP

static gboolean priv_upnp_timeout_cb (gpointer user_data)
{
  NiceAgent *agent = (NiceAgent*)user_data;
  GSList *i;

1159
1160
1161
1162
1163
1164
  agent_lock();

  if (g_source_is_destroyed (g_main_current_source ())) {
    agent_unlock ();
    return FALSE;
  }
Youness Alaoui's avatar
Youness Alaoui committed
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182

  nice_debug ("Agent %p : UPnP port mapping timed out", agent);

  for (i = agent->upnp_mapping; i; i = i->next) {
    NiceAddress *a = i->data;
    nice_address_free (a);
  }
  g_slist_free (agent->upnp_mapping);
  agent->upnp_mapping = NULL;

  if (agent->upnp_timer_source != NULL) {
    g_source_destroy (agent->upnp_timer_source);
    g_source_unref (agent->upnp_timer_source);
    agent->upnp_timer_source = NULL;
  }

  agent_gathering_done (agent);

1183
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
  return FALSE;
}

static void _upnp_mapped_external_port (GUPnPSimpleIgd *self, gchar *proto,
    gchar *external_ip, gchar *replaces_external_ip, guint external_port,
    gchar *local_ip, guint local_port, gchar *description, gpointer user_data)
{
  NiceAgent *agent = (NiceAgent*)user_data;
  NiceAddress localaddr;
  NiceAddress externaddr;

  GSList *i, *j, *k;

1197
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
1198
1199
1200
1201

  nice_debug ("Agent %p : Sucessfully mapped %s:%d to %s:%d", agent, local_ip,
      local_port, external_ip, external_port);

1202
1203
  if (!nice_address_set_from_string (&localaddr, local_ip))
    goto end;
Youness Alaoui's avatar
Youness Alaoui committed
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
  nice_address_set_port (&localaddr, local_port);

  for (i = agent->upnp_mapping; i; i = i->next) {
    NiceAddress *addr = i->data;
    if (nice_address_equal (&localaddr, addr)) {
      agent->upnp_mapping = g_slist_remove (agent->upnp_mapping, addr);
      nice_address_free (addr);
      break;
    }
  }

1215
1216
1217
  if (!nice_address_set_from_string (&externaddr, external_ip))
    goto end;
  nice_address_set_port (&externaddr, external_port);
Youness Alaoui's avatar
Youness Alaoui committed
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248

  for (i = agent->streams; i; i = i->next) {
    Stream *stream = i->data;
    for (j = stream->components; j; j = j->next) {
      Component *component = j->data;
      for (k = component->local_candidates; k; k = k->next) {
        NiceCandidate *local_candidate = k->data;

        if (nice_address_equal (&localaddr, &local_candidate->base_addr)) {
          discovery_add_server_reflexive_candidate (
              agent,
              stream->id,
              component->id,
              &externaddr,
              local_candidate->sockptr);
          goto end;
        }
      }
    }
  }

 end:
  if (g_slist_length (agent->upnp_mapping)) {
    if (agent->upnp_timer_source != NULL) {
      g_source_destroy (agent->upnp_timer_source);
      g_source_unref (agent->upnp_timer_source);
      agent->upnp_timer_source = NULL;
    }
    agent_gathering_done (agent);
  }

1249
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
}

static void _upnp_error_mapping_port (GUPnPSimpleIgd *self, GError *error,
    gchar *proto, guint external_port, gchar *local_ip, guint local_port,
    gchar *description, gpointer user_data)
{
  NiceAgent *agent = (NiceAgent*)user_data;
  NiceAddress localaddr;
  GSList *i;

1260
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
1261
1262
1263

  nice_debug ("Agent %p : Error mapping %s:%d to %d (%d) : %s", agent, local_ip,
      local_port, external_port, error->domain, error->message);
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
  if (nice_address_set_from_string (&localaddr, local_ip)) {
    nice_address_set_port (&localaddr, local_port);

    for (i = agent->upnp_mapping; i; i = i->next) {
      NiceAddress *addr = i->data;
      if (nice_address_equal (&localaddr, addr)) {
        agent->upnp_mapping = g_slist_remove (agent->upnp_mapping, addr);
        nice_address_free (addr);
        break;
      }
Youness Alaoui's avatar
Youness Alaoui committed
1274
1275
    }

1276
1277
1278
1279
1280
1281
1282
    if (g_slist_length (agent->upnp_mapping)) {
      if (agent->upnp_timer_source != NULL) {
        g_source_destroy (agent->upnp_timer_source);
        g_source_unref (agent->upnp_timer_source);
        agent->upnp_timer_source = NULL;
      }
      agent_gathering_done (agent);
Youness Alaoui's avatar
Youness Alaoui committed
1283
1284
1285
    }
  }

1286
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
1287
1288
1289
}

#endif
1290