agent.c 107 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 49
#include <glib.h>

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

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

59 60
#include "debug.h"

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

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

74 75
#include "pseudotcp.h"

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

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

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

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


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

static guint signals[N_SIGNALS];

123 124 125 126 127
#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
128

Youness Alaoui's avatar
Youness Alaoui committed
129 130
static void priv_free_upnp (NiceAgent *agent);

131
#if GLIB_CHECK_VERSION(2,31,8)
132 133
void agent_lock (void)
{
134
  g_rec_mutex_lock (&agent_mutex);
135 136 137 138
}

void agent_unlock (void)
{
139 140 141 142 143 144 145
  g_rec_mutex_unlock (&agent_mutex);
}

#else
void agent_lock(void)
{
  g_static_rec_mutex_lock (&agent_mutex);
146 147
}

148 149 150 151
void agent_unlock(void)
{
  g_static_rec_mutex_unlock (&agent_mutex);
}
152

153
#endif
154

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


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

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

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

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

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

  return NULL;
}


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

231
  s = agent_find_stream (agent, stream_id);
232

233
  if (s == NULL)
234 235
    return FALSE;

236 237 238 239 240
  c = stream_find_component_by_id (s, component_id);

  if (c == NULL)
    return FALSE;

241 242 243 244
  if (stream)
    *stream = s;

  if (component)
245
    *component = c;
246 247 248 249 250

  return TRUE;
}


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

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

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

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

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

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
  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));

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

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

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

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

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

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

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

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

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

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

500 501
  /* install signals */

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

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

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

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

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

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

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

669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
  /**
   * 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,
          G_TYPE_POINTER,
          G_TYPE_INVALID);
693

694 695
  /* Init debug options depending on env variables */
  nice_debug_init ();
Dafydd Harries's avatar
Dafydd Harries committed
696 697
}

698 699 700 701
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
702 703 704 705 706 707

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

709
  /* set defaults; not construct params, so set here */
710
  agent->stun_server_port = DEFAULT_STUN_PORT;
711
  agent->controlling_mode = TRUE;
712
  agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT;
713 714

  agent->discovery_list = NULL;
715
  agent->discovery_unsched_items = 0;
716 717 718
  agent->discovery_timer_source = NULL;
  agent->conncheck_timer_source = NULL;
  agent->keepalive_timer_source = NULL;
719
  agent->refresh_list = NULL;
720
  agent->media_after_tick = FALSE;
721
  agent->software_attribute = NULL;
722

723
  agent->compatibility = NICE_COMPATIBILITY_RFC5245;
724
  agent->reliable = FALSE;
725

726
  stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
727
      STUN_COMPATIBILITY_RFC5389,
728 729 730
      STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
      STUN_AGENT_USAGE_USE_FINGERPRINT);

Dafydd Harries's avatar
Dafydd Harries committed
731
  agent->rng = nice_rng_new ();
732
  priv_generate_tie_breaker (agent);
Dafydd Harries's avatar
Dafydd Harries committed
733 734 735
}


736
NICEAPI_EXPORT NiceAgent *
737
nice_agent_new (GMainContext *ctx, NiceCompatibility compat)
Dafydd Harries's avatar
Dafydd Harries committed
738
{
739 740
  NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
      "compatibility", compat,
741
      "main-context", ctx,
742 743 744 745 746 747 748 749 750 751 752 753 754 755
      "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
756
      NULL);
757 758

  return agent;
Dafydd Harries's avatar
Dafydd Harries committed
759 760 761 762 763 764 765 766 767 768 769
}


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
770

771
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
772

Dafydd Harries's avatar
Dafydd Harries committed
773 774
  switch (property_id)
    {
775 776 777 778
    case PROP_MAIN_CONTEXT:
      g_value_set_pointer (value, agent->main_context);
      break;

779 780 781 782
    case PROP_COMPATIBILITY:
      g_value_set_uint (value, agent->compatibility);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
783
    case PROP_STUN_SERVER:
784
      g_value_set_string (value, agent->stun_server_ip);
785
      break;
786 787 788

    case PROP_STUN_SERVER_PORT:
      g_value_set_uint (value, agent->stun_server_port);
789
      break;
790

791 792 793 794 795 796 797
    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
798

799 800 801 802
    case PROP_STUN_PACING_TIMER:
      g_value_set_uint (value, agent->timer_ta);
      break;

803 804 805 806 807
    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;

808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
    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
828
    case PROP_UPNP:
829
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
830
      g_value_set_boolean (value, agent->upnp_enabled);
831 832 833
#else
      g_value_set_boolean (value, FALSE);
#endif
Youness Alaoui's avatar
Youness Alaoui committed
834 835 836
      break;

    case PROP_UPNP_TIMEOUT:
837
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
838
      g_value_set_uint (value, agent->upnp_timeout);
839 840
#else
      g_value_set_uint (value, DEFAULT_UPNP_TIMEOUT);
Youness Alaoui's avatar
Youness Alaoui committed
841
#endif
842
      break;
Youness Alaoui's avatar
Youness Alaoui committed
843

844 845 846 847
    case PROP_RELIABLE:
      g_value_set_boolean (value, agent->reliable);
      break;

Dafydd Harries's avatar
Dafydd Harries committed
848 849 850
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
851

852
  agent_unlock();
Dafydd Harries's avatar
Dafydd Harries committed
853 854 855 856 857 858 859 860 861 862 863 864
}


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

