agent.c 53.4 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
42
43
44
45
46
47
/**
 * @file agent.c
 * @brief ICE agent API implementation
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

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

51
#include <sys/select.h>
52
53
54
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
Dafydd Harries's avatar
Dafydd Harries committed
55
56
57

#include <glib.h>

58
59
#include "debug.h"

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

70
#include "stream.h"
Dafydd Harries's avatar
Dafydd Harries committed
71

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

78

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

enum
{
83
  PROP_COMPATIBILITY = 1,
84
  PROP_MAIN_CONTEXT,
85
  PROP_STUN_SERVER,
86
  PROP_STUN_SERVER_PORT,
87
  PROP_CONTROLLING_MODE,
88
  PROP_FULL_MODE,
89
90
  PROP_STUN_PACING_TIMER,
  PROP_MAX_CONNECTIVITY_CHECKS
Dafydd Harries's avatar
Dafydd Harries committed
91
92
93
};


94
95
96
enum
{
  SIGNAL_COMPONENT_STATE_CHANGED,
97
  SIGNAL_CANDIDATE_GATHERING_DONE,
98
99
  SIGNAL_NEW_SELECTED_PAIR,
  SIGNAL_NEW_CANDIDATE,
100
  SIGNAL_NEW_REMOTE_CANDIDATE,
101
  SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED,
102
103
104
105
106
107
  N_SIGNALS,
};


static guint signals[N_SIGNALS];

108
109
static gboolean priv_attach_stream_component (NiceAgent *agent,
    Stream *stream,
110
    Component *component);
Youness Alaoui's avatar
Youness Alaoui committed
111
static void priv_detach_stream_component (Stream *stream, Component *component);
112

113
Stream *agent_find_stream (NiceAgent *agent, guint stream_id)
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
{
  GSList *i;

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

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

  return NULL;
}


129
130
gboolean
agent_find_component (
131
132
133
134
135
136
137
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  Stream **stream,
  Component **component)
{
  Stream *s;
138
  Component *c;
139

140
  s = agent_find_stream (agent, stream_id);
141

142
  if (s == NULL)
143
144
    return FALSE;

145
146
147
148
149
  c = stream_find_component_by_id (s, component_id);

  if (c == NULL)
    return FALSE;

150
151
152
153
  if (stream)
    *stream = s;

  if (component)
154
    *component = c;
155
156
157
158
159

  return TRUE;
}


Dafydd Harries's avatar
Dafydd Harries committed
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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;

187
  /* install properties */
Youness Alaoui's avatar
Youness Alaoui committed
188

189
190
191
192
193
194
195
  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));

196
197
198
199
200
  g_object_class_install_property (gobject_class, PROP_COMPATIBILITY,
      g_param_spec_uint (
         "compatibility",
         "ICE specification compatibility",
         "The compatibility mode for the agent",
201
202
         NICE_COMPATIBILITY_DRAFT19, NICE_COMPATIBILITY_LAST,
         NICE_COMPATIBILITY_DRAFT19,
203
204
         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

Dafydd Harries's avatar
Dafydd Harries committed
205
  g_object_class_install_property (gobject_class, PROP_STUN_SERVER,
206
      g_param_spec_string (
Dafydd Harries's avatar
Dafydd Harries committed
207
208
209
        "stun-server",
        "STUN server",
        "The STUN server used to obtain server-reflexive candidates",
210
        NULL,
Dafydd Harries's avatar
Dafydd Harries committed
211
212
        G_PARAM_READWRITE));

213
214
215
216
217
218
  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, 
219
	1, /* not a construct property, ignored */
220
221
        G_PARAM_READWRITE));

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  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));

238
239
240
241
242
243
244
245
246
  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",
        1, 0xffffffff, 
	NICE_AGENT_TIMER_TA_DEFAULT,
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

247
  /* note: according to spec recommendation in sect 5.7.3 (ID-19) */
248
249
250
251
252
253
254
255
256
  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",
        0, 0xffffffff, 
	0, /* default set in init */
        G_PARAM_READWRITE));

257
258
  /* install signals */

259
  /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer self) */
