agent.c 137 KB
Newer Older
1 2 3
/*
 * This file is part of the Nice GLib ICE library.
 *
4
 * (C) 2006-2010, 2013 Collabora Ltd.
5 6
 *  Contact: Youness Alaoui
 * (C) 2006-2010 Nokia Corporation. All rights reserved.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *  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
 *   Youness Alaoui, Collabora Ltd.
27
 *   Kai Vehmanen, Nokia
28
 *   Philip Withnall, Collabora Ltd.
29 30 31 32 33 34 35 36 37 38 39
 *
 * 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
40

41 42 43

#ifdef HAVE_CONFIG_H
# include <config.h>
44 45
#else
#define NICEAPI_EXPORT
46 47
#endif

48
#include <glib.h>
49
#include <gobject/gvaluecollector.h>
50

Dafydd Harries's avatar
Dafydd Harries committed
51
#include <string.h>
52
#include <errno.h>
Dafydd Harries's avatar
Dafydd Harries committed
53

54
#ifndef G_OS_WIN32
55 56 57
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
58
#endif
Dafydd Harries's avatar
Dafydd Harries committed
59

60 61
#include "debug.h"

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

73
#include "stream.h"
74
#include "interfaces.h"
Dafydd Harries's avatar
Dafydd Harries committed
75

76 77
#include "pseudotcp.h"

78 79 80 81
/* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b
 * wide. */
#define MAX_BUFFER_SIZE ((1 << 16) - 1)  /* 65535 */

82
#define DEFAULT_STUN_PORT  3478
83
#define DEFAULT_UPNP_TIMEOUT 200
84

85 86
#define MAX_TCP_MTU 1400 /* Use 1400 because of VPNs and we assume IEE 802.3 */

87
static void
88 89
nice_debug_input_message_composition (const NiceInputMessage *messages,
    guint n_messages);
90

Dafydd Harries's avatar
Dafydd Harries committed
91 92 93 94
G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT);

enum
{
95
  PROP_COMPATIBILITY = 1,
96
  PROP_MAIN_CONTEXT,
97
  PROP_STUN_SERVER,
98
  PROP_STUN_SERVER_PORT,
99
  PROP_CONTROLLING_MODE,
100
  PROP_FULL_MODE,
101
  PROP_STUN_PACING_TIMER,
102 103 104 105 106
  PROP_MAX_CONNECTIVITY_CHECKS,
  PROP_PROXY_TYPE,
  PROP_PROXY_IP,
  PROP_PROXY_PORT,
  PROP_PROXY_USERNAME,
Youness Alaoui's avatar
Youness Alaoui committed
107 108
  PROP_PROXY_PASSWORD,
  PROP_UPNP,
109 110
  PROP_UPNP_TIMEOUT,
  PROP_RELIABLE
Dafydd Harries's avatar
Dafydd Harries committed
111 112 113
};


114 115 116
enum
{
  SIGNAL_COMPONENT_STATE_CHANGED,
117
  SIGNAL_CANDIDATE_GATHERING_DONE,
118 119
  SIGNAL_NEW_SELECTED_PAIR,
  SIGNAL_NEW_CANDIDATE,
120
  SIGNAL_NEW_REMOTE_CANDIDATE,
121
  SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED,
122
  SIGNAL_RELIABLE_TRANSPORT_WRITABLE,
123
  SIGNAL_STREAMS_REMOVED,
124 125 126 127 128
  N_SIGNALS,
};

static guint signals[N_SIGNALS];

129
#if GLIB_CHECK_VERSION(2,31,8)
130
static GMutex agent_mutex;    /* Mutex used for thread-safe lib */
131
#else
132
static GStaticMutex agent_mutex = G_STATIC_REC_MUTEX_INIT;
133
#endif
134

Youness Alaoui's avatar
Youness Alaoui committed
135 136
static void priv_free_upnp (NiceAgent *agent);

137
#if GLIB_CHECK_VERSION(2,31,8)
138 139
void agent_lock (void)
{
140
  g_mutex_lock (&agent_mutex);
141 142 143 144
}

void agent_unlock (void)
{
145
  g_mutex_unlock (&agent_mutex);
146 147 148 149 150
}

#else
void agent_lock(void)
{
151
  g_static_mutex_lock (&agent_mutex);
152 153
}

