agent.c 82.1 KB
Newer Older
1 2 3
/*
 * This file is part of the Nice GLib ICE library.
 *
4 5 6
 * (C) 2006-2010 Collabora Ltd.
 *  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 29 30 31 32 33 34 35 36 37 38
 *
 * 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
39

40 41 42

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

47 48
#include <glib.h>

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

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

58 59
#include "debug.h"

60
#include "socket.h"
61
#include "stun/usages/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"
71
#include "interfaces.h"
Dafydd Harries's avatar
Dafydd Harries committed
72

73 74
#include "pseudotcp.h"

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

82 83
#define MAX_TCP_MTU 1400 /* Use 1400 because of VPNs and we assume IEE 802.3 */

Dafydd Harries's avatar
Dafydd Harries committed
84 85 86 87
G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT);

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


107 108 109
enum
{
  SIGNAL_COMPONENT_STATE_CHANGED,
110
  SIGNAL_CANDIDATE_GATHERING_DONE,
111 112
  SIGNAL_NEW_SELECTED_PAIR,
  SIGNAL_NEW_CANDIDATE,
113
  SIGNAL_NEW_REMOTE_CANDIDATE,
114
  SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED,
115
  SIGNAL_RELIABLE_TRANSPORT_WRITABLE,
116 117 118 119 120
  N_SIGNALS,
};

static guint signals[N_SIGNALS];

121 122 123 124 125
#if GLIB_CHECK_VERSION(2,31,8)
static GRecMutex agent_mutex;    /* Mutex used for thread-safe lib */
#else
static GStaticRecMutex agent_mutex = G_STATIC_REC_MUTEX_INIT;
#endif
126

127 128
static gboolean priv_attach_stream_component (NiceAgent *agent,
    Stream *stream,
129
    Component *component);
Youness Alaoui's avatar
Youness Alaoui committed
130
static void priv_detach_stream_component (Stream *stream, Component *component);
131

Youness Alaoui's avatar
Youness Alaoui committed
132 133
static void priv_free_upnp (NiceAgent *agent);

134
#if GLIB_CHECK_VERSION(2,31,8)
135 136
void agent_lock (void)
{
137
  g_rec_mutex_lock (&agent_mutex);
138 139 140 141
}

void agent_unlock (void)
{
142 143 144 145 146 147 148
  g_rec_mutex_unlock (&agent_mutex);
}

#else
void agent_lock(void)
{
  g_static_rec_mutex_lock (&agent_mutex);
149 150
}

151 152 153 154
void agent_unlock(void)
{
  g_static_rec_mutex_unlock (&agent_mutex);
}
155

156
#endif
157

158 159 160
StunUsageIceCompatibility
agent_to_ice_compatibility (NiceAgent *agent)
{
161
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
162 163
      STUN_USAGE_ICE_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
164
      STUN_USAGE_ICE_COMPATIBILITY_MSN :
Jakub Adam's avatar
Jakub Adam committed
165 166
      agent->compatibility == NICE_COMPATIBILITY_WLM2009 ?
      STUN_USAGE_ICE_COMPATIBILITY_WLM2009 :
Jakub Adam's avatar
Jakub Adam committed
167 168
      agent->compatibility == NICE_COMPATIBILITY_OC2007 ?
      STUN_USAGE_ICE_COMPATIBILITY_MSN :
Jakub Adam's avatar
Jakub Adam committed
169 170
      agent->compatibility == NICE_COMPATIBILITY_OC2007R2 ?
      STUN_USAGE_ICE_COMPATIBILITY_WLM2009 :
171
      STUN_USAGE_ICE_COMPATIBILITY_RFC5245;
172 173 174 175 176 177
}


StunUsageTurnCompatibility
agent_to_turn_compatibility (NiceAgent *agent)
{
178
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
179 180
      STUN_USAGE_TURN_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
181 182
      STUN_USAGE_TURN_COMPATIBILITY_MSN :
      agent->compatibility == NICE_COMPATIBILITY_WLM2009 ?
Jakub Adam's avatar
Jakub Adam committed
183 184
      STUN_USAGE_TURN_COMPATIBILITY_MSN :
      agent->compatibility == NICE_COMPATIBILITY_OC2007 ?
185 186 187
      STUN_USAGE_TURN_COMPATIBILITY_OC2007 :
      agent->compatibility == NICE_COMPATIBILITY_OC2007R2 ?
      STUN_USAGE_TURN_COMPATIBILITY_OC2007 :
Marcus Lundblad's avatar
Marcus Lundblad committed
188
      STUN_USAGE_TURN_COMPATIBILITY_RFC5766;
189 190
}