260
261
262
263
264
265
266
267
268
269
270
271
272
  signals[SIGNAL_COMPONENT_STATE_CHANGED] =
      g_signal_new (
          "component-state-changed",
          G_OBJECT_CLASS_TYPE (klass),
          G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
          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);
273

274
  /* signature: void cb(NiceAgent *agent, gpointer self) */
275
276
277
278
279
280
281
282
283
284
285
286
  signals[SIGNAL_CANDIDATE_GATHERING_DONE] =
      g_signal_new (
          "candidate-gathering-done",
          G_OBJECT_CLASS_TYPE (klass),
          G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
          0,
          NULL,
          NULL,
          agent_marshal_VOID__VOID,
          G_TYPE_NONE,
          0,
          G_TYPE_INVALID);
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318

 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, 
                gchar *lfoundation, gchar* rfoundation, gpointer self) */
  signals[SIGNAL_NEW_SELECTED_PAIR] =
      g_signal_new (
          "new-selected-pair",
          G_OBJECT_CLASS_TYPE (klass),
          G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
          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);

 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation) */
  signals[SIGNAL_NEW_CANDIDATE] =
      g_signal_new (
          "new-candidate",
          G_OBJECT_CLASS_TYPE (klass),
          G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
          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);

319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation) */
  signals[SIGNAL_NEW_REMOTE_CANDIDATE] =
      g_signal_new (
          "new-remote-candidate",
          G_OBJECT_CLASS_TYPE (klass),
          G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
          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);

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  /* signature: void cb(NiceAgent *agent, guint stream_id, gpointer self) */
  signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED] =
      g_signal_new (
          "initial-binding-request-received",
          G_OBJECT_CLASS_TYPE (klass),
          G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT,
          G_TYPE_NONE,
          1,
          G_TYPE_UINT,
          G_TYPE_INVALID);

349
350
  /* Init debug options depending on env variables */
  nice_debug_init ();
Dafydd Harries's avatar
Dafydd Harries committed
351
352
}

353
354
355
356
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
357
358
359
360
361
362

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

364
  /* set defaults; not construct params, so set here */
365
  agent->stun_server_port = DEFAULT_STUN_PORT;
366
  agent->controlling_mode = TRUE;
367
  agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT;
368
369

  agent->discovery_list = NULL;
370
  agent->discovery_unsched_items = 0;
371
372
373
  agent->discovery_timer_source = NULL;
  agent->conncheck_timer_source = NULL;
  agent->keepalive_timer_source = NULL;
374
  agent->compatibility = NICE_COMPATIBILITY_DRAFT19;
375

376
377
378
379
380
  stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
      STUN_COMPATIBILITY_3489BIS,
      STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
      STUN_AGENT_USAGE_USE_FINGERPRINT);

Dafydd Harries's avatar
Dafydd Harries committed
381
  agent->rng = nice_rng_new ();
382
  priv_generate_tie_breaker (agent);
383
384

  g_static_rec_mutex_init (&agent->mutex);
Dafydd Harries's avatar
Dafydd Harries committed
385
386
387
}


388
389
390
391
/**
 * nice_agent_new:
 *
 * Create a new NiceAgent.
Dafydd Harries's avatar
Dafydd Harries committed
392
393
 *
 * Returns: the new agent
394
 **/
395
NICEAPI_EXPORT NiceAgent *
396
nice_agent_new (GMainContext *ctx, NiceCompatibility compat)
Dafydd Harries's avatar
Dafydd Harries committed
397
{
398
399
  NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
      "compatibility", compat,
400
      "main-context", ctx,
Dafydd Harries's avatar
Dafydd Harries committed
401
      NULL);
402
403

  return agent;
Dafydd Harries's avatar
Dafydd Harries committed
404
405
406
407
408
409
410
411
412
413
414
}


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
415

416
  g_static_rec_mutex_lock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
417

