* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Christian Kellner <gicmo@gnome.org>
* Samuel Cormier-Iijima <sciyoshi@gmail.com>
#include "config.h"
+#include "gsocket.h"
+
+#ifdef G_OS_UNIX
+#include "glib-unix.h"
+#endif
+
#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
#ifndef G_OS_WIN32
-# include <netinet/in.h>
-# include <arpa/inet.h>
-# include <netdb.h>
# include <fcntl.h>
# include <unistd.h>
-# include <sys/types.h>
-#else
-# include <winsock2.h>
-# include <mswsock.h>
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#ifdef G_OS_UNIX
+#include <sys/uio.h>
#endif
-#include "gsocket.h"
#include "gcancellable.h"
#include "gioenumtypes.h"
+#include "ginetaddress.h"
#include "ginitable.h"
-#include "gasynchelper.h"
#include "gioerror.h"
#include "gioenums.h"
#include "gioerror.h"
+#include "gnetworkingprivate.h"
+#include "gsocketaddress.h"
+#include "gsocketcontrolmessage.h"
+#include "gcredentials.h"
+#include "gcredentialsprivate.h"
#include "glibintl.h"
-#include "gioalias.h"
-
/**
* SECTION:gsocket
- * @short_description: Low-level network socket handling
+ * @short_description: Low-level socket object
* @include: gio/gio.h
- * @see_also: #GInitable
+ * @see_also: #GInitable, [<gnetworking.h>][gio-gnetworking.h]
*
* A #GSocket is a low-level networking primitive. It is a more or less
* direct mapping of the BSD socket API in a portable GObject based API.
- * It supports both the unix socket implementations and winsock2 on Windows.
+ * It supports both the UNIX socket implementations and winsock2 on Windows.
*
* #GSocket is the platform independent base upon which the higher level
* network primitives are based. Applications are not typically meant to
- * use it directly, but rather through classes like #GSocketClient, etc.
- * However there may be cases where direct use of #GSocket is useful.
- *
- * TODO: Add more references to the highlevel API once that is more
- * finalized.
+ * use it directly, but rather through classes like #GSocketClient,
+ * #GSocketService and #GSocketConnection. However there may be cases where
+ * direct use of #GSocket is useful.
*
* #GSocket implements the #GInitable interface, so if it is manually constructed
* by e.g. g_object_new() you must call g_initable_init() and check the
* reasons. For instance, on Windows a socket is always seen as writable
* until a write returns %G_IO_ERROR_WOULD_BLOCK.
*
- * #GSocket<!-- -->s can be either connection oriented or datagram based.
+ * #GSockets can be either connection oriented or datagram based.
* For connection oriented types you must first establish a connection by
* either connecting to an address or accepting a connection from another
* address. For connectionless socket types the target/source address is
*
* All socket file descriptors are set to be close-on-exec.
*
+ * Note that creating a #GSocket causes the signal %SIGPIPE to be
+ * ignored for the remainder of the program. If you are writing a
+ * command-line utility that uses #GSocket, you may need to take into
+ * account the fact that your program will not automatically be killed
+ * if it tries to write to %stdout after it has been closed.
+ *
+ * Like most other APIs in GLib, #GSocket is not inherently thread safe. To use
+ * a #GSocket concurrently from multiple threads, you must implement your own
+ * locking.
+ *
* Since: 2.22
- **/
+ */
static void g_socket_initable_iface_init (GInitableIface *iface);
static gboolean g_socket_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error);
-G_DEFINE_TYPE_WITH_CODE (GSocket, g_socket, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
- g_socket_initable_iface_init));
-
enum
{
PROP_0,
PROP_LISTEN_BACKLOG,
PROP_KEEPALIVE,
PROP_LOCAL_ADDRESS,
- PROP_REMOTE_ADDRESS
+ PROP_REMOTE_ADDRESS,
+ PROP_TIMEOUT,
+ PROP_TTL,
+ PROP_BROADCAST,
+ PROP_MULTICAST_LOOPBACK,
+ PROP_MULTICAST_TTL
};
+/* Size of the receiver cache for g_socket_receive_from() */
+#define RECV_ADDR_CACHE_SIZE 8
+
struct _GSocketPrivate
{
GSocketFamily family;
GSocketType type;
- char *protocol;
+ GSocketProtocol protocol;
gint fd;
gint listen_backlog;
+ guint timeout;
GError *construct_error;
- GSocketAddress *local_address;
GSocketAddress *remote_address;
guint inited : 1;
guint blocking : 1;
guint keepalive : 1;
guint closed : 1;
+ guint connected : 1;
+ guint listening : 1;
+ guint timed_out : 1;
+ guint connect_pending : 1;
#ifdef G_OS_WIN32
WSAEVENT event;
int current_events;
int current_errors;
int selected_events;
GList *requested_conditions; /* list of requested GIOCondition * */
+ GMutex win32_source_lock;
#endif
+
+ struct {
+ GSocketAddress *addr;
+ struct sockaddr *native;
+ gint native_len;
+ guint64 last_used;
+ } recv_addr_cache[RECV_ADDR_CACHE_SIZE];
};
+G_DEFINE_TYPE_WITH_CODE (GSocket, g_socket, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GSocket)
+ g_networking_init ();
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_socket_initable_iface_init));
+
static int
get_socket_errno (void)
{
static GIOErrorEnum
socket_io_error_from_errno (int err)
{
-#ifndef G_OS_WIN32
- return g_io_error_from_errno (err);
+#ifdef G_OS_WIN32
+ return g_io_error_from_win32_error (err);
#else
- switch (err)
- {
- case WSAEADDRINUSE:
- return G_IO_ERROR_ADDRESS_IN_USE;
- case WSAEWOULDBLOCK:
- return G_IO_ERROR_WOULD_BLOCK;
- case WSAEACCES:
- return G_IO_ERROR_PERMISSION_DENIED;
- case WSA_INVALID_HANDLE:
- case WSA_INVALID_PARAMETER:
- case WSAEBADF:
- case WSAENOTSOCK:
- return G_IO_ERROR_INVALID_ARGUMENT;
- case WSAEPROTONOSUPPORT:
- return G_IO_ERROR_NOT_SUPPORTED;
- case WSAECANCELLED:
- return G_IO_ERROR_CANCELLED;
- case WSAESOCKTNOSUPPORT:
- case WSAEOPNOTSUPP:
- case WSAEPFNOSUPPORT:
- case WSAEAFNOSUPPORT:
- return G_IO_ERROR_NOT_SUPPORTED;
- default:
- return G_IO_ERROR_FAILED;
- }
+ return g_io_error_from_errno (err);
#endif
}
#ifndef G_OS_WIN32
return g_strerror (err);
#else
- static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT;
- char *buf, *msg;
-
- buf = g_static_private_get (&msg_private);
- if (!buf)
- {
- buf = g_new (gchar, 128);
- g_static_private_set (&msg_private, buf, g_free);
- }
+ const char *msg_ret;
+ char *msg;
msg = g_win32_error_message (err);
- strncpy (buf, msg, 128);
+
+ msg_ret = g_intern_string (msg);
g_free (msg);
- return buf;
+
+ return msg_ret;
#endif
}
#define win32_unset_event_mask(_socket, _mask)
#endif
-static void
-set_fd_nonblocking (int fd)
-{
-#ifndef G_OS_WIN32
- glong arg;
-#else
- gulong arg;
-#endif
-
-#ifndef G_OS_WIN32
- if ((arg = fcntl (fd, F_GETFL, NULL)) < 0)
- {
- g_warning ("Error getting socket status flags: %s", socket_strerror (errno));
- arg = 0;
- }
-
- arg = arg | O_NONBLOCK;
-
- if (fcntl (fd, F_SETFL, arg) < 0)
- g_warning ("Error setting socket status flags: %s", socket_strerror (errno));
-#else
- arg = TRUE;
-
- if (ioctlsocket (fd, FIONBIO, &arg) == SOCKET_ERROR)
- {
- int errsv = get_socket_errno ();
- g_warning ("Error setting socket status flags: %s", socket_strerror (errsv));
- }
+/* Windows has broken prototypes... */
+#ifdef G_OS_WIN32
+#define getsockopt(sockfd, level, optname, optval, optlen) \
+ getsockopt (sockfd, level, optname, (gpointer) optval, (int*) optlen)
+#define setsockopt(sockfd, level, optname, optval, optlen) \
+ setsockopt (sockfd, level, optname, (gpointer) optval, optlen)
+#define getsockname(sockfd, addr, addrlen) \
+ getsockname (sockfd, addr, (int *)addrlen)
+#define getpeername(sockfd, addr, addrlen) \
+ getpeername (sockfd, addr, (int *)addrlen)
+#define recv(sockfd, buf, len, flags) \
+ recv (sockfd, (gpointer)buf, len, flags)
#endif
-}
static gboolean
check_socket (GSocket *socket,
{
if (!socket->priv->inited)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
- _("Invalid socket, not initialized"));
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
+ _("Invalid socket, not initialized"));
return FALSE;
}
_("Socket is already closed"));
return FALSE;
}
+
+ return TRUE;
+}
+
+static gboolean
+check_timeout (GSocket *socket,
+ GError **error)
+{
+ if (socket->priv->timed_out)
+ {
+ socket->priv->timed_out = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ _("Socket I/O timed out"));
+ return FALSE;
+ }
+
return TRUE;
}
struct sockaddr_storage address;
gint fd;
guint addrlen;
- guint optlen;
- int value;
+ int value, family;
int errsv;
-#ifdef G_OS_WIN32
- BOOL bool_val;
-#else
- int bool_val;
-#endif
fd = socket->priv->fd;
- optlen = sizeof value;
- if (getsockopt (fd, SOL_SOCKET, SO_TYPE, (void *)&value, &optlen) != 0)
+ if (!g_socket_get_option (socket, SOL_SOCKET, SO_TYPE, &value, NULL))
{
errsv = get_socket_errno ();
{
#ifdef ENOTSOCK
case ENOTSOCK:
-#endif
+#else
#ifdef WSAENOTSOCK
case WSAENOTSOCK:
#endif
+#endif
case EBADF:
/* programmer error */
g_error ("creating GSocket from fd %d: %s\n",
goto err;
}
- g_assert (optlen == sizeof value);
switch (value)
{
case SOCK_STREAM:
goto err;
}
- g_assert (G_STRUCT_OFFSET (struct sockaddr, sa_family) +
- sizeof address.ss_family <= addrlen);
- switch (address.ss_family)
+ if (addrlen > 0)
+ {
+ g_assert (G_STRUCT_OFFSET (struct sockaddr, sa_family) +
+ sizeof address.ss_family <= addrlen);
+ family = address.ss_family;
+ }
+ else
+ {
+ /* On Solaris, this happens if the socket is not yet connected.
+ * But we can use SO_DOMAIN as a workaround there.
+ */
+#ifdef SO_DOMAIN
+ if (!g_socket_get_option (socket, SOL_SOCKET, SO_DOMAIN, &family, NULL))
+ {
+ errsv = get_socket_errno ();
+ goto err;
+ }
+#else
+ /* This will translate to G_IO_ERROR_FAILED on either unix or windows */
+ errsv = -1;
+ goto err;
+#endif
+ }
+
+ switch (family)
{
case G_SOCKET_FAMILY_IPV4:
case G_SOCKET_FAMILY_IPV6:
+ socket->priv->family = address.ss_family;
+ switch (socket->priv->type)
+ {
+ case G_SOCKET_TYPE_STREAM:
+ socket->priv->protocol = G_SOCKET_PROTOCOL_TCP;
+ break;
+
+ case G_SOCKET_TYPE_DATAGRAM:
+ socket->priv->protocol = G_SOCKET_PROTOCOL_UDP;
+ break;
+
+ case G_SOCKET_TYPE_SEQPACKET:
+ socket->priv->protocol = G_SOCKET_PROTOCOL_SCTP;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
case G_SOCKET_FAMILY_UNIX:
- socket->priv->family = address.ss_family;
- break;
+ socket->priv->family = G_SOCKET_FAMILY_UNIX;
+ socket->priv->protocol = G_SOCKET_PROTOCOL_DEFAULT;
+ break;
default:
- socket->priv->family = G_SOCKET_FAMILY_INVALID;
- break;
+ socket->priv->family = G_SOCKET_FAMILY_INVALID;
+ break;
}
if (socket->priv->family != G_SOCKET_FAMILY_INVALID)
{
- socket->priv->local_address =
- g_socket_address_new_from_native (&address, addrlen);
-
addrlen = sizeof address;
if (getpeername (fd, (struct sockaddr *) &address, &addrlen) >= 0)
- socket->priv->remote_address =
- g_socket_address_new_from_native (&address, addrlen);
+ socket->priv->connected = TRUE;
}
- optlen = sizeof bool_val;
- if (getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE,
- (void *)&bool_val, &optlen) == 0)
+ if (g_socket_get_option (socket, SOL_SOCKET, SO_KEEPALIVE, &value, NULL))
{
- g_assert (optlen == sizeof bool_val);
- socket->priv->keepalive = !!bool_val;
+ socket->priv->keepalive = !!value;
}
else
{
socket_strerror (errsv));
}
+/* Wrapper around socket() that is shared with gnetworkmonitornetlink.c */
+gint
+g_socket (gint domain,
+ gint type,
+ gint protocol,
+ GError **error)
+{
+ int fd;
+
+#ifdef SOCK_CLOEXEC
+ fd = socket (domain, type | SOCK_CLOEXEC, protocol);
+ if (fd != -1)
+ return fd;
+
+ /* It's possible that libc has SOCK_CLOEXEC but the kernel does not */
+ if (fd < 0 && (errno == EINVAL || errno == EPROTOTYPE))
+#endif
+ fd = socket (domain, type, protocol);
+
+ if (fd < 0)
+ {
+ int errsv = get_socket_errno ();
+
+ g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+ _("Unable to create socket: %s"), socket_strerror (errsv));
+ errno = errsv;
+ return -1;
+ }
+
+#ifndef G_OS_WIN32
+ {
+ int flags;
+
+ /* We always want to set close-on-exec to protect users. If you
+ need to so some weird inheritance to exec you can re-enable this
+ using lower level hacks with g_socket_get_fd(). */
+ flags = fcntl (fd, F_GETFD, 0);
+ if (flags != -1 &&
+ (flags & FD_CLOEXEC) == 0)
+ {
+ flags |= FD_CLOEXEC;
+ fcntl (fd, F_SETFD, flags);
+ }
+ }
+#endif
+
+ return fd;
+}
+
static gint
g_socket_create_socket (GSocketFamily family,
GSocketType type,
- const char *protocol_name,
+ int protocol,
GError **error)
{
gint native_type;
- gint fd;
- gint protocol;
- struct protoent *protoent;
switch (type)
{
g_assert_not_reached ();
}
- protocol = 0;
- if (protocol_name)
+ if (family <= 0)
{
-#ifdef HAVE_GETPROTOBYNAME_R
- char buffer[1024];
- struct protoent my_protoent;
-
- protoent = NULL;
- getprotobyname_r (protocol_name,
- &my_protoent, buffer, sizeof (buffer),
- &protoent);
-#else
- protoent = getprotobyname (protocol_name);
-#endif
- if (protoent == NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("unable to create socket: Protocol %s not supported"),
- protocol_name);
- return -1;
- }
- protocol = protoent->p_proto;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Unable to create socket: %s"), _("Unknown family was specified"));
+ return -1;
}
-#ifdef SOCK_CLOEXEC
- native_type |= SOCK_CLOEXEC;
-#endif
- fd = socket (family, native_type, protocol);
-
- if (fd < 0)
+ if (protocol == -1)
{
- int errsv = get_socket_errno ();
-
- g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
- _("unable to create socket: %s"), socket_strerror (errsv));
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Unable to create socket: %s"), _("Unknown protocol was specified"));
+ return -1;
}
-#ifndef G_OS_WIN32
- {
- int flags;
-
- /* We always want to set close-on-exec to protect users. If you
- need to so some weird inheritance to exec you can re-enable this
- using lower level hacks with g_socket_get_fd(). */
- flags = fcntl (fd, F_GETFD, 0);
- if (flags != -1 &&
- (flags & FD_CLOEXEC) == 0)
- {
- flags |= FD_CLOEXEC;
- fcntl (fd, F_SETFD, flags);
- }
- }
-#endif
-
- return fd;
+ return g_socket (family, native_type, protocol, error);
}
static void
socket->priv->protocol,
&socket->priv->construct_error);
- /* Always use native nonblocking sockets, as
- windows sets sockets to nonblocking automatically
- in certain operations. This way we make things work
- the same on all platforms */
if (socket->priv->fd != -1)
- set_fd_nonblocking (socket->priv->fd);
+ {
+#ifndef G_OS_WIN32
+ GError *error = NULL;
+#else
+ gulong arg;
+#endif
+
+ /* Always use native nonblocking sockets, as Windows sets sockets to
+ * nonblocking automatically in certain operations. This way we make
+ * things work the same on all platforms.
+ */
+#ifndef G_OS_WIN32
+ if (!g_unix_set_fd_nonblocking (socket->priv->fd, TRUE, &error))
+ {
+ g_warning ("Error setting socket nonblocking: %s", error->message);
+ g_clear_error (&error);
+ }
+#else
+ arg = TRUE;
+
+ if (ioctlsocket (socket->priv->fd, FIONBIO, &arg) == SOCKET_ERROR)
+ {
+ int errsv = get_socket_errno ();
+ g_warning ("Error setting socket status flags: %s", socket_strerror (errsv));
+ }
+#endif
+
+#ifdef SO_NOSIGPIPE
+ /* See note about SIGPIPE below. */
+ g_socket_set_option (socket, SOL_SOCKET, SO_NOSIGPIPE, TRUE, NULL);
+#endif
+ }
}
static void
GParamSpec *pspec)
{
GSocket *socket = G_SOCKET (object);
+ GSocketAddress *address;
switch (prop_id)
{
break;
case PROP_PROTOCOL:
- g_value_set_string (value, socket->priv->protocol);
+ g_value_set_enum (value, socket->priv->protocol);
break;
case PROP_FD:
break;
case PROP_LOCAL_ADDRESS:
- g_value_set_object (value, socket->priv->local_address);
+ address = g_socket_get_local_address (socket, NULL);
+ g_value_take_object (value, address);
break;
case PROP_REMOTE_ADDRESS:
- g_value_set_object (value, socket->priv->remote_address);
+ address = g_socket_get_remote_address (socket, NULL);
+ g_value_take_object (value, address);
+ break;
+
+ case PROP_TIMEOUT:
+ g_value_set_uint (value, socket->priv->timeout);
+ break;
+
+ case PROP_TTL:
+ g_value_set_uint (value, g_socket_get_ttl (socket));
+ break;
+
+ case PROP_BROADCAST:
+ g_value_set_boolean (value, g_socket_get_broadcast (socket));
+ break;
+
+ case PROP_MULTICAST_LOOPBACK:
+ g_value_set_boolean (value, g_socket_get_multicast_loopback (socket));
+ break;
+
+ case PROP_MULTICAST_TTL:
+ g_value_set_uint (value, g_socket_get_multicast_ttl (socket));
break;
default:
break;
case PROP_PROTOCOL:
- socket->priv->protocol = g_value_dup_string (value);
+ socket->priv->protocol = g_value_get_enum (value);
break;
case PROP_FD:
g_socket_set_keepalive (socket, g_value_get_boolean (value));
break;
+ case PROP_TIMEOUT:
+ g_socket_set_timeout (socket, g_value_get_uint (value));
+ break;
+
+ case PROP_TTL:
+ g_socket_set_ttl (socket, g_value_get_uint (value));
+ break;
+
+ case PROP_BROADCAST:
+ g_socket_set_broadcast (socket, g_value_get_boolean (value));
+ break;
+
+ case PROP_MULTICAST_LOOPBACK:
+ g_socket_set_multicast_loopback (socket, g_value_get_boolean (value));
+ break;
+
+ case PROP_MULTICAST_TTL:
+ g_socket_set_multicast_ttl (socket, g_value_get_uint (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
g_socket_finalize (GObject *object)
{
GSocket *socket = G_SOCKET (object);
+ gint i;
g_clear_error (&socket->priv->construct_error);
!socket->priv->closed)
g_socket_close (socket, NULL);
- g_free (socket->priv->protocol);
+ if (socket->priv->remote_address)
+ g_object_unref (socket->priv->remote_address);
#ifdef G_OS_WIN32
- g_assert (socket->priv->requested_conditions == NULL);
-#endif
-
- if (G_OBJECT_CLASS (g_socket_parent_class)->finalize)
- (*G_OBJECT_CLASS (g_socket_parent_class)->finalize) (object);
-}
-
-static void
-g_socket_dispose (GObject *object)
-{
- GSocket *socket = G_SOCKET (object);
-
- if (socket->priv->local_address)
+ if (socket->priv->event != WSA_INVALID_EVENT)
{
- g_object_unref (socket->priv->local_address);
- socket->priv->local_address = NULL;
+ WSACloseEvent (socket->priv->event);
+ socket->priv->event = WSA_INVALID_EVENT;
}
- if (socket->priv->remote_address)
+ g_assert (socket->priv->requested_conditions == NULL);
+ g_mutex_clear (&socket->priv->win32_source_lock);
+#endif
+
+ for (i = 0; i < RECV_ADDR_CACHE_SIZE; i++)
{
- g_object_unref (socket->priv->remote_address);
- socket->priv->remote_address = NULL;
+ if (socket->priv->recv_addr_cache[i].addr)
+ {
+ g_object_unref (socket->priv->recv_addr_cache[i].addr);
+ g_free (socket->priv->recv_addr_cache[i].native);
+ }
}
- if (G_OBJECT_CLASS (g_socket_parent_class)->dispose)
- (*G_OBJECT_CLASS (g_socket_parent_class)->dispose) (object);
+ if (G_OBJECT_CLASS (g_socket_parent_class)->finalize)
+ (*G_OBJECT_CLASS (g_socket_parent_class)->finalize) (object);
}
static void
g_socket_class_init (GSocketClass *klass)
{
GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
- volatile GType type;
- /* Make sure winsock has been initialized */
- type = g_inet_address_get_type ();
-
- g_type_class_add_private (klass, sizeof (GSocketPrivate));
+#ifdef SIGPIPE
+ /* There is no portable, thread-safe way to avoid having the process
+ * be killed by SIGPIPE when calling send() or sendmsg(), so we are
+ * forced to simply ignore the signal process-wide.
+ *
+ * Even if we ignore it though, gdb will still stop if the app
+ * receives a SIGPIPE, which can be confusing and annoying. So when
+ * possible, we also use MSG_NOSIGNAL / SO_NOSIGPIPE elsewhere to
+ * prevent the signal from occurring at all.
+ */
+ signal (SIGPIPE, SIG_IGN);
+#endif
gobject_class->finalize = g_socket_finalize;
- gobject_class->dispose = g_socket_dispose;
gobject_class->constructed = g_socket_constructed;
gobject_class->set_property = g_socket_set_property;
gobject_class->get_property = g_socket_get_property;
P_("The sockets address family"),
G_TYPE_SOCKET_FAMILY,
G_SOCKET_FAMILY_INVALID,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TYPE,
g_param_spec_enum ("type",
P_("The sockets type"),
G_TYPE_SOCKET_TYPE,
G_SOCKET_TYPE_STREAM,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PROTOCOL,
- g_param_spec_string ("protocol",
- P_("Socket protocol"),
- P_("The name of the protocol to use, or NULL"),
- NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_param_spec_enum ("protocol",
+ P_("Socket protocol"),
+ P_("The id of the protocol to use, or -1 for unknown"),
+ G_TYPE_SOCKET_PROTOCOL,
+ G_SOCKET_PROTOCOL_UNKNOWN,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_FD,
g_param_spec_int ("fd",
G_MININT,
G_MAXINT,
-1,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_BLOCKING,
g_param_spec_boolean ("blocking",
P_("blocking"),
P_("Whether or not I/O on this socket is blocking"),
TRUE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_LISTEN_BACKLOG,
g_param_spec_int ("listen-backlog",
P_("Listen backlog"),
- P_("outstanding connections in the listen queue"),
+ P_("Outstanding connections in the listen queue"),
0,
SOMAXCONN,
10,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_KEEPALIVE,
g_param_spec_boolean ("keepalive",
P_("Keep connection alive"),
P_("Keep connection alive by sending periodic pings"),
FALSE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_LOCAL_ADDRESS,
g_param_spec_object ("local-address",
P_("Local address"),
P_("The local address the socket is bound to"),
G_TYPE_SOCKET_ADDRESS,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_REMOTE_ADDRESS,
g_param_spec_object ("remote-address",
P_("Remote address"),
P_("The remote address the socket is connected to"),
G_TYPE_SOCKET_ADDRESS,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GSocket:timeout:
+ *
+ * The timeout in seconds on socket I/O
+ *
+ * Since: 2.26
+ */
+ g_object_class_install_property (gobject_class, PROP_TIMEOUT,
+ g_param_spec_uint ("timeout",
+ P_("Timeout"),
+ P_("The timeout in seconds on socket I/O"),
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GSocket:broadcast:
+ *
+ * Whether the socket should allow sending to broadcast addresses.
+ *
+ * Since: 2.32
+ */
+ g_object_class_install_property (gobject_class, PROP_BROADCAST,
+ g_param_spec_boolean ("broadcast",
+ P_("Broadcast"),
+ P_("Whether to allow sending to broadcast addresses"),
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GSocket:ttl:
+ *
+ * Time-to-live for outgoing unicast packets
+ *
+ * Since: 2.32
+ */
+ g_object_class_install_property (gobject_class, PROP_TTL,
+ g_param_spec_uint ("ttl",
+ P_("TTL"),
+ P_("Time-to-live of outgoing unicast packets"),
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GSocket:multicast-loopback:
+ *
+ * Whether outgoing multicast packets loop back to the local host.
+ *
+ * Since: 2.32
+ */
+ g_object_class_install_property (gobject_class, PROP_MULTICAST_LOOPBACK,
+ g_param_spec_boolean ("multicast-loopback",
+ P_("Multicast loopback"),
+ P_("Whether outgoing multicast packets loop back to the local host"),
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GSocket:multicast-ttl:
+ *
+ * Time-to-live out outgoing multicast packets
+ *
+ * Since: 2.32
+ */
+ g_object_class_install_property (gobject_class, PROP_MULTICAST_TTL,
+ g_param_spec_uint ("multicast-ttl",
+ P_("Multicast TTL"),
+ P_("Time-to-live of outgoing multicast packets"),
+ 0, G_MAXUINT, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
}
static void
static void
g_socket_init (GSocket *socket)
{
- socket->priv = G_TYPE_INSTANCE_GET_PRIVATE (socket, G_TYPE_SOCKET, GSocketPrivate);
+ socket->priv = g_socket_get_instance_private (socket);
socket->priv->fd = -1;
socket->priv->blocking = TRUE;
socket->priv->listen_backlog = 10;
socket->priv->construct_error = NULL;
- socket->priv->remote_address = NULL;
- socket->priv->local_address = NULL;
#ifdef G_OS_WIN32
socket->priv->event = WSA_INVALID_EVENT;
+ g_mutex_init (&socket->priv->win32_source_lock);
#endif
}
if (cancellable != NULL)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("Cancellable initialization not supported"));
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Cancellable initialization not supported"));
return FALSE;
}
* g_socket_new:
* @family: the socket family to use, e.g. %G_SOCKET_FAMILY_IPV4.
* @type: the socket type to use.
- * @protocol: the name of the protocol to use, or %NULL.
+ * @protocol: the id of the protocol to use, or 0 for default.
* @error: #GError for error reporting, or %NULL to ignore.
*
* Creates a new #GSocket with the defined family, type and protocol.
- * If @protocol is %NULL the default protocol type for the family and
- * type is used.
+ * If @protocol is 0 (%G_SOCKET_PROTOCOL_DEFAULT) the default protocol type
+ * for the family and type is used.
+ *
+ * The @protocol is a family and type specific int that specifies what
+ * kind of protocol to use. #GSocketProtocol lists several common ones.
+ * Many families only support one protocol, and use 0 for this, others
+ * support several and using 0 means to use the default protocol for
+ * the family and type.
+ *
+ * The protocol id is passed directly to the operating
+ * system, so you can use protocols not listed in #GSocketProtocol if you
+ * know the protocol number used for it.
*
* Returns: a #GSocket or %NULL on error.
* Free the returned object with g_object_unref().
*
* Since: 2.22
- **/
+ */
GSocket *
-g_socket_new (GSocketFamily family,
- GSocketType type,
- const char *protocol,
- GError **error)
+g_socket_new (GSocketFamily family,
+ GSocketType type,
+ GSocketProtocol protocol,
+ GError **error)
{
return G_SOCKET (g_initable_new (G_TYPE_SOCKET,
NULL, error,
* Free the returned object with g_object_unref().
*
* Since: 2.22
- **/
+ */
GSocket *
-g_socket_new_from_fd (gint fd,
+g_socket_new_from_fd (gint fd,
GError **error)
{
return G_SOCKET (g_initable_new (G_TYPE_SOCKET,
* is a GSocket level feature.
*
* Since: 2.22
- **/
+ */
void
g_socket_set_blocking (GSocket *socket,
gboolean blocking)
* Returns: %TRUE if blocking I/O is used, %FALSE otherwise.
*
* Since: 2.22
- **/
+ */
gboolean
g_socket_get_blocking (GSocket *socket)
{
/**
* g_socket_set_keepalive:
* @socket: a #GSocket.
- * @keepalive: Whether to use try to keep the connection alive or not.
+ * @keepalive: Value for the keepalive flag
+ *
+ * Sets or unsets the %SO_KEEPALIVE flag on the underlying socket. When
+ * this flag is set on a socket, the system will attempt to verify that the
+ * remote socket endpoint is still present if a sufficiently long period of
+ * time passes with no data being exchanged. If the system is unable to
+ * verify the presence of the remote endpoint, it will automatically close
+ * the connection.
*
- * Setting @keepalive to %TRUE enables the sending of periodic ping requests
- * on idle connections in order to keep the connection alive. This is only
- * useful for connection oriented sockets. The exact period used between
- * each ping is system and protocol dependent.
+ * This option is only functional on certain kinds of sockets. (Notably,
+ * %G_SOCKET_PROTOCOL_TCP sockets.)
*
- * Sending keepalive requests like this has a few disadvantages. For instance,
- * it uses more network bandwidth, and it makes your application more sensitive
- * to temporary outages in the network (i.e. if a cable is pulled your otherwise
- * idle connection could be terminated, whereas otherwise it would survive unless
- * actually used before the cable was reinserted). However, it is sometimes
- * useful to ensure that connections are eventually terminated if e.g. the
- * remote side is disconnected, so as to avoid leaking resources forever.
+ * The exact time between pings is system- and protocol-dependent, but will
+ * normally be at least two hours. Most commonly, you would set this flag
+ * on a server socket if you want to allow clients to remain idle for long
+ * periods of time, but also want to ensure that connections are eventually
+ * garbage-collected if clients crash or become unreachable.
*
* Since: 2.22
- **/
+ */
void
-g_socket_set_keepalive (GSocket *socket,
- gboolean keepalive)
+g_socket_set_keepalive (GSocket *socket,
+ gboolean keepalive)
{
- int value;
+ GError *error = NULL;
g_return_if_fail (G_IS_SOCKET (socket));
if (socket->priv->keepalive == keepalive)
return;
- value = (gint) keepalive;
- if (setsockopt (socket->priv->fd, SOL_SOCKET, SO_KEEPALIVE,
- (gpointer) &value, sizeof (value)) < 0)
+ if (!g_socket_set_option (socket, SOL_SOCKET, SO_KEEPALIVE,
+ keepalive, &error))
{
- int errsv = get_socket_errno ();
- g_warning ("error setting keepalive: %s", socket_strerror (errsv));
+ g_warning ("error setting keepalive: %s", error->message);
+ g_error_free (error);
return;
}
* Returns: %TRUE if keepalive is active, %FALSE otherwise.
*
* Since: 2.22
- **/
+ */
gboolean
g_socket_get_keepalive (GSocket *socket)
{
* Returns: the maximum number of pending connections.
*
* Since: 2.22
- **/
+ */
gint
-g_socket_get_listen_backlog (GSocket *socket)
+g_socket_get_listen_backlog (GSocket *socket)
{
g_return_val_if_fail (G_IS_SOCKET (socket), 0);
* connecting to the socket and the application is not handling them
* on time then the new connections will be refused.
*
+ * Note that this must be called before g_socket_listen() and has no
+ * effect if called after that.
+ *
* Since: 2.22
- **/
+ */
void
g_socket_set_listen_backlog (GSocket *socket,
- gint backlog)
+ gint backlog)
{
g_return_if_fail (G_IS_SOCKET (socket));
+ g_return_if_fail (!socket->priv->listening);
if (backlog != socket->priv->listen_backlog)
{
}
/**
- * g_socket_get_family:
+ * g_socket_get_timeout:
* @socket: a #GSocket.
*
- * Gets the socket family of the socket.
+ * Gets the timeout setting of the socket. For details on this, see
+ * g_socket_set_timeout().
*
- * Returns: a #GSocketFamily
+ * Returns: the timeout in seconds
*
- * Since: 2.22
- **/
-GSocketFamily
-g_socket_get_family (GSocket *socket)
+ * Since: 2.26
+ */
+guint
+g_socket_get_timeout (GSocket *socket)
{
- g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_FAMILY_INVALID);
+ g_return_val_if_fail (G_IS_SOCKET (socket), 0);
- return socket->priv->family;
+ return socket->priv->timeout;
+}
+
+/**
+ * g_socket_set_timeout:
+ * @socket: a #GSocket.
+ * @timeout: the timeout for @socket, in seconds, or 0 for none
+ *
+ * Sets the time in seconds after which I/O operations on @socket will
+ * time out if they have not yet completed.
+ *
+ * On a blocking socket, this means that any blocking #GSocket
+ * operation will time out after @timeout seconds of inactivity,
+ * returning %G_IO_ERROR_TIMED_OUT.
+ *
+ * On a non-blocking socket, calls to g_socket_condition_wait() will
+ * also fail with %G_IO_ERROR_TIMED_OUT after the given time. Sources
+ * created with g_socket_create_source() will trigger after
+ * @timeout seconds of inactivity, with the requested condition
+ * set, at which point calling g_socket_receive(), g_socket_send(),
+ * g_socket_check_connect_result(), etc, will fail with
+ * %G_IO_ERROR_TIMED_OUT.
+ *
+ * If @timeout is 0 (the default), operations will never time out
+ * on their own.
+ *
+ * Note that if an I/O operation is interrupted by a signal, this may
+ * cause the timeout to be reset.
+ *
+ * Since: 2.26
+ */
+void
+g_socket_set_timeout (GSocket *socket,
+ guint timeout)
+{
+ g_return_if_fail (G_IS_SOCKET (socket));
+
+ if (timeout != socket->priv->timeout)
+ {
+ socket->priv->timeout = timeout;
+ g_object_notify (G_OBJECT (socket), "timeout");
+ }
+}
+
+/**
+ * g_socket_get_ttl:
+ * @socket: a #GSocket.
+ *
+ * Gets the unicast time-to-live setting on @socket; see
+ * g_socket_set_ttl() for more details.
+ *
+ * Returns: the time-to-live setting on @socket
+ *
+ * Since: 2.32
+ */
+guint
+g_socket_get_ttl (GSocket *socket)
+{
+ GError *error = NULL;
+ gint value;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), 0);
+
+ if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+ {
+ g_socket_get_option (socket, IPPROTO_IP, IP_TTL,
+ &value, &error);
+ }
+ else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+ {
+ g_socket_get_option (socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ &value, &error);
+ }
+ else
+ g_return_val_if_reached (0);
+
+ if (error)
+ {
+ g_warning ("error getting unicast ttl: %s", error->message);
+ g_error_free (error);
+ return 0;
+ }
+
+ return value;
+}
+
+/**
+ * g_socket_set_ttl:
+ * @socket: a #GSocket.
+ * @ttl: the time-to-live value for all unicast packets on @socket
+ *
+ * Sets the time-to-live for outgoing unicast packets on @socket.
+ * By default the platform-specific default value is used.
+ *
+ * Since: 2.32
+ */
+void
+g_socket_set_ttl (GSocket *socket,
+ guint ttl)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (G_IS_SOCKET (socket));
+
+ if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+ {
+ g_socket_set_option (socket, IPPROTO_IP, IP_TTL,
+ ttl, &error);
+ }
+ else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+ {
+ g_socket_set_option (socket, IPPROTO_IP, IP_TTL,
+ ttl, NULL);
+ g_socket_set_option (socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ ttl, &error);
+ }
+ else
+ g_return_if_reached ();
+
+ if (error)
+ {
+ g_warning ("error setting unicast ttl: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_object_notify (G_OBJECT (socket), "ttl");
+}
+
+/**
+ * g_socket_get_broadcast:
+ * @socket: a #GSocket.
+ *
+ * Gets the broadcast setting on @socket; if %TRUE,
+ * it is possible to send packets to broadcast
+ * addresses.
+ *
+ * Returns: the broadcast setting on @socket
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_get_broadcast (GSocket *socket)
+{
+ GError *error = NULL;
+ gint value;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+ if (!g_socket_get_option (socket, SOL_SOCKET, SO_BROADCAST,
+ &value, &error))
+ {
+ g_warning ("error getting broadcast: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return !!value;
+}
+
+/**
+ * g_socket_set_broadcast:
+ * @socket: a #GSocket.
+ * @broadcast: whether @socket should allow sending to broadcast
+ * addresses
+ *
+ * Sets whether @socket should allow sending to broadcast addresses.
+ * This is %FALSE by default.
+ *
+ * Since: 2.32
+ */
+void
+g_socket_set_broadcast (GSocket *socket,
+ gboolean broadcast)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (G_IS_SOCKET (socket));
+
+ broadcast = !!broadcast;
+
+ if (!g_socket_set_option (socket, SOL_SOCKET, SO_BROADCAST,
+ broadcast, &error))
+ {
+ g_warning ("error setting broadcast: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_object_notify (G_OBJECT (socket), "broadcast");
+}
+
+/**
+ * g_socket_get_multicast_loopback:
+ * @socket: a #GSocket.
+ *
+ * Gets the multicast loopback setting on @socket; if %TRUE (the
+ * default), outgoing multicast packets will be looped back to
+ * multicast listeners on the same host.
+ *
+ * Returns: the multicast loopback setting on @socket
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_get_multicast_loopback (GSocket *socket)
+{
+ GError *error = NULL;
+ gint value;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+ if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+ {
+ g_socket_get_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &value, &error);
+ }
+ else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+ {
+ g_socket_get_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &value, &error);
+ }
+ else
+ g_return_val_if_reached (FALSE);
+
+ if (error)
+ {
+ g_warning ("error getting multicast loopback: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return !!value;
+}
+
+/**
+ * g_socket_set_multicast_loopback:
+ * @socket: a #GSocket.
+ * @loopback: whether @socket should receive messages sent to its
+ * multicast groups from the local host
+ *
+ * Sets whether outgoing multicast packets will be received by sockets
+ * listening on that multicast address on the same host. This is %TRUE
+ * by default.
+ *
+ * Since: 2.32
+ */
+void
+g_socket_set_multicast_loopback (GSocket *socket,
+ gboolean loopback)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (G_IS_SOCKET (socket));
+
+ loopback = !!loopback;
+
+ if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+ {
+ g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ loopback, &error);
+ }
+ else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+ {
+ g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ loopback, NULL);
+ g_socket_set_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ loopback, &error);
+ }
+ else
+ g_return_if_reached ();
+
+ if (error)
+ {
+ g_warning ("error setting multicast loopback: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_object_notify (G_OBJECT (socket), "multicast-loopback");
+}
+
+/**
+ * g_socket_get_multicast_ttl:
+ * @socket: a #GSocket.
+ *
+ * Gets the multicast time-to-live setting on @socket; see
+ * g_socket_set_multicast_ttl() for more details.
+ *
+ * Returns: the multicast time-to-live setting on @socket
+ *
+ * Since: 2.32
+ */
+guint
+g_socket_get_multicast_ttl (GSocket *socket)
+{
+ GError *error = NULL;
+ gint value;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), 0);
+
+ if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+ {
+ g_socket_get_option (socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ &value, &error);
+ }
+ else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+ {
+ g_socket_get_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &value, &error);
+ }
+ else
+ g_return_val_if_reached (FALSE);
+
+ if (error)
+ {
+ g_warning ("error getting multicast ttl: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return value;
+}
+
+/**
+ * g_socket_set_multicast_ttl:
+ * @socket: a #GSocket.
+ * @ttl: the time-to-live value for all multicast datagrams on @socket
+ *
+ * Sets the time-to-live for outgoing multicast datagrams on @socket.
+ * By default, this is 1, meaning that multicast packets will not leave
+ * the local network.
+ *
+ * Since: 2.32
+ */
+void
+g_socket_set_multicast_ttl (GSocket *socket,
+ guint ttl)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (G_IS_SOCKET (socket));
+
+ if (socket->priv->family == G_SOCKET_FAMILY_IPV4)
+ {
+ g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ ttl, &error);
+ }
+ else if (socket->priv->family == G_SOCKET_FAMILY_IPV6)
+ {
+ g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ ttl, NULL);
+ g_socket_set_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ ttl, &error);
+ }
+ else
+ g_return_if_reached ();
+
+ if (error)
+ {
+ g_warning ("error setting multicast ttl: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_object_notify (G_OBJECT (socket), "multicast-ttl");
+}
+
+/**
+ * g_socket_get_family:
+ * @socket: a #GSocket.
+ *
+ * Gets the socket family of the socket.
+ *
+ * Returns: a #GSocketFamily
+ *
+ * Since: 2.22
+ */
+GSocketFamily
+g_socket_get_family (GSocket *socket)
+{
+ g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_FAMILY_INVALID);
+
+ return socket->priv->family;
}
/**
* Returns: a #GSocketType
*
* Since: 2.22
- **/
+ */
GSocketType
g_socket_get_socket_type (GSocket *socket)
{
* g_socket_get_protocol:
* @socket: a #GSocket.
*
- * Gets the socket protocol type name the socket was created with.
- * This can be %NULL if the socket was created with a NULL protocol.
+ * Gets the socket protocol id the socket was created with.
+ * In case the protocol is unknown, -1 is returned.
*
- * Returns: a string or %NULL, do not free
+ * Returns: a protocol id, or -1 if unknown
*
* Since: 2.22
- **/
-const char *
+ */
+GSocketProtocol
g_socket_get_protocol (GSocket *socket)
{
- g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
+ g_return_val_if_fail (G_IS_SOCKET (socket), -1);
return socket->priv->protocol;
}
* @socket: a #GSocket.
*
* Returns the underlying OS socket object. On unix this
- * is a socket file descriptor, and on windows this is
+ * is a socket file descriptor, and on Windows this is
* a Winsock2 SOCKET handle. This may be useful for
* doing platform specific or otherwise unusual operations
* on the socket.
* Returns: the file descriptor of the socket.
*
* Since: 2.22
- **/
+ */
int
g_socket_get_fd (GSocket *socket)
{
* @error: #GError for error reporting, or %NULL to ignore.
*
* Try to get the local address of a bound socket. This is only
- * useful if the socket has been bound to a local address.
+ * useful if the socket has been bound to a local address,
+ * either explicitly or implicitly when connecting.
*
- * Returns: a #GSocketAddress or %NULL on error.
+ * Returns: (transfer full): a #GSocketAddress or %NULL on error.
+ * Free the returned object with g_object_unref().
*
* Since: 2.22
- **/
+ */
GSocketAddress *
g_socket_get_local_address (GSocket *socket,
GError **error)
{
- gchar buffer[256];
- guint32 len = 256;
+ struct sockaddr_storage buffer;
+ guint len = sizeof (buffer);
g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
- if (socket->priv->local_address)
- return socket->priv->local_address;
-
- if (getsockname (socket->priv->fd, (struct sockaddr *) buffer, &len) < 0)
+ if (getsockname (socket->priv->fd, (struct sockaddr *) &buffer, &len) < 0)
{
int errsv = get_socket_errno ();
g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
return NULL;
}
- socket->priv->local_address = g_socket_address_new_from_native (buffer, len);
- return socket->priv->local_address;
+ return g_socket_address_new_from_native (&buffer, len);
}
/**
* Try to get the remove address of a connected socket. This is only
* useful for connection oriented sockets that have been connected.
*
- * Returns: a #GSocketAddress or %NULL on error.
+ * Returns: (transfer full): a #GSocketAddress or %NULL on error.
+ * Free the returned object with g_object_unref().
*
* Since: 2.22
- **/
+ */
GSocketAddress *
g_socket_get_remote_address (GSocket *socket,
GError **error)
{
- gchar buffer[256];
- guint32 len = 256;
+ struct sockaddr_storage buffer;
+ guint len = sizeof (buffer);
g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
- if (socket->priv->remote_address)
- return socket->priv->remote_address;
+ if (socket->priv->connect_pending)
+ {
+ if (!g_socket_check_connect_result (socket, error))
+ return NULL;
+ else
+ socket->priv->connect_pending = FALSE;
+ }
- if (getpeername (socket->priv->fd, (struct sockaddr *) buffer, &len) < 0)
+ if (!socket->priv->remote_address)
{
- int errsv = get_socket_errno ();
- g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
- _("could not get remote address: %s"), socket_strerror (errsv));
- return NULL;
+ if (getpeername (socket->priv->fd, (struct sockaddr *) &buffer, &len) < 0)
+ {
+ int errsv = get_socket_errno ();
+ g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+ _("could not get remote address: %s"), socket_strerror (errsv));
+ return NULL;
+ }
+
+ socket->priv->remote_address = g_socket_address_new_from_native (&buffer, len);
}
- socket->priv->remote_address = g_socket_address_new_from_native (buffer, len);
- return socket->priv->remote_address;
+ return g_object_ref (socket->priv->remote_address);
}
/**
* Returns: %TRUE if socket is connected, %FALSE otherwise.
*
* Since: 2.22
- **/
+ */
gboolean
g_socket_is_connected (GSocket *socket)
{
g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
- return socket->priv->remote_address != NULL;
+ return socket->priv->connected;
}
/**
* Before calling this the socket must be bound to a local address using
* g_socket_bind().
*
+ * To set the maximum amount of outstanding clients, use
+ * g_socket_set_listen_backlog().
+ *
* Returns: %TRUE on success, %FALSE on error.
*
* Since: 2.22
- **/
+ */
gboolean
g_socket_listen (GSocket *socket,
GError **error)
return FALSE;
}
+ socket->priv->listening = TRUE;
+
return TRUE;
}
*
* It is generally required to bind to a local address before you can
* receive connections. (See g_socket_listen() and g_socket_accept() ).
- *
- * If @allow_reuse is %TRUE this allows the bind call to succeed in some
- * situation where it would otherwise return a %G_IO_ERROR_ADDRESS_IN_USE
- * error. The main example is for a TCP server socket where there are
- * outstanding connections in the WAIT state, which are generally safe
- * to ignore. However, setting it to %TRUE doesn't mean the call will
- * succeed if there is a socket actively bound to the address.
- *
- * In general, pass %TRUE if the socket will be used to accept connections,
- * otherwise pass %FALSE.
+ * In certain situations, you may also want to bind a socket that will be
+ * used to initiate connections, though this is not normally required.
+ *
+ * If @socket is a TCP socket, then @allow_reuse controls the setting
+ * of the `SO_REUSEADDR` socket option; normally it should be %TRUE for
+ * server sockets (sockets that you will eventually call
+ * g_socket_accept() on), and %FALSE for client sockets. (Failing to
+ * set this flag on a server socket may cause g_socket_bind() to return
+ * %G_IO_ERROR_ADDRESS_IN_USE if the server program is stopped and then
+ * immediately restarted.)
+ *
+ * If @socket is a UDP socket, then @allow_reuse determines whether or
+ * not other UDP sockets can be bound to the same address at the same
+ * time. In particular, you can have several UDP sockets bound to the
+ * same address, and they will all receive all of the multicast and
+ * broadcast packets sent to that address. (The behavior of unicast
+ * UDP packets to an address with multiple listeners is not defined.)
*
* Returns: %TRUE on success, %FALSE on error.
*
* Since: 2.22
- **/
+ */
gboolean
g_socket_bind (GSocket *socket,
GSocketAddress *address,
gboolean reuse_address,
GError **error)
{
- gchar addr[256];
- int value;
+ struct sockaddr_storage addr;
+ gboolean so_reuseaddr;
+#ifdef SO_REUSEPORT
+ gboolean so_reuseport;
+#endif
+
+ g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE);
+
+ if (!check_socket (socket, error))
+ return FALSE;
+
+ if (!g_socket_address_to_native (address, &addr, sizeof addr, error))
+ return FALSE;
+
+ /* On Windows, SO_REUSEADDR has the semantics we want for UDP
+ * sockets, but has nasty side effects we don't want for TCP
+ * sockets.
+ *
+ * On other platforms, we set SO_REUSEPORT, if it exists, for
+ * UDP sockets, and SO_REUSEADDR for all sockets, hoping that
+ * if SO_REUSEPORT doesn't exist, then SO_REUSEADDR will have
+ * the desired semantics on UDP (as it does on Linux, although
+ * Linux has SO_REUSEPORT too as of 3.9).
+ */
+
+#ifdef G_OS_WIN32
+ so_reuseaddr = reuse_address && (socket->priv->type == G_SOCKET_TYPE_DATAGRAM);
+#else
+ so_reuseaddr = !!reuse_address;
+#endif
+
+#ifdef SO_REUSEPORT
+ so_reuseport = reuse_address && (socket->priv->type == G_SOCKET_TYPE_DATAGRAM);
+#endif
+
+ /* Ignore errors here, the only likely error is "not supported", and
+ * this is a "best effort" thing mainly.
+ */
+ g_socket_set_option (socket, SOL_SOCKET, SO_REUSEADDR, so_reuseaddr, NULL);
+#ifdef SO_REUSEPORT
+ g_socket_set_option (socket, SOL_SOCKET, SO_REUSEPORT, so_reuseport, NULL);
+#endif
+
+ if (bind (socket->priv->fd, (struct sockaddr *) &addr,
+ g_socket_address_get_native_size (address)) < 0)
+ {
+ int errsv = get_socket_errno ();
+ g_set_error (error,
+ G_IO_ERROR, socket_io_error_from_errno (errsv),
+ _("Error binding to address: %s"), socket_strerror (errsv));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#if !defined(HAVE_IF_NAMETOINDEX) && defined(G_OS_WIN32)
+static guint
+if_nametoindex (const gchar *iface)
+{
+ PIP_ADAPTER_ADDRESSES addresses = NULL, p;
+ gulong addresses_len = 0;
+ guint idx = 0;
+ DWORD res;
+
+ res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, NULL, &addresses_len);
+ if (res != NO_ERROR && res != ERROR_BUFFER_OVERFLOW)
+ {
+ if (res == ERROR_NO_DATA)
+ errno = ENXIO;
+ else
+ errno = EINVAL;
+ return 0;
+ }
+
+ addresses = g_malloc (addresses_len);
+ res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, addresses, &addresses_len);
+
+ if (res != NO_ERROR)
+ {
+ g_free (addresses);
+ if (res == ERROR_NO_DATA)
+ errno = ENXIO;
+ else
+ errno = EINVAL;
+ return 0;
+ }
+
+ p = addresses;
+ while (p)
+ {
+ if (strcmp (p->AdapterName, iface) == 0)
+ {
+ idx = p->IfIndex;
+ break;
+ }
+ p = p->Next;
+ }
+
+ if (p == NULL)
+ errno = ENXIO;
+
+ g_free (addresses);
+
+ return idx;
+}
+
+#define HAVE_IF_NAMETOINDEX 1
+#endif
+
+static gboolean
+g_socket_multicast_group_operation (GSocket *socket,
+ GInetAddress *group,
+ gboolean source_specific,
+ const gchar *iface,
+ gboolean join_group,
+ GError **error)
+{
+ const guint8 *native_addr;
+ gint optname, result;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+ g_return_val_if_fail (socket->priv->type == G_SOCKET_TYPE_DATAGRAM, FALSE);
+ g_return_val_if_fail (G_IS_INET_ADDRESS (group), FALSE);
+
+ if (!check_socket (socket, error))
+ return FALSE;
+
+ native_addr = g_inet_address_to_bytes (group);
+ if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV4)
+ {
+#ifdef HAVE_IP_MREQN
+ struct ip_mreqn mc_req;
+#else
+ struct ip_mreq mc_req;
+#endif
+
+ memset (&mc_req, 0, sizeof (mc_req));
+ memcpy (&mc_req.imr_multiaddr, native_addr, sizeof (struct in_addr));
+
+#ifdef HAVE_IP_MREQN
+ if (iface)
+ mc_req.imr_ifindex = if_nametoindex (iface);
+ else
+ mc_req.imr_ifindex = 0; /* Pick any. */
+#elif defined(G_OS_WIN32)
+ if (iface)
+ mc_req.imr_interface.s_addr = g_htonl (if_nametoindex (iface));
+ else
+ mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
+#else
+ mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY);
+#endif
+
+ if (source_specific)
+ {
+#ifdef IP_ADD_SOURCE_MEMBERSHIP
+ optname = join_group ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP;
+#else
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ join_group ?
+ _("Error joining multicast group: %s") :
+ _("Error leaving multicast group: %s"),
+ _("No support for source-specific multicast"));
+ return FALSE;
+#endif
+ }
+ else
+ optname = join_group ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
+ result = setsockopt (socket->priv->fd, IPPROTO_IP, optname,
+ &mc_req, sizeof (mc_req));
+ }
+ else if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV6)
+ {
+ struct ipv6_mreq mc_req_ipv6;
+
+ memset (&mc_req_ipv6, 0, sizeof (mc_req_ipv6));
+ memcpy (&mc_req_ipv6.ipv6mr_multiaddr, native_addr, sizeof (struct in6_addr));
+#ifdef HAVE_IF_NAMETOINDEX
+ if (iface)
+ mc_req_ipv6.ipv6mr_interface = if_nametoindex (iface);
+ else
+#endif
+ mc_req_ipv6.ipv6mr_interface = 0;
+
+ optname = join_group ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
+ result = setsockopt (socket->priv->fd, IPPROTO_IPV6, optname,
+ &mc_req_ipv6, sizeof (mc_req_ipv6));
+ }
+ else
+ g_return_val_if_reached (FALSE);
+
+ if (result < 0)
+ {
+ int errsv = get_socket_errno ();
+
+ g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+ join_group ?
+ _("Error joining multicast group: %s") :
+ _("Error leaving multicast group: %s"),
+ socket_strerror (errsv));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * g_socket_join_multicast_group:
+ * @socket: a #GSocket.
+ * @group: a #GInetAddress specifying the group address to join.
+ * @iface: (allow-none): Name of the interface to use, or %NULL
+ * @source_specific: %TRUE if source-specific multicast should be used
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Registers @socket to receive multicast messages sent to @group.
+ * @socket must be a %G_SOCKET_TYPE_DATAGRAM socket, and must have
+ * been bound to an appropriate interface and port with
+ * g_socket_bind().
+ *
+ * If @iface is %NULL, the system will automatically pick an interface
+ * to bind to based on @group.
+ *
+ * If @source_specific is %TRUE, source-specific multicast as defined
+ * in RFC 4604 is used. Note that on older platforms this may fail
+ * with a %G_IO_ERROR_NOT_SUPPORTED error.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_join_multicast_group (GSocket *socket,
+ GInetAddress *group,
+ gboolean source_specific,
+ const gchar *iface,
+ GError **error)
+{
+ return g_socket_multicast_group_operation (socket, group, source_specific, iface, TRUE, error);
+}
+
+/**
+ * g_socket_leave_multicast_group:
+ * @socket: a #GSocket.
+ * @group: a #GInetAddress specifying the group address to leave.
+ * @iface: (allow-none): Interface used
+ * @source_specific: %TRUE if source-specific multicast was used
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Removes @socket from the multicast group defined by @group, @iface,
+ * and @source_specific (which must all have the same values they had
+ * when you joined the group).
+ *
+ * @socket remains bound to its address and port, and can still receive
+ * unicast messages after calling this.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_leave_multicast_group (GSocket *socket,
+ GInetAddress *group,
+ gboolean source_specific,
+ const gchar *iface,
+ GError **error)
+{
+ return g_socket_multicast_group_operation (socket, group, source_specific, iface, FALSE, error);
+}
+
+/**
+ * g_socket_speaks_ipv4:
+ * @socket: a #GSocket
+ *
+ * Checks if a socket is capable of speaking IPv4.
+ *
+ * IPv4 sockets are capable of speaking IPv4. On some operating systems
+ * and under some combinations of circumstances IPv6 sockets are also
+ * capable of speaking IPv4. See RFC 3493 section 3.7 for more
+ * information.
+ *
+ * No other types of sockets are currently considered as being capable
+ * of speaking IPv4.
+ *
+ * Returns: %TRUE if this socket can be used with IPv4.
+ *
+ * Since: 2.22
+ **/
+gboolean
+g_socket_speaks_ipv4 (GSocket *socket)
+{
+ switch (socket->priv->family)
+ {
+ case G_SOCKET_FAMILY_IPV4:
+ return TRUE;
- g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE);
+ case G_SOCKET_FAMILY_IPV6:
+#if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY)
+ {
+ gint v6_only;
- if (!check_socket (socket, error))
- return FALSE;
+ if (!g_socket_get_option (socket,
+ IPPROTO_IPV6, IPV6_V6ONLY,
+ &v6_only, NULL))
+ return FALSE;
- /* SO_REUSEADDR on windows means something else and is not what we want.
- It always allows the unix variant of SO_REUSEADDR anyway */
-#ifndef G_OS_WIN32
- value = (int) !!reuse_address;
- if (setsockopt (socket->priv->fd, SOL_SOCKET, SO_REUSEADDR,
- (gpointer) &value, sizeof (value)) < 0)
- {
- int errsv = get_socket_errno ();
- g_set_error (error,
- G_IO_ERROR, socket_io_error_from_errno (errsv),
- _("Error setting reuse_address: %s"), socket_strerror (errsv));
+ return !v6_only;
+ }
+#else
return FALSE;
- }
#endif
- if (!g_socket_address_to_native (address, addr, sizeof addr))
- return FALSE;
-
- if (bind (socket->priv->fd, (struct sockaddr *) addr,
- g_socket_address_get_native_size (address)) < 0)
- {
- int errsv = get_socket_errno ();
- g_set_error (error,
- G_IO_ERROR, socket_io_error_from_errno (errsv),
- _("Error binding to address: %s"), socket_strerror (errsv));
+ default:
return FALSE;
}
-
- socket->priv->local_address = g_object_ref (address);
-
- return TRUE;
}
/**
* g_socket_accept:
* @socket: a #GSocket.
+ * @cancellable: (allow-none): a %GCancellable or %NULL
* @error: #GError for error reporting, or %NULL to ignore.
*
* Accept incoming connections on a connection-based socket. This removes
* or return %G_IO_ERROR_WOULD_BLOCK if non-blocking I/O is enabled.
* To be notified of an incoming connection, wait for the %G_IO_IN condition.
*
- * Returns: a new #GSocket, or %NULL on error.
+ * Returns: (transfer full): a new #GSocket, or %NULL on error.
* Free the returned object with g_object_unref().
*
* Since: 2.22
- **/
+ */
GSocket *
g_socket_accept (GSocket *socket,
+ GCancellable *cancellable,
GError **error)
{
GSocket *new_socket;
if (!check_socket (socket, error))
return NULL;
- while (1)
+ if (!check_timeout (socket, error))
+ return NULL;
+
+ while (TRUE)
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
- G_IO_IN, NULL, error))
+ G_IO_IN, cancellable, error))
return NULL;
if ((ret = accept (socket->priv->fd, NULL, 0)) < 0)
close (ret);
#endif
}
+ else
+ new_socket->priv->protocol = socket->priv->protocol;
return new_socket;
}
* g_socket_connect:
* @socket: a #GSocket.
* @address: a #GSocketAddress specifying the remote address.
+ * @cancellable: (allow-none): a %GCancellable or %NULL
* @error: #GError for error reporting, or %NULL to ignore.
*
* Connect the socket to the specified remote address.
* the default address for g_socket_send() and discards all incoming datagrams
* from other sources.
*
- * Generally connection oriented sockets can only connect once, but connection-less
- * sockets can connect multiple times to change the default address.
+ * Generally connection oriented sockets can only connect once, but
+ * connection-less sockets can connect multiple times to change the
+ * default address.
*
* If the connect call needs to do network I/O it will block, unless
* non-blocking I/O is enabled. Then %G_IO_ERROR_PENDING is returned
* and the user can be notified of the connection finishing by waiting
- * for the G_IO_OUT condition. The result of the connection can then be
- * checked with g_socket_check_pending_error().
+ * for the G_IO_OUT condition. The result of the connection must then be
+ * checked with g_socket_check_connect_result().
*
* Returns: %TRUE if connected, %FALSE on error.
*
* Since: 2.22
- **/
+ */
gboolean
g_socket_connect (GSocket *socket,
GSocketAddress *address,
+ GCancellable *cancellable,
GError **error)
{
- gchar buffer[256];
+ struct sockaddr_storage buffer;
g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE);
if (!check_socket (socket, error))
return FALSE;
- g_socket_address_to_native (address, buffer, sizeof buffer);
+ if (!g_socket_address_to_native (address, &buffer, sizeof buffer, error))
+ return FALSE;
+
+ if (socket->priv->remote_address)
+ g_object_unref (socket->priv->remote_address);
+ socket->priv->remote_address = g_object_ref (address);
while (1)
{
- if (socket->priv->blocking &&
- !g_socket_condition_wait (socket,
- G_IO_IN, NULL, error))
- return FALSE;
-
- if (connect (socket->priv->fd, (struct sockaddr *) buffer,
+ if (connect (socket->priv->fd, (struct sockaddr *) &buffer,
g_socket_address_get_native_size (address)) < 0)
{
int errsv = get_socket_errno ();
#ifndef G_OS_WIN32
if (errsv == EINPROGRESS)
#else
- if (errsv == WSAEINPROGRESS)
+ if (errsv == WSAEWOULDBLOCK)
#endif
{
if (socket->priv->blocking)
{
- g_socket_condition_wait (socket, G_IO_OUT, NULL, NULL);
- if (g_socket_check_pending_error (socket, error))
- break;
- else
- g_prefix_error (error, _("Error connecting: "));
+ if (g_socket_condition_wait (socket, G_IO_OUT, cancellable, error))
+ {
+ if (g_socket_check_connect_result (socket, error))
+ break;
+ }
}
else
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
- _("Connection in progress"));
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+ _("Connection in progress"));
+ socket->priv->connect_pending = TRUE;
+ }
}
else
- g_set_error (error, G_IO_ERROR,
- socket_io_error_from_errno (errsv),
- _("Error connecting: %s"), socket_strerror (errsv));
+ g_set_error_literal (error, G_IO_ERROR,
+ socket_io_error_from_errno (errsv),
+ socket_strerror (errsv));
return FALSE;
}
win32_unset_event_mask (socket, FD_CONNECT);
- socket->priv->remote_address = g_object_ref (address);
+ socket->priv->connected = TRUE;
return TRUE;
}
/**
- * g_socket_check_pending_error:
+ * g_socket_check_connect_result:
* @socket: a #GSocket
* @error: #GError for error reporting, or %NULL to ignore.
*
- * Checks and resets the pending error for the socket. This is typically
- * used to check for errors when g_socket_connect() is used in non-blocking mode.
+ * Checks and resets the pending connect error for the socket.
+ * This is used to check for errors when g_socket_connect() is
+ * used in non-blocking mode.
*
* Returns: %TRUE if no error, %FALSE otherwise, setting @error to the error
*
* Since: 2.22
- **/
+ */
gboolean
-g_socket_check_pending_error (GSocket *socket,
- GError **error)
+g_socket_check_connect_result (GSocket *socket,
+ GError **error)
{
- guint optlen;
int value;
- if (getsockopt (socket->priv->fd, SOL_SOCKET, SO_ERROR, (void *)&value, &optlen) != 0)
- {
- int errsv = get_socket_errno ();
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
- g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
- _("Unable to get pending error: %s"), socket_strerror (errsv));
+ if (!check_socket (socket, error))
+ return FALSE;
+
+ if (!check_timeout (socket, error))
+ return FALSE;
+
+ if (!g_socket_get_option (socket, SOL_SOCKET, SO_ERROR, &value, error))
+ {
+ g_prefix_error (error, _("Unable to get pending error: "));
return FALSE;
}
if (value != 0)
{
- g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (value),
- "%s", socket_strerror (value));
+ g_set_error_literal (error, G_IO_ERROR, socket_io_error_from_errno (value),
+ socket_strerror (value));
+ if (socket->priv->remote_address)
+ {
+ g_object_unref (socket->priv->remote_address);
+ socket->priv->remote_address = NULL;
+ }
return FALSE;
}
+
+ socket->priv->connected = TRUE;
return TRUE;
}
/**
+ * g_socket_get_available_bytes:
+ * @socket: a #GSocket
+ *
+ * Get the amount of data pending in the OS input buffer.
+ *
+ * If @socket is a UDP or SCTP socket, this will return the size of
+ * just the next packet, even if additional packets are buffered after
+ * that one.
+ *
+ * Note that on Windows, this function is rather inefficient in the
+ * UDP case, and so if you know any plausible upper bound on the size
+ * of the incoming packet, it is better to just do a
+ * g_socket_receive() with a buffer of that size, rather than calling
+ * g_socket_get_available_bytes() first and then doing a receive of
+ * exactly the right size.
+ *
+ * Returns: the number of bytes that can be read from the socket
+ * without blocking or truncating, or -1 on error.
+ *
+ * Since: 2.32
+ */
+gssize
+g_socket_get_available_bytes (GSocket *socket)
+{
+#ifdef G_OS_WIN32
+ const gint bufsize = 64 * 1024;
+ static guchar *buf = NULL;
+ u_long avail;
+#else
+ gint avail;
+#endif
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+
+#if defined (SO_NREAD)
+ if (!g_socket_get_option (socket, SOL_SOCKET, SO_NREAD, &avail, NULL))
+ return -1;
+#elif !defined (G_OS_WIN32)
+ if (ioctl (socket->priv->fd, FIONREAD, &avail) < 0)
+ avail = -1;
+#else
+ if (socket->priv->type == G_SOCKET_TYPE_DATAGRAM)
+ {
+ if (G_UNLIKELY (g_once_init_enter (&buf)))
+ g_once_init_leave (&buf, g_malloc (bufsize));
+
+ avail = recv (socket->priv->fd, buf, bufsize, MSG_PEEK);
+ if (avail == -1 && get_socket_errno () == WSAEWOULDBLOCK)
+ avail = 0;
+ }
+ else
+ {
+ if (ioctlsocket (socket->priv->fd, FIONREAD, &avail) < 0)
+ avail = -1;
+ }
+#endif
+
+ return avail;
+}
+
+/**
* g_socket_receive:
* @socket: a #GSocket
- * @buffer: a buffer to read data into (which should be at least count bytes long).
- * @size: the number of bytes that will be read from the stream
+ * @buffer: (array length=size) (element-type guint8): a buffer to
+ * read data into (which should be at least @size bytes long).
+ * @size: the number of bytes you want to read from the socket
+ * @cancellable: (allow-none): a %GCancellable or %NULL
* @error: #GError for error reporting, or %NULL to ignore.
*
* Receive data (up to @size bytes) from a socket. This is mainly used by
- * connection oriented sockets, it is identical to g_socket_receive_from()
+ * connection-oriented sockets; it is identical to g_socket_receive_from()
* with @address set to %NULL.
*
- * If a message is too long to fit in @buffer, excess bytes may be discarded
- * depending on the type of socket the message is received from.
+ * For %G_SOCKET_TYPE_DATAGRAM and %G_SOCKET_TYPE_SEQPACKET sockets,
+ * g_socket_receive() will always read either 0 or 1 complete messages from
+ * the socket. If the received message is too large to fit in @buffer, then
+ * the data beyond @size bytes will be discarded, without any explicit
+ * indication that this has occurred.
*
- * If the socket is in blocking mode the call will block until there is
- * some data to receive or there is an error. If there is no data available
- * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error
- * will be returned. To be notified of available data, wait for the %G_IO_IN
- * condition.
+ * For %G_SOCKET_TYPE_STREAM sockets, g_socket_receive() can return any
+ * number of bytes, up to @size. If more than @size bytes have been
+ * received, the additional data will be returned in future calls to
+ * g_socket_receive().
+ *
+ * If the socket is in blocking mode the call will block until there
+ * is some data to receive, the connection is closed, or there is an
+ * error. If there is no data available and the socket is in
+ * non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error will be
+ * returned. To be notified when data is available, wait for the
+ * %G_IO_IN condition.
*
* On error -1 is returned and @error is set accordingly.
*
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
*
* Since: 2.22
- **/
+ */
gssize
g_socket_receive (GSocket *socket,
gchar *buffer,
gsize size,
+ GCancellable *cancellable,
GError **error)
{
+ return g_socket_receive_with_blocking (socket, buffer, size,
+ socket->priv->blocking,
+ cancellable, error);
+}
+
+/**
+ * g_socket_receive_with_blocking:
+ * @socket: a #GSocket
+ * @buffer: (array length=size) (element-type guint8): a buffer to
+ * read data into (which should be at least @size bytes long).
+ * @size: the number of bytes you want to read from the socket
+ * @blocking: whether to do blocking or non-blocking I/O
+ * @cancellable: (allow-none): a %GCancellable or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * This behaves exactly the same as g_socket_receive(), except that
+ * the choice of blocking or non-blocking behavior is determined by
+ * the @blocking argument rather than by @socket's properties.
+ *
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
+ *
+ * Since: 2.26
+ */
+gssize
+g_socket_receive_with_blocking (GSocket *socket,
+ gchar *buffer,
+ gsize size,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
gssize ret;
- g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE);
+ g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, -1);
if (!check_socket (socket, error))
return -1;
+ if (!check_timeout (socket, error))
+ return -1;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return -1;
+
while (1)
{
- if (socket->priv->blocking &&
+ if (blocking &&
!g_socket_condition_wait (socket,
- G_IO_IN, NULL, error))
+ G_IO_IN, cancellable, error))
return -1;
if ((ret = recv (socket->priv->fd, buffer, size, 0)) < 0)
if (errsv == EINTR)
continue;
- if (socket->priv->blocking)
+ if (blocking)
{
#ifdef WSAEWOULDBLOCK
if (errsv == WSAEWOULDBLOCK)
/**
* g_socket_receive_from:
* @socket: a #GSocket
- * @address: a pointer to a #GSocketAddress pointer, or %NULL
- * @buffer: a buffer to read data into (which should be at least count bytes long).
- * @size: the number of bytes that will be read from the stream
+ * @address: (out) (allow-none): a pointer to a #GSocketAddress
+ * pointer, or %NULL
+ * @buffer: (array length=size) (element-type guint8): a buffer to
+ * read data into (which should be at least @size bytes long).
+ * @size: the number of bytes you want to read from the socket
+ * @cancellable: (allow-none): a %GCancellable or %NULL
* @error: #GError for error reporting, or %NULL to ignore.
*
* Receive data (up to @size bytes) from a socket.
* source address of the received packet.
* @address is owned by the caller.
*
- * If the socket is in blocking mode the call will block until there is
- * some data to receive or there is an error. If there is no data available
- * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error
- * will be returned. To be notified of available data, wait for the %G_IO_IN
- * condition.
- *
- * On error -1 is returned and @error is set accordingly.
+ * See g_socket_receive() for additional information.
*
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
*
* Since: 2.22
- **/
+ */
gssize
-g_socket_receive_from (GSocket *socket,
- GSocketAddress **address,
- gchar *buffer,
- gsize size,
- GError **error)
+g_socket_receive_from (GSocket *socket,
+ GSocketAddress **address,
+ gchar *buffer,
+ gsize size,
+ GCancellable *cancellable,
+ GError **error)
{
GInputVector v;
address,
&v, 1,
NULL, 0, NULL,
+ cancellable,
error);
}
+/* See the comment about SIGPIPE above. */
+#ifdef MSG_NOSIGNAL
+#define G_SOCKET_DEFAULT_SEND_FLAGS MSG_NOSIGNAL
+#else
+#define G_SOCKET_DEFAULT_SEND_FLAGS 0
+#endif
+
/**
* g_socket_send:
* @socket: a #GSocket
- * @buffer: the buffer containing the data to send.
+ * @buffer: (array length=size) (element-type guint8): the buffer
+ * containing the data to send.
* @size: the number of bytes to send
+ * @cancellable: (allow-none): a %GCancellable or %NULL
* @error: #GError for error reporting, or %NULL to ignore.
*
- * Tries to send @size bytes from @buffer on the socket. This is mainly used by
- * connection oriented sockets, it is identical to g_socket_send_to()
- * with @address set to %NULL.
+ * Tries to send @size bytes from @buffer on the socket. This is
+ * mainly used by connection-oriented sockets; it is identical to
+ * g_socket_send_to() with @address set to %NULL.
*
* If the socket is in blocking mode the call will block until there is
* space for the data in the socket queue. If there is no space available
* and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error
- * will be returned. To be notified of available space, wait for the %G_IO_OUT
- * condition.
- *
- * Note that on Windows you can't rely on a %G_IO_OUT condition
- * not producing a %G_IO_ERROR_WOULD_BLOCK error, as this is how Winsock
- * write notification works. However, robust apps should always be able to
- * handle this since it can easily appear in other cases too.
+ * will be returned. To be notified when space is available, wait for the
+ * %G_IO_OUT condition. Note though that you may still receive
+ * %G_IO_ERROR_WOULD_BLOCK from g_socket_send() even if you were previously
+ * notified of a %G_IO_OUT condition. (On Windows in particular, this is
+ * very common due to the way the underlying APIs work.)
*
* On error -1 is returned and @error is set accordingly.
*
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes written (which may be less than @size), or -1
+ * on error
*
* Since: 2.22
- **/
+ */
+gssize
+g_socket_send (GSocket *socket,
+ const gchar *buffer,
+ gsize size,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_socket_send_with_blocking (socket, buffer, size,
+ socket->priv->blocking,
+ cancellable, error);
+}
+
+/**
+ * g_socket_send_with_blocking:
+ * @socket: a #GSocket
+ * @buffer: (array length=size) (element-type guint8): the buffer
+ * containing the data to send.
+ * @size: the number of bytes to send
+ * @blocking: whether to do blocking or non-blocking I/O
+ * @cancellable: (allow-none): a %GCancellable or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * This behaves exactly the same as g_socket_send(), except that
+ * the choice of blocking or non-blocking behavior is determined by
+ * the @blocking argument rather than by @socket's properties.
+ *
+ * Returns: Number of bytes written (which may be less than @size), or -1
+ * on error
+ *
+ * Since: 2.26
+ */
gssize
-g_socket_send (GSocket *socket,
- const gchar *buffer,
- gsize size,
- GError **error)
+g_socket_send_with_blocking (GSocket *socket,
+ const gchar *buffer,
+ gsize size,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
{
gssize ret;
- g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE);
+ g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, -1);
if (!check_socket (socket, error))
return -1;
+ if (!check_timeout (socket, error))
+ return -1;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return -1;
+
while (1)
{
- if (socket->priv->blocking &&
+ if (blocking &&
!g_socket_condition_wait (socket,
- G_IO_OUT, NULL, error))
+ G_IO_OUT, cancellable, error))
return -1;
- if ((ret = send (socket->priv->fd, buffer, size, 0)) < 0)
+ if ((ret = send (socket->priv->fd, buffer, size, G_SOCKET_DEFAULT_SEND_FLAGS)) < 0)
{
int errsv = get_socket_errno ();
win32_unset_event_mask (socket, FD_WRITE);
#endif
- if (socket->priv->blocking)
+ if (blocking)
{
#ifdef WSAEWOULDBLOCK
if (errsv == WSAEWOULDBLOCK)
/**
* g_socket_send_to:
* @socket: a #GSocket
- * @address: a #GSocketAddress, or %NULL
- * @buffer: the buffer containing the data to send.
+ * @address: (allow-none): a #GSocketAddress, or %NULL
+ * @buffer: (array length=size) (element-type guint8): the buffer
+ * containing the data to send.
* @size: the number of bytes to send
+ * @cancellable: (allow-none): a %GCancellable or %NULL
* @error: #GError for error reporting, or %NULL to ignore.
*
* Tries to send @size bytes from @buffer to @address. If @address is
* %NULL then the message is sent to the default receiver (set by
* g_socket_connect()).
*
- * If the socket is in blocking mode the call will block until there is
- * space for the data in the socket queue. If there is no space available
- * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error
- * will be returned. To be notified of available space, wait for the %G_IO_OUT
- * condition.
- *
- * Note that on Windows you can't rely on a %G_IO_OUT condition
- * not producing a %G_IO_ERROR_WOULD_BLOCK error, as this is how Winsock
- * write notification works. However, robust apps should always be able to
- * handle this since it can easily appear in other cases too.
- *
- * On error -1 is returned and @error is set accordingly.
+ * See g_socket_send() for additional information.
*
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes written (which may be less than @size), or -1
+ * on error
*
* Since: 2.22
- **/
+ */
gssize
-g_socket_send_to (GSocket *socket,
- GSocketAddress *address,
- const gchar *buffer,
- gsize size,
- GError **error)
+g_socket_send_to (GSocket *socket,
+ GSocketAddress *address,
+ const gchar *buffer,
+ gsize size,
+ GCancellable *cancellable,
+ GError **error)
{
GOutputVector v;
address,
&v, 1,
NULL, 0,
- 0, error);
+ 0,
+ cancellable,
+ error);
+}
+
+/**
+ * g_socket_shutdown:
+ * @socket: a #GSocket
+ * @shutdown_read: whether to shut down the read side
+ * @shutdown_write: whether to shut down the write side
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Shut down part of a full-duplex connection.
+ *
+ * If @shutdown_read is %TRUE then the receiving side of the connection
+ * is shut down, and further reading is disallowed.
+ *
+ * If @shutdown_write is %TRUE then the sending side of the connection
+ * is shut down, and further writing is disallowed.
+ *
+ * It is allowed for both @shutdown_read and @shutdown_write to be %TRUE.
+ *
+ * One example where this is used is graceful disconnect for TCP connections
+ * where you close the sending side, then wait for the other side to close
+ * the connection, thus ensuring that the other side saw all sent data.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 2.22
+ */
+gboolean
+g_socket_shutdown (GSocket *socket,
+ gboolean shutdown_read,
+ gboolean shutdown_write,
+ GError **error)
+{
+ int how;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), TRUE);
+
+ if (!check_socket (socket, error))
+ return FALSE;
+
+ /* Do nothing? */
+ if (!shutdown_read && !shutdown_write)
+ return TRUE;
+
+#ifndef G_OS_WIN32
+ if (shutdown_read && shutdown_write)
+ how = SHUT_RDWR;
+ else if (shutdown_read)
+ how = SHUT_RD;
+ else
+ how = SHUT_WR;
+#else
+ if (shutdown_read && shutdown_write)
+ how = SD_BOTH;
+ else if (shutdown_read)
+ how = SD_RECEIVE;
+ else
+ how = SD_SEND;
+#endif
+
+ if (shutdown (socket->priv->fd, how) != 0)
+ {
+ int errsv = get_socket_errno ();
+ g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv),
+ _("Unable to shutdown socket: %s"), socket_strerror (errsv));
+ return FALSE;
+ }
+
+ if (shutdown_read && shutdown_write)
+ socket->priv->connected = FALSE;
+
+ return TRUE;
}
/**
*
* Closes the socket, shutting down any active connection.
*
- * Closing a socket does not wait for all outstanding I/O operations to finish,
- * so the caller should not rely on them to be guaranteed to complete even
- * if the close returns with no error.
+ * Closing a socket does not wait for all outstanding I/O operations
+ * to finish, so the caller should not rely on them to be guaranteed
+ * to complete even if the close returns with no error.
*
- * Once the socket is closed, all other operations will return %G_IO_ERROR_CLOSED.
- * Closing a stream multiple times will not return an error.
+ * Once the socket is closed, all other operations will return
+ * %G_IO_ERROR_CLOSED. Closing a socket multiple times will not
+ * return an error.
*
* Sockets will be automatically closed when the last reference
* is dropped, but you might want to call this function to make sure
* resources are released as early as possible.
*
+ * Beware that due to the way that TCP works, it is possible for
+ * recently-sent data to be lost if either you close a socket while the
+ * %G_IO_IN condition is set, or else if the remote connection tries to
+ * send something to you after you close the socket but before it has
+ * finished reading all of the data you sent. There is no easy generic
+ * way to avoid this problem; the easiest fix is to design the network
+ * protocol such that the client will never send data "out of turn".
+ * Another solution is for the server to half-close the connection by
+ * calling g_socket_shutdown() with only the @shutdown_write flag set,
+ * and then wait for the client to notice this and close its side of the
+ * connection, after which the server can safely call g_socket_close().
+ * (This is what #GTcpConnection does if you call
+ * g_tcp_connection_set_graceful_disconnect(). But of course, this
+ * only works if the client will close its connection after the server
+ * does.)
+ *
* Returns: %TRUE on success, %FALSE on error
*
* Since: 2.22
- **/
+ */
gboolean
-g_socket_close (GSocket *socket,
- GError **error)
+g_socket_close (GSocket *socket,
+ GError **error)
{
int res;
if (socket->priv->closed)
return TRUE; /* Multiple close not an error */
- if (!check_socket (socket, NULL))
+ if (!check_socket (socket, error))
return FALSE;
while (1)
break;
}
-#ifdef G_OS_WIN32
- if (socket->priv->event != WSA_INVALID_EVENT)
+ socket->priv->connected = FALSE;
+ socket->priv->closed = TRUE;
+ if (socket->priv->remote_address)
{
- WSACloseEvent (socket->priv->event);
- socket->priv->event = WSA_INVALID_EVENT;
+ g_object_unref (socket->priv->remote_address);
+ socket->priv->remote_address = NULL;
}
-#endif
-
- socket->priv->closed = TRUE;
return TRUE;
}
* Returns: %TRUE if socket is closed, %FALSE otherwise
*
* Since: 2.22
- **/
+ */
gboolean
g_socket_is_closed (GSocket *socket)
{
#ifdef G_OS_WIN32
/* Broken source, used on errors */
static gboolean
-broken_prepare (GSource *source,
- gint *timeout)
-{
- return FALSE;
-}
-
-static gboolean
-broken_check (GSource *source)
-{
- return FALSE;
-}
-
-static gboolean
-broken_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
+broken_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
{
return TRUE;
}
static GSourceFuncs broken_funcs =
{
- broken_prepare,
- broken_check,
+ NULL,
+ NULL,
broken_dispatch,
NULL
};
}
static void
-add_condition_watch (GSocket *socket,
+add_condition_watch (GSocket *socket,
GIOCondition *condition)
{
+ g_mutex_lock (&socket->priv->win32_source_lock);
g_assert (g_list_find (socket->priv->requested_conditions, condition) == NULL);
socket->priv->requested_conditions =
g_list_prepend (socket->priv->requested_conditions, condition);
update_select_events (socket);
+ g_mutex_unlock (&socket->priv->win32_source_lock);
}
static void
-remove_condition_watch (GSocket *socket,
+remove_condition_watch (GSocket *socket,
GIOCondition *condition)
{
+ g_mutex_lock (&socket->priv->win32_source_lock);
g_assert (g_list_find (socket->priv->requested_conditions, condition) != NULL);
socket->priv->requested_conditions =
g_list_remove (socket->priv->requested_conditions, condition);
update_select_events (socket);
+ g_mutex_unlock (&socket->priv->win32_source_lock);
}
static GIOCondition
if (socket->priv->current_events & (FD_READ | FD_ACCEPT))
condition |= G_IO_IN;
- if (socket->priv->current_events & FD_CLOSE ||
- socket->priv->closed)
+ if (socket->priv->current_events & FD_CLOSE)
+ {
+ int r, errsv, buffer;
+
+ r = recv (socket->priv->fd, &buffer, sizeof (buffer), MSG_PEEK);
+ if (r < 0)
+ errsv = get_socket_errno ();
+
+ if (r > 0 ||
+ (r < 0 && errsv == WSAENOTCONN))
+ condition |= G_IO_IN;
+ else if (r == 0 ||
+ (r < 0 && (errsv == WSAESHUTDOWN || errsv == WSAECONNRESET ||
+ errsv == WSAECONNABORTED || errsv == WSAENETRESET)))
+ condition |= G_IO_HUP;
+ else
+ condition |= G_IO_ERR;
+ }
+
+ if (socket->priv->closed)
condition |= G_IO_HUP;
/* Never report both G_IO_OUT and HUP, these are
return condition;
}
+#endif
typedef struct {
GSource source;
+#ifdef G_OS_WIN32
GPollFD pollfd;
+#else
+ gpointer fd_tag;
+#endif
GSocket *socket;
GIOCondition condition;
- GCancellable *cancellable;
- GPollFD cancel_pollfd;
- GIOCondition result_condition;
-} GWinsockSource;
+} GSocketSource;
+#ifdef G_OS_WIN32
static gboolean
-winsock_prepare (GSource *source,
- gint *timeout)
+socket_source_prepare_win32 (GSource *source,
+ gint *timeout)
{
- GWinsockSource *winsock_source = (GWinsockSource *)source;
- GIOCondition current_condition;
+ GSocketSource *socket_source = (GSocketSource *)source;
- current_condition = update_condition (winsock_source->socket);
+ *timeout = -1;
- if (g_cancellable_is_cancelled (winsock_source->cancellable))
- {
- winsock_source->result_condition = current_condition;
- return TRUE;
- }
+ return (update_condition (socket_source->socket) & socket_source->condition) != 0;
+}
- if ((winsock_source->condition & current_condition) != 0)
- {
- winsock_source->result_condition = current_condition;
- return TRUE;
- }
+static gboolean
+socket_source_check_win32 (GSource *source)
+{
+ int timeout;
- return FALSE;
+ return socket_source_prepare_win32 (source, &timeout);
}
+#endif
static gboolean
-winsock_check (GSource *source)
+socket_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
{
- GWinsockSource *winsock_source = (GWinsockSource *)source;
- GIOCondition current_condition;
+ GSocketSourceFunc func = (GSocketSourceFunc)callback;
+ GSocketSource *socket_source = (GSocketSource *)source;
+ GSocket *socket = socket_source->socket;
+ gint64 timeout;
+ guint events;
+ gboolean ret;
+
+#ifdef G_OS_WIN32
+ events = update_condition (socket_source->socket);
+#else
+ events = g_source_query_unix_fd (source, socket_source->fd_tag);
+#endif
+
+ timeout = g_source_get_ready_time (source);
+ if (timeout >= 0 && timeout < g_source_get_time (source))
+ {
+ socket->priv->timed_out = TRUE;
+ events |= (G_IO_IN | G_IO_OUT);
+ }
- current_condition = update_condition (winsock_source->socket);
+ ret = (*func) (socket, events & socket_source->condition, user_data);
+
+ if (socket->priv->timeout)
+ g_source_set_ready_time (source, g_get_monotonic_time () + socket->priv->timeout * 1000000);
+ else
+ g_source_set_ready_time (source, -1);
+
+ return ret;
+}
+
+static void
+socket_source_finalize (GSource *source)
+{
+ GSocketSource *socket_source = (GSocketSource *)source;
+ GSocket *socket;
- if (g_cancellable_is_cancelled (winsock_source->cancellable))
- {
- winsock_source->result_condition = current_condition;
- return TRUE;
- }
+ socket = socket_source->socket;
- if ((winsock_source->condition & current_condition) != 0)
- {
- winsock_source->result_condition = current_condition;
- return TRUE;
- }
+#ifdef G_OS_WIN32
+ remove_condition_watch (socket, &socket_source->condition);
+#endif
- return FALSE;
+ g_object_unref (socket);
}
static gboolean
-winsock_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
+socket_source_closure_callback (GSocket *socket,
+ GIOCondition condition,
+ gpointer data)
{
- GSocketSourceFunc func = (GSocketSourceFunc)callback;
- GWinsockSource *winsock_source = (GWinsockSource *)source;
+ GClosure *closure = data;
- return (*func) (user_data,
- winsock_source->result_condition & winsock_source->condition);
-}
+ GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
+ GValue result_value = G_VALUE_INIT;
+ gboolean result;
-static void
-winsock_finalize (GSource *source)
-{
- GWinsockSource *winsock_source = (GWinsockSource *)source;
- GSocket *socket;
+ g_value_init (&result_value, G_TYPE_BOOLEAN);
- socket = winsock_source->socket;
+ g_value_init (¶ms[0], G_TYPE_SOCKET);
+ g_value_set_object (¶ms[0], socket);
+ g_value_init (¶ms[1], G_TYPE_IO_CONDITION);
+ g_value_set_flags (¶ms[1], condition);
- remove_condition_watch (socket, &winsock_source->condition);
- g_object_unref (socket);
+ g_closure_invoke (closure, &result_value, 2, params, NULL);
+
+ result = g_value_get_boolean (&result_value);
+ g_value_unset (&result_value);
+ g_value_unset (¶ms[0]);
+ g_value_unset (¶ms[1]);
- if (winsock_source->cancellable)
- g_object_unref (winsock_source->cancellable);
+ return result;
}
-static GSourceFuncs winsock_funcs =
+static GSourceFuncs socket_source_funcs =
{
- winsock_prepare,
- winsock_check,
- winsock_dispatch,
- winsock_finalize
+#ifdef G_OS_WIN32
+ socket_source_prepare_win32,
+ socket_source_check_win32,
+#else
+ NULL, NULL, /* check, prepare */
+#endif
+ socket_source_dispatch,
+ socket_source_finalize,
+ (GSourceFunc)socket_source_closure_callback,
};
static GSource *
-winsock_source_new (GSocket *socket,
- GIOCondition condition,
- GCancellable *cancellable)
+socket_source_new (GSocket *socket,
+ GIOCondition condition,
+ GCancellable *cancellable)
{
GSource *source;
- GWinsockSource *winsock_source;
+ GSocketSource *socket_source;
+#ifdef G_OS_WIN32
ensure_event (socket);
if (socket->priv->event == WSA_INVALID_EVENT)
g_warning ("Failed to create WSAEvent");
return g_source_new (&broken_funcs, sizeof (GSource));
}
+#endif
- condition |= G_IO_HUP | G_IO_ERR;
+ condition |= G_IO_HUP | G_IO_ERR | G_IO_NVAL;
- source = g_source_new (&winsock_funcs, sizeof (GWinsockSource));
- winsock_source = (GWinsockSource *)source;
+ source = g_source_new (&socket_source_funcs, sizeof (GSocketSource));
+ g_source_set_name (source, "GSocket");
+ socket_source = (GSocketSource *)source;
- winsock_source->socket = g_object_ref (socket);
- winsock_source->condition = condition;
- add_condition_watch (socket, &winsock_source->condition);
+ socket_source->socket = g_object_ref (socket);
+ socket_source->condition = condition;
if (cancellable)
{
- winsock_source->cancellable = g_object_ref (cancellable);
- g_cancellable_make_pollfd (cancellable,
- &winsock_source->cancel_pollfd);
- g_source_add_poll (source, &winsock_source->cancel_pollfd);
+ GSource *cancellable_source;
+
+ cancellable_source = g_cancellable_source_new (cancellable);
+ g_source_add_child_source (source, cancellable_source);
+ g_source_set_dummy_callback (cancellable_source);
+ g_source_unref (cancellable_source);
}
- winsock_source->pollfd.fd = (gintptr) socket->priv->event;
- winsock_source->pollfd.events = condition;
- g_source_add_poll (source, &winsock_source->pollfd);
+#ifdef G_OS_WIN32
+ add_condition_watch (socket, &socket_source->condition);
+ socket_source->pollfd.fd = (gintptr) socket->priv->event;
+ socket_source->pollfd.events = condition;
+ socket_source->pollfd.revents = 0;
+ g_source_add_poll (source, &socket_source->pollfd);
+#else
+ socket_source->fd_tag = g_source_add_unix_fd (source, socket->priv->fd, condition);
+#endif
+
+ if (socket->priv->timeout)
+ g_source_set_ready_time (source, g_get_monotonic_time () + socket->priv->timeout * 1000000);
+ else
+ g_source_set_ready_time (source, -1);
return source;
}
-#endif
/**
- * g_socket_create_source:
+ * g_socket_create_source: (skip)
* @socket: a #GSocket
* @condition: a #GIOCondition mask to monitor
- * @cancellable: a %GCancellable or %NULL
+ * @cancellable: (allow-none): a %GCancellable or %NULL
*
- * Creates a %GSource that can be attached to a %GMainContext to monitor
- * for the availibility of the specified @condition on the socket.
+ * Creates a #GSource that can be attached to a %GMainContext to monitor
+ * for the availability of the specified @condition on the socket. The #GSource
+ * keeps a reference to the @socket.
*
* The callback on the source is of the #GSocketSourceFunc type.
*
- * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in condition;
+ * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in @condition;
* these conditions will always be reported output if they are true.
*
* @cancellable if not %NULL can be used to cancel the source, which will
- * cause the source to trigger, reporting the current condition. You can
- * check for this in the callback using g_cancellable_is_cancelled().
+ * cause the source to trigger, reporting the current condition (which
+ * is likely 0 unless cancellation happened at the same time as a
+ * condition change). You can check for this in the callback using
+ * g_cancellable_is_cancelled().
+ *
+ * If @socket has a timeout set, and it is reached before @condition
+ * occurs, the source will then trigger anyway, reporting %G_IO_IN or
+ * %G_IO_OUT depending on @condition. However, @socket will have been
+ * marked as having had a timeout, and so the next #GSocket I/O method
+ * you call will then fail with a %G_IO_ERROR_TIMED_OUT.
*
- * Returns: a newly allocated %GSource, free with g_source_unref().
+ * Returns: (transfer full): a newly allocated %GSource, free with g_source_unref().
*
* Since: 2.22
- **/
+ */
GSource *
g_socket_create_source (GSocket *socket,
GIOCondition condition,
GCancellable *cancellable)
{
- GSource *source;
g_return_val_if_fail (G_IS_SOCKET (socket) && (cancellable == NULL || G_IS_CANCELLABLE (cancellable)), NULL);
-#ifdef G_OS_WIN32
- source = winsock_source_new (socket, condition, cancellable);
-#else
- source =_g_fd_source_new (socket->priv->fd, condition, cancellable);
-#endif
- return source;
+ return socket_source_new (socket, condition, cancellable);
}
/**
* @socket: a #GSocket
* @condition: a #GIOCondition mask to check
*
- * Checks on the readiness of @socket to perform operations. The
- * operations specified in @condition are checked for and masked
- * against the currently-satisfied conditions on @socket. The result
+ * Checks on the readiness of @socket to perform operations.
+ * The operations specified in @condition are checked for and masked
+ * against the currently-satisfied conditions on @socket. The result
* is returned.
*
+ * Note that on Windows, it is possible for an operation to return
+ * %G_IO_ERROR_WOULD_BLOCK even immediately after
+ * g_socket_condition_check() has claimed that the socket is ready for
+ * writing. Rather than calling g_socket_condition_check() and then
+ * writing to the socket if it succeeds, it is generally better to
+ * simply try writing to the socket right away, and try again later if
+ * the initial attempt returns %G_IO_ERROR_WOULD_BLOCK.
+ *
* It is meaningless to specify %G_IO_ERR or %G_IO_HUP in condition;
* these conditions will always be set in the output if they are true.
*
* Returns: the @GIOCondition mask of the current state
*
* Since: 2.22
- **/
+ */
GIOCondition
-g_socket_condition_check (GSocket *socket,
- GIOCondition condition)
+g_socket_condition_check (GSocket *socket,
+ GIOCondition condition)
{
+ g_return_val_if_fail (G_IS_SOCKET (socket), 0);
+
if (!check_socket (socket, NULL))
return 0;
gint result;
poll_fd.fd = socket->priv->fd;
poll_fd.events = condition;
+ poll_fd.revents = 0;
do
result = g_poll (&poll_fd, 1, 0);
* g_socket_condition_wait:
* @socket: a #GSocket
* @condition: a #GIOCondition mask to wait for
- * @cancellable: a #GCancellable, or %NULL
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
* @error: a #GError pointer, or %NULL
*
- * Waits for @condition to become true on @socket. When the condition
- * becomes true, %TRUE is returned.
+ * Waits for @condition to become true on @socket. When the condition
+ * is met, %TRUE is returned.
*
- * If @cancellable is cancelled before the condition becomes true then
- * %FALSE is returned and @error, if non-%NULL, is set to %G_IO_ERROR_CANCELLED.
+ * If @cancellable is cancelled before the condition is met, or if the
+ * socket has a timeout set and it is reached before the condition is
+ * met, then %FALSE is returned and @error, if non-%NULL, is set to
+ * the appropriate value (%G_IO_ERROR_CANCELLED or
+ * %G_IO_ERROR_TIMED_OUT).
+ *
+ * See also g_socket_condition_timed_wait().
*
* Returns: %TRUE if the condition was met, %FALSE otherwise
*
* Since: 2.22
- **/
+ */
gboolean
g_socket_condition_wait (GSocket *socket,
GIOCondition condition,
GCancellable *cancellable,
GError **error)
{
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+ return g_socket_condition_timed_wait (socket, condition, -1,
+ cancellable, error);
+}
+
+/**
+ * g_socket_condition_timed_wait:
+ * @socket: a #GSocket
+ * @condition: a #GIOCondition mask to wait for
+ * @timeout: the maximum time (in microseconds) to wait, or -1
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: a #GError pointer, or %NULL
+ *
+ * Waits for up to @timeout microseconds for @condition to become true
+ * on @socket. If the condition is met, %TRUE is returned.
+ *
+ * If @cancellable is cancelled before the condition is met, or if
+ * @timeout (or the socket's #GSocket:timeout) is reached before the
+ * condition is met, then %FALSE is returned and @error, if non-%NULL,
+ * is set to the appropriate value (%G_IO_ERROR_CANCELLED or
+ * %G_IO_ERROR_TIMED_OUT).
+ *
+ * If you don't want a timeout, use g_socket_condition_wait().
+ * (Alternatively, you can pass -1 for @timeout.)
+ *
+ * Note that although @timeout is in microseconds for consistency with
+ * other GLib APIs, this function actually only has millisecond
+ * resolution, and the behavior is undefined if @timeout is not an
+ * exact number of milliseconds.
+ *
+ * Returns: %TRUE if the condition was met, %FALSE otherwise
+ *
+ * Since: 2.32
+ */
+gboolean
+g_socket_condition_timed_wait (GSocket *socket,
+ GIOCondition condition,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gint64 start_time;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
if (!check_socket (socket, error))
return FALSE;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
+ if (socket->priv->timeout &&
+ (timeout < 0 || socket->priv->timeout < timeout / G_USEC_PER_SEC))
+ timeout = socket->priv->timeout * 1000;
+ else if (timeout != -1)
+ timeout = timeout / 1000;
+
+ start_time = g_get_monotonic_time ();
+
#ifdef G_OS_WIN32
{
GIOCondition current_condition;
num_events = 0;
events[num_events++] = socket->priv->event;
- if (cancellable)
- {
- g_cancellable_make_pollfd (cancellable, &cancel_fd);
- events[num_events++] = (WSAEVENT)cancel_fd.fd;
- }
+ if (g_cancellable_make_pollfd (cancellable, &cancel_fd))
+ events[num_events++] = (WSAEVENT)cancel_fd.fd;
+
+ if (timeout == -1)
+ timeout = WSA_INFINITE;
current_condition = update_condition (socket);
while ((condition & current_condition) == 0)
{
- res = WSAWaitForMultipleEvents(num_events, events,
- FALSE, WSA_INFINITE, FALSE);
+ res = WSAWaitForMultipleEvents (num_events, events,
+ FALSE, timeout, FALSE);
if (res == WSA_WAIT_FAILED)
{
int errsv = get_socket_errno ();
socket_strerror (errsv));
break;
}
+ else if (res == WSA_WAIT_TIMEOUT)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ _("Socket I/O timed out"));
+ break;
+ }
if (g_cancellable_set_error_if_cancelled (cancellable, error))
break;
current_condition = update_condition (socket);
+
+ if (timeout != WSA_INFINITE)
+ {
+ timeout -= (g_get_monotonic_time () - start_time) * 1000;
+ if (timeout < 0)
+ timeout = 0;
+ }
}
remove_condition_watch (socket, &condition);
+ if (num_events > 1)
+ g_cancellable_release_fd (cancellable);
return (condition & current_condition) != 0;
}
poll_fd[0].events = condition;
num = 1;
- if (cancellable)
+ if (g_cancellable_make_pollfd (cancellable, &poll_fd[1]))
+ num++;
+
+ while (TRUE)
{
- g_cancellable_make_pollfd (cancellable, &poll_fd[1]);
- num++;
+ result = g_poll (poll_fd, num, timeout);
+ if (result != -1 || errno != EINTR)
+ break;
+
+ if (timeout != -1)
+ {
+ timeout -= (g_get_monotonic_time () - start_time) / 1000;
+ if (timeout < 0)
+ timeout = 0;
+ }
}
+
+ if (num > 1)
+ g_cancellable_release_fd (cancellable);
- do
- result = g_poll (poll_fd, num, -1);
- while (result == -1 && get_socket_errno () == EINTR);
+ if (result == 0)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ _("Socket I/O timed out"));
+ return FALSE;
+ }
- return cancellable == NULL ||
- !g_cancellable_set_error_if_cancelled (cancellable, error);
+ return !g_cancellable_set_error_if_cancelled (cancellable, error);
}
#endif
}
/**
- * g_socket_send_to:
+ * g_socket_send_message:
* @socket: a #GSocket
- * @address: a #GSocketAddress, or %NULL
- * @vectors: an array of #GOutputVector structs
+ * @address: (allow-none): a #GSocketAddress, or %NULL
+ * @vectors: (array length=num_vectors): an array of #GOutputVector structs
* @num_vectors: the number of elements in @vectors, or -1
- * @messages: a pointer to an array of #GSocketControlMessages, or
- * %NULL.
+ * @messages: (array length=num_messages) (allow-none): a pointer to an
+ * array of #GSocketControlMessages, or %NULL.
* @num_messages: number of elements in @messages, or -1.
* @flags: an int containing #GSocketMsgFlags flags
+ * @cancellable: (allow-none): a %GCancellable or %NULL
* @error: #GError for error reporting, or %NULL to ignore.
*
* Send data to @address on @socket. This is the most complicated and
* If @address is %NULL then the message is sent to the default receiver
* (set by g_socket_connect()).
*
- * @vector must point to an array of #GOutputVector structs and
- * @num_vectors must be the length of this array. These structs
- * describe the buffers that the sent data will be gathered from.
- * If @num_vector is -1, then @vector is assumed to be terminated
- * by a #GOutputVector with a %NULL buffer pointer.
- *
+ * @vectors must point to an array of #GOutputVector structs and
+ * @num_vectors must be the length of this array. (If @num_vectors is -1,
+ * then @vectors is assumed to be terminated by a #GOutputVector with a
+ * %NULL buffer pointer.) The #GOutputVector structs describe the buffers
+ * that the sent data will be gathered from. Using multiple
+ * #GOutputVectors is more memory-efficient than manually copying
+ * data from multiple sources into a single buffer, and more
+ * network-efficient than making multiple calls to g_socket_send().
*
* @messages, if non-%NULL, is taken to point to an array of @num_messages
* #GSocketControlMessage instances. These correspond to the control
* If @num_messages is -1 then @messages is treated as a %NULL-terminated
* array.
*
- * @flags modify how the message sent. The commonly available arguments
- * for this is available in the #GSocketMsgFlags enum, but the
+ * @flags modify how the message is sent. The commonly available arguments
+ * for this are available in the #GSocketMsgFlags enum, but the
* values there are the same as the system values, and the flags
- * are passed in as-is, so you can pass in system specific flags too.
+ * are passed in as-is, so you can pass in system-specific flags too.
*
* If the socket is in blocking mode the call will block until there is
* space for the data in the socket queue. If there is no space available
* and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error
- * will be returned. To be notified of available space, wait for the %G_IO_OUT
- * condition.
- *
- * Note that on Windows you can't rely on a %G_IO_OUT condition
- * not producing a %G_IO_ERROR_WOULD_BLOCK error, as this is how Winsock
- * write notification works. However, robust apps should always be able to
- * handle this since it can easily appear in other cases too.
+ * will be returned. To be notified when space is available, wait for the
+ * %G_IO_OUT condition. Note though that you may still receive
+ * %G_IO_ERROR_WOULD_BLOCK from g_socket_send() even if you were previously
+ * notified of a %G_IO_OUT condition. (On Windows in particular, this is
+ * very common due to the way the underlying APIs work.)
*
* On error -1 is returned and @error is set accordingly.
*
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes written (which may be less than @size), or -1
+ * on error
*
* Since: 2.22
- **/
+ */
gssize
g_socket_send_message (GSocket *socket,
GSocketAddress *address,
GSocketControlMessage **messages,
gint num_messages,
gint flags,
+ GCancellable *cancellable,
GError **error)
{
GOutputVector one_vector;
char zero;
+ g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+ g_return_val_if_fail (address == NULL || G_IS_SOCKET_ADDRESS (address), -1);
+ g_return_val_if_fail (num_vectors == 0 || vectors != NULL, -1);
+ g_return_val_if_fail (num_messages == 0 || messages != NULL, -1);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), -1);
+ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
if (!check_socket (socket, error))
return -1;
+ if (!check_timeout (socket, error))
+ return -1;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return -1;
+
if (num_vectors == -1)
{
for (num_vectors = 0;
struct msghdr msg;
gssize result;
+ msg.msg_flags = 0;
+
/* name */
if (address)
{
msg.msg_namelen = g_socket_address_get_native_size (address);
msg.msg_name = g_alloca (msg.msg_namelen);
- g_socket_address_to_native (address, msg.msg_name, msg.msg_namelen);
+ if (!g_socket_address_to_native (address, msg.msg_name, msg.msg_namelen, error))
+ return -1;
+ }
+ else
+ {
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
}
/* iov */
for (i = 0; i < num_messages; i++)
msg.msg_controllen += CMSG_SPACE (g_socket_control_message_get_size (messages[i]));
- msg.msg_control = g_alloca (msg.msg_controllen);
+ if (msg.msg_controllen == 0)
+ msg.msg_control = NULL;
+ else
+ {
+ msg.msg_control = g_alloca (msg.msg_controllen);
+ memset (msg.msg_control, '\0', msg.msg_controllen);
+ }
cmsg = CMSG_FIRSTHDR (&msg);
for (i = 0; i < num_messages; i++)
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
- G_IO_OUT, NULL, error))
+ G_IO_OUT, cancellable, error))
return -1;
- result = sendmsg (socket->priv->fd, &msg, flags);
+ result = sendmsg (socket->priv->fd, &msg, flags | G_SOCKET_DEFAULT_SEND_FLAGS);
if (result < 0)
{
int errsv = get_socket_errno ();
seem very useful */
if (num_messages != 0)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("GSocketControlMessage not supported on windows"));
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("GSocketControlMessage not supported on Windows"));
return -1;
}
}
/* name */
+ addrlen = 0; /* Avoid warning */
if (address)
{
addrlen = g_socket_address_get_native_size (address);
- g_socket_address_to_native (address, &addr, sizeof addr);
+ if (!g_socket_address_to_native (address, &addr, sizeof addr, error))
+ return -1;
}
while (1)
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
- G_IO_OUT, NULL, error))
+ G_IO_OUT, cancellable, error))
return -1;
if (address)
#endif
}
+static GSocketAddress *
+cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len)
+{
+ GSocketAddress *saddr;
+ gint i;
+ guint64 oldest_time = G_MAXUINT64;
+ gint oldest_index = 0;
+
+ if (native_len <= 0)
+ return NULL;
+
+ saddr = NULL;
+ for (i = 0; i < RECV_ADDR_CACHE_SIZE; i++)
+ {
+ GSocketAddress *tmp = socket->priv->recv_addr_cache[i].addr;
+ gpointer tmp_native = socket->priv->recv_addr_cache[i].native;
+ gint tmp_native_len = socket->priv->recv_addr_cache[i].native_len;
+
+ if (!tmp)
+ continue;
+
+ if (tmp_native_len != native_len)
+ continue;
+
+ if (memcmp (tmp_native, native, native_len) == 0)
+ {
+ saddr = g_object_ref (tmp);
+ socket->priv->recv_addr_cache[i].last_used = g_get_monotonic_time ();
+ return saddr;
+ }
+
+ if (socket->priv->recv_addr_cache[i].last_used < oldest_time)
+ {
+ oldest_time = socket->priv->recv_addr_cache[i].last_used;
+ oldest_index = i;
+ }
+ }
+
+ saddr = g_socket_address_new_from_native (native, native_len);
+
+ if (socket->priv->recv_addr_cache[oldest_index].addr)
+ {
+ g_object_unref (socket->priv->recv_addr_cache[oldest_index].addr);
+ g_free (socket->priv->recv_addr_cache[oldest_index].native);
+ }
+
+ socket->priv->recv_addr_cache[oldest_index].native = g_memdup (native, native_len);
+ socket->priv->recv_addr_cache[oldest_index].native_len = native_len;
+ socket->priv->recv_addr_cache[oldest_index].addr = g_object_ref (saddr);
+ socket->priv->recv_addr_cache[oldest_index].last_used = g_get_monotonic_time ();
+
+ return saddr;
+}
+
/**
* g_socket_receive_message:
* @socket: a #GSocket
- * @address: a pointer to a #GSocketAddress pointer, or %NULL
- * @vectors: an array of #GInputVector structs
+ * @address: (out) (allow-none): a pointer to a #GSocketAddress
+ * pointer, or %NULL
+ * @vectors: (array length=num_vectors): an array of #GInputVector structs
* @num_vectors: the number of elements in @vectors, or -1
- * @messages: a pointer which will be filled with an array of
- * #GSocketControlMessages, or %NULL
+ * @messages: (array length=num_messages) (allow-none): a pointer which
+ * may be filled with an array of #GSocketControlMessages, or %NULL
* @num_messages: a pointer which will be filled with the number of
* elements in @messages, or %NULL
* @flags: a pointer to an int containing #GSocketMsgFlags flags
+ * @cancellable: (allow-none): a %GCancellable or %NULL
* @error: a #GError pointer, or %NULL
*
* Receive data from a socket. This is the most complicated and
* @vector must point to an array of #GInputVector structs and
* @num_vectors must be the length of this array. These structs
* describe the buffers that received data will be scattered into.
- * If @num_vector is -1, then @vector is assumed to be terminated
+ * If @num_vectors is -1, then @vectors is assumed to be terminated
* by a #GInputVector with a %NULL buffer pointer.
*
- * As a special case, if the size of the array is zero (in which case,
- * @vectors may of course be %NULL), then a single byte is received
- * and discarded. This is to facilitate the common practice of
- * sending a single '\0' byte for the purposes of transferring
- * ancillary data.
+ * As a special case, if @num_vectors is 0 (in which case, @vectors
+ * may of course be %NULL), then a single byte is received and
+ * discarded. This is to facilitate the common practice of sending a
+ * single '\0' byte for the purposes of transferring ancillary data.
*
- * @messages, if non-%NULL, is taken to point to a pointer that will
- * be set to point to a newly-allocated array of
- * #GSocketControlMessage instances. These correspond to the control
- * messages received from the kernel, one #GSocketControlMessage per
- * message from the kernel. This array is %NULL-terminated and must be
- * freed by the caller using g_free().
+ * @messages, if non-%NULL, will be set to point to a newly-allocated
+ * array of #GSocketControlMessage instances or %NULL if no such
+ * messages was received. These correspond to the control messages
+ * received from the kernel, one #GSocketControlMessage per message
+ * from the kernel. This array is %NULL-terminated and must be freed
+ * by the caller using g_free() after calling g_object_unref() on each
+ * element. If @messages is %NULL, any control messages received will
+ * be discarded.
*
* @num_messages, if non-%NULL, will be set to the number of control
* messages received.
* in @messages (ie: not including the %NULL terminator).
*
* @flags is an in/out parameter. The commonly available arguments
- * for this is available in the #GSocketMsgFlags enum, but the
+ * for this are available in the #GSocketMsgFlags enum, but the
* values there are the same as the system values, and the flags
- * are passed in as-is, so you can pass in system specific flags too.
- *
- * If the socket is in blocking mode the call will block until there is
- * some data to receive or there is an error. If there is no data available
- * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error
- * will be returned. To be notified of available data, wait for the %G_IO_IN
- * condition.
+ * are passed in as-is, so you can pass in system-specific flags too
+ * (and g_socket_receive_message() may pass system-specific flags out).
+ *
+ * As with g_socket_receive(), data may be discarded if @socket is
+ * %G_SOCKET_TYPE_DATAGRAM or %G_SOCKET_TYPE_SEQPACKET and you do not
+ * provide enough buffer space to read a complete message. You can pass
+ * %G_SOCKET_MSG_PEEK in @flags to peek at the current message without
+ * removing it from the receive queue, but there is no portable way to find
+ * out the length of the message other than by reading it into a
+ * sufficiently-large buffer.
+ *
+ * If the socket is in blocking mode the call will block until there
+ * is some data to receive, the connection is closed, or there is an
+ * error. If there is no data available and the socket is in
+ * non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error will be
+ * returned. To be notified when data is available, wait for the
+ * %G_IO_IN condition.
*
* On error -1 is returned and @error is set accordingly.
*
- * Returns: Number of bytes read, or -1 on error
+ * Returns: Number of bytes read, or 0 if the connection was closed by
+ * the peer, or -1 on error
*
* Since: 2.22
- **/
+ */
gssize
g_socket_receive_message (GSocket *socket,
GSocketAddress **address,
GSocketControlMessage ***messages,
gint *num_messages,
gint *flags,
+ GCancellable *cancellable,
GError **error)
{
GInputVector one_vector;
char one_byte;
+ g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+
if (!check_socket (socket, error))
return -1;
+ if (!check_timeout (socket, error))
+ return -1;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return -1;
+
if (num_vectors == -1)
{
for (num_vectors = 0;
else
msg.msg_flags = 0;
+ /* We always set the close-on-exec flag so we don't leak file
+ * descriptors into child processes. Note that gunixfdmessage.c
+ * will later call fcntl (fd, FD_CLOEXEC), but that isn't atomic.
+ */
+#ifdef MSG_CMSG_CLOEXEC
+ msg.msg_flags |= MSG_CMSG_CLOEXEC;
+#endif
+
/* do it */
while (1)
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
- G_IO_IN, NULL, error))
+ G_IO_IN, cancellable, error))
return -1;
result = recvmsg (socket->priv->fd, &msg, msg.msg_flags);
+#ifdef MSG_CMSG_CLOEXEC
+ if (result < 0 && get_socket_errno () == EINVAL)
+ {
+ /* We must be running on an old kernel. Call without the flag. */
+ msg.msg_flags &= ~(MSG_CMSG_CLOEXEC);
+ result = recvmsg (socket->priv->fd, &msg, msg.msg_flags);
+ }
+#endif
if (result < 0)
{
/* decode address */
if (address != NULL)
{
- if (msg.msg_namelen > 0)
- *address = g_socket_address_new_from_native (msg.msg_name,
- msg.msg_namelen);
- else
- *address = NULL;
+ *address = cache_recv_address (socket, msg.msg_name, msg.msg_namelen);
}
/* decode control messages */
{
- GSocketControlMessage **my_messages = NULL;
- gint allocated = 0, index = 0;
- const gchar *scm_pointer;
+ GPtrArray *my_messages = NULL;
struct cmsghdr *cmsg;
- gsize scm_size;
-
- scm_pointer = (const gchar *) msg.msg_control;
- scm_size = msg.msg_controllen;
- for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg))
- {
- GSocketControlMessage *message;
-
- message = g_socket_control_message_deserialize (cmsg->cmsg_level,
- cmsg->cmsg_type,
- cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg),
- CMSG_DATA (cmsg));
- if (message == NULL)
- /* We've already spewed about the problem in the
- deserialization code, so just continue */
- continue;
-
- if (index == allocated)
- {
- /* estimated 99% case: exactly 1 control message */
- allocated = MIN (allocated * 2, 1);
- my_messages = g_new (GSocketControlMessage *, (allocated + 1));
- allocated = 1;
- }
-
- my_messages[index++] = message;
- }
+ if (msg.msg_controllen >= sizeof (struct cmsghdr))
+ {
+ for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg))
+ {
+ GSocketControlMessage *message;
+
+ message = g_socket_control_message_deserialize (cmsg->cmsg_level,
+ cmsg->cmsg_type,
+ cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg),
+ CMSG_DATA (cmsg));
+ if (message == NULL)
+ /* We've already spewed about the problem in the
+ deserialization code, so just continue */
+ continue;
+
+ if (messages == NULL)
+ {
+ /* we have to do it this way if the user ignores the
+ * messages so that we will close any received fds.
+ */
+ g_object_unref (message);
+ }
+ else
+ {
+ if (my_messages == NULL)
+ my_messages = g_ptr_array_new ();
+ g_ptr_array_add (my_messages, message);
+ }
+ }
+ }
if (num_messages)
- *num_messages = index;
+ *num_messages = my_messages != NULL ? my_messages->len : 0;
if (messages)
{
- my_messages[index++] = NULL;
- *messages = my_messages;
+ if (my_messages == NULL)
+ {
+ *messages = NULL;
+ }
+ else
+ {
+ g_ptr_array_add (my_messages, NULL);
+ *messages = (GSocketControlMessage **) g_ptr_array_free (my_messages, FALSE);
+ }
}
else
{
- gint i;
-
- /* free all those messages we just constructed.
- * we have to do it this way if the user ignores the
- * messages so that we will close any received fds.
- */
- for (i = 0; i < index; i++)
- g_object_unref (my_messages[i]);
- g_free (my_messages);
+ g_assert (my_messages == NULL);
}
}
{
if (socket->priv->blocking &&
!g_socket_condition_wait (socket,
- G_IO_IN, NULL, error))
+ G_IO_IN, cancellable, error))
return -1;
addrlen = sizeof addr;
/* decode address */
if (address != NULL)
{
- if (addrlen > 0)
- *address = g_socket_address_new_from_native (&addr, addrlen);
- else
- *address = NULL;
+ *address = cache_recv_address (socket, (struct sockaddr *)&addr, addrlen);
}
/* capture the flags */
if (flags != NULL)
*flags = win_flags;
+ if (messages != NULL)
+ *messages = NULL;
+ if (num_messages != NULL)
+ *num_messages = 0;
+
return bytes_received;
}
#endif
}
-#define __G_SOCKET_C__
-#include "gioaliasdef.c"
+/**
+ * g_socket_get_credentials:
+ * @socket: a #GSocket.
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Returns the credentials of the foreign process connected to this
+ * socket, if any (e.g. it is only supported for %G_SOCKET_FAMILY_UNIX
+ * sockets).
+ *
+ * If this operation isn't supported on the OS, the method fails with
+ * the %G_IO_ERROR_NOT_SUPPORTED error. On Linux this is implemented
+ * by reading the %SO_PEERCRED option on the underlying socket.
+ *
+ * Other ways to obtain credentials from a foreign peer includes the
+ * #GUnixCredentialsMessage type and
+ * g_unix_connection_send_credentials() /
+ * g_unix_connection_receive_credentials() functions.
+ *
+ * Returns: (transfer full): %NULL if @error is set, otherwise a #GCredentials object
+ * that must be freed with g_object_unref().
+ *
+ * Since: 2.26
+ */
+GCredentials *
+g_socket_get_credentials (GSocket *socket,
+ GError **error)
+{
+ GCredentials *ret;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ ret = NULL;
+
+#if G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED
+
+#ifdef SO_PEERCRED
+ {
+ guint8 native_creds_buf[G_CREDENTIALS_NATIVE_SIZE];
+ socklen_t optlen = sizeof (native_creds_buf);
+
+ if (getsockopt (socket->priv->fd,
+ SOL_SOCKET,
+ SO_PEERCRED,
+ native_creds_buf,
+ &optlen) == 0)
+ {
+ ret = g_credentials_new ();
+ g_credentials_set_native (ret,
+ G_CREDENTIALS_NATIVE_TYPE,
+ native_creds_buf);
+ }
+ }
+#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
+ {
+ struct unpcbid cred;
+ socklen_t optlen = sizeof (cred);
+
+ if (getsockopt (socket->priv->fd,
+ 0,
+ LOCAL_PEEREID,
+ &cred,
+ &optlen) == 0)
+ {
+ ret = g_credentials_new ();
+ g_credentials_set_native (ret,
+ G_CREDENTIALS_NATIVE_TYPE,
+ &cred);
+ }
+ }
+#elif G_CREDENTIALS_USE_SOLARIS_UCRED
+ {
+ ucred_t *ucred = NULL;
+
+ if (getpeerucred (socket->priv->fd, &ucred) == 0)
+ {
+ ret = g_credentials_new ();
+ g_credentials_set_native (ret,
+ G_CREDENTIALS_TYPE_SOLARIS_UCRED,
+ ucred);
+ ucred_free (ucred);
+ }
+ }
+#else
+ #error "G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED is set but this is no code for this platform"
+#endif
+
+ if (!ret)
+ {
+ int errsv = get_socket_errno ();
+
+ g_set_error (error,
+ G_IO_ERROR,
+ socket_io_error_from_errno (errsv),
+ _("Unable to read socket credentials: %s"),
+ socket_strerror (errsv));
+ }
+
+#else
+
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("g_socket_get_credentials not implemented for this OS"));
+#endif
+
+ return ret;
+}
+
+/**
+ * g_socket_get_option:
+ * @socket: a #GSocket
+ * @level: the "API level" of the option (eg, `SOL_SOCKET`)
+ * @optname: the "name" of the option (eg, `SO_BROADCAST`)
+ * @value: (out): return location for the option value
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Gets the value of an integer-valued option on @socket, as with
+ * getsockopt(). (If you need to fetch a non-integer-valued option,
+ * you will need to call getsockopt() directly.)
+ *
+ * The [<gio/gnetworking.h>][gio-gnetworking.h]
+ * header pulls in system headers that will define most of the
+ * standard/portable socket options. For unusual socket protocols or
+ * platform-dependent options, you may need to include additional
+ * headers.
+ *
+ * Note that even for socket options that are a single byte in size,
+ * @value is still a pointer to a #gint variable, not a #guchar;
+ * g_socket_get_option() will handle the conversion internally.
+ *
+ * Returns: success or failure. On failure, @error will be set, and
+ * the system error value (`errno` or WSAGetLastError()) will still
+ * be set to the result of the getsockopt() call.
+ *
+ * Since: 2.36
+ */
+gboolean
+g_socket_get_option (GSocket *socket,
+ gint level,
+ gint optname,
+ gint *value,
+ GError **error)
+{
+ guint size;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+ *value = 0;
+ size = sizeof (gint);
+ if (getsockopt (socket->priv->fd, level, optname, value, &size) != 0)
+ {
+ int errsv = get_socket_errno ();
+
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ socket_io_error_from_errno (errsv),
+ socket_strerror (errsv));
+#ifndef G_OS_WIN32
+ /* Reset errno in case the caller wants to look at it */
+ errno = errsv;
+#endif
+ return FALSE;
+ }
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ /* If the returned value is smaller than an int then we need to
+ * slide it over into the low-order bytes of *value.
+ */
+ if (size != sizeof (gint))
+ *value = *value >> (8 * (sizeof (gint) - size));
+#endif
+
+ return TRUE;
+}
+
+/**
+ * g_socket_set_option:
+ * @socket: a #GSocket
+ * @level: the "API level" of the option (eg, `SOL_SOCKET`)
+ * @optname: the "name" of the option (eg, `SO_BROADCAST`)
+ * @value: the value to set the option to
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Sets the value of an integer-valued option on @socket, as with
+ * setsockopt(). (If you need to set a non-integer-valued option,
+ * you will need to call setsockopt() directly.)
+ *
+ * The [<gio/gnetworking.h>][gio-gnetworking.h]
+ * header pulls in system headers that will define most of the
+ * standard/portable socket options. For unusual socket protocols or
+ * platform-dependent options, you may need to include additional
+ * headers.
+ *
+ * Returns: success or failure. On failure, @error will be set, and
+ * the system error value (`errno` or WSAGetLastError()) will still
+ * be set to the result of the setsockopt() call.
+ *
+ * Since: 2.36
+ */
+gboolean
+g_socket_set_option (GSocket *socket,
+ gint level,
+ gint optname,
+ gint value,
+ GError **error)
+{
+ gint errsv;
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+ if (setsockopt (socket->priv->fd, level, optname, &value, sizeof (gint)) == 0)
+ return TRUE;
+
+#if !defined (__linux__) && !defined (G_OS_WIN32)
+ /* Linux and Windows let you set a single-byte value from an int,
+ * but most other platforms don't.
+ */
+ if (errno == EINVAL && value >= SCHAR_MIN && value <= CHAR_MAX)
+ {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ value = value << (8 * (sizeof (gint) - 1));
+#endif
+ if (setsockopt (socket->priv->fd, level, optname, &value, 1) == 0)
+ return TRUE;
+ }
+#endif
+
+ errsv = get_socket_errno ();
+
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ socket_io_error_from_errno (errsv),
+ socket_strerror (errsv));
+#ifndef G_OS_WIN32
+ errno = errsv;
+#endif
+ return FALSE;
+}
+