865
  agent_lock();
Youness Alaoui's avatar
Youness Alaoui committed
866

Dafydd Harries's avatar
Dafydd Harries committed
867 868
  switch (property_id)
    {
869 870
    case PROP_MAIN_CONTEXT:
      agent->main_context = g_value_get_pointer (value);
871 872
      if (agent->main_context != NULL)
        g_main_context_ref (agent->main_context);
873 874
      break;

875 876
    case PROP_COMPATIBILITY:
      agent->compatibility = g_value_get_uint (value);
877
      if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
878 879 880 881
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
882
      } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
Youness Alaoui's avatar
Youness Alaoui committed
883 884
        stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
            STUN_COMPATIBILITY_RFC3489,
885 886
            STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
            STUN_AGENT_USAGE_FORCE_VALIDATER);
887 888 889 890
      } 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
891
            STUN_AGENT_USAGE_USE_FINGERPRINT);
892 893 894 895 896 897
      } 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
898 899 900 901
      } 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 |
902 903
            STUN_AGENT_USAGE_USE_FINGERPRINT |
            STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES);
904 905 906 907 908
      } 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);
909
      }
910
      stun_agent_set_software (&agent->stun_agent, agent->software_attribute);
911

912 913
      break;

Dafydd Harries's avatar
Dafydd Harries committed
914
    case PROP_STUN_SERVER:
915
      g_free (agent->stun_server_ip);
916 917 918 919 920 921 922
      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;

923 924 925 926 927 928 929 930
    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;

931 932 933 934
    case PROP_STUN_PACING_TIMER:
      agent->timer_ta = g_value_get_uint (value);
      break;

935 936 937 938
    case PROP_MAX_CONNECTIVITY_CHECKS:
      agent->max_conn_checks = g_value_get_uint (value);
      break;

939
    case PROP_PROXY_IP:
940
      g_free (agent->proxy_ip);
941 942 943 944 945 946 947 948 949 950 951 952
      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:
953
      g_free (agent->proxy_username);
954 955 956 957
      agent->proxy_username = g_value_dup_string (value);
      break;

    case PROP_PROXY_PASSWORD:
958
      g_free (agent->proxy_password);
959 960 961
      agent->proxy_password = g_value_dup_string (value);
      break;

Youness Alaoui's avatar
Youness Alaoui committed
962
    case PROP_UPNP_TIMEOUT:
963
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
964
      agent->upnp_timeout = g_value_get_uint (value);
965
#endif
Youness Alaoui's avatar
Youness Alaoui committed
966 967 968
      break;

    case PROP_UPNP:
969
#ifdef HAVE_GUPNP
Youness Alaoui's avatar
Youness Alaoui committed
970 971
      agent->upnp_enabled = g_value_get_boolean (value);
#endif
972
      break;
973 974 975 976 977

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

Dafydd Harries's avatar
Dafydd Harries committed
978 979 980
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
Youness Alaoui's avatar
Youness Alaoui committed
981

982
  agent_unlock();
Youness Alaoui's avatar
Youness Alaoui committed
983

Dafydd Harries's avatar
Dafydd Harries committed
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);
992
    component_detach_all_sockets (component);
993 994 995 996 997 998 999 1000 1001
    pseudo_tcp_socket_close (component->tcp, TRUE);
    g_object_unref (component->tcp);
    component->tcp = NULL;
  }

  if (component->tcp_clock) {
    g_source_destroy (component->tcp_clock);
    g_source_unref (component->tcp_clock);
    component->tcp_clock = NULL;
1002 1003 1004 1005 1006
  }
}

static void
adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component);
1007 1008 1009 1010 1011