Dafydd Harries's avatar
Dafydd Harries committed
418
419
  switch (property_id)
    {
420
421
422
423
    case PROP_MAIN_CONTEXT:
      g_value_set_pointer (value, agent->main_context);
      break;

424
425
426
427
    case PROP_COMPATIBILITY:
      g_value_set_uint (value, agent->compatibility);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
428
    case PROP_STUN_SERVER:
429
      g_value_set_string (value, agent->stun_server_ip);
430
      break;
431
432
433

    case PROP_STUN_SERVER_PORT:
      g_value_set_uint (value, agent->stun_server_port);
434
      break;
435

436
437
438
439
440
441
442
    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
443

444
445
446
447
    case PROP_STUN_PACING_TIMER:
      g_value_set_uint (value, agent->timer_ta);
      break;

448
449
450
451
452
    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;

Dafydd Harries's avatar
Dafydd Harries committed
453
454
455
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
456

457
  g_static_rec_mutex_unlock (&agent->mutex);
Dafydd Harries's avatar
Dafydd Harries committed
458
459
460
461
462
463
464
465
466
467
468
469
}


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

470
  g_static_rec_mutex_lock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
471

Dafydd Harries's avatar
Dafydd Harries committed
472
473
  switch (property_id)
    {
474
475
476
477
    case PROP_MAIN_CONTEXT:
      agent->main_context = g_value_get_pointer (value);
      break;

478
479
    case PROP_COMPATIBILITY:
      agent->compatibility = g_value_get_uint (value);
480
      if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19) {
481
482
483
484
485
486
487
488
489
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_3489BIS,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_USE_FINGERPRINT);
      } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
        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
490
491
492
      } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
493
494
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_FORCE_VALIDATER);
495
496
      }

497
498
      break;

Dafydd Harries's avatar
Dafydd Harries committed
499
    case PROP_STUN_SERVER:
500
501
502
503
504
505
506
      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;

507
508
509
510
511
512
513
514
    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;

515
516
517
518
    case PROP_STUN_PACING_TIMER:
      agent->timer_ta = g_value_get_uint (value);
      break;

519
520
521
522
    case PROP_MAX_CONNECTIVITY_CHECKS:
      agent->max_conn_checks = g_value_get_uint (value);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
523
524
525
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
526

527
  g_static_rec_mutex_unlock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
528

Dafydd Harries's avatar
Dafydd Harries committed
529
530
}

531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
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;

        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);
          }
        }
      }
    }
  }

  agent_signal_gathering_done (agent);
}

564
void agent_signal_gathering_done (NiceAgent *agent)
565
{
566
  g_signal_emit (agent, signals[SIGNAL_CANDIDATE_GATHERING_DONE], 0);
567
}
Dafydd Harries's avatar
Dafydd Harries committed
568

569
void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream)
570
{
571
572
573
574
  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);
  }
575
576
}

577
void agent_signal_new_selected_pair (NiceAgent *agent, guint stream_id, guint component_id, const gchar *local_foundation, const gchar *remote_foundation)
578
579
{
  Component *component;
580
581
  gchar *lf_copy;
  gchar *rf_copy;
582

583
  if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
584
585
    return;

586
587
588
  lf_copy = g_strdup (local_foundation);
  rf_copy = g_strdup (remote_foundation);

589
590
591
592
  if (component->selected_pair.local->type == NICE_CANDIDATE_TYPE_RELAYED) {
    nice_udp_turn_socket_set_peer (component->selected_pair.local->sockptr,
                                   &component->selected_pair.remote->addr);
  }
593
594
595
596
597
598

  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);
599
600
}

601
void agent_signal_new_candidate (NiceAgent *agent, NiceCandidate *candidate)
602
{
603
604
605
  g_signal_emit (agent, signals[SIGNAL_NEW_CANDIDATE], 0,
		 candidate->stream_id,
		 candidate->component_id,
606
		 candidate->foundation);
607
608
}

609
610
611
612
613
614
615
616
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);
}

617
void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state)
618
619
620
{
  Component *component;

621
  if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
622
623
    return;

624
  if (component->state != state && state < NICE_COMPONENT_STATE_LAST) {
625
    nice_debug ("Agent %p : stream %u component %u STATE-CHANGE %u -> %u.", agent,
626
	     stream_id, component_id, component->state, state);
627

628
    component->state = state;
629
630

    g_signal_emit (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], 0,
631
		   stream_id, component_id, state);
632
633
634
  }
}