154 155
void agent_unlock(void)
{
156
  g_static_mutex_unlock (&agent_mutex);
157
}
158

159
#endif
160

161 162 163 164 165 166
static GType _nice_agent_stream_ids_get_type (void);

G_DEFINE_POINTER_TYPE (_NiceAgentStreamIds, _nice_agent_stream_ids);

#define NICE_TYPE_AGENT_STREAM_IDS _nice_agent_stream_ids_get_type ()

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
typedef struct {
  guint signal_id;
  GSignalQuery query;
  GValue *params;
} QueuedSignal;


static void
free_queued_signal (QueuedSignal *sig)
{
  guint i;

  for (i = 0; i < sig->query.n_params; i++) {
      g_free (g_value_get_pointer (&sig->params[i]));
    g_value_unset (&sig->params[i]);
182
    if (G_VALUE_HOLDS(&sig->params[i + 1], NICE_TYPE_AGENT_STREAM_IDS))
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
  }

  g_slice_free1 (sizeof(GValue) * (sig->query.n_params + 1), sig->params);
  g_slice_free (QueuedSignal, sig);
}

void
agent_unlock_and_emit (NiceAgent *agent)
{
  GQueue queue = G_QUEUE_INIT;
  QueuedSignal *sig;

  queue = agent->pending_signals;
  g_queue_init (&agent->pending_signals);

  agent_unlock ();

  while ((sig = g_queue_pop_head (&queue))) {
    g_signal_emitv (sig->params, sig->signal_id, 0, NULL);

    free_queued_signal (sig);
  }
}

static void
agent_queue_signal (NiceAgent *agent, guint signal_id, ...)
{
  QueuedSignal *sig;
  guint i;
  gchar *error = NULL;
  va_list var_args;

  sig = g_slice_new (QueuedSignal);
  g_signal_query (signal_id, &sig->query);

  sig->signal_id = signal_id;
  sig->params = g_slice_alloc0 (sizeof(GValue) * (sig->query.n_params + 1));

  g_value_init (&sig->params[0], G_TYPE_OBJECT);
  g_value_set_object (&sig->params[0], agent);

  va_start (var_args, signal_id);
  for (i = 0; i < sig->query.n_params; i++) {
    G_VALUE_COLLECT_INIT (&sig->params[i + 1], sig->query.param_types[i],
        var_args, 0, &error);
    if (error)
      break;
  }
  va_end (var_args);

  if (error) {
    free_queued_signal (sig);
    g_critical ("Error collecting values for signal: %s", error);
    g_free (error);
    return;
  }

  g_queue_push_tail (&agent->pending_signals, sig);
}


244 245 246
StunUsageIceCompatibility
agent_to_ice_compatibility (NiceAgent *agent)
{
247
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
248 249
      STUN_USAGE_ICE_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
250
      STUN_USAGE_ICE_COMPATIBILITY_MSN :
Jakub Adam's avatar
Jakub Adam committed
251 252
      agent->compatibility == NICE_COMPATIBILITY_WLM2009 ?
      STUN_USAGE_ICE_COMPATIBILITY_WLM2009 :
Jakub Adam's avatar
Jakub Adam committed
253 254
      agent->compatibility == NICE_COMPATIBILITY_OC2007 ?
      STUN_USAGE_ICE_COMPATIBILITY_MSN :
Jakub Adam's avatar
Jakub Adam committed
255 256
      agent->compatibility == NICE_COMPATIBILITY_OC2007R2 ?
      STUN_USAGE_ICE_COMPATIBILITY_WLM2009 :
257
      STUN_USAGE_ICE_COMPATIBILITY_RFC5245;
258 259 260 261 262 263
}


StunUsageTurnCompatibility
agent_to_turn_compatibility (NiceAgent *agent)
{
264
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
265 266
      STUN_USAGE_TURN_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
267 268
      STUN_USAGE_TURN_COMPATIBILITY_MSN :
      agent->compatibility == NICE_COMPATIBILITY_WLM2009 ?
Jakub Adam's avatar
Jakub Adam committed
269 270
      STUN_USAGE_TURN_COMPATIBILITY_MSN :
      agent->compatibility == NICE_COMPATIBILITY_OC2007 ?
271 272 273
      STUN_USAGE_TURN_COMPATIBILITY_OC2007 :
      agent->compatibility == NICE_COMPATIBILITY_OC2007R2 ?
      STUN_USAGE_TURN_COMPATIBILITY_OC2007 :
Marcus Lundblad's avatar
Marcus Lundblad committed
274
      STUN_USAGE_TURN_COMPATIBILITY_RFC5766;
275 276
}