191 192 193
NiceTurnSocketCompatibility
agent_to_turn_socket_compatibility (NiceAgent *agent)
{
194
  return agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
195 196 197
      NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE :
      agent->compatibility == NICE_COMPATIBILITY_MSN ?
      NICE_TURN_SOCKET_COMPATIBILITY_MSN :
198 199
      agent->compatibility == NICE_COMPATIBILITY_WLM2009 ?
      NICE_TURN_SOCKET_COMPATIBILITY_MSN :
Jakub Adam's avatar
Jakub Adam committed
200
      agent->compatibility == NICE_COMPATIBILITY_OC2007 ?
201 202 203
      NICE_TURN_SOCKET_COMPATIBILITY_OC2007 :
      agent->compatibility == NICE_COMPATIBILITY_OC2007R2 ?
      NICE_TURN_SOCKET_COMPATIBILITY_OC2007 :
Marcus Lundblad's avatar
Marcus Lundblad committed
204
      NICE_TURN_SOCKET_COMPATIBILITY_RFC5766;
205 206
}

207
Stream *agent_find_stream (NiceAgent *agent, guint stream_id)
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
{
  GSList *i;

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

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

  return NULL;
}


223 224
gboolean
agent_find_component (
225 226 227 228 229 230 231
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  Stream **stream,
  Component **component)
{
  Stream *s;
232
  Component *c;
233

234
  s = agent_find_stream (agent, stream_id);
235

236
  if (s == NULL)
237 238
    return FALSE;

239 240 241 242 243
  c = stream_find_component_by_id (s, component_id);

  if (c == NULL)
    return FALSE;

244 245 246 247
  if (stream)
    *stream = s;

  if (component)
248
    *component = c;
249 250 251 252 253

  return TRUE;
}


Dafydd Harries's avatar
Dafydd Harries committed
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
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;

281
  /* install properties */
282 283 284 285 286 287
  /**
   * 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.
   */
288 289 290 291 292 293 294
  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));

295 296 297 298 299 300 301
  /**
   * NiceAgent:compatibility:
   *
   * The Nice agent can work in various compatibility modes depending on
   * what the application/peer needs.
   * <para> See also: #NiceCompatibility</para>
   */
302 303 304 305 306
  g_object_class_install_property (gobject_class, PROP_COMPATIBILITY,
      g_param_spec_uint (
         "compatibility",
         "ICE specification compatibility",
         "The compatibility mode for the agent",
307 308
         NICE_COMPATIBILITY_RFC5245, NICE_COMPATIBILITY_LAST,
         NICE_COMPATIBILITY_RFC5245,
309 310
         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

Dafydd Harries's avatar
Dafydd Harries committed
311
  g_object_class_install_property (gobject_class, PROP_STUN_SERVER,
312
      g_param_spec_string (
Dafydd Harries's avatar
Dafydd Harries committed
313
        "stun-server",
314 315
        "STUN server IP address",
        "The IP address (not the hostname) of the STUN server to use",
316
        NULL,
Dafydd Harries's avatar
Dafydd Harries committed
317 318
        G_PARAM_READWRITE));

319 320 321 322
  g_object_class_install_property (gobject_class, PROP_STUN_SERVER_PORT,
      g_param_spec_uint (
        "stun-server-port",
        "STUN server port",
323
        "Port of the STUN server used to gather server-reflexive candidates",
Youness Alaoui's avatar
Youness Alaoui committed
324
        1, 65536,
325
	1, /* not a construct property, ignored */
326 327
        G_PARAM_READWRITE));

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
  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));

344 345 346 347
  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
348 349
        "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
350
        1, 0xffffffff,
351 352 353
	NICE_AGENT_TIMER_TA_DEFAULT,
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

354
  /* note: according to spec recommendation in sect 5.7.3 (ID-19) */
355 356 357 358 359
  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
360
        0, 0xffffffff,
361 362 363
	0, /* default set in init */
        G_PARAM_READWRITE));

364 365 366 367 368 369 370
  /**
   * NiceAgent:proxy-ip:
   *
   * The proxy server IP used to bypass a proxy firewall
   *
   * Since: 0.0.4
   */