635
636
637
guint64
agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandidate *remote)
{
Olivier Crête's avatar
Olivier Crête committed
638
  if (agent->controlling_mode)
639
    return nice_candidate_pair_priority (local->priority, remote->priority);
Olivier Crête's avatar
Olivier Crête committed
640
641
  else
    return nice_candidate_pair_priority (remote->priority, local->priority);
642
643
}

644
static gboolean
645
priv_add_new_candidate_discovery_stun (NiceAgent *agent,
646
647
    NiceCandidate *host_candidate, NiceAddress server,
    Stream *stream, guint component_id,
648
    NiceAddress *addr)
649
650
651
652
653
654
{
  CandidateDiscovery *cdisco;
  GSList *modified_list;

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

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

660
    if (modified_list) {
661
      cdisco->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
662
663
      cdisco->socket = host_candidate->sockptr->fileno;
      cdisco->nicesock = host_candidate->sockptr;
664
      cdisco->server = server;
665
666
667
668
      cdisco->interface = addr;
      cdisco->stream = stream;
      cdisco->component = stream_find_component_by_id (stream, component_id);
      cdisco->agent = agent;
669
      nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p\n", agent, cdisco);
670
671
      agent->discovery_list = modified_list;
      ++agent->discovery_unsched_items;
672
673
674
675
676
677
678
679
    }

    return TRUE;
  }

  return FALSE;
}