277 278 279
NiceTurnSocketCompatibility
agent_to_turn_socket_compatibility (NiceAgent *agent)
{
280
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
281 282 283
      NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
      NICE_TURN_SOCKET_COMPATIBILITY_MSN :
284 285
      agent->compatibility == NICE_COMPATIBILITY_WLM2009 ?
      NICE_TURN_SOCKET_COMPATIBILITY_MSN :
Jakub Adam's avatar
Jakub Adam committed
286
      agent->compatibility == NICE_COMPATIBILITY_OC2007 ?
287 288 289
      NICE_TURN_SOCKET_COMPATIBILITY_OC2007 :
      agent->compatibility == NICE_COMPATIBILITY_OC2007R2 ?
      NICE_TURN_SOCKET_COMPATIBILITY_OC2007 :
Marcus Lundblad's avatar
Marcus Lundblad committed
290
      NICE_TURN_SOCKET_COMPATIBILITY_RFC5766;
291 292
}

293
Stream *agent_find_stream (NiceAgent *agent, guint stream_id)
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
{
  GSList *i;

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

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

  return NULL;
}


309 310
gboolean
agent_find_component (
311 312 313 314 315 316 317
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  Stream **stream,
  Component **component)
{
  Stream *s;
318
  Component *c;
319

320
  s = agent_find_stream (agent, stream_id);
321

322
  if (s == NULL)
323 324
    return FALSE;

325 326 327 328 329
  c = stream_find_component_by_id (s, component_id);

  if (c == NULL)
    return FALSE;

330 331 332 333
  if (stream)
    *stream = s;

  if (component)
334
    *component = c;
335 336 337 338 339

  return TRUE;
}


Dafydd Harries's avatar
Dafydd Harries committed
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
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;

367
  /* install properties */
368 369 370 371 372 373
  /**
   * 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.
   */
374 375 376 377 378 379 380
  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));

381 382 383 384 385 386 387
  /**
   * NiceAgent:compatibility:
   *
   * The Nice agent can work in various compatibility modes depending on
   * what the application/peer needs.
   * <para> See also: #NiceCompatibility</para>
   */
388 389 390 391 392
  g_object_class_install_property (gobject_class, PROP_COMPATIBILITY,
      g_param_spec_uint (
         "compatibility",
         "ICE specification compatibility",
         "The compatibility mode for the agent",
393 394
         NICE_COMPATIBILITY_RFC5245, NICE_COMPATIBILITY_LAST,
         NICE_COMPATIBILITY_RFC5245,
395 396
         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

Dafydd Harries's avatar
Dafydd Harries committed
397
  g_object_class_install_property (gobject_class, PROP_STUN_SERVER,
398
      g_param_spec_string (
Dafydd Harries's avatar
Dafydd Harries committed
399
        "stun-server",
400 401
        "STUN server IP address",
        "The IP address (not the hostname) of the STUN server to use",
402
        NULL,
Dafydd Harries's avatar
Dafydd Harries committed
403 404
        G_PARAM_READWRITE));

405 406 407 408
  g_object_class_install_property (gobject_class, PROP_STUN_SERVER_PORT,
      g_param_spec_uint (
        "stun-server-port",
        "STUN server port",
409
        "Port of the STUN server used to gather server-reflexive candidates",
Youness Alaoui's avatar
Youness Alaoui committed
410
        1, 65536,
411
	1, /* not a construct property, ignored */
412 413
        G_PARAM_READWRITE));

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
  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));

430 431 432 433
  g_object_class_install_property (gobject_class, PROP_STUN_PACING_TIMER,
      g_param_spec_uint (
        "stun-pacing-timer",
        "STUN pacing timer",
Youness Alaoui's avatar
Youness Alaoui committed
434 435
        "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
436
        1, 0xffffffff,
437 438 439
	NICE_AGENT_TIMER_TA_DEFAULT,
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

440
  /* note: according to spec recommendation in sect 5.7.3 (ID-19) */
441 442 443 444 445
  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
446
        0, 0xffffffff,
447 448 449
	0, /* default set in init */
        G_PARAM_READWRITE));