static void
pseudo_tcp_socket_opened (PseudoTcpSocket *sock, gpointer user_data)
{
1012 1013 1014
  Component *component = user_data;
  NiceAgent *agent = component->agent;
  Stream *stream = component->stream;
1015

1016
  nice_debug ("Agent %p: s%d:%d pseudo Tcp socket Opened", agent,
1017 1018 1019 1020 1021 1022 1023 1024
      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)
{
1025 1026 1027
  Component *component = user_data;
  NiceAgent *agent = component->agent;
  Stream *stream = component->stream;
1028 1029
  guint8 buf[MAX_BUFFER_SIZE];
  gssize len;
1030
  gboolean has_io_callback;
1031 1032 1033 1034

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

1035 1036
  component->tcp_readable = TRUE;

1037 1038
  g_object_add_weak_pointer (G_OBJECT (sock), (gpointer *)&sock);
  g_object_add_weak_pointer (G_OBJECT (agent), (gpointer *)&agent);
1039
  has_io_callback = component_has_io_callback (component);
1040

1041
  do {
1042 1043
    /* Only dequeue pseudo-TCP data if we can reliably inform the client. */
    if (has_io_callback) {
1044
      len = pseudo_tcp_socket_recv (sock, (gchar *) buf, sizeof(buf));
1045 1046 1047 1048 1049
    } else if (component->recv_buf != NULL) {
      len = pseudo_tcp_socket_recv (sock,
          (gchar *) component->recv_buf + component->recv_buf_valid_len,
          component->recv_buf_len - component->recv_buf_valid_len);
    } else {
1050
      len = 0;
1051
    }
1052

1053 1054 1055
    nice_debug ("%s: len %" G_GSSIZE_FORMAT, G_STRFUNC, len);

    if (len > 0 && has_io_callback) {
1056
      component_emit_io_callback (component, buf, len);
1057 1058 1059 1060
      if (sock == NULL) {
        nice_debug ("PseudoTCP socket got destroyed in readable callback!");
        break;
      }
1061 1062 1063 1064
    } else if (len > 0 && component->recv_buf != NULL) {
      /* No callback to call. The data has been copied directly into the
       * client’s receive buffer. */
      component->recv_buf_valid_len += len;
1065 1066 1067
    } else if (len < 0 &&
        pseudo_tcp_socket_get_error (sock) != EWOULDBLOCK) {
      /* Signal error */
1068
      nice_debug ("%s: calling priv_pseudo_tcp_error()", G_STRFUNC);
1069
      priv_pseudo_tcp_error (agent, stream, component);
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081

      if (component->recv_buf != NULL) {
        GIOErrorEnum error_code;

        if (pseudo_tcp_socket_get_error (sock) == ENOTCONN)
          error_code = G_IO_ERROR_BROKEN_PIPE;
        else
          error_code = G_IO_ERROR_FAILED;

        g_set_error (component->recv_buf_error, G_IO_ERROR, error_code,
            "Error reading data from pseudo-TCP socket.");
      }
1082 1083 1084
    } else if (len < 0 &&
        pseudo_tcp_socket_get_error (sock) == EWOULDBLOCK){
      component->tcp_readable = FALSE;
1085
    }
1086 1087 1088 1089 1090

    has_io_callback = component_has_io_callback (component);
  } while (len > 0 &&
           (has_io_callback ||
            component->recv_buf_valid_len < component->recv_buf_len));
1091

1092
  if (agent) {
1093
    adjust_tcp_clock (agent, stream, component);
1094 1095
    g_object_remove_weak_pointer (G_OBJECT (agent), (gpointer *)&agent);
  } else {
1096
    nice_debug ("Not calling adjust_tcp_clock.. agent got destroyed!");
1097 1098 1099
  }
  if (sock)
    g_object_remove_weak_pointer (G_OBJECT (sock), (gpointer *)&sock);
1100 1101 1102 1103 1104
}

static void
pseudo_tcp_socket_writable (PseudoTcpSocket *sock, gpointer user_data)
{
1105 1106 1107
  Component *component = user_data;
  NiceAgent *agent = component->agent;
  Stream *stream = component->stream;
1108

1109 1110
  nice_debug ("Agent %p: s%d:%d pseudo Tcp socket writable", agent,
      stream->id, component->id);
1111 1112 1113 1114 1115 1116 1117 1118
  g_signal_emit (agent, signals[SIGNAL_RELIABLE_TRANSPORT_WRITABLE], 0,
      stream->id, component->id);
}

static void
pseudo_tcp_socket_closed (PseudoTcpSocket *sock, guint32 err,
    gpointer user_data)
{
1119 1120 1121
  Component *component = user_data;
  NiceAgent *agent = component->agent;
  Stream *stream = component->stream;
1122

1123 1124
  nice_debug ("Agent %p: s%d:%d pseudo Tcp socket closed. "
      "Calling priv_pseudo_tcp_error().",  agent, stream->id, component->id);
1125
  priv_pseudo_tcp_error (agent, stream, component);
1126 1127 1128