371 372 373 374
  g_object_class_install_property (gobject_class, PROP_PROXY_IP,
      g_param_spec_string (
        "proxy-ip",
        "Proxy server IP",
375
        "The proxy server IP used to bypass a proxy firewall",
376 377 378
        NULL,
        G_PARAM_READWRITE));

379 380 381 382 383 384 385
  /**
   * NiceAgent:proxy-port:
   *
   * The proxy server port used to bypass a proxy firewall
   *
   * Since: 0.0.4
   */
386 387 388 389
  g_object_class_install_property (gobject_class, PROP_PROXY_PORT,
      g_param_spec_uint (
        "proxy-port",
        "Proxy server port",
390
        "The Proxy server port used to bypass a proxy firewall",
391 392 393 394
        1, 65536,
	1,
        G_PARAM_READWRITE));

395 396 397 398 399 400 401
  /**
   * NiceAgent:proxy-type:
   *
   * The type of proxy set in the proxy-ip property
   *
   * Since: 0.0.4
   */
402 403 404 405 406 407 408 409 410
  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));

411 412 413 414 415 416 417
  /**
   * NiceAgent:proxy-username:
   *
   * The username used to authenticate with the proxy
   *
   * Since: 0.0.4
   */
418 419 420 421 422 423 424 425
  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));

426 427 428 429 430 431 432
  /**
   * NiceAgent:proxy-password:
   *
   * The password used to authenticate with the proxy
   *
   * Since: 0.0.4
   */
433 434 435 436 437 438 439 440
  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));

441 442 443 444 445 446 447 448
  /**
   * 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
449 450 451
   g_object_class_install_property (gobject_class, PROP_UPNP,
      g_param_spec_boolean (
        "upnp",
452
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
453 454 455
        "Use UPnP",
        "Whether the agent should use UPnP to open a port in the router and "
        "get the external IP",
456 457 458 459
#else
        "Use UPnP (disabled in build)",
        "Does nothing because libnice was not built with UPnP support",
#endif
Youness Alaoui's avatar
Youness Alaoui committed
460 461 462
	TRUE, /* enable UPnP by default */
        G_PARAM_READWRITE| G_PARAM_CONSTRUCT));

463 464 465 466
  /**
   * NiceAgent:upnp-timeout:
   *
   * The maximum amount of time to wait for UPnP discovery to finish before
467
   * signaling the #NiceAgent::candidate-gathering-done signal
468 469 470
   *
   * Since: 0.0.7
   */