450 451 452 453 454 455 456
  /**
   * NiceAgent:proxy-ip:
   *
   * The proxy server IP used to bypass a proxy firewall
   *
   * Since: 0.0.4
   */
457 458 459 460
  g_object_class_install_property (gobject_class, PROP_PROXY_IP,
      g_param_spec_string (
        "proxy-ip",
        "Proxy server IP",
461
        "The proxy server IP used to bypass a proxy firewall",
462 463 464
        NULL,
        G_PARAM_READWRITE));

465 466 467 468 469 470 471
  /**
   * NiceAgent:proxy-port:
   *
   * The proxy server port used to bypass a proxy firewall
   *
   * Since: 0.0.4
   */
472 473 474 475
  g_object_class_install_property (gobject_class, PROP_PROXY_PORT,
      g_param_spec_uint (
        "proxy-port",
        "Proxy server port",
476
        "The Proxy server port used to bypass a proxy firewall",
477 478 479 480
        1, 65536,
	1,
        G_PARAM_READWRITE));

481 482 483 484 485 486 487
  /**
   * NiceAgent:proxy-type:
   *
   * The type of proxy set in the proxy-ip property
   *
   * Since: 0.0.4
   */
488 489 490 491 492 493 494 495 496
  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));

497 498 499 500 501 502 503
  /**
   * NiceAgent:proxy-username:
   *
   * The username used to authenticate with the proxy
   *
   * Since: 0.0.4
   */
504 505 506 507 508 509 510 511
  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));

512 513 514 515 516 517 518
  /**
   * NiceAgent:proxy-password:
   *
   * The password used to authenticate with the proxy
   *
   * Since: 0.0.4
   */
519 520 521 522 523 524 525 526
  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));

527 528 529 530 531 532 533 534
  /**
   * NiceAgent:upnp:
   *
   * Whether the agent should use UPnP to open a port in the router and
   * get the external IP
   *
   * Since: 0.0.7
   */
Youness Alaoui's avatar
Youness Alaoui committed
535 536 537
   g_object_class_install_property (gobject_class, PROP_UPNP,
      g_param_spec_boolean (
        "upnp",
538
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
539 540 541
        "Use UPnP",
        "Whether the agent should use UPnP to open a port in the router and "
        "get the external IP",
542 543 544 545
#else
        "Use UPnP (disabled in build)",
        "Does nothing because libnice was not built with UPnP support",
#endif
Youness Alaoui's avatar
Youness Alaoui committed
546 547 548
	TRUE, /* enable UPnP by default */
        G_PARAM_READWRITE| G_PARAM_CONSTRUCT));

549 550 551 552
  /**
   * NiceAgent:upnp-timeout:
   *
   * The maximum amount of time to wait for UPnP discovery to finish before
553
   * signaling the #NiceAgent::candidate-gathering-done signal
554 555 556
   *
   * Since: 0.0.7
   */
