...
 
Commits (118)
......@@ -142,6 +142,7 @@ tests/test-send-recv
tests/test-mainloop
tests/test-priority
tests/test-pseudotcp
tests/test-pseudotcp-fin
tests/test-pseudotcp-fuzzy
tests/test-restart
tests/test-thread
......
......@@ -44,6 +44,10 @@
#include <string.h>
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include "address.h"
#ifdef G_OS_WIN32
......@@ -197,18 +201,21 @@ nice_address_get_port (const NiceAddress *addr)
NICEAPI_EXPORT gboolean
nice_address_set_from_string (NiceAddress *addr, const gchar *str)
{
union
{
struct in_addr ipv4;
struct in6_addr ipv6;
} a;
if (inet_pton (AF_INET, str, &a.ipv4) > 0)
nice_address_set_ipv4 (addr, ntohl (a.ipv4.s_addr));
else if (inet_pton (AF_INET6, str, &a.ipv6) > 0)
nice_address_set_ipv6 (addr, a.ipv6.s6_addr);
else
return FALSE; /* Invalid address */
struct addrinfo hints;
struct addrinfo *res;
memset (&hints, 0, sizeof (hints));
/* AI_NUMERICHOST prevents getaddrinfo() from doing DNS resolution. */
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo (str, NULL, &hints, &res) != 0)
return FALSE; /* invalid address */
nice_address_set_from_sockaddr (addr, res->ai_addr);
freeaddrinfo (res);
return TRUE;
}
......
......@@ -147,17 +147,17 @@ struct _NiceAgent
guint64 tie_breaker; /* tie breaker (ICE sect 5.2
"Determining Role" ID-19) */
NiceCompatibility compatibility; /* property: Compatibility mode */
StunAgent stun_agent; /* STUN agent */
gboolean media_after_tick; /* Received media after keepalive tick */
#ifdef HAVE_GUPNP
GUPnPSimpleIgdThread* upnp; /* GUPnP Single IGD agent */
gboolean upnp_enabled; /* whether UPnP discovery is enabled */
guint upnp_timeout; /* UPnP discovery timeout */
GSList *upnp_mapping; /* list of Candidates being mapped */
GSList *upnp_mapping; /* NiceAddresses of cands being mapped */
GSource *upnp_timer_source; /* source of upnp timeout timer */
#endif
gchar *software_attribute; /* SOFTWARE attribute */
gboolean reliable; /* property: reliable */
gboolean keepalive_conncheck; /* property: keepalive_conncheck */
GQueue pending_signals;
/* XXX: add pointer to internal data struct for ABI-safe extensions */
......@@ -203,12 +203,18 @@ void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *st
guint64 agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandidate *remote);
GSource *agent_timeout_add_with_context (NiceAgent *agent, guint interval, GSourceFunc function, gpointer data);
void agent_timeout_add_with_context (NiceAgent *agent, GSource **out,
const gchar *name, guint interval, GSourceFunc function, gpointer data);
StunUsageIceCompatibility agent_to_ice_compatibility (NiceAgent *agent);
StunUsageTurnCompatibility agent_to_turn_compatibility (NiceAgent *agent);
NiceTurnSocketCompatibility agent_to_turn_socket_compatibility (NiceAgent *agent);
void agent_remove_local_candidate (NiceAgent *agent,
NiceCandidate *candidate);
void nice_agent_init_stun_agent (NiceAgent *agent, StunAgent *stun_agent);
void _priv_set_socket_tos (NiceAgent *agent, NiceSocket *sock, gint tos);
gboolean
......@@ -240,10 +246,14 @@ void nice_debug_init (void);
#ifdef NDEBUG
static inline gboolean nice_debug_is_enabled (void) { return FALSE; }
static inline gboolean nice_debug_is_verbose (void) { return FALSE; }
static inline void nice_debug (const char *fmt, ...) { }
static inline void nice_debug_verbose (const char *fmt, ...) { }
#else
gboolean nice_debug_is_enabled (void);
gboolean nice_debug_is_verbose (void);
void nice_debug (const char *fmt, ...) G_GNUC_PRINTF (1, 2);
void nice_debug_verbose (const char *fmt, ...) G_GNUC_PRINTF (1, 2);
#endif
#endif /*_NICE_AGENT_PRIV_H */
This diff is collapsed.
......@@ -431,7 +431,10 @@ nice_agent_add_stream (
* @agent: The #NiceAgent Object
* @stream_id: The ID of the stream to remove
*
* Remove and free a previously created data stream from @agent
* Remove and free a previously created data stream from @agent. If any I/O
* streams have been created using nice_agent_get_io_stream(), they should be
* closed completely using g_io_stream_close() before this is called, or they
* will get broken pipe errors.
*
**/
void
......@@ -858,7 +861,8 @@ nice_agent_attach_recv (
* A single-message version of nice_agent_recv_messages().
*
* Returns: the number of bytes written to @buf on success (guaranteed to be
* greater than 0 unless @buf_len is 0), or -1 on error
* greater than 0 unless @buf_len is 0), 0 if in reliable mode and the remote
* peer closed the stream, or -1 on error
*
* Since: 0.1.5
*/
......@@ -915,7 +919,8 @@ nice_agent_recv (
* cancelled. %G_IO_ERROR_FAILED will be returned for other errors.
*
* Returns: the number of valid messages written to @messages on success
* (guaranteed to be greater than 0 unless @n_messages is 0), or -1 on error
* (guaranteed to be greater than 0 unless @n_messages is 0), 0 if the remote
* peer closed the stream, or -1 on error
*
* Since: 0.1.5
*/
......@@ -944,7 +949,8 @@ nice_agent_recv_messages (
* A single-message version of nice_agent_recv_messages_nonblocking().
*
* Returns: the number of bytes received into @buf on success (guaranteed to be
* greater than 0 unless @buf_len is 0), or -1 on error
* greater than 0 unless @buf_len is 0), 0 if in reliable mode and the remote
* peer closed the stream, or -1 on error
*
* Since: 0.1.5
*/
......@@ -993,7 +999,8 @@ nice_agent_recv_nonblocking (
* same stream/component pair.
*
* Returns: the number of valid messages written to @messages on success
* (guaranteed to be greater than 0 unless @n_messages is 0), or -1 on error
* (guaranteed to be greater than 0 unless @n_messages is 0), 0 if in reliable
* mode and the remote peer closed the stream, or -1 on error
*
* Since: 0.1.5
*/
......@@ -1467,6 +1474,24 @@ nice_agent_forget_relays (NiceAgent *agent,
guint stream_id,
guint component_id);
/**
* nice_agent_get_component_state:
* @agent: The #NiceAgent Object
* @stream_id: The ID of the stream
* @component_id: The ID of the component
*
* Retrieves the current state of a component.
*
* Returns: the #NiceComponentState of the component and
* %NICE_COMPONENT_STATE_FAILED if the component was invalid.
*
* Since: 0.1.7
*/
NiceComponentState
nice_agent_get_component_state (NiceAgent *agent,
guint stream_id,
guint component_id);
G_END_DECLS
#endif /* _AGENT_H */
......
......@@ -179,8 +179,11 @@ nice_candidate_pair_priority (guint32 o_prio, guint32 a_prio)
NICEAPI_EXPORT NiceCandidate *
nice_candidate_copy (const NiceCandidate *candidate)
{
NiceCandidate *copy = nice_candidate_new (candidate->type);
NiceCandidate *copy;
g_return_val_if_fail (candidate != NULL, NULL);
copy = nice_candidate_new (candidate->type);
memcpy (copy, candidate, sizeof(NiceCandidate));
copy->turn = NULL;
......
This diff is collapsed.
......@@ -74,7 +74,7 @@ struct _CandidatePairKeepalive
guint stream_id;
guint component_id;
StunTimer timer;
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE];
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
StunMessage stun_message;
};
......@@ -96,6 +96,9 @@ struct _IncomingCheck
uint16_t username_len;
};
void
incoming_check_free (IncomingCheck *icheck);
/* A pair of a socket and the GSource which polls it from the main loop. All
* GSources in a Component must be attached to the same main context:
* component->ctx.
......@@ -140,12 +143,12 @@ struct _Component
NiceComponentType type;
guint id; /**< component id */
NiceComponentState state;
GSList *local_candidates; /**< list of Candidate objs */
GSList *remote_candidates; /**< list of Candidate objs */
GSList *local_candidates; /**< list of NiceCandidate objs */
GSList *remote_candidates; /**< list of NiceCandidate objs */
GSList *socket_sources; /**< list of SocketSource objs; must only grow monotonically */
guint socket_sources_age; /**< incremented when socket_sources changes */
GSList *incoming_checks; /**< list of IncomingCheck objs */
GList *turn_servers; /**< List of TURN servers */
GList *turn_servers; /**< List of TurnServer objs */
CandidatePair selected_pair; /**< independent from checklists,
see ICE 11.1. "Sending Media" (ID-19) */
NiceCandidate *restart_candidate; /**< for storing active remote candidate during a restart */
......@@ -186,7 +189,11 @@ struct _Component
Stream *stream; /* unowned, immutable: can be accessed without holding the
* agent lock */
StunAgent stun_agent; /* This stun agent is used to validate all stun requests */
GCancellable *stop_cancellable;
GSource *stop_cancellable_source; /* owned */
PseudoTcpSocket *tcp;
GSource* tcp_clock;
......@@ -208,6 +215,9 @@ struct _Component
Component *
component_new (guint component_id, NiceAgent *agent, Stream *stream);
void
component_close (Component *cmp);
void
component_free (Component *cmp);
......@@ -237,9 +247,9 @@ void
component_free_socket_sources (Component *component);
GSource *
component_input_source_new (NiceAgent *agent, guint stream_id,
guint component_id, GPollableInputStream *pollable_istream,
GCancellable *cancellable);
component_source_new (NiceAgent *agent, guint stream_id,
guint component_id, GObject *pollable_istream,
GIOCondition condition, GCancellable *cancellable);
GMainContext *
component_dup_io_context (Component *component);
......@@ -260,6 +270,9 @@ component_has_io_callback (Component *component);
void
component_clean_turn_servers (Component *component);
void
component_clear_selected_pair (Component *component);
TurnServer *
turn_server_new (const gchar *server_ip, guint server_port,
......
This diff is collapsed.
......@@ -77,12 +77,13 @@ struct _CandidateCheckPair
guint64 priority;
GTimeVal next_tick; /* next tick timestamp */
StunTimer timer;
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE];
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
StunMessage stun_message;
};
int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote);
int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local);
gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote);
void conn_check_free (NiceAgent *agent);
gboolean conn_check_schedule_next (NiceAgent *agent);
int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair);
......@@ -90,5 +91,8 @@ void conn_check_prune_stream (NiceAgent *agent, Stream *stream);
gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len);
gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b);
void conn_check_remote_candidates_set(NiceAgent *agent);
void
conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component,
NiceSocket *sock);
#endif /*_NICE_CONNCHECK_H */
......@@ -38,6 +38,8 @@
# include <config.h>
#endif
#include <string.h>
#include "debug.h"
#include "stunagent.h"
......@@ -46,50 +48,63 @@
#include "agent-priv.h"
static int debug_enabled = 0;
static int debug_verbose_enabled = 0;
#define NICE_DEBUG_STUN 1
#define NICE_DEBUG_NICE 2
#define NICE_DEBUG_PSEUDOTCP 4
#define NICE_DEBUG_PSEUDOTCP_VERBOSE 8
#define NICE_DEBUG_NICE_VERBOSE 16
static const GDebugKey keys[] = {
{ (gchar *)"stun", NICE_DEBUG_STUN },
{ (gchar *)"nice", NICE_DEBUG_NICE },
{ (gchar *)"pseudotcp", NICE_DEBUG_PSEUDOTCP },
{ (gchar *)"pseudotcp-verbose", NICE_DEBUG_PSEUDOTCP_VERBOSE },
{ (gchar *)"nice-verbose", NICE_DEBUG_NICE_VERBOSE },
{ NULL, 0},
};
static void
stun_handler (const char *format, va_list ap)
{
g_logv ("libnice-stun", G_LOG_LEVEL_DEBUG, format, ap);
}
void nice_debug_init (void)
{
static gboolean debug_initialized = FALSE;
const gchar *flags_string;
guint flags;
const gchar *gflags_string;
guint flags = 0;
if (!debug_initialized) {
debug_initialized = TRUE;
flags_string = g_getenv ("NICE_DEBUG");
gflags_string = g_getenv ("G_MESSAGES_DEBUG");
nice_debug_disable (TRUE);
if (flags_string != NULL) {
if (flags_string)
flags = g_parse_debug_string (flags_string, keys, 4);
if (flags & NICE_DEBUG_NICE)
nice_debug_enable (FALSE);
if (flags & NICE_DEBUG_STUN)
stun_debug_enable ();
/* Set verbose before normal so that if we use 'all', then only
normal debug is enabled, we'd need to set pseudotcp-verbose without the
pseudotcp flag in order to actually enable verbose pseudotcp */
if (flags & NICE_DEBUG_PSEUDOTCP_VERBOSE)
pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE);
if (flags & NICE_DEBUG_PSEUDOTCP)
pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_NORMAL);
if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose"))
flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE;
if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) {
flags |= NICE_DEBUG_NICE_VERBOSE;
}
stun_set_debug_handler (stun_handler);
nice_debug_enable (TRUE);
if (flags & NICE_DEBUG_NICE_VERBOSE)
debug_verbose_enabled = TRUE;
/* Set verbose before normal so that if we use 'all', then only
normal debug is enabled, we'd need to set pseudotcp-verbose without the
pseudotcp flag in order to actually enable verbose pseudotcp */
if (flags & NICE_DEBUG_PSEUDOTCP_VERBOSE)
pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE);
else
pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_NORMAL);
}
}
......@@ -98,6 +113,10 @@ gboolean nice_debug_is_enabled (void)
{
return debug_enabled;
}
gboolean nice_debug_is_verbose (void)
{
return debug_verbose_enabled;
}
#else
/* Defined in agent-priv.h. */
#endif
......@@ -127,6 +146,15 @@ void nice_debug (const char *fmt, ...)
va_end (ap);
}
}
void nice_debug_verbose (const char *fmt, ...)
{
va_list ap;
if (debug_verbose_enabled) {
va_start (ap, fmt);
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, fmt, ap);
va_end (ap);
}
}
#else
/* Defined in agent-priv.h. */
#endif
......@@ -124,6 +124,33 @@ void discovery_prune_stream (NiceAgent *agent, guint stream_id)
}
}
/*
* Prunes the list of discovery processes for items related
* to socket @sock.
*
* @return TRUE on success, FALSE on a fatal error
*/
void discovery_prune_socket (NiceAgent *agent, NiceSocket *sock)
{
GSList *i;
for (i = agent->discovery_list; i ; ) {
CandidateDiscovery *discovery = i->data;
GSList *next = i->next;
if (discovery->nicesock == sock) {
agent->discovery_list = g_slist_remove (agent->discovery_list, discovery);
discovery_free_item (discovery);
}
i = next;
}
if (agent->discovery_list == NULL) {
/* noone using the timer anymore, clean it up */
discovery_free (agent);
}
}
/*
* Frees the CandidateDiscovery structure pointed to
......@@ -248,6 +275,23 @@ void refresh_prune_candidate (NiceAgent *agent, NiceCandidate *candidate)
}
}
void refresh_prune_socket (NiceAgent *agent, NiceSocket *sock)
{
GSList *i;
for (i = agent->refresh_list; i;) {
GSList *next = i->next;
CandidateRefresh *refresh = i->data;
if (refresh->nicesock == sock) {
agent->refresh_list = g_slist_delete_link (agent->refresh_list, i);
refresh_free_item (refresh);
}
i = next;
}
}
void refresh_cancel (CandidateRefresh *refresh)
{
refresh->agent->refresh_list = g_slist_remove (refresh->agent->refresh_list,
......@@ -265,6 +309,8 @@ static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_
{
GSList *i;
g_assert (candidate != NULL);
for (i = component->local_candidates; i ; i = i->next) {
NiceCandidate *c = i->data;
......@@ -291,7 +337,8 @@ static guint priv_highest_remote_foundation (Component *component)
for (highest = 1;; highest++) {
gboolean taken = FALSE;
g_snprintf (foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", highest);
g_snprintf (foundation, NICE_CANDIDATE_MAX_FOUNDATION, "peer-rflx-%u",
highest);
for (i = component->remote_candidates; i; i = i->next) {
NiceCandidate *cand = i->data;
if (strncmp (foundation, cand->foundation,
......@@ -413,7 +460,7 @@ static void priv_assign_remote_foundation (NiceAgent *agent, NiceCandidate *cand
if (component) {
next_remote_id = priv_highest_remote_foundation (component);
g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
"%u", next_remote_id);
"peer-rflx-%u", next_remote_id);
}
}
......@@ -863,7 +910,7 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer)
if (nice_debug_is_enabled ()) {
gchar tmpbuf[INET6_ADDRSTRLEN];
nice_address_to_string (&cand->server, tmpbuf);
nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.\n",
nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.",
agent, cand->type, tmpbuf);
}
if (nice_address_is_valid (&cand->server) &&
......@@ -970,7 +1017,7 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer)
/* case: not ready complete, so schedule next timeout */
unsigned int timeout = stun_timer_remainder (&cand->timer);
stun_debug ("STUN transaction retransmitted (timeout %dms).\n",
stun_debug ("STUN transaction retransmitted (timeout %dms).",
timeout);
/* retransmit */
......@@ -1062,7 +1109,9 @@ void discovery_schedule (NiceAgent *agent)
/* step: run first iteration immediately */
gboolean res = priv_discovery_tick_unlocked (agent);
if (res == TRUE) {
agent->discovery_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_discovery_tick, agent);
agent_timeout_add_with_context (agent, &agent->discovery_timer_source,
"Candidate discovery tick", agent->timer_ta,
priv_discovery_tick, agent);
}
}
}
......
......@@ -58,7 +58,7 @@ typedef struct
TurnServer *turn;
StunAgent stun_agent;
StunTimer timer;
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE];
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
StunMessage stun_message;
uint8_t stun_resp_buffer[STUN_MAX_MESSAGE_SIZE];
StunMessage stun_resp_msg;
......@@ -76,7 +76,7 @@ typedef struct
GSource *timer_source;
GSource *tick_source;
StunTimer timer;
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE];
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
StunMessage stun_message;
uint8_t stun_resp_buffer[STUN_MAX_MESSAGE_SIZE];
StunMessage stun_resp_msg;
......@@ -85,11 +85,13 @@ typedef struct
void refresh_free (NiceAgent *agent);
void refresh_prune_stream (NiceAgent *agent, guint stream_id);
void refresh_prune_candidate (NiceAgent *agent, NiceCandidate *candidate);
void refresh_prune_socket (NiceAgent *agent, NiceSocket *sock);
void refresh_cancel (CandidateRefresh *refresh);
void discovery_free (NiceAgent *agent);
void discovery_prune_stream (NiceAgent *agent, guint stream_id);
void discovery_prune_socket (NiceAgent *agent, NiceSocket *sock);
void discovery_schedule (NiceAgent *agent);
NiceCandidate *
......
......@@ -50,8 +50,8 @@
* component triple, and will be closed as soon as that stream is removed from
* the agent (e.g. if nice_agent_remove_stream() is called from another thread).
* If g_input_stream_close() is called on a #NiceInputStream, the input stream
* will be marked as closed, but the underlying #NiceAgent stream will not be
* removed. Use nice_agent_remove_stream() to do that.
* and underlying #NiceAgent stream will be closed, but the underlying stream
* will not be removed. Use nice_agent_remove_stream() to do that.
*
* Since: 0.1.5
*/
......@@ -96,6 +96,8 @@ static void nice_input_stream_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static gssize nice_input_stream_read (GInputStream *stream, void *buffer,
gsize count, GCancellable *cancellable, GError **error);
static gboolean nice_input_stream_close (GInputStream *stream,
GCancellable *cancellable, GError **error);
static gboolean nice_input_stream_is_readable (GPollableInputStream *stream);
static gssize nice_input_stream_read_nonblocking (GPollableInputStream *stream,
void *buffer, gsize count, GError **error);
......@@ -115,6 +117,7 @@ nice_input_stream_class_init (NiceInputStreamClass *klass)
gobject_class->dispose = nice_input_stream_dispose;
stream_class->read_fn = nice_input_stream_read;
stream_class->close_fn = nice_input_stream_close;
/***
* NiceInputStream:agent:
......@@ -174,6 +177,11 @@ nice_input_stream_dispose (GObject *object)
NiceInputStream *self = NICE_INPUT_STREAM (object);
NiceAgent *agent;
/* Ensure the stream is closed first, otherwise the agent can’t be found in
* the close handler called by the parent implementation. */
if (!g_input_stream_is_closed (G_INPUT_STREAM (object)))
g_input_stream_close (G_INPUT_STREAM (object), NULL, NULL);
agent = g_weak_ref_get (&self->priv->agent_ref);
if (agent != NULL) {
g_signal_handlers_disconnect_by_func (agent, streams_removed_cb, self);
......@@ -300,9 +308,7 @@ nice_input_stream_read (GInputStream *stream, void *buffer, gsize count,
/* Closed streams are not readable. */
if (g_input_stream_is_closed (stream)) {
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
"Stream is closed.");
return -1;
return 0;
}
/* Has the agent disappeared? */
......@@ -321,6 +327,36 @@ nice_input_stream_read (GInputStream *stream, void *buffer, gsize count,
return len;
}
static gboolean
nice_input_stream_close (GInputStream *stream, GCancellable *cancellable,
GError **error)
{
NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv;
Component *component = NULL;
Stream *_stream = NULL;
NiceAgent *agent; /* owned */
/* Has the agent disappeared? */
agent = g_weak_ref_get (&priv->agent_ref);
if (agent == NULL)
return TRUE;
agent_lock ();
/* Shut down the read side of the pseudo-TCP stream, if it still exists. */
if (agent_find_component (agent, priv->stream_id, priv->component_id,
&_stream, &component) && agent->reliable &&
!pseudo_tcp_socket_is_closed (component->tcp)) {
pseudo_tcp_socket_shutdown (component->tcp, PSEUDO_TCP_SHUTDOWN_RD);
}
agent_unlock ();
g_object_unref (agent);
return TRUE;
}
static gboolean
nice_input_stream_is_readable (GPollableInputStream *stream)
{
......@@ -351,7 +387,7 @@ nice_input_stream_is_readable (GPollableInputStream *stream)
/* If it’s a reliable agent, see if there’s any pending data in the pseudo-TCP
* buffer. */
if (component->tcp != NULL &&
if (agent->reliable &&
pseudo_tcp_socket_get_available_bytes (component->tcp) > 0) {
retval = TRUE;
goto done;
......@@ -386,9 +422,7 @@ nice_input_stream_read_nonblocking (GPollableInputStream *stream, void *buffer,
/* Closed streams are not readable. */
if (g_input_stream_is_closed (G_INPUT_STREAM (stream))) {
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
"Stream is closed.");
return -1;
return 0;
}
/* Has the agent disappeared? */
......@@ -424,8 +458,8 @@ nice_input_stream_create_source (GPollableInputStream *stream,
if (agent == NULL)
goto dummy_source;
component_source = component_input_source_new (agent, priv->stream_id,
priv->component_id, stream, cancellable);
component_source = component_source_new (agent, priv->stream_id,
priv->component_id, G_OBJECT (stream), G_IO_IN, cancellable);
g_object_unref (agent);
......
......@@ -41,6 +41,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#ifdef __sun
#include <sys/sockio.h>
......@@ -174,6 +175,9 @@ nice_interfaces_is_private_ip (const struct sockaddr *_sa)
#ifdef HAVE_GETIFADDRS
static gchar *
sockaddr_to_string (const struct sockaddr *addr);
GList *
nice_interfaces_get_local_ips (gboolean include_loopback)
{
......@@ -187,50 +191,36 @@ nice_interfaces_get_local_ips (gboolean include_loopback)
/* Loop through the interface list and get the IP address of each IF */
for (ifa = results; ifa; ifa = ifa->ifa_next) {
char addr_as_string[INET6_ADDRSTRLEN+1];
union {
struct sockaddr *addr;
struct sockaddr_in *in;
struct sockaddr_in6 *in6;
} sa;
sa.addr = ifa->ifa_addr;
gchar *addr_string;
/* no ip address from interface that is down */
if ((ifa->ifa_flags & IFF_UP) == 0)
continue;
if (ifa->ifa_addr == NULL) {
if (ifa->ifa_addr == NULL)
continue;
} else if (ifa->ifa_addr->sa_family == AF_INET) {
if (inet_ntop (AF_INET, &sa.in->sin_addr, addr_as_string,
INET6_ADDRSTRLEN) == NULL)
continue;
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
/* Skip link-local addresses, they require a scope */
if (IN6_IS_ADDR_LINKLOCAL (&sa.in6->sin6_addr))
continue;
if (inet_ntop (AF_INET6, &sa.in6->sin6_addr, addr_as_string,
INET6_ADDRSTRLEN) == NULL)
continue;
} else
/* Convert to a string. */
addr_string = sockaddr_to_string (ifa->ifa_addr);
if (addr_string == NULL) {
nice_debug ("Failed to convert address to string for interface ‘%s’.",
ifa->ifa_name);
continue;
}
nice_debug ("Interface: %s", ifa->ifa_name);
nice_debug ("IP Address: %s", addr_as_string);
nice_debug ("IP Address: %s", addr_string);
if ((ifa->ifa_flags & IFF_LOOPBACK) == IFF_LOOPBACK) {
if (include_loopback)
loopbacks = g_list_append (loopbacks, g_strdup (addr_as_string));
loopbacks = g_list_append (loopbacks, addr_string);
else
nice_debug ("Ignoring loopback interface");
g_free (addr_string);
} else {
if (nice_interfaces_is_private_ip (ifa->ifa_addr))
ips = g_list_append (ips, g_strdup (addr_as_string));
ips = g_list_append (ips, addr_string);
else
ips = g_list_prepend (ips, g_strdup (addr_as_string));
ips = g_list_prepend (ips, addr_string);
}
}
......@@ -410,7 +400,7 @@ SOCKET nice_interfaces_get_WSA_socket ()
}
#endif
GList * nice_interfaces_get_local_interfaces ()
GList * nice_interfaces_get_local_interfaces (void)
{
ULONG size = 0;
PMIB_IFTABLE if_table;
......@@ -435,17 +425,46 @@ GList * nice_interfaces_get_local_interfaces ()
return ret;
}
static gchar *
sockaddr_to_string (const struct sockaddr *addr);
GList * nice_interfaces_get_local_ips (gboolean include_loopback)
{
ULONG size = 0;
IP_ADAPTER_ADDRESSES *addresses = NULL, *a;
ULONG status;
guint iterations;
ULONG addresses_size;
DWORD pref = 0;
PMIB_IPADDRTABLE ip_table;
GList * ret = NULL;
GList *ret = NULL;
GetIpAddrTable (NULL, &size, TRUE);
/* As suggested on
* http://msdn.microsoft.com/en-gb/library/windows/desktop/aa365915%28v=vs.85%29.aspx */
#define MAX_TRIES 3
#define INITIAL_BUFFER_SIZE 15000
if (!size)
addresses_size = INITIAL_BUFFER_SIZE;
iterations = 0;
do {
g_free (addresses);
addresses = g_malloc0 (addresses_size);
status = GetAdaptersAddresses (AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &addresses_size);
} while ((status == ERROR_BUFFER_OVERFLOW) && (iterations++ < MAX_TRIES));
nice_debug ("Queried addresses with status %lu.", status);
#undef INITIAL_BUFFER_SIZE
#undef MAX_TRIES
/* Error? */
if (status != NO_ERROR) {
nice_debug ("Error retrieving local addresses (error code %lu).", status);
g_free (addresses);
return NULL;
}
/*
* Get the best interface for transport to 0.0.0.0.
......@@ -454,42 +473,49 @@ GList * nice_interfaces_get_local_ips (gboolean include_loopback)
if (GetBestInterface (0, &pref) != NO_ERROR)
pref = 0;
ip_table = (PMIB_IPADDRTABLE)g_malloc0 (size);
/* Loop over the adapters. */
for (a = addresses; a != NULL; a = a->Next) {
IP_ADAPTER_UNICAST_ADDRESS *unicast;
if (GetIpAddrTable (ip_table, &size, TRUE) == ERROR_SUCCESS) {
DWORD i;
for (i = 0; i < ip_table->dwNumEntries; i++) {
gchar * ipstr;
PMIB_IPADDRROW ipaddr = &ip_table->table[i];
nice_debug ("Interface ‘%S’:", a->FriendlyName);
if (!(ipaddr->wType & (MIB_IPADDR_DISCONNECTED | MIB_IPADDR_DELETED)) &&
ipaddr->dwAddr) {
if (!include_loopback) {
DWORD type = 0;
PMIB_IFROW ifr = (PMIB_IFROW)g_malloc0 (sizeof (MIB_IFROW));
ifr->dwIndex = ipaddr->dwIndex;
if (GetIfEntry (ifr) == NO_ERROR)
type = ifr->dwType;
g_free (ifr);
if (type == IF_TYPE_SOFTWARE_LOOPBACK)
continue;
}
ipstr = g_strdup_printf ("%lu.%lu.%lu.%lu",
(ipaddr->dwAddr ) & 0xFF,
(ipaddr->dwAddr >> 8) & 0xFF,
(ipaddr->dwAddr >> 16) & 0xFF,
(ipaddr->dwAddr >> 24) & 0xFF);
if (ipaddr->dwIndex == pref)
ret = g_list_prepend (ret, ipstr);
else
ret = g_list_append (ret, ipstr);
/* Various conditions for ignoring the interface. */
if (a->Flags & IP_ADAPTER_RECEIVE_ONLY ||
a->OperStatus == IfOperStatusDown ||
a->OperStatus == IfOperStatusNotPresent ||
a->OperStatus == IfOperStatusLowerLayerDown) {
nice_debug ("Rejecting interface due to being down or read-only.");
continue;
}
if (!include_loopback &&
a->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
nice_debug ("Rejecting loopback interface ‘%S’.", a->FriendlyName);
continue;
}
/* Grab the interface’s unicast addresses. */
for (unicast = a->FirstUnicastAddress;
unicast != NULL; unicast = unicast->Next) {
gchar *addr_string;
addr_string = sockaddr_to_string (unicast->Address.lpSockaddr);
if (addr_string == NULL) {
nice_debug ("Failed to convert address to string for interface ‘%S’.",
a->FriendlyName);
continue;
}
nice_debug ("IP address: %s", addr_string);
if (a->IfIndex == pref || a->Ipv6IfIndex == pref)
ret = g_list_prepend (ret, addr_string);
else
ret = g_list_append (ret, addr_string);
}
}
g_free(ip_table);
g_free (addresses);
return ret;
}
......@@ -576,3 +602,25 @@ gchar * nice_interfaces_get_ip_for_interface (gchar *interface_name)
#error Can not use this method for retreiving ip list from OS other than unix or windows
#endif /* G_OS_WIN32 */
#endif /* G_OS_UNIX */
/* Works on both UNIX and Windows. Magic! */
static gchar *
sockaddr_to_string (const struct sockaddr *addr)
{
char addr_as_string[INET6_ADDRSTRLEN+1];
size_t addr_len;
switch (addr->sa_family) {
case AF_INET: addr_len = sizeof (struct sockaddr_in); break;
case AF_INET6: addr_len = sizeof (struct sockaddr_in6); break;
default: return NULL;
}
if (getnameinfo (addr, addr_len,
addr_as_string, sizeof (addr_as_string), NULL, 0,
NI_NUMERICHOST) != 0) {
return NULL;
}
return g_strdup (addr_as_string);
}
......@@ -50,9 +50,11 @@
* A single #NiceIOStream can only be used with a single agent, stream and
* component triple, and will be closed as soon as that stream is removed from
* the agent (e.g. if nice_agent_remove_stream() is called from another thread).
* If g_io_stream_close() is called on a #NiceIOStream, the I/O stream will be
* marked as closed in both directions, but the underlying #NiceAgent stream
* will not be removed. Use nice_agent_remove_stream() to do that.
* If g_io_stream_close() is called on a #NiceIOStream, the I/O stream and
* underlying #NiceAgent stream will be closed in both directions, but the
* underlying stream will not be removed. Use nice_agent_remove_stream() to do
* that, but only do so after g_io_stream_close() has completed, or the stream
* will return broken pipe errors.
*
* Since: 0.1.5
*/
......@@ -80,6 +82,8 @@ struct _NiceIOStreamPrivate
guint stream_id;
guint component_id;
gboolean is_datagram;
GInputStream *input_stream; /* owned */
GOutputStream *output_stream; /* owned */
};
......@@ -94,6 +98,7 @@ static GOutputStream *nice_io_stream_get_output_stream (GIOStream *stream);
static void streams_removed_cb (NiceAgent *agent, guint *stream_ids,
gpointer user_data);
static gboolean nice_io_stream_is_datagram (GIOStream *stream);
static void
nice_io_stream_class_init (NiceIOStreamClass *klass)
......@@ -109,6 +114,7 @@ nice_io_stream_class_init (NiceIOStreamClass *klass)
stream_class->get_input_stream = nice_io_stream_get_input_stream;
stream_class->get_output_stream = nice_io_stream_get_output_stream;
stream_class->is_datagram = nice_io_stream_is_datagram;
/*
* NiceIOStream:agent:
......@@ -277,17 +283,24 @@ nice_io_stream_set_property (GObject *object, guint prop_id,
* Since: 0.1.5
*/
GIOStream *
nice_io_stream_new (NiceAgent *agent, guint stream_id, guint component_id)
nice_io_stream_new (NiceAgent *agent, guint stream_id, guint component_id,
gboolean reliable)
{
GIOStream *stream;
g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
g_return_val_if_fail (stream_id > 0, NULL);
g_return_val_if_fail (component_id > 0, NULL);
return g_object_new (NICE_TYPE_IO_STREAM,
stream = g_object_new (NICE_TYPE_IO_STREAM,
"agent", agent,
"stream-id", stream_id,
"component-id", component_id,
NULL);
NICE_IO_STREAM (stream)->priv->is_datagram = !reliable;
return stream;
}
static GInputStream *
......@@ -348,3 +361,12 @@ streams_removed_cb (NiceAgent *agent, guint *stream_ids, gpointer user_data)
}
}
}
static gboolean
nice_io_stream_is_datagram (GIOStream *stream)
{
NiceIOStream *self = NICE_IO_STREAM (stream);
return self->priv->is_datagram;
}
......@@ -84,7 +84,7 @@ struct _NiceIOStream
};
GIOStream *nice_io_stream_new (NiceAgent *agent,
guint stream_id, guint component_id);
guint stream_id, guint component_id, gboolean reliable);
G_END_DECLS
......
......@@ -50,8 +50,8 @@
* component triple, and will be closed as soon as that stream is removed from
* the agent (e.g. if nice_agent_remove_stream() is called from another thread).
* If g_output_stream_close() is called on a #NiceOutputStream, the output
* stream will be marked as closed, but the underlying #NiceAgent stream will
* not be removed. Use nice_agent_remove_stream() to do that.
* stream and underlying #NiceAgent stream will be closed, but the underlying
* stream will not be removed. Use nice_agent_remove_stream() to do that.
*
* The output stream can only be used once the
* #NiceAgent::reliable-transport-writable signal has been received for the
......@@ -104,6 +104,8 @@ static void nice_output_stream_set_property (GObject *object, guint prop_id,
static gssize nice_output_stream_write (GOutputStream *stream,
const void *buffer, gsize count, GCancellable *cancellable, GError **error);
static gboolean nice_output_stream_close (GOutputStream *stream,
GCancellable *cancellable, GError **error);
static gboolean nice_output_stream_is_writable (GPollableOutputStream *stream);
static gssize nice_output_stream_write_nonblocking (
......@@ -122,6 +124,7 @@ nice_output_stream_class_init (NiceOutputStreamClass *klass)
g_type_class_add_private (klass, sizeof (NiceOutputStreamPrivate));
stream_class->write_fn = nice_output_stream_write;
stream_class->close_fn = nice_output_stream_close;
gobject_class->set_property = nice_output_stream_set_property;
gobject_class->get_property = nice_output_stream_get_property;
......@@ -185,6 +188,11 @@ nice_output_stream_dispose (GObject *object)
NiceOutputStream *self = NICE_OUTPUT_STREAM (object);
NiceAgent *agent;
/* Ensure the stream is closed first, otherwise the agent can’t be found in
* the close handler called by the parent implementation. */
if (!g_output_stream_is_closed (G_OUTPUT_STREAM (object)))
g_output_stream_close (G_OUTPUT_STREAM (object), NULL, NULL);
agent = g_weak_ref_get (&self->priv->agent_ref);
if (agent != NULL) {
g_signal_handlers_disconnect_by_func (agent, streams_removed_cb, self);
......@@ -463,6 +471,36 @@ nice_output_stream_write (GOutputStream *stream, const void *buffer, gsize count
return len;
}
static gboolean
nice_output_stream_close (GOutputStream *stream, GCancellable *cancellable,
GError **error)
{
NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
Component *component = NULL;
Stream *_stream = NULL;
NiceAgent *agent; /* owned */
/* Has the agent disappeared? */
agent = g_weak_ref_get (&priv->agent_ref);
if (agent == NULL)
return TRUE;
agent_lock ();
/* Shut down the write side of the pseudo-TCP stream. */
if (agent_find_component (agent, priv->stream_id, priv->component_id,
&_stream, &component) && agent->reliable &&
!pseudo_tcp_socket_is_closed (component->tcp)) {
pseudo_tcp_socket_shutdown (component->tcp, PSEUDO_TCP_SHUTDOWN_WR);
}
agent_unlock ();
g_object_unref (agent);
return TRUE;
}
static gboolean
nice_output_stream_is_writable (GPollableOutputStream *stream)
{
......@@ -493,11 +531,19 @@ nice_output_stream_is_writable (GPollableOutputStream *stream)
/* If it’s a reliable agent, see if there’s any space in the pseudo-TCP output
* buffer. */
if (component->tcp != NULL) {
if (agent->reliable) {
retval = pseudo_tcp_socket_can_send (component->tcp);
goto done;
}
/* If it's a non-reliable agent, it never blocks, so one can
* always write
*/
if (!agent->reliable) {
retval = TRUE;
goto done;
}
/* Check whether any of the component’s FDs are pollable. */
for (i = component->socket_sources; i != NULL; i = i->next) {
SocketSource *socket_source = i->data;
......@@ -540,14 +586,17 @@ nice_output_stream_write_nonblocking (GPollableOutputStream *stream,
return -1;
}
if (count == 0)
return 0;
if (count == 0) {
n_sent = 0;
goto done;
}
/* This is equivalent to the default GPollableOutputStream implementation. */
if (!g_pollable_output_stream_is_writable (stream)) {
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
g_strerror (EAGAIN));
return -1;
n_sent = -1;
goto done;
}
n_sent = nice_agent_send (agent, priv->stream_id, priv->component_id,
......@@ -557,7 +606,7 @@ nice_output_stream_write_nonblocking (GPollableOutputStream *stream,
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
g_strerror (EAGAIN));
done:
g_object_unref (agent);
......@@ -610,6 +659,15 @@ nice_output_stream_create_source (GPollableOutputStream *stream,
g_source_set_dummy_callback (cancellable_source);
g_source_add_child_source (component_source, cancellable_source);
g_source_unref (cancellable_source);
} else if (!agent->reliable) {
/* UDP streams are almost always writeable. */
GSource *child_source;
child_source = component_source_new (agent, priv->stream_id,
priv->component_id, G_OBJECT (stream), G_IO_OUT, cancellable);
g_source_set_dummy_callback (child_source);
g_source_add_child_source (component_source, child_source);
g_source_unref (child_source);
}
done:
......
This diff is collapsed.
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2010 Collabora Ltd.
* Contact: Youness Alaoui
* (C) 2010, 2014 Collabora Ltd.
* Contact: Philip Withnall
*
* The contents of this file are subject to the Mozilla Public License Version
......@@ -22,6 +22,7 @@
*
* Contributors:
* Youness Alaoui, Collabora Ltd.
* Philip Withnall, Collabora Ltd.
*
* 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
......@@ -58,6 +59,7 @@
#include <glib-object.h>
#ifndef __GTK_DOC_IGNORE__
#ifdef G_OS_WIN32
# include <winsock2.h>
# define ECONNABORTED WSAECONNABORTED
......@@ -65,6 +67,7 @@
# define EWOULDBLOCK WSAEWOULDBLOCK
# define ECONNRESET WSAECONNRESET
#endif
#endif
#include "agent.h"
......@@ -136,8 +139,21 @@ typedef enum {
* @TCP_SYN_RECEIVED: The socket has received a connection request (SYN) packet.
* @TCP_ESTABLISHED: The socket is connected
* @TCP_CLOSED: The socket has been closed
*
* An enum representing the state of the #PseudoTcpSocket.
* @TCP_FIN_WAIT_1: The socket has been closed locally but not remotely
* (Since: UNRELEASED)
* @TCP_FIN_WAIT_2: The socket has been closed locally but not remotely
* (Since: UNRELEASED)
* @TCP_CLOSING: The socket has been closed locally and remotely
* (Since: UNRELEASED)
* @TCP_TIME_WAIT: The socket has been closed locally and remotely
* (Since: UNRELEASED)
* @TCP_CLOSE_WAIT: The socket has been closed remotely but not locally
* (Since: UNRELEASED)
* @TCP_LAST_ACK: The socket has been closed locally and remotely
* (Since: UNRELEASED)
*
* An enum representing the state of the #PseudoTcpSocket. These states
* correspond to the TCP states in RFC 793.
* <para> See also: #PseudoTcpSocket:state </para>
*
* Since: 0.0.11
......@@ -147,7 +163,13 @@ typedef enum {
TCP_SYN_SENT,
TCP_SYN_RECEIVED,
TCP_ESTABLISHED,
TCP_CLOSED
TCP_CLOSED,
TCP_FIN_WAIT_1,
TCP_FIN_WAIT_2,
TCP_CLOSING,
TCP_TIME_WAIT,
TCP_CLOSE_WAIT,
TCP_LAST_ACK,
} PseudoTcpState;
/**
......@@ -169,13 +191,31 @@ typedef enum {
WR_FAIL
} PseudoTcpWriteResult;
/**
* PseudoTcpShutdown:
* @PSEUDO_TCP_SHUTDOWN_RD: Shut down the local reader only
* @PSEUDO_TCP_SHUTDOWN_WR: Shut down the local writer only
* @PSEUDO_TCP_SHUTDOWN_RDWR: Shut down both reading and writing
*
* Options for which parts of a connection to shut down when calling
* pseudo_tcp_socket_shutdown(). These correspond to the values passed to POSIX
* shutdown().
*
* Since: UNRELEASED
*/
typedef enum {
PSEUDO_TCP_SHUTDOWN_RD,
PSEUDO_TCP_SHUTDOWN_WR,
PSEUDO_TCP_SHUTDOWN_RDWR,
} PseudoTcpShutdown;
/**
* PseudoTcpCallbacks:
* @user_data: A user defined pointer to be passed to the callbacks
* @PseudoTcpOpened: The #PseudoTcpSocket is now connected
* @PseudoTcpReadable: The socket is readable
* @PseudoTcpWritable: The socket is writable
* @PseudoTcpClosed: The socket was closed
* @PseudoTcpClosed: The socket was closed (both sides)
* @WritePacket: This callback is called when the socket needs to send data.
*
* A structure containing callbacks functions that will be called by the
......@@ -294,8 +334,16 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer,
* @self: The #PseudoTcpSocket object.
* @force: %TRUE to close the socket forcefully, %FALSE to close it gracefully
*
* Close the socket. IF @force is set to %FALSE, the socket will finish sending
* pending data before closing.
* Close the socket for sending. If @force is set to %FALSE, the socket will
* finish sending pending data before closing. If it is set to %TRUE, the socket
* will discard pending data and close the connection immediately (sending a TCP
* RST segment).
*
* The socket will be closed in both directions – sending and receiving – and
* any pending received data must be read before calling this function, by
* calling pseudo_tcp_socket_recv() until it blocks. If any pending data is in
* the receive buffer when pseudo_tcp_socket_close() is called, a TCP RST
* segment will be sent to the peer to notify it of the data loss.
*
<note>
<para>
......@@ -312,6 +360,24 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer,
*/
void pseudo_tcp_socket_close(PseudoTcpSocket *self, gboolean force);
/**
* pseudo_tcp_socket_shutdown:
* @self: The #PseudoTcpSocket object.
* @how: The directions of the connection to shut down.
*
* Shut down sending, receiving, or both on the socket, depending on the value
* of @how. The behaviour of pseudo_tcp_socket_send() and
* pseudo_tcp_socket_recv() will immediately change after this function returns
* (depending on the value of @how), though the socket may continue to process
* network traffic in the background even if sending or receiving data is
* forbidden.
*
* This is equivalent to the POSIX shutdown() function. Setting @how to
* %PSEUDO_TCP_SHUTDOWN_RDWR is equivalent to calling pseudo_tcp_socket_close().
*
* Since: UNRELEASED
*/
void pseudo_tcp_socket_shutdown (PseudoTcpSocket *self, PseudoTcpShutdown how);
/**
* pseudo_tcp_socket_get_error:
......@@ -465,12 +531,59 @@ gboolean pseudo_tcp_socket_can_send (PseudoTcpSocket *self);
*
* Gets the number of bytes of space available in the transmission buffer.
*
* Returns: The numbero f bytes, or 0 if the connection is not established.
* Returns: The number of bytes, or 0 if the connection is not established.
*
* Since: 0.1.5
*/
gsize pseudo_tcp_socket_get_available_send_space (PseudoTcpSocket *self);
/**
* pseudo_tcp_socket_set_time:
* @self: The #PseudoTcpSocket object.
* @current_time: Current monotonic time, in milliseconds; or zero to use the
* system monotonic clock.
*
* Sets the current monotonic time to be used by the TCP socket when calculating
* timeouts and expiry times. If this function is not called, or is called with
* @current_time as zero, g_get_monotonic_time() will be used. Otherwise, the
* specified @current_time will be used until it is updated by calling this
* function again.
*
* This function is intended for testing only, and should not be used in
* production code.
*
* Since: UNRELEASED
*/
void pseudo_tcp_socket_set_time (PseudoTcpSocket *self, guint32 current_time);
/**
* pseudo_tcp_socket_is_closed:
* @self: The #PseudoTcpSocket object.
*
* Gets whether the socket is closed, with the shutdown handshake completed,
* and both peers no longer able to read or write data to the connection.
*
* Returns: %TRUE if the socket is closed in both directions, %FALSE otherwise
* Since: UNRELEASED
*/
gboolean pseudo_tcp_socket_is_closed (PseudoTcpSocket *self);
/**
* pseudo_tcp_socket_is_closed_remotely:
* @self: The #PseudoTcpSocket object.
*
* Gets whether the socket has been closed on the remote peer’s side of the
* connection (i.e. whether pseudo_tcp_socket_close() has been called there).
* This is guaranteed to return %TRUE if pseudo_tcp_socket_is_closed() returns
* %TRUE. It will not return %TRUE after pseudo_tcp_socket_close() is called
* until a FIN segment is received from the remote peer.
*
* Returns: %TRUE if the remote peer has closed its side of the connection,
* %FALSE otherwise
* Since: UNRELEASED
*/
gboolean pseudo_tcp_socket_is_closed_remotely (PseudoTcpSocket *self);
G_END_DECLS
#endif /* _PSEUDOTCP_H */
......
......@@ -43,6 +43,11 @@
#include "stream.h"
/* Simple tracking for the number of alive streams. These must be accessed
* atomically. */
static volatile unsigned int n_streams_created = 0;
static volatile unsigned int n_streams_destroyed = 0;
/*
* @file stream.c
* @brief ICE stream functionality
......@@ -54,6 +59,10 @@ stream_new (guint n_components, NiceAgent *agent)
guint n;
Component *component;
g_atomic_int_inc (&n_streams_created);
nice_debug ("Created NiceStream (%u created, %u destroyed)",
n_streams_created, n_streams_destroyed);
stream = g_slice_new0 (Stream);
for (n = 0; n < n_components; n++) {
component = component_new (n + 1, agent, stream);
......@@ -67,19 +76,26 @@ stream_new (guint n_components, NiceAgent *agent)
}
void
stream_free (Stream *stream)
stream_close (Stream *stream)
{
GSList *i;
if (stream->name)
g_free (stream->name);
for (i = stream->components; i; i = i->next) {
Component *component = i->data;
component_free (component);
i->data = NULL;
component_close (component);
}
g_slist_free (stream->components);
}
void
stream_free (Stream *stream)
{
g_free (stream->name);
g_slist_free_full (stream->components, (GDestroyNotify) component_free);
g_slice_free (Stream, stream);
g_atomic_int_inc (&n_streams_destroyed);
nice_debug ("Destroyed NiceStream (%u created, %u destroyed)",
n_streams_created, n_streams_destroyed);
}
Component *
......
......@@ -66,7 +66,7 @@ struct _Stream
guint n_components;
gboolean initial_binding_request_received;
GSList *components; /* list of 'Component' structs */
GSList *conncheck_list; /* list of CandidatePair items */
GSList *conncheck_list; /* list of CandidateCheckPair items */
gchar local_ufrag[NICE_STREAM_MAX_UFRAG];
gchar local_password[NICE_STREAM_MAX_PWD];
gchar remote_ufrag[NICE_STREAM_MAX_UFRAG];
......@@ -80,6 +80,9 @@ struct _Stream
Stream *
stream_new (guint n_components, NiceAgent *agent);
void
stream_close (Stream *stream);
void
stream_free (Stream *stream);
......
This diff is collapsed.
......@@ -57,7 +57,8 @@ CFILE_GLOB=$(top_srcdir)/agent/agent.c \
IGNORE_HFILES= conncheck.h discovery.h stream.h component.h agent-priv.h \
iostream.h inputstream.h outputstream.h \
gstnice.h gstnicesrc.h gstnicesink.h \
md5.h sha1.h stunhmac.h utils.h rand.h stun5389.h stuncrc32.h
md5.h sha1.h stunhmac.h utils.h rand.h stun5389.h stuncrc32.h \
stund.h agent-signals-marshal.h win32_common.h
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.