Youness Alaoui's avatar
Youness Alaoui committed
471 472 473
  g_object_class_install_property (gobject_class, PROP_UPNP_TIMEOUT,
      g_param_spec_uint (
        "upnp-timeout",
474
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
475 476 477
        "Timeout for UPnP discovery",
        "The maximum amount of time to wait for UPnP discovery to finish before "
        "signaling the candidate-gathering-done signal",
478 479 480 481
#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
482
        100, 60000,
483
	DEFAULT_UPNP_TIMEOUT,
Youness Alaoui's avatar
Youness Alaoui committed
484 485
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

Youness Alaoui's avatar
Youness Alaoui committed
486 487 488 489 490 491 492 493
  /**
   * NiceAgent:reliable:
   *
   * Whether the agent should use PseudoTcp to ensure a reliable transport
   * of messages
   *
   * Since: 0.0.11
   */
494 495 496 497
   g_object_class_install_property (gobject_class, PROP_RELIABLE,
      g_param_spec_boolean (
        "reliable",
        "reliable mode",
Youness Alaoui's avatar
Youness Alaoui committed
498
        "Whether the agent should use PseudoTcp to ensure a reliable transport"
499 500 501 502
        "of messages",
	FALSE,
        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

503 504
  /* install signals */

505 506
  /**
   * NiceAgent::component-state-changed
Youness Alaoui's avatar
Youness Alaoui committed
507
   * @agent: The #NiceAgent object
508 509 510 511 512 513
   * @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
   */
514 515 516 517
  signals[SIGNAL_COMPONENT_STATE_CHANGED] =
      g_signal_new (
          "component-state-changed",
          G_OBJECT_CLASS_TYPE (klass),
518
          G_SIGNAL_RUN_LAST,
519 520 521 522 523 524 525 526
          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);
527

528 529
  /**
   * NiceAgent::candidate-gathering-done:
Youness Alaoui's avatar
Youness Alaoui committed
530
   * @agent: The #NiceAgent object
531 532 533 534 535
   * @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()
   */
536 537 538 539
  signals[SIGNAL_CANDIDATE_GATHERING_DONE] =
      g_signal_new (
          "candidate-gathering-done",
          G_OBJECT_CLASS_TYPE (klass),
540
          G_SIGNAL_RUN_LAST,
541 542 543
          0,
          NULL,
          NULL,
544
          agent_marshal_VOID__UINT,
545
          G_TYPE_NONE,
546 547
          1,
          G_TYPE_UINT, G_TYPE_INVALID);
548

549 550
  /**
   * NiceAgent::new-selected-pair
Youness Alaoui's avatar
Youness Alaoui committed
551
   * @agent: The #NiceAgent object
552 553 554 555 556 557 558 559
   * @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
   */
560 561 562 563
  signals[SIGNAL_NEW_SELECTED_PAIR] =
      g_signal_new (
          "new-selected-pair",
          G_OBJECT_CLASS_TYPE (klass),
564
          G_SIGNAL_RUN_LAST,
565 566 567 568 569 570 571 572 573
          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);

574 575
  /**
   * NiceAgent::new-candidate
Youness Alaoui's avatar
Youness Alaoui committed
576
   * @agent: The #NiceAgent object
577 578 579 580 581
   * @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
582
   * <para> See also: #NiceAgent::candidate-gathering-done </para>
583
   */
584 585 586 587
  signals[SIGNAL_NEW_CANDIDATE] =
      g_signal_new (
          "new-candidate",
          G_OBJECT_CLASS_TYPE (klass),
588
          G_SIGNAL_RUN_LAST,
589 590 591 592 593 594 595 596 597
          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);

598 599
  /**
   * NiceAgent::new-remote-candidate
Youness Alaoui's avatar
Youness Alaoui committed
600
   * @agent: The #NiceAgent object
601 602 603 604 605 606 607
   * @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.
   */
608 609 610 611
  signals[SIGNAL_NEW_REMOTE_CANDIDATE] =
      g_signal_new (
          "new-remote-candidate",
          G_OBJECT_CLASS_TYPE (klass),
612
          G_SIGNAL_RUN_LAST,
613 614 615 616 617 618 619 620 621
          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);

622 623
  /**
   * NiceAgent::initial-binding-request-received
Youness Alaoui's avatar
Youness Alaoui committed
624
   * @agent: The #NiceAgent object
625 626 627 628 629
   * @stream_id: The ID of the stream
   *
   * This signal is fired when we received our first binding request from
   * the peer.
   */
630 631 632 633
  signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED] =
      g_signal_new (
          "initial-binding-request-received",
          G_OBJECT_CLASS_TYPE (klass),
634
          G_SIGNAL_RUN_LAST,
635 636 637 638 639 640 641 642 643
          0,
          NULL,
          NULL,
          agent_marshal_VOID__UINT,
          G_TYPE_NONE,
          1,
          G_TYPE_UINT,
          G_TYPE_INVALID);

644 645
  /**
   * NiceAgent::reliable-transport-writable
Youness Alaoui's avatar
Youness Alaoui committed
646
   * @agent: The #NiceAgent object
647 648 649 650 651 652 653 654
   * @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.
655 656
   *
   * Since: 0.0.11
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
   */
  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);


673 674
  /* Init debug options depending on env variables */
  nice_debug_init ();
Dafydd Harries's avatar
Dafydd Harries committed
675 676
}

677 678 679 680
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
681 682 683 684 685 686

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

688
  /* set defaults; not construct params, so set here */
689
  agent->stun_server_port = DEFAULT_STUN_PORT;
690
  agent->controlling_mode = TRUE;
691
  agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT;
692 693

  agent->discovery_list = NULL;
694
  agent->discovery_unsched_items = 0;
695 696 697
  agent->discovery_timer_source = NULL;
  agent->conncheck_timer_source = NULL;
  agent->keepalive_timer_source = NULL;
698
  agent->refresh_list = NULL;
699
  agent->media_after_tick = FALSE;
700
  agent->software_attribute = NULL;
701

702
  agent->compatibility = NICE_COMPATIBILITY_RFC5245;
703
  agent->reliable = FALSE;
704

705
  stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
706
      STUN_COMPATIBILITY_RFC5389,
707 708 709
      STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
      STUN_AGENT_USAGE_USE_FINGERPRINT);