Youness Alaoui's avatar
Youness Alaoui committed
557 558 559
  g_object_class_install_property (gobject_class, PROP_UPNP_TIMEOUT,
      g_param_spec_uint (
        "upnp-timeout",
560
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
561 562 563
        "Timeout for UPnP discovery",
        "The maximum amount of time to wait for UPnP discovery to finish before "
        "signaling the candidate-gathering-done signal",
564 565 566 567
#else
        "Timeout for UPnP discovery (disabled in build)",
        "Does nothing because libnice was not built with UPnP support",
#endif
Youness Alaoui's avatar
Youness Alaoui committed
568
        100, 60000,
569
	DEFAULT_UPNP_TIMEOUT,
Youness Alaoui's avatar
Youness Alaoui committed
570 571
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

Youness Alaoui's avatar
Youness Alaoui committed
572 573 574 575 576 577 578 579
  /**
   * NiceAgent:reliable:
   *
   * Whether the agent should use PseudoTcp to ensure a reliable transport
   * of messages
   *
   * Since: 0.0.11
   */
580 581 582 583
   g_object_class_install_property (gobject_class, PROP_RELIABLE,
      g_param_spec_boolean (
        "reliable",
        "reliable mode",
Youness Alaoui's avatar
Youness Alaoui committed
584
        "Whether the agent should use PseudoTcp to ensure a reliable transport"
585 586 587 588
        "of messages",
	FALSE,
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

589 590
  /* install signals */

591 592
  /**
   * NiceAgent::component-state-changed
Youness Alaoui's avatar
Youness Alaoui committed
593
   * @agent: The #NiceAgent object
594 595 596 597 598 599
   * @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
   */
600 601 602 603
  signals[SIGNAL_COMPONENT_STATE_CHANGED] =
      g_signal_new (
          "component-state-changed",
          G_OBJECT_CLASS_TYPE (klass),
604
          G_SIGNAL_RUN_LAST,
605 606 607 608 609 610 611 612
          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);
613

614 615
  /**
   * NiceAgent::candidate-gathering-done:
Youness Alaoui's avatar
Youness Alaoui committed
616
   * @agent: The #NiceAgent object
617 618 619 620 621
   * @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()
   */
622 623 624 625
  signals[SIGNAL_CANDIDATE_GATHERING_DONE] =
      g_signal_new (
          "candidate-gathering-done",
          G_OBJECT_CLASS_TYPE (klass),
626
          G_SIGNAL_RUN_LAST,
627 628 629
          0,
          NULL,
          NULL,
630
          agent_marshal_VOID__UINT,
631
          G_TYPE_NONE,
632 633
          1,
          G_TYPE_UINT, G_TYPE_INVALID);
634

635 636
  /**
   * NiceAgent::new-selected-pair
Youness Alaoui's avatar
Youness Alaoui committed
637
   * @agent: The #NiceAgent object
638 639 640 641 642 643 644 645
   * @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
   */
646 647 648 649
  signals[SIGNAL_NEW_SELECTED_PAIR] =
      g_signal_new (
          "new-selected-pair",
          G_OBJECT_CLASS_TYPE (klass),
650
          G_SIGNAL_RUN_LAST,
651 652 653 654 655 656 657 658 659
          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);

660 661
  /**
   * NiceAgent::new-candidate
Youness Alaoui's avatar
Youness Alaoui committed
662
   * @agent: The #NiceAgent object
663 664 665 666 667
   * @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
668
   * <para> See also: #NiceAgent::candidate-gathering-done </para>
669
   */
670 671 672 673
  signals[SIGNAL_NEW_CANDIDATE] =
      g_signal_new (
          "new-candidate",
          G_OBJECT_CLASS_TYPE (klass),
674
          G_SIGNAL_RUN_LAST,
675 676 677 678 679 680 681 682 683
          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);

684 685
  /**
   * NiceAgent::new-remote-candidate
Youness Alaoui's avatar
Youness Alaoui committed
686
   * @agent: The #NiceAgent object
687 688 689 690 691 692 693
   * @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.
   */
694 695 696 697
  signals[SIGNAL_NEW_REMOTE_CANDIDATE] =
      g_signal_new (
          "new-remote-candidate",
          G_OBJECT_CLASS_TYPE (klass),
698
          G_SIGNAL_RUN_LAST,
699 700 701 702 703 704 705 706 707
          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);

708 709
  /**
   * NiceAgent::initial-binding-request-received
Youness Alaoui's avatar
Youness Alaoui committed
710
   * @agent: The #NiceAgent object
711 712 713 714 715
   * @stream_id: The ID of the stream
   *
   * This signal is fired when we received our first binding request from
   * the peer.
   */
716 717 718 719
  signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED] =
      g_signal_new (
          "initial-binding-request-received",
          G_OBJECT_CLASS_TYPE (klass),
720
          G_SIGNAL_RUN_LAST,
721 722 723 724 725 726 727 728 729
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT,
          G_TYPE_NONE,
          1,
          G_TYPE_UINT,
          G_TYPE_INVALID);

730 731
  /**
   * NiceAgent::reliable-transport-writable
Youness Alaoui's avatar
Youness Alaoui committed
732
   * @agent: The #NiceAgent object
733 734 735 736 737 738 739 740
   * @stream_id: The ID of the stream
   * @component_id: The ID of the component
   *
   * This signal is fired on the reliable #NiceAgent when the underlying reliable
   * transport becomes writable.
   * This signal is only emitted when the nice_agent_send() function returns less
   * bytes than requested to send (or -1) and once when the connection
   * is established.
741 742
   *
   * Since: 0.0.11
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
   */
  signals[SIGNAL_RELIABLE_TRANSPORT_WRITABLE] =
      g_signal_new (
          "reliable-transport-writable",
          G_OBJECT_CLASS_TYPE (klass),
          G_SIGNAL_RUN_LAST,
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT_UINT,
          G_TYPE_NONE,
          2,
          G_TYPE_UINT, G_TYPE_UINT,
          G_TYPE_INVALID);

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
  /**
   * NiceAgent::streams-removed
   * @agent: The #NiceAgent object
   * @stream_ids: (array zero-terminated=1) (element-type uint): An array of
   * unsigned integer stream IDs, ending with a 0 ID
   *
   * This signal is fired whenever one or more streams are removed from the
   * @agent.
   *
   * Since: 0.1.5
   */
  signals[SIGNAL_STREAMS_REMOVED] =
      g_signal_new (
          "streams-removed",
          G_OBJECT_CLASS_TYPE (klass),
          G_SIGNAL_RUN_LAST,
          0,
          NULL,
          NULL,
          g_cclosure_marshal_VOID__POINTER,
          G_TYPE_NONE,
          1,
780
          NICE_TYPE_AGENT_STREAM_IDS,
781
          G_TYPE_INVALID);
782

783 784
  /* Init debug options depending on env variables */
  nice_debug_init ();
Dafydd Harries's avatar
Dafydd Harries committed
785 786
}

787 788 789 790
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
791 792 793 794 795 796

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

798
  /* set defaults; not construct params, so set here */
799
  agent->stun_server_port = DEFAULT_STUN_PORT;
800
  agent->controlling_mode = TRUE;
801
  agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT;
802 803

  agent->discovery_list = NULL;
804
  agent->discovery_unsched_items = 0;
805 806 807
  agent->discovery_timer_source = NULL;
  agent->conncheck_timer_source = NULL;
  agent->keepalive_timer_source = NULL;
808
  agent->refresh_list = NULL;
809
  agent->media_after_tick = FALSE;
810
  agent->software_attribute = NULL;
811

812
  agent->compatibility = NICE_COMPATIBILITY_RFC5245;
813
  agent->reliable = FALSE;
814

815
  stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
816
      STUN_COMPATIBILITY_RFC5389,
817 818 819
      STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
      STUN_AGENT_USAGE_USE_FINGERPRINT);

Dafydd Harries's avatar
Dafydd Harries committed
820
  agent->rng = nice_rng_new ();
821
  priv_generate_tie_breaker (agent);
822 823

  g_queue_init (&agent->pending_signals);
Dafydd Harries's avatar
Dafydd Harries committed
824 825 826
}


827
NICEAPI_EXPORT NiceAgent *
828
nice_agent_new (GMainContext *ctx, NiceCompatibility compat)
Dafydd Harries's avatar
Dafydd Harries committed
829
{
830 831
  NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
      "compatibility", compat,
832
      "main-context", ctx,
833 834 835 836 837 838 839 840 841 842 843 844 845 846
      "reliable", FALSE,
      NULL);

  return agent;
}


NICEAPI_EXPORT NiceAgent *
nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat)
{
  NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
      "compatibility", compat,
      "main-context", ctx,
      "reliable", TRUE,
Dafydd Harries's avatar
Dafydd Harries committed
847
      NULL);
848 849

  return agent;
Dafydd Harries's avatar
Dafydd Harries committed
850 851 852 853 854 855 856 857 858 859 860
}


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
861

862
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
863

Dafydd Harries's avatar
Dafydd Harries committed
864 865
  switch (property_id)
    {
866 867 868 869
    case PROP_MAIN_CONTEXT:
      g_value_set_pointer (value, agent->main_context);
      break;

870 871 872 873
    case PROP_COMPATIBILITY:
      g_value_set_uint (value, agent->compatibility);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
874
    case PROP_STUN_SERVER:
875
      g_value_set_string (value, agent->stun_server_ip);
876
      break;
877 878 879

    case PROP_STUN_SERVER_PORT:
      g_value_set_uint (value, agent->stun_server_port);
880
      break;
881

882 883 884 885 886 887 888
    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
889

890 891 892 893
    case PROP_STUN_PACING_TIMER:
      g_value_set_uint (value, agent->timer_ta);
      break;

894 895 896 897 898
    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;

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
    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
919
    case PROP_UPNP:
920
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
921
      g_value_set_boolean (value, agent->upnp_enabled);
922 923 924
#else
      g_value_set_boolean (value, FALSE);
#endif
Youness Alaoui's avatar
Youness Alaoui committed
925 926 927
      break;

    case PROP_UPNP_TIMEOUT:
928
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
929
      g_value_set_uint (value, agent->upnp_timeout);
930 931
#else
      g_value_set_uint (value, DEFAULT_UPNP_TIMEOUT);
Youness Alaoui's avatar
Youness Alaoui committed
932
#endif
933
      break;
Youness Alaoui's avatar
Youness Alaoui committed
934

935 936 937 938
    case PROP_RELIABLE:
      g_value_set_boolean (value, agent->reliable);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
939 940 941
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
942

943
  agent_unlock_and_emit(agent);
Dafydd Harries's avatar
Dafydd Harries committed
944 945 946 947 948 949 950 951 952 953 954 955
}


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