680
681
682
683
static gboolean
priv_add_new_candidate_discovery_turn (NiceAgent *agent,
    NiceCandidate *host_candidate, NiceAddress server,
    Stream *stream, guint component_id,
684
    NiceAddress *addr)
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
{
  CandidateDiscovery *cdisco;
  GSList *modified_list;

  /* 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) {
      cdisco->type = NICE_CANDIDATE_TYPE_RELAYED;
      cdisco->socket = host_candidate->sockptr->fileno;
      cdisco->nicesock = host_candidate->sockptr;
      cdisco->server = server;
      cdisco->interface = addr;
      cdisco->stream = stream;
      cdisco->component = stream_find_component_by_id (stream, component_id);
      cdisco->agent = agent;

706
      if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19) {
707
708
        stun_agent_init (&cdisco->turn_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_3489BIS,
709
            STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS);
Youness Alaoui's avatar
Youness Alaoui committed
710
      } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
711
712
713
        stun_agent_init (&cdisco->turn_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS);
Youness Alaoui's avatar
Youness Alaoui committed
714
715
716
717
718
      } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
        stun_agent_init (&cdisco->turn_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
719
720
      }

721
      nice_debug ("Agent %p : Adding new relay-rflx candidate discovery %p\n", agent, cdisco);
722
723
724
725
726
727
728
729
730
731
      agent->discovery_list = modified_list;
      ++agent->discovery_unsched_items;
    }

    return TRUE;
  }

  return FALSE;
}

732

733
734
/**
 * nice_agent_add_stream:
Dafydd Harries's avatar
Dafydd Harries committed
735
 *  @agent: a NiceAgent
736
 *  @n_components: number of components
737
738
 *
 * Add a data stream to @agent.
Dafydd Harries's avatar
Dafydd Harries committed
739
 *
740
741
 * @pre local addresses must be set with nice_agent_add_local_address()
 *
742
 * Returns: the ID of the new stream, 0 on failure
743
 **/
744
NICEAPI_EXPORT guint
745
746
nice_agent_add_stream (
  NiceAgent *agent,
747
  guint n_components)
Dafydd Harries's avatar
Dafydd Harries committed
748
749
{
  Stream *stream;
750
  GSList *modified_list = NULL;
751
  guint ret = 0;
752

753
  g_static_rec_mutex_lock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
754
755

  if (!agent->local_addresses) {
756
    g_warning ("Agent %p: no local addresses defined!", agent);
757
    goto done;
Youness Alaoui's avatar
Youness Alaoui committed
758
  }
759

760
  stream = stream_new (n_components);
761
762
763
764
  if (stream) {
    modified_list = g_slist_append (agent->streams, stream);
    if (modified_list) {
      stream->id = agent->next_stream_id++;
765
      nice_debug ("Agent %p : allocating stream id %u (%p)", agent, stream->id, stream);
766

767
      stream_initialize_credentials (stream, agent->rng);
768
769
770
771
772
773

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

775
776
777
  ret = stream->id;

 done:
778
  g_static_rec_mutex_unlock (&agent->mutex);
779
780
781
782
  return ret;
}


783
784
785
NICEAPI_EXPORT void nice_agent_set_relay_info(NiceAgent *agent,
    guint stream_id, guint component_id,
    const gchar *server_ip, guint server_port,
786
    const gchar *username, const gchar *password)
787
788
789
790
791
792
793
794
795
796
797
798
799
{

  Component *component = NULL;

  g_static_rec_mutex_lock (&agent->mutex);

  if (agent_find_component (agent, stream_id, component_id, NULL, &component)) {
    nice_address_init (&component->turn_server);

    if (nice_address_set_from_string (&component->turn_server, server_ip)) {
      nice_address_set_port (&component->turn_server, server_port);
    }

800

801
802
803
804
805
    g_free (component->turn_username);
    component->turn_username = g_strdup (username);

    g_free (component->turn_password);
    component->turn_password = g_strdup (password);
806

807
808
809
810
811
  }
  g_static_rec_mutex_unlock (&agent->mutex);
}


812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
/**
 * nice_agent_gather_candidates:
 *
 * start the candidate gathering process
 */

NICEAPI_EXPORT void
nice_agent_gather_candidates (
  NiceAgent *agent,
  guint stream_id)
{
  guint n;
  GSList *i;
  Stream *stream;

827
  g_static_rec_mutex_lock (&agent->mutex);
828
829
830

  stream = agent_find_stream (agent, stream_id);
  if (stream == NULL) {
831
    goto done;
Youness Alaoui's avatar
Youness Alaoui committed
832
  }
Dafydd Harries's avatar
Dafydd Harries committed
833

834
  nice_debug ("Agent %p : In %s mode, starting candidate gathering.", agent, agent->full_mode ? "ICE-FULL" : "ICE-LITE");
Dafydd Harries's avatar
Dafydd Harries committed
835

836
  /* generate a local host candidate for each local address */
837

Dafydd Harries's avatar
Dafydd Harries committed
838
839
  for (i = agent->local_addresses; i; i = i->next)
    {
840
      NiceAddress *addr = i->data;
841
      NiceCandidate *host_candidate;
Dafydd Harries's avatar
Dafydd Harries committed
842

843
      for (n = 0; n < stream->n_components; n++) {
844
        Component *component = stream_find_component_by_id (stream, n + 1);
845
846
	host_candidate = discovery_add_local_host_candidate (agent, stream->id,
							     n + 1, addr);
847

848
	if (!host_candidate) {
849
          g_error ("No host candidate??");
850
851
	  break;
	}
852

853
854
	if (agent->full_mode &&
	    agent->stun_server_ip) {
855
          NiceAddress stun_server;
856
          if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
857
858
859
            nice_address_set_port (&stun_server, agent->stun_server_port);

            gboolean res =
860
                priv_add_new_candidate_discovery_stun (agent,
861
862
863
864
                    host_candidate,
                    stun_server,
                    stream,
                    n + 1 /* component-id */,
865
                    addr);
866
867
868
869
870
871

            if (res != TRUE) {
              /* note: memory allocation failure, return error */
              g_error ("Memory allocation failure?");
            }
          }
872
	}
873

874
	if (agent->full_mode &&
875
            component && nice_address_is_valid (&component->turn_server)) {
876
877

	  gboolean res =
878
	    priv_add_new_candidate_discovery_turn (agent,
879
                host_candidate,
880
                component->turn_server,
881
882
                stream,
                n + 1 /* component-id */,
883
                addr);
884
885
886

	  if (res != TRUE) {
	    /* note: memory allocation failure, return error */
887
	    g_error ("Memory allocation failure?");
888
	  }
889
	}
890
      }
Dafydd Harries's avatar
Dafydd Harries committed
891
    }
Dafydd Harries's avatar
Dafydd Harries committed
892

893

894
  /* note: no async discoveries pending, signal that we are ready */
895
896
897
  if (agent->discovery_unsched_items == 0) {
    agent_gathering_done (agent);
  } else {
898
    g_assert (agent->discovery_list);
899
    discovery_schedule (agent);
900
  }
901

902
 done:
903

904
  g_static_rec_mutex_unlock (&agent->mutex);
Dafydd Harries's avatar
Dafydd Harries committed
905
906
}

907
908
static void priv_remove_keepalive_timer (NiceAgent *agent)
{
909
910
911
912
  if (agent->keepalive_timer_source != NULL) {
    g_source_destroy (agent->keepalive_timer_source);
    g_source_unref (agent->keepalive_timer_source);
    agent->keepalive_timer_source = NULL;
913
914
915
  }
}

Dafydd Harries's avatar
Dafydd Harries committed
916
917
918
919
920
/**
 * nice_agent_remove_stream:
 *  @agent: a NiceAgent
 *  @stream_id: the ID of the stream to remove
 **/
921
NICEAPI_EXPORT void
Dafydd Harries's avatar
Dafydd Harries committed
922
923
924
925
926
927
928
nice_agent_remove_stream (
  NiceAgent *agent,
  guint stream_id)
{
  /* note that streams/candidates can be in use by other threads */

  Stream *stream;
929
  GSList *i;
Dafydd Harries's avatar
Dafydd Harries committed
930

931
  g_static_rec_mutex_lock (&agent->mutex);
932
  stream = agent_find_stream (agent, stream_id);
Dafydd Harries's avatar
Dafydd Harries committed
933

Youness Alaoui's avatar
Youness Alaoui committed
934
  if (!stream) {
935
    goto done;
Youness Alaoui's avatar
Youness Alaoui committed
936
  }
Dafydd Harries's avatar
Dafydd Harries committed
937

938
  /* note: remove items with matching stream_ids from both lists */
939
  conn_check_prune_stream (agent, stream);
940
  discovery_prune_stream (agent, stream_id);
Dafydd Harries's avatar
Dafydd Harries committed
941

942
  /* remove the stream itself */
943
  for (i = stream->components; i; i = i->next) {
Youness Alaoui's avatar
Youness Alaoui committed
944
    priv_detach_stream_component (stream, (Component *) i->data);
945
946
  }

Dafydd Harries's avatar
Dafydd Harries committed
947
  agent->streams = g_slist_remove (agent->streams, stream);
948
949
950
951
  stream_free (stream);

  if (!agent->streams)
    priv_remove_keepalive_timer (agent);
Youness Alaoui's avatar
Youness Alaoui committed
952

953
 done:
954
  g_static_rec_mutex_unlock (&agent->mutex);
Dafydd Harries's avatar
Dafydd Harries committed
955
956
}

957
958
/**
 * nice_agent_add_local_address:
Dafydd Harries's avatar
Dafydd Harries committed
959
960
 *  @agent: A NiceAgent
 *  @addr: the address of a local IP interface
961
 *
962
963
964
965
 * Inform the agent of the presence of an address that a local 
 * network interface is bound to.
 *
 * @return FALSE on fatal (memory allocation) errors
966
 **/
967
NICEAPI_EXPORT gboolean
968
nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr)
Dafydd Harries's avatar
Dafydd Harries committed
969
{
970
  NiceAddress *dup;
971
  GSList *modified_list;
972
  gboolean ret = FALSE;
973

974
  g_static_rec_mutex_lock (&agent->mutex);
Youness Alaoui's avatar
Youness Alaoui committed
975

976
  dup = nice_address_dup (addr);
977
  nice_address_set_port (dup, 0);
978
979
980
  modified_list = g_slist_append (agent->local_addresses, dup);
  if (modified_list) {
    agent->local_addresses = modified_list;
Youness Alaoui's avatar
Youness Alaoui committed
981

982
983
    ret = TRUE;
    goto done;
984
  }
Youness Alaoui's avatar
Youness Alaoui committed
985

986
 done:
987
  g_static_rec_mutex_unlock (&agent->mutex);
988
  return ret;
Dafydd Harries's avatar
Dafydd Harries committed
989
990
}

991
992
993
994
995
996
/**
 * Adds a new, or updates an existing, remote candidate.
 *
 * @return TRUE if candidate was succesfully added or 
 *         update, otherwise FALSE
 */
997
static gboolean priv_add_remote_candidate (
998
999
1000
1001
1002
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceCandidateType type,
  const NiceAddress *addr,
1003
  const NiceAddress *base_addr,
1004
1005
1006
1007
1008
1009
1010
1011
  NiceCandidateTransport transport,
  guint32 priority,
  const gchar *username,
  const gchar *password,
  const gchar *foundation)
{
  Component *component;
  NiceCandidate *candidate;
1012
1013
  gchar *username_dup = NULL, *password_dup = NULL;
  gboolean error_flag = FALSE;
1014
1015

  if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
1016
    return FALSE;
1017

1018
1019
  /* step: check whether the candidate already exists */
  candidate = component_find_remote_candidate(component, addr, transport);
1020
  if (candidate) {
1021
    nice_debug ("Agent %p : Update existing remote candidate %p.", agent, candidate);
1022
1023
    /* case 1: an existing candidate, update the attributes */
    candidate->type = type;
1024
1025
    if (base_addr)
      candidate->base_addr = *base_addr;
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
    candidate->priority = priority;
    if (foundation)
      strncpy(candidate->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION);
    /* note: username and password must remain the same during
     *       a session; see sect 9.1.2 in ICE ID-19 */
    if (conn_check_add_for_candidate (agent, stream_id, component, candidate) < 0)
      error_flag = TRUE;
  }
  else {
    /* case 2: add a new candidate */
    if (username)
      username_dup = g_strdup (username);
    if (password) 
      password_dup = g_strdup (password);

    candidate = nice_candidate_new (type);
    if (candidate) {
      GSList *modified_list = g_slist_append (component->remote_candidates, candidate);
      if (modified_list) {
	component->remote_candidates = modified_list;
	
	candidate->stream_id = stream_id;
	candidate->component_id = component_id;

	candidate->type = type;
	if (addr)
	  candidate->addr = *addr;
1053
#ifndef NDEBUG
1054
1055
1056
	{
	  gchar tmpbuf[INET6_ADDRSTRLEN];
	  nice_address_to_string (addr, tmpbuf);
1057
	  nice_debug ("Agent %p : Adding remote candidate with addr [%s]:%u.", agent, tmpbuf,
1058
1059
		   nice_address_get_port (addr));
	}
1060
#endif
1061
	
1062
1063
	if (base_addr)
	  candidate->base_addr = *base_addr;
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
	
	candidate->transport = transport;
	candidate->priority = priority;
	candidate->username = username_dup;
	candidate->password = password_dup;
	
	if (foundation)
	  g_strlcpy (candidate->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION);

	if (conn_check_add_for_candidate (agent, stream_id, component, candidate) < 0)
	  error_flag = TRUE;
      }
      else /* memory alloc error: list insert */
1077
1078
	error_flag = TRUE;
    }
1079
    else /* memory alloc error: candidate creation */
1080
      error_flag = TRUE;
1081
  }  
1082

1083
1084
1085
1086
1087
1088
1089
  if (error_flag) {
    if (candidate) 
      nice_candidate_free (candidate);
    g_free (username_dup);
    g_free (password_dup);
    return FALSE;
  }
1090

1091
  return TRUE;
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
}

/**
 * Sets the remote credentials for stream 'stream_id'.
 *
 * Note: stream credentials do not override per-candidate 
 *       credentials if set
 *
 * @agent: a NiceAgent
 * @stream_id: identifier returnedby nice_agent_add_stream()
 * @ufrag: NULL-terminated string containing an ICE username fragment
1103
 *    (length must be between 22 and 256 chars)
1104
 * @pwd: NULL-terminated string containing an ICE password
1105
 *    (length must be between 4 and 256 chars)
1106
1107
1108
 *
 * @return TRUE on success
 */
1109
NICEAPI_EXPORT gboolean
1110
1111
1112
1113
1114
1115
nice_agent_set_remote_credentials (
  NiceAgent *agent,
  guint stream_id,
  const gchar *ufrag, const gchar *pwd)
{
  Stream *stream;
1116
  gboolean ret = FALSE;