Dafydd Harries's avatar
Dafydd Harries committed
710
  agent->rng = nice_rng_new ();
711
  priv_generate_tie_breaker (agent);
Dafydd Harries's avatar
Dafydd Harries committed
712 713 714
}


715
NICEAPI_EXPORT NiceAgent *
716
nice_agent_new (GMainContext *ctx, NiceCompatibility compat)
Dafydd Harries's avatar
Dafydd Harries committed
717
{
718 719
  NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
      "compatibility", compat,
720
      "main-context", ctx,
721 722 723 724 725 726 727 728 729 730 731 732 733 734
      "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
735
      NULL);
736 737

  return agent;
Dafydd Harries's avatar
Dafydd Harries committed
738 739 740 741 742 743 744 745 746 747 748
}


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
749

750
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
751

Dafydd Harries's avatar
Dafydd Harries committed
752 753
  switch (property_id)
    {
754 755 756 757
    case PROP_MAIN_CONTEXT:
      g_value_set_pointer (value, agent->main_context);
      break;

758 759 760 761
    case PROP_COMPATIBILITY:
      g_value_set_uint (value, agent->compatibility);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
762
    case PROP_STUN_SERVER:
763
      g_value_set_string (value, agent->stun_server_ip);
764
      break;
765 766 767

    case PROP_STUN_SERVER_PORT:
      g_value_set_uint (value, agent->stun_server_port);
768
      break;
769

770 771 772 773 774 775 776
    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
777

778 779 780 781
    case PROP_STUN_PACING_TIMER:
      g_value_set_uint (value, agent->timer_ta);
      break;

782 783 784 785 786
    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;

787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
    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
807
    case PROP_UPNP:
808
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
809
      g_value_set_boolean (value, agent->upnp_enabled);
810 811 812
#else
      g_value_set_boolean (value, FALSE);
#endif
Youness Alaoui's avatar
Youness Alaoui committed
813 814 815
      break;

    case PROP_UPNP_TIMEOUT:
816
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
817
      g_value_set_uint (value, agent->upnp_timeout);
818 819
#else
      g_value_set_uint (value, DEFAULT_UPNP_TIMEOUT);
Youness Alaoui's avatar
Youness Alaoui committed
820
#endif
821
      break;
Youness Alaoui's avatar
Youness Alaoui committed
822

823 824 825 826
    case PROP_RELIABLE:
      g_value_set_boolean (value, agent->reliable);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
827 828 829
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
830

831
  agent_unlock();
Dafydd Harries's avatar
Dafydd Harries committed
832 833 834 835 836 837 838 839 840 841 842 843
}


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

844
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
845

Dafydd Harries's avatar
Dafydd Harries committed
846 847
  switch (property_id)
    {
848 849
    case PROP_MAIN_CONTEXT:
      agent->main_context = g_value_get_pointer (value);
850 851
      if (agent->main_context != NULL)
        g_main_context_ref (agent->main_context);
852 853
      break;

854 855
    case PROP_COMPATIBILITY:
      agent->compatibility = g_value_get_uint (value);
856
      if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
857 858 859 860
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
861
      } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
Youness Alaoui's avatar
Youness Alaoui committed
862 863
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
864 865
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_FORCE_VALIDATER);
866 867 868 869
      } 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
870
            STUN_AGENT_USAGE_USE_FINGERPRINT);
871 872 873 874 875 876
      } 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
877 878 879 880
      } 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 |
881 882
            STUN_AGENT_USAGE_USE_FINGERPRINT |
            STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES);
883 884 885 886 887
      } 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);
888
      }
889
      stun_agent_set_software (&agent->stun_agent, agent->software_attribute);
890

891 892
      break;

Dafydd Harries's avatar
Dafydd Harries committed
893
    case PROP_STUN_SERVER:
894
      g_free (agent->stun_server_ip);
895 896 897 898 899 900 901
      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;

902 903 904 905 906 907 908 909
    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;

910 911 912 913
    case PROP_STUN_PACING_TIMER:
      agent->timer_ta = g_value_get_uint (value);
      break;

914 915 916 917
    case PROP_MAX_CONNECTIVITY_CHECKS:
      agent->max_conn_checks = g_value_get_uint (value);
      break;

918
    case PROP_PROXY_IP:
919
      g_free (agent->proxy_ip);
920 921 922 923 924 925 926 927 928 929 930 931
      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:
932
      g_free (agent->proxy_username);
933 934 935 936
      agent->proxy_username = g_value_dup_string (value);
      break;

    case PROP_PROXY_PASSWORD:
937
      g_free (agent->proxy_password);
938 939 940
      agent->proxy_password = g_value_dup_string (value);
      break;

Youness Alaoui's avatar
Youness Alaoui committed
941
    case PROP_UPNP_TIMEOUT:
942
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
943
      agent->upnp_timeout = g_value_get_uint (value);
944
#endif
Youness Alaoui's avatar
Youness Alaoui committed
945 946 947
      break;

    case PROP_UPNP:
948
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
949 950
      agent->upnp_enabled = g_value_get_boolean (value);
#endif
951
      break;
952 953 954 955 956

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

Dafydd Harries's avatar
Dafydd Harries committed
957 958 959
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
960

961
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
962

Dafydd Harries's avatar
Dafydd Harries committed
963 964
}

965

966 967 968 969 970 971 972 973 974 975 976 977
static void priv_destroy_component_tcp (Component *component)
{
    if (component->tcp_clock) {
      g_source_destroy (component->tcp_clock);
      g_source_unref (component->tcp_clock);
      component->tcp_clock = NULL;
    }
    if (component->tcp) {
      pseudo_tcp_socket_close (component->tcp, TRUE);
      g_object_unref (component->tcp);
      component->tcp = NULL;
    }
978 979 980 981
    if (component->tcp_data != NULL) {
      g_slice_free (TcpUserData, component->tcp_data);
      component->tcp_data = NULL;
    }
982 983 984 985 986 987 988 989 990 991
}

static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream,
    Component *component)
{
  if (component->tcp) {
    agent_signal_component_state_change (agent, stream->id,
        component->id, NICE_COMPONENT_STATE_FAILED);
    priv_detach_stream_component (stream, component);
  }
992
  priv_destroy_component_tcp (component);
993 994 995 996
}

static void
adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component);
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019


static void
pseudo_tcp_socket_opened (PseudoTcpSocket *sock, gpointer user_data)
{
  TcpUserData *data = (TcpUserData *)user_data;
  NiceAgent *agent = data->agent;
  Component *component = data->component;
  Stream *stream = data->stream;

  nice_debug ("Agent %p: s%d:%d pseudo Tcp socket Opened", data->agent,
      stream->id, component->id);
  g_signal_emit (agent, signals[SIGNAL_RELIABLE_TRANSPORT_WRITABLE], 0,
      stream->id, component->id);
}

static void
pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data)
{
  TcpUserData *data = (TcpUserData *)user_data;
  NiceAgent *agent = data->agent;
  Component *component = data->component;
  Stream *stream = data->stream;
1020
  gchar buf[MAX_BUFFER_SIZE];
1021 1022 1023 1024 1025
  gint len;

  nice_debug ("Agent %p: s%d:%d pseudo Tcp socket readable", agent,
      stream->id, component->id);

1026 1027
  component->tcp_readable = TRUE;

1028 1029 1030
  g_object_add_weak_pointer (G_OBJECT (sock), (gpointer *)&sock);
  g_object_add_weak_pointer (G_OBJECT (agent), (gpointer *)&agent);

1031
  do {
1032 1033 1034 1035
    if (component->g_source_io_cb)
      len = pseudo_tcp_socket_recv (sock, buf, sizeof(buf));
    else
      len = 0;
1036

1037
    if (len > 0) {
1038 1039 1040 1041 1042 1043 1044 1045
      gpointer data = component->data;
      gint sid = stream->id;
      gint cid = component->id;
      NiceAgentRecvFunc callback = component->g_source_io_cb;
      /* Unlock the agent before calling the callback */
      agent_unlock();
      callback (agent, sid, cid, len, buf, data);
      agent_lock();
1046 1047 1048 1049
      if (sock == NULL) {
        nice_debug ("PseudoTCP socket got destroyed in readable callback!");
        break;
      }
1050 1051 1052
    } else if (len < 0 &&
        pseudo_tcp_socket_get_error (sock) != EWOULDBLOCK) {
      /* Signal error */
1053
      priv_pseudo_tcp_error (agent, stream, component);
1054 <