956
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
957

Dafydd Harries's avatar
Dafydd Harries committed
958 959
  switch (property_id)
    {
960 961
    case PROP_MAIN_CONTEXT:
      agent->main_context = g_value_get_pointer (value);
962 963
      if (agent->main_context != NULL)
        g_main_context_ref (agent->main_context);
964 965
      break;

966 967
    case PROP_COMPATIBILITY:
      agent->compatibility = g_value_get_uint (value);
968
      if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
969 970 971 972
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
973
      } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
Youness Alaoui's avatar
Youness Alaoui committed
974 975
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
976 977
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_FORCE_VALIDATER);
978 979 980 981
      } 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 |
Jakub Adam's avatar
Jakub Adam committed
982
            STUN_AGENT_USAGE_USE_FINGERPRINT);
983 984 985 986 987 988
      } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007) {
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_FORCE_VALIDATER |
            STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES);
Jakub Adam's avatar
Jakub Adam committed
989 990 991 992
      } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_WLM2009,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
993 994
            STUN_AGENT_USAGE_USE_FINGERPRINT |
            STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES);
995 996 997 998 999
      } 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);
1000
      }
1001
      stun_agent_set_software (&agent->stun_agent, agent->software_attribute);
1002

1003 1004
      break;

Dafydd Harries's avatar
Dafydd Harries committed
1005
    case PROP_STUN_SERVER:
1006
      g_free (agent->stun_server_ip);
1007 1008 1009 1010 1011 1012 1013
      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;

1014 1015 1016 1017 1018 1019 1020 1021
    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;

1022 1023 1024 1025
    case PROP_STUN_PACING_TIMER:
      agent->timer_ta = g_value_get_uint (value);
      break;

1026 1027 1028 1029
    case PROP_MAX_CONNECTIVITY_CHECKS:
      agent->max_conn_checks = g_value_get_uint (value);
      break;

1030
    case PROP_PROXY_IP:
1031
      g_free (agent->proxy_ip);
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
      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:
1044
      g_free (agent->proxy_username);
1045 1046 1047 1048
      agent->proxy_username = g_value_dup_string (value);
      break;

    case PROP_PROXY_PASSWORD:
1049
      g_free (agent->proxy_password);
1050 1051 1052
      agent->proxy_password = g_value_dup_string (value);
      break;

Youness Alaoui's avatar
Youness Alaoui committed
1053
    case PROP_UPNP_TIMEOUT:
1054
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
1055
      agent->upnp_timeout = g_value_get_uint (value);
1056
#endif
Youness Alaoui's avatar
Youness Alaoui committed
1057 1058 1059
      break;

    case PROP_UPNP:
1060
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
1061 1062
      agent->upnp_enabled = g_value_get_boolean (value);
#endif
1063
      break;
1064 1065 1066 1067 1068

    case PROP_RELIABLE:
      agent->reliable = g_value_get_boolean (value);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
1069 1070 1071
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id