| /* GIO - GLib Input, Output and Streaming Library | |
| * | |
| * Copyright (C) 2008 Christian Kellner, Samuel Cormier-Iijima | |
| * Copyright © 2009 Codethink Limited | |
| * Copyright © 2009 Red Hat, Inc | |
| * Copyright © 2015 Collabora, Ltd. | |
| * | |
| * SPDX-License-Identifier: LGPL-2.1-or-later | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * 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, see <http://www.gnu.org/licenses/>. | |
| * | |
| * Authors: Christian Kellner <gicmo@gnome.org> | |
| * Samuel Cormier-Iijima <sciyoshi@gmail.com> | |
| * Ryan Lortie <desrt@desrt.ca> | |
| * Alexander Larsson <alexl@redhat.com> | |
| * Philip Withnall <philip.withnall@collabora.co.uk> | |
| */ | |
| #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 <fcntl.h> | |
| # include <unistd.h> | |
| # include <sys/ioctl.h> | |
| #endif | |
| #ifdef HAVE_SIOCGIFADDR | |
| #include <net/if.h> | |
| #endif | |
| #ifdef HAVE_SYS_FILIO_H | |
| # include <sys/filio.h> | |
| #endif | |
| #ifdef G_OS_UNIX | |
| #include <sys/uio.h> | |
| #endif | |
| #define GOBJECT_COMPILATION | |
| #include "gobject/gtype-private.h" /* For _PRELUDE type define */ | |
| #undef GOBJECT_COMPILATION | |
| #include "gcancellable.h" | |
| #include "gdatagrambased.h" | |
| #include "gioenumtypes.h" | |
| #include "ginetaddress.h" | |
| #include "ginetsocketaddress.h" | |
| #include "ginitable.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 "gioprivate.h" | |
| #ifdef G_OS_WIN32 | |
| #include "giowin32-afunix.h" | |
| #endif | |
| /** | |
| * GSocket: | |
| * | |
| * 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. | |
| * | |
| * `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 [class@Gio.SocketClient], | |
| * [class@Gio.SocketService] and [class@Gio.SocketConnection]. However there may | |
| * be cases where direct use of `GSocket` is useful. | |
| * | |
| * `GSocket` implements the [iface@Gio.Initable] interface, so if it is manually | |
| * constructed by e.g. [ctor@GObject.Object.new] you must call | |
| * [method@Gio.Initable.init] and check the results before using the object. | |
| * This is done automatically in [ctor@Gio.Socket.new] and | |
| * [ctor@Gio.Socket.new_from_fd], so these functions can return `NULL`. | |
| * | |
| * Sockets operate in two general modes, blocking or non-blocking. When | |
| * in blocking mode all operations (which don’t take an explicit blocking | |
| * parameter) block until the requested operation | |
| * is finished or there is an error. In non-blocking mode all calls that | |
| * would block return immediately with a `G_IO_ERROR_WOULD_BLOCK` error. | |
| * To know when a call would successfully run you can call | |
| * [method@Gio.Socket.condition_check], or [method@Gio.Socket.condition_wait]. | |
| * You can also use [method@Gio.Socket.create_source] and attach it to a | |
| * [type@GLib.MainContext] to get callbacks when I/O is possible. | |
| * Note that all sockets are always set to non blocking mode in the system, and | |
| * blocking mode is emulated in `GSocket`. | |
| * | |
| * When working in non-blocking mode applications should always be able to | |
| * handle getting a `G_IO_ERROR_WOULD_BLOCK` error even when some other | |
| * function said that I/O was possible. This can easily happen in case | |
| * of a race condition in the application, but it can also happen for other | |
| * 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. | |
| * 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 | |
| * specified or received in each I/O operation. | |
| * | |
| * 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. | |
| * | |
| * ## Nagle’s algorithm | |
| * | |
| * Since GLib 2.80, `GSocket` will automatically set the `TCP_NODELAY` option on | |
| * all `G_SOCKET_TYPE_STREAM` sockets. This disables | |
| * [Nagle’s algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm) as it | |
| * typically does more harm than good on modern networks. | |
| * | |
| * If your application needs Nagle’s algorithm enabled, call | |
| * [method@Gio.Socket.set_option] after constructing a `GSocket` to enable it: | |
| * ```c | |
| * socket = g_socket_new (…, G_SOCKET_TYPE_STREAM, …); | |
| * if (socket != NULL) | |
| * { | |
| * g_socket_set_option (socket, IPPROTO_TCP, TCP_NODELAY, FALSE, &local_error); | |
| * // handle error if needed | |
| * } | |
| * ``` | |
| * | |
| * Since: 2.22 | |
| */ | |
| static void g_socket_initable_iface_init (GInitableIface *iface); | |
| static gboolean g_socket_initable_init (GInitable *initable, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static void g_socket_datagram_based_iface_init (GDatagramBasedInterface *iface); | |
| static gint g_socket_datagram_based_receive_messages (GDatagramBased *self, | |
| GInputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static gint g_socket_datagram_based_send_messages (GDatagramBased *self, | |
| GOutputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static GSource *g_socket_datagram_based_create_source (GDatagramBased *self, | |
| GIOCondition condition, | |
| GCancellable *cancellable); | |
| static GIOCondition g_socket_datagram_based_condition_check (GDatagramBased *datagram_based, | |
| GIOCondition condition); | |
| static gboolean g_socket_datagram_based_condition_wait (GDatagramBased *datagram_based, | |
| GIOCondition condition, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static GSocketAddress * | |
| cache_recv_address (GSocket *socket, struct sockaddr *native, size_t native_len); | |
| static gssize | |
| g_socket_receive_message_with_timeout (GSocket *socket, | |
| GSocketAddress **address, | |
| GInputVector *vectors, | |
| gint num_vectors, | |
| GSocketControlMessage ***messages, | |
| gint *num_messages, | |
| gint *flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static gint | |
| g_socket_receive_messages_with_timeout (GSocket *socket, | |
| GInputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static gint | |
| g_socket_send_messages_with_timeout (GSocket *socket, | |
| GOutputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error); | |
| enum | |
| { | |
| PROP_0, | |
| PROP_FAMILY, | |
| PROP_TYPE, | |
| PROP_PROTOCOL, | |
| PROP_FD, | |
| PROP_BLOCKING, | |
| PROP_LISTEN_BACKLOG, | |
| PROP_KEEPALIVE, | |
| PROP_LOCAL_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; | |
| GSocketProtocol protocol; | |
| gint fd; | |
| gint listen_backlog; | |
| guint timeout; | |
| GError *construct_error; | |
| GSocketAddress *remote_address; | |
| guint inited : 1; | |
| guint blocking : 1; | |
| guint keepalive : 1; | |
| guint closed : 1; | |
| guint connected_read : 1; | |
| guint connected_write : 1; | |
| guint listening : 1; | |
| guint timed_out : 1; | |
| guint connect_pending : 1; | |
| #ifdef G_OS_WIN32 | |
| WSAEVENT event; | |
| gboolean waiting; | |
| DWORD waiting_result; | |
| int current_events; | |
| int current_errors; | |
| int selected_events; | |
| GList *requested_conditions; /* list of requested GIOCondition * */ | |
| GMutex win32_source_lock; | |
| GCond win32_source_cond; | |
| #endif | |
| struct { | |
| GSocketAddress *addr; | |
| struct sockaddr *native; | |
| gsize native_len; | |
| guint64 last_used; | |
| } recv_addr_cache[RECV_ADDR_CACHE_SIZE]; | |
| }; | |
| _G_DEFINE_TYPE_EXTENDED_WITH_PRELUDE (GSocket, g_socket, G_TYPE_OBJECT, 0, | |
| /* Need a prelude for https://bugzilla.gnome.org/show_bug.cgi?id=674885 */ | |
| g_type_ensure (G_TYPE_SOCKET_FAMILY); | |
| g_type_ensure (G_TYPE_SOCKET_TYPE); | |
| g_type_ensure (G_TYPE_SOCKET_PROTOCOL); | |
| g_type_ensure (G_TYPE_SOCKET_ADDRESS); | |
| /* And networking init is appropriate for the prelude */ | |
| g_networking_init (); | |
| , /* And now the regular type init code */ | |
| G_ADD_PRIVATE (GSocket) | |
| G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, | |
| g_socket_initable_iface_init); | |
| G_IMPLEMENT_INTERFACE (G_TYPE_DATAGRAM_BASED, | |
| g_socket_datagram_based_iface_init)); | |
| static int | |
| get_socket_errno (void) | |
| { | |
| #ifndef G_OS_WIN32 | |
| return errno; | |
| #else | |
| return WSAGetLastError (); | |
| #endif | |
| } | |
| static GIOErrorEnum | |
| socket_io_error_from_errno (int err) | |
| { | |
| #ifdef G_OS_WIN32 | |
| return g_io_error_from_win32_error (err); | |
| #else | |
| return g_io_error_from_errno (err); | |
| #endif | |
| } | |
| static const char * | |
| socket_strerror (int err) | |
| { | |
| #ifndef G_OS_WIN32 | |
| return g_strerror (err); | |
| #else | |
| const char *msg_ret; | |
| char *msg; | |
| msg = g_win32_error_message (err); | |
| msg_ret = g_intern_string (msg); | |
| g_free (msg); | |
| return msg_ret; | |
| #endif | |
| } | |
| /* Wrapper around g_set_error() to avoid doing excess work */ | |
| #define socket_set_error_lazy(err, errsv, fmt) \ | |
| G_STMT_START { \ | |
| GError **__err = (err); \ | |
| int __errsv = (errsv); \ | |
| \ | |
| if (__err) \ | |
| { \ | |
| int __code = socket_io_error_from_errno (__errsv); \ | |
| const char *__strerr = socket_strerror (__errsv); \ | |
| \ | |
| if (__code == G_IO_ERROR_WOULD_BLOCK) \ | |
| g_set_error_literal (__err, G_IO_ERROR, __code, __strerr); \ | |
| else \ | |
| g_set_error (__err, G_IO_ERROR, __code, fmt, __strerr); \ | |
| } \ | |
| } G_STMT_END | |
| #ifdef G_OS_WIN32 | |
| #define win32_unset_event_mask(_socket, _mask) _win32_unset_event_mask (_socket, _mask) | |
| static void | |
| _win32_unset_event_mask (GSocket *socket, int mask) | |
| { | |
| g_mutex_lock (&socket->priv->win32_source_lock); | |
| socket->priv->current_events &= ~mask; | |
| socket->priv->current_errors &= ~mask; | |
| g_mutex_unlock (&socket->priv->win32_source_lock); | |
| } | |
| #else | |
| #define win32_unset_event_mask(_socket, _mask) | |
| #endif | |
| /* 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 gchar * | |
| address_to_string (GSocketAddress *address) | |
| { | |
| GString *ret = g_string_new (""); | |
| if (G_IS_INET_SOCKET_ADDRESS (address)) | |
| { | |
| GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (address); | |
| GInetAddress *ia = g_inet_socket_address_get_address (isa); | |
| GSocketFamily family = g_inet_address_get_family (ia); | |
| gchar *tmp; | |
| /* Represent IPv6 addresses in URL style: | |
| * ::1 port 12345 -> [::1]:12345 */ | |
| if (family == G_SOCKET_FAMILY_IPV6) | |
| g_string_append_c (ret, '['); | |
| tmp = g_inet_address_to_string (ia); | |
| g_string_append (ret, tmp); | |
| g_free (tmp); | |
| if (family == G_SOCKET_FAMILY_IPV6) | |
| { | |
| guint32 scope = g_inet_socket_address_get_scope_id (isa); | |
| if (scope != 0) | |
| g_string_append_printf (ret, "%%%u", scope); | |
| g_string_append_c (ret, ']'); | |
| } | |
| g_string_append_c (ret, ':'); | |
| g_string_append_printf (ret, "%u", g_inet_socket_address_get_port (isa)); | |
| } | |
| else | |
| { | |
| /* For unknown address types, just show the type */ | |
| g_string_append_printf (ret, "(%s)", G_OBJECT_TYPE_NAME (address)); | |
| } | |
| return g_string_free (ret, FALSE); | |
| } | |
| static gboolean | |
| check_socket (GSocket *socket, | |
| GError **error) | |
| { | |
| if (!socket->priv->inited) | |
| { | |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, | |
| _("Invalid socket, not initialized")); | |
| return FALSE; | |
| } | |
| if (socket->priv->construct_error) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, | |
| _("Invalid socket, initialization failed due to: %s"), | |
| socket->priv->construct_error->message); | |
| return FALSE; | |
| } | |
| if (socket->priv->closed) | |
| { | |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, | |
| _("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; | |
| } | |
| static void | |
| g_socket_details_from_fd (GSocket *socket) | |
| { | |
| union { | |
| struct sockaddr_storage storage; | |
| struct sockaddr sa; | |
| } address; | |
| gint fd; | |
| socklen_t addrlen; | |
| int value, family; | |
| int errsv; | |
| memset (&address, 0, sizeof (address)); | |
| fd = socket->priv->fd; | |
| if (!g_socket_get_option (socket, SOL_SOCKET, SO_TYPE, &value, NULL)) | |
| { | |
| errsv = get_socket_errno (); | |
| goto err; | |
| } | |
| switch (value) | |
| { | |
| case SOCK_STREAM: | |
| socket->priv->type = G_SOCKET_TYPE_STREAM; | |
| break; | |
| case SOCK_DGRAM: | |
| socket->priv->type = G_SOCKET_TYPE_DATAGRAM; | |
| break; | |
| case SOCK_SEQPACKET: | |
| socket->priv->type = G_SOCKET_TYPE_SEQPACKET; | |
| break; | |
| default: | |
| socket->priv->type = G_SOCKET_TYPE_INVALID; | |
| break; | |
| } | |
| addrlen = sizeof address; | |
| if (getsockname (fd, &address.sa, &addrlen) != 0) | |
| { | |
| errsv = get_socket_errno (); | |
| goto err; | |
| } | |
| if (addrlen > 0) | |
| { | |
| g_assert (G_STRUCT_OFFSET (struct sockaddr, sa_family) + | |
| (socklen_t) sizeof address.storage.ss_family <= addrlen); | |
| family = address.storage.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.storage.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 = G_SOCKET_FAMILY_UNIX; | |
| socket->priv->protocol = G_SOCKET_PROTOCOL_DEFAULT; | |
| break; | |
| default: | |
| socket->priv->family = G_SOCKET_FAMILY_INVALID; | |
| break; | |
| } | |
| if (socket->priv->family != G_SOCKET_FAMILY_INVALID) | |
| { | |
| addrlen = sizeof address; | |
| if (getpeername (fd, &address.sa, &addrlen) >= 0) | |
| { | |
| socket->priv->connected_read = TRUE; | |
| socket->priv->connected_write = TRUE; | |
| } | |
| } | |
| if (g_socket_get_option (socket, SOL_SOCKET, SO_KEEPALIVE, &value, NULL)) | |
| { | |
| socket->priv->keepalive = !!value; | |
| } | |
| else | |
| { | |
| /* Can't read, maybe not supported, assume FALSE */ | |
| socket->priv->keepalive = FALSE; | |
| } | |
| return; | |
| err: | |
| g_set_error (&socket->priv->construct_error, G_IO_ERROR, | |
| socket_io_error_from_errno (errsv), | |
| _("creating GSocket from fd: %s"), | |
| socket_strerror (errsv)); | |
| } | |
| static void | |
| socket_set_nonblock (int 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 (fd, TRUE, &error)) | |
| { | |
| g_warning ("Error setting socket to nonblocking mode: %s", error->message); | |
| g_clear_error (&error); | |
| } | |
| #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)); | |
| } | |
| #endif | |
| } | |
| /* Wrapper around socket() that is shared with gnetworkmonitornetlink.c. | |
| * It always sets SOCK_CLOEXEC | SOCK_NONBLOCK. */ | |
| gint | |
| g_socket (gint domain, | |
| gint type, | |
| gint protocol, | |
| GError **error) | |
| { | |
| int fd, errsv; | |
| #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) | |
| fd = socket (domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol); | |
| errsv = errno; | |
| if (fd != -1) | |
| return fd; | |
| /* It's possible that libc has SOCK_CLOEXEC and/or SOCK_NONBLOCK but the kernel does not */ | |
| if (fd < 0 && (errsv == EINVAL || errsv == EPROTOTYPE)) | |
| #endif | |
| fd = socket (domain, type, protocol); | |
| if (fd < 0) | |
| { | |
| 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; | |
| (void) fcntl (fd, F_SETFD, flags); | |
| } | |
| } | |
| #else | |
| if ((domain == AF_INET || domain == AF_INET6) && type == SOCK_DGRAM) | |
| { | |
| BOOL new_behavior = FALSE; | |
| DWORD bytes_returned = 0; | |
| /* Disable connection reset error on ICMP port unreachable. */ | |
| WSAIoctl (fd, SIO_UDP_CONNRESET, &new_behavior, sizeof (new_behavior), | |
| NULL, 0, &bytes_returned, NULL, NULL); | |
| } | |
| #endif | |
| /* Ensure the socket is non-blocking. */ | |
| socket_set_nonblock (fd); | |
| return fd; | |
| } | |
| /* Returned socket has SOCK_CLOEXEC | SOCK_NONBLOCK set. */ | |
| static gint | |
| g_socket_create_socket (GSocketFamily family, | |
| GSocketType type, | |
| int protocol, | |
| GError **error) | |
| { | |
| gint native_type; | |
| switch (type) | |
| { | |
| case G_SOCKET_TYPE_STREAM: | |
| native_type = SOCK_STREAM; | |
| break; | |
| case G_SOCKET_TYPE_DATAGRAM: | |
| native_type = SOCK_DGRAM; | |
| break; | |
| case G_SOCKET_TYPE_SEQPACKET: | |
| native_type = SOCK_SEQPACKET; | |
| break; | |
| default: | |
| g_assert_not_reached (); | |
| } | |
| if (family <= 0) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, | |
| _("Unable to create socket: %s"), _("Unknown family was specified")); | |
| return -1; | |
| } | |
| if (protocol == -1) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, | |
| _("Unable to create socket: %s"), _("Unknown protocol was specified")); | |
| return -1; | |
| } | |
| return g_socket (family, native_type, protocol, error); | |
| } | |
| static void | |
| g_socket_constructed (GObject *object) | |
| { | |
| GSocket *socket = G_SOCKET (object); | |
| if (socket->priv->fd >= 0) | |
| { | |
| /* create socket->priv info from the fd and ensure it’s non-blocking */ | |
| g_socket_details_from_fd (socket); | |
| socket_set_nonblock (socket->priv->fd); | |
| } | |
| else | |
| { | |
| /* create the fd from socket->priv info; this sets it non-blocking by construction */ | |
| socket->priv->fd = g_socket_create_socket (socket->priv->family, | |
| socket->priv->type, | |
| socket->priv->protocol, | |
| &socket->priv->construct_error); | |
| } | |
| if (socket->priv->fd != -1) | |
| { | |
| #ifdef SO_NOSIGPIPE | |
| /* See note about SIGPIPE below. */ | |
| g_socket_set_option (socket, SOL_SOCKET, SO_NOSIGPIPE, TRUE, NULL); | |
| #endif | |
| if (socket->priv->type == G_SOCKET_TYPE_STREAM) | |
| g_socket_set_option (socket, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); | |
| } | |
| } | |
| static void | |
| g_socket_get_property (GObject *object, | |
| guint prop_id, | |
| GValue *value, | |
| GParamSpec *pspec) | |
| { | |
| GSocket *socket = G_SOCKET (object); | |
| GSocketAddress *address; | |
| switch (prop_id) | |
| { | |
| case PROP_FAMILY: | |
| g_value_set_enum (value, socket->priv->family); | |
| break; | |
| case PROP_TYPE: | |
| g_value_set_enum (value, socket->priv->type); | |
| break; | |
| case PROP_PROTOCOL: | |
| g_value_set_enum (value, socket->priv->protocol); | |
| break; | |
| case PROP_FD: | |
| g_value_set_int (value, socket->priv->fd); | |
| break; | |
| case PROP_BLOCKING: | |
| g_value_set_boolean (value, socket->priv->blocking); | |
| break; | |
| case PROP_LISTEN_BACKLOG: | |
| g_value_set_int (value, socket->priv->listen_backlog); | |
| break; | |
| case PROP_KEEPALIVE: | |
| g_value_set_boolean (value, socket->priv->keepalive); | |
| break; | |
| case PROP_LOCAL_ADDRESS: | |
| address = g_socket_get_local_address (socket, NULL); | |
| g_value_take_object (value, address); | |
| break; | |
| case PROP_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: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| } | |
| } | |
| static void | |
| g_socket_set_property (GObject *object, | |
| guint prop_id, | |
| const GValue *value, | |
| GParamSpec *pspec) | |
| { | |
| GSocket *socket = G_SOCKET (object); | |
| switch (prop_id) | |
| { | |
| case PROP_FAMILY: | |
| socket->priv->family = g_value_get_enum (value); | |
| break; | |
| case PROP_TYPE: | |
| socket->priv->type = g_value_get_enum (value); | |
| break; | |
| case PROP_PROTOCOL: | |
| socket->priv->protocol = g_value_get_enum (value); | |
| break; | |
| case PROP_FD: | |
| socket->priv->fd = g_value_get_int (value); | |
| break; | |
| case PROP_BLOCKING: | |
| g_socket_set_blocking (socket, g_value_get_boolean (value)); | |
| break; | |
| case PROP_LISTEN_BACKLOG: | |
| g_socket_set_listen_backlog (socket, g_value_get_int (value)); | |
| break; | |
| case PROP_KEEPALIVE: | |
| 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); | |
| } | |
| } | |
| static void | |
| g_socket_finalize (GObject *object) | |
| { | |
| GSocket *socket = G_SOCKET (object); | |
| gint i; | |
| g_clear_error (&socket->priv->construct_error); | |
| if (socket->priv->fd != -1 && | |
| !socket->priv->closed) | |
| g_socket_close (socket, NULL); | |
| if (socket->priv->remote_address) | |
| g_object_unref (socket->priv->remote_address); | |
| #ifdef G_OS_WIN32 | |
| if (socket->priv->event != WSA_INVALID_EVENT) | |
| { | |
| WSACloseEvent (socket->priv->event); | |
| socket->priv->event = WSA_INVALID_EVENT; | |
| } | |
| g_assert (socket->priv->requested_conditions == NULL); | |
| g_mutex_clear (&socket->priv->win32_source_lock); | |
| g_cond_clear (&socket->priv->win32_source_cond); | |
| #endif | |
| for (i = 0; i < RECV_ADDR_CACHE_SIZE; i++) | |
| { | |
| 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)->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); | |
| #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->constructed = g_socket_constructed; | |
| gobject_class->set_property = g_socket_set_property; | |
| gobject_class->get_property = g_socket_get_property; | |
| /** | |
| * GSocket:family: | |
| * | |
| * The socket’s address family. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_FAMILY, | |
| g_param_spec_enum ("family", NULL, NULL, | |
| G_TYPE_SOCKET_FAMILY, | |
| G_SOCKET_FAMILY_INVALID, | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GSocket:type: | |
| * | |
| * The socket’s type. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_TYPE, | |
| g_param_spec_enum ("type", NULL, NULL, | |
| G_TYPE_SOCKET_TYPE, | |
| G_SOCKET_TYPE_STREAM, | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GSocket:protocol: | |
| * | |
| * The ID of the protocol to use, or `-1` for unknown. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_PROTOCOL, | |
| g_param_spec_enum ("protocol", NULL, NULL, | |
| G_TYPE_SOCKET_PROTOCOL, | |
| G_SOCKET_PROTOCOL_UNKNOWN, | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GSocket:fd: | |
| * | |
| * The socket’s file descriptor. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_FD, | |
| g_param_spec_int ("fd", NULL, NULL, | |
| G_MININT, | |
| G_MAXINT, | |
| -1, | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GSocket:blocking: | |
| * | |
| * Whether I/O on this socket is blocking. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_BLOCKING, | |
| g_param_spec_boolean ("blocking", NULL, NULL, | |
| TRUE, | |
| G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GSocket:listen-backlog: | |
| * | |
| * The number of outstanding connections in the listen queue. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_LISTEN_BACKLOG, | |
| g_param_spec_int ("listen-backlog", NULL, NULL, | |
| 0, | |
| SOMAXCONN, | |
| 10, | |
| G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GSocket:keepalive: | |
| * | |
| * Whether to keep the connection alive by sending periodic pings. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_KEEPALIVE, | |
| g_param_spec_boolean ("keepalive", NULL, NULL, | |
| FALSE, | |
| G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GSocket:local-address: | |
| * | |
| * The local address the socket is bound to. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_LOCAL_ADDRESS, | |
| g_param_spec_object ("local-address", NULL, NULL, | |
| G_TYPE_SOCKET_ADDRESS, | |
| G_PARAM_READABLE | | |
| G_PARAM_STATIC_STRINGS)); | |
| /** | |
| * GSocket:remote-address: | |
| * | |
| * The remote address the socket is connected to. | |
| * | |
| * Since: 2.22 | |
| */ | |
| g_object_class_install_property (gobject_class, PROP_REMOTE_ADDRESS, | |
| g_param_spec_object ("remote-address", NULL, NULL, | |
| G_TYPE_SOCKET_ADDRESS, | |
| 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", NULL, NULL, | |
| 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", NULL, NULL, | |
| 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", NULL, NULL, | |
| 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", NULL, NULL, | |
| 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", NULL, NULL, | |
| 0, G_MAXUINT, 1, | |
| G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS)); | |
| } | |
| static void | |
| g_socket_initable_iface_init (GInitableIface *iface) | |
| { | |
| iface->init = g_socket_initable_init; | |
| } | |
| static void | |
| g_socket_datagram_based_iface_init (GDatagramBasedInterface *iface) | |
| { | |
| iface->receive_messages = g_socket_datagram_based_receive_messages; | |
| iface->send_messages = g_socket_datagram_based_send_messages; | |
| iface->create_source = g_socket_datagram_based_create_source; | |
| iface->condition_check = g_socket_datagram_based_condition_check; | |
| iface->condition_wait = g_socket_datagram_based_condition_wait; | |
| } | |
| static void | |
| g_socket_init (GSocket *socket) | |
| { | |
| 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; | |
| #ifdef G_OS_WIN32 | |
| socket->priv->event = WSA_INVALID_EVENT; | |
| g_mutex_init (&socket->priv->win32_source_lock); | |
| g_cond_init (&socket->priv->win32_source_cond); | |
| #endif | |
| } | |
| static gboolean | |
| g_socket_initable_init (GInitable *initable, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GSocket *socket; | |
| g_return_val_if_fail (G_IS_SOCKET (initable), FALSE); | |
| socket = G_SOCKET (initable); | |
| if (cancellable != NULL) | |
| { | |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, | |
| _("Cancellable initialization not supported")); | |
| return FALSE; | |
| } | |
| socket->priv->inited = TRUE; | |
| if (socket->priv->construct_error) | |
| { | |
| if (error) | |
| *error = g_error_copy (socket->priv->construct_error); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| static gboolean | |
| check_datagram_based (GDatagramBased *self, | |
| GError **error) | |
| { | |
| switch (g_socket_get_socket_type (G_SOCKET (self))) | |
| { | |
| case G_SOCKET_TYPE_INVALID: | |
| case G_SOCKET_TYPE_STREAM: | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, | |
| _("Cannot use datagram operations on a non-datagram " | |
| "socket.")); | |
| return FALSE; | |
| case G_SOCKET_TYPE_DATAGRAM: | |
| case G_SOCKET_TYPE_SEQPACKET: | |
| /* Fall through. */ | |
| break; | |
| } | |
| /* Due to us sharing #GSocketSource with the #GSocket implementation, it is | |
| * pretty tricky to split out #GSocket:timeout so that it does not affect | |
| * #GDatagramBased operations (but still affects #GSocket operations). It is | |
| * not worth that effort — just disallow it and require the user to specify | |
| * timeouts on a per-operation basis. */ | |
| if (g_socket_get_timeout (G_SOCKET (self)) != 0) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, | |
| _("Cannot use datagram operations on a socket with a " | |
| "timeout set.")); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| static gint | |
| g_socket_datagram_based_receive_messages (GDatagramBased *self, | |
| GInputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| if (!check_datagram_based (self, error)) | |
| return FALSE; | |
| return g_socket_receive_messages_with_timeout (G_SOCKET (self), messages, | |
| num_messages, flags, timeout_us, | |
| cancellable, error); | |
| } | |
| static gint | |
| g_socket_datagram_based_send_messages (GDatagramBased *self, | |
| GOutputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| if (!check_datagram_based (self, error)) | |
| return FALSE; | |
| return g_socket_send_messages_with_timeout (G_SOCKET (self), messages, | |
| num_messages, flags, timeout_us, | |
| cancellable, error); | |
| } | |
| static GSource * | |
| g_socket_datagram_based_create_source (GDatagramBased *self, | |
| GIOCondition condition, | |
| GCancellable *cancellable) | |
| { | |
| if (!check_datagram_based (self, NULL)) | |
| return NULL; | |
| return g_socket_create_source (G_SOCKET (self), condition, cancellable); | |
| } | |
| static GIOCondition | |
| g_socket_datagram_based_condition_check (GDatagramBased *datagram_based, | |
| GIOCondition condition) | |
| { | |
| if (!check_datagram_based (datagram_based, NULL)) | |
| return G_IO_ERR; | |
| return g_socket_condition_check (G_SOCKET (datagram_based), condition); | |
| } | |
| static gboolean | |
| g_socket_datagram_based_condition_wait (GDatagramBased *datagram_based, | |
| GIOCondition condition, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| if (!check_datagram_based (datagram_based, error)) | |
| return FALSE; | |
| return g_socket_condition_timed_wait (G_SOCKET (datagram_based), condition, | |
| timeout_us, cancellable, error); | |
| } | |
| /** | |
| * g_socket_new: | |
| * @family: the socket family to use, e.g. %G_SOCKET_FAMILY_IPV4. | |
| * @type: the socket type to use. | |
| * @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 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, | |
| GSocketProtocol protocol, | |
| GError **error) | |
| { | |
| return G_SOCKET (g_initable_new (G_TYPE_SOCKET, | |
| NULL, error, | |
| "family", family, | |
| "type", type, | |
| "protocol", protocol, | |
| NULL)); | |
| } | |
| /** | |
| * g_socket_new_from_fd: | |
| * @fd: a native socket file descriptor. | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * Creates a new #GSocket from a native file descriptor | |
| * or winsock SOCKET handle. | |
| * | |
| * This reads all the settings from the file descriptor so that | |
| * all properties should work. Note that the file descriptor | |
| * will be set to non-blocking mode, independent on the blocking | |
| * mode of the #GSocket. | |
| * | |
| * On success, the returned #GSocket takes ownership of @fd. On failure, the | |
| * caller must close @fd themselves. | |
| * | |
| * Since GLib 2.46, it is no longer a fatal error to call this on a non-socket | |
| * descriptor. Instead, a GError will be set with code %G_IO_ERROR_FAILED | |
| * | |
| * Returns: a #GSocket or %NULL on error. | |
| * Free the returned object with g_object_unref(). | |
| * | |
| * Since: 2.22 | |
| */ | |
| GSocket * | |
| g_socket_new_from_fd (gint fd, | |
| GError **error) | |
| { | |
| return G_SOCKET (g_initable_new (G_TYPE_SOCKET, | |
| NULL, error, | |
| "fd", fd, | |
| NULL)); | |
| } | |
| /** | |
| * g_socket_set_blocking: | |
| * @socket: a #GSocket. | |
| * @blocking: Whether to use blocking I/O or not. | |
| * | |
| * Sets the blocking mode of the socket. In blocking mode | |
| * all operations (which don’t take an explicit blocking parameter) block until | |
| * they succeed or there is an error. In | |
| * non-blocking mode all functions return results immediately or | |
| * with a %G_IO_ERROR_WOULD_BLOCK error. | |
| * | |
| * All sockets are created in blocking mode. However, note that the | |
| * platform level socket is always non-blocking, and blocking mode | |
| * is a GSocket level feature. | |
| * | |
| * Since: 2.22 | |
| */ | |
| void | |
| g_socket_set_blocking (GSocket *socket, | |
| gboolean blocking) | |
| { | |
| g_return_if_fail (G_IS_SOCKET (socket)); | |
| blocking = !!blocking; | |
| if (socket->priv->blocking == blocking) | |
| return; | |
| socket->priv->blocking = blocking; | |
| g_object_notify (G_OBJECT (socket), "blocking"); | |
| } | |
| /** | |
| * g_socket_get_blocking: | |
| * @socket: a #GSocket. | |
| * | |
| * Gets the blocking mode of the socket. For details on blocking I/O, | |
| * see g_socket_set_blocking(). | |
| * | |
| * Returns: %TRUE if blocking I/O is used, %FALSE otherwise. | |
| * | |
| * Since: 2.22 | |
| */ | |
| gboolean | |
| g_socket_get_blocking (GSocket *socket) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); | |
| return socket->priv->blocking; | |
| } | |
| /** | |
| * g_socket_set_keepalive: | |
| * @socket: a #GSocket. | |
| * @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. | |
| * | |
| * This option is only functional on certain kinds of sockets. (Notably, | |
| * %G_SOCKET_PROTOCOL_TCP sockets.) | |
| * | |
| * 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) | |
| { | |
| GError *error = NULL; | |
| g_return_if_fail (G_IS_SOCKET (socket)); | |
| keepalive = !!keepalive; | |
| if (socket->priv->keepalive == keepalive) | |
| return; | |
| if (!g_socket_set_option (socket, SOL_SOCKET, SO_KEEPALIVE, | |
| keepalive, &error)) | |
| { | |
| g_warning ("error setting keepalive: %s", error->message); | |
| g_error_free (error); | |
| return; | |
| } | |
| socket->priv->keepalive = keepalive; | |
| g_object_notify (G_OBJECT (socket), "keepalive"); | |
| } | |
| /** | |
| * g_socket_get_keepalive: | |
| * @socket: a #GSocket. | |
| * | |
| * Gets the keepalive mode of the socket. For details on this, | |
| * see g_socket_set_keepalive(). | |
| * | |
| * Returns: %TRUE if keepalive is active, %FALSE otherwise. | |
| * | |
| * Since: 2.22 | |
| */ | |
| gboolean | |
| g_socket_get_keepalive (GSocket *socket) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); | |
| return socket->priv->keepalive; | |
| } | |
| /** | |
| * g_socket_get_listen_backlog: | |
| * @socket: a #GSocket. | |
| * | |
| * Gets the listen backlog setting of the socket. For details on this, | |
| * see g_socket_set_listen_backlog(). | |
| * | |
| * Returns: the maximum number of pending connections. | |
| * | |
| * Since: 2.22 | |
| */ | |
| gint | |
| g_socket_get_listen_backlog (GSocket *socket) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket), 0); | |
| return socket->priv->listen_backlog; | |
| } | |
| /** | |
| * g_socket_set_listen_backlog: | |
| * @socket: a #GSocket. | |
| * @backlog: the maximum number of pending connections. | |
| * | |
| * Sets the maximum number of outstanding connections allowed | |
| * when listening on this socket. If more clients than this are | |
| * 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) | |
| { | |
| g_return_if_fail (G_IS_SOCKET (socket)); | |
| g_return_if_fail (!socket->priv->listening); | |
| if (backlog != socket->priv->listen_backlog) | |
| { | |
| socket->priv->listen_backlog = backlog; | |
| g_object_notify (G_OBJECT (socket), "listen-backlog"); | |
| } | |
| } | |
| /** | |
| * g_socket_get_timeout: | |
| * @socket: a #GSocket. | |
| * | |
| * Gets the timeout setting of the socket. For details on this, see | |
| * g_socket_set_timeout(). | |
| * | |
| * Returns: the timeout in seconds | |
| * | |
| * Since: 2.26 | |
| */ | |
| guint | |
| g_socket_get_timeout (GSocket *socket) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket), 0); | |
| 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; | |
| } | |
| /** | |
| * g_socket_get_socket_type: | |
| * @socket: a #GSocket. | |
| * | |
| * Gets the socket type of the socket. | |
| * | |
| * Returns: a #GSocketType | |
| * | |
| * Since: 2.22 | |
| */ | |
| GSocketType | |
| g_socket_get_socket_type (GSocket *socket) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_TYPE_INVALID); | |
| return socket->priv->type; | |
| } | |
| /** | |
| * g_socket_get_protocol: | |
| * @socket: a #GSocket. | |
| * | |
| * Gets the socket protocol id the socket was created with. | |
| * In case the protocol is unknown, -1 is returned. | |
| * | |
| * Returns: a protocol id, or -1 if unknown | |
| * | |
| * Since: 2.22 | |
| */ | |
| GSocketProtocol | |
| g_socket_get_protocol (GSocket *socket) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket), -1); | |
| return socket->priv->protocol; | |
| } | |
| /** | |
| * g_socket_get_fd: | |
| * @socket: a #GSocket. | |
| * | |
| * Returns the underlying OS socket object. On unix this | |
| * 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) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket), -1); | |
| return socket->priv->fd; | |
| } | |
| /** | |
| * g_socket_get_local_address: | |
| * @socket: a #GSocket. | |
| * @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, | |
| * either explicitly or implicitly when connecting. | |
| * | |
| * 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) | |
| { | |
| union { | |
| struct sockaddr_storage storage; | |
| struct sockaddr sa; | |
| } buffer; | |
| socklen_t len = sizeof (buffer); | |
| g_return_val_if_fail (G_IS_SOCKET (socket), NULL); | |
| if (getsockname (socket->priv->fd, &buffer.sa, &len) < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), | |
| _("could not get local address: %s"), socket_strerror (errsv)); | |
| return NULL; | |
| } | |
| return g_socket_address_new_from_native (&buffer.storage, len); | |
| } | |
| /** | |
| * g_socket_get_remote_address: | |
| * @socket: a #GSocket. | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * Try to get the remote address of a connected socket. This is only | |
| * useful for connection oriented sockets that have been connected. | |
| * | |
| * 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) | |
| { | |
| union { | |
| struct sockaddr_storage storage; | |
| struct sockaddr sa; | |
| } buffer; | |
| socklen_t len = sizeof (buffer); | |
| g_return_val_if_fail (G_IS_SOCKET (socket), NULL); | |
| if (socket->priv->connect_pending) | |
| { | |
| if (!g_socket_check_connect_result (socket, error)) | |
| return NULL; | |
| else | |
| socket->priv->connect_pending = FALSE; | |
| } | |
| if (!socket->priv->remote_address) | |
| { | |
| if (getpeername (socket->priv->fd, &buffer.sa, &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.storage, len); | |
| } | |
| return g_object_ref (socket->priv->remote_address); | |
| } | |
| /** | |
| * g_socket_is_connected: | |
| * @socket: a #GSocket. | |
| * | |
| * Check whether the socket is connected. This is only useful for | |
| * connection-oriented sockets. | |
| * | |
| * If using g_socket_shutdown(), this function will return %TRUE until the | |
| * socket has been shut down for reading and writing. If you do a non-blocking | |
| * connect, this function will not return %TRUE until after you call | |
| * g_socket_check_connect_result(). | |
| * | |
| * 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->connected_read || socket->priv->connected_write); | |
| } | |
| /** | |
| * g_socket_listen: | |
| * @socket: a #GSocket. | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * Marks the socket as a server socket, i.e. a socket that is used | |
| * to accept incoming requests using g_socket_accept(). | |
| * | |
| * 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) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); | |
| if (!check_socket (socket, error)) | |
| return FALSE; | |
| if (listen (socket->priv->fd, socket->priv->listen_backlog) < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), | |
| _("could not listen: %s"), socket_strerror (errsv)); | |
| return FALSE; | |
| } | |
| socket->priv->listening = TRUE; | |
| return TRUE; | |
| } | |
| /** | |
| * g_socket_bind: | |
| * @socket: a #GSocket. | |
| * @address: a #GSocketAddress specifying the local address. | |
| * @allow_reuse: whether to allow reusing this address | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * When a socket is created it is attached to an address family, but it | |
| * doesn't have an address in this family. g_socket_bind() assigns the | |
| * address (sometimes called name) of the socket. | |
| * | |
| * It is generally required to bind to a local address before you can | |
| * receive connections. (See g_socket_listen() and g_socket_accept() ). | |
| * 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) | |
| { | |
| union { | |
| struct sockaddr_storage storage; | |
| struct sockaddr sa; | |
| } 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.storage, 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, &addr.sa, | |
| g_socket_address_get_native_size (address)) < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| gchar *address_string = address_to_string (address); | |
| g_set_error (error, | |
| G_IO_ERROR, socket_io_error_from_errno (errsv), | |
| _("Error binding to address %s: %s"), | |
| address_string, socket_strerror (errsv)); | |
| g_free (address_string); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| #ifdef G_OS_WIN32 | |
| static gulong | |
| g_socket_w32_get_adapter_ipv4_addr (const gchar *name_or_ip) | |
| { | |
| ULONG bufsize = 15000; /* MS-recommended initial bufsize */ | |
| DWORD ret = ERROR_BUFFER_OVERFLOW; | |
| unsigned int malloc_iterations = 0; | |
| PIP_ADAPTER_ADDRESSES addr_buf = NULL, eth_adapter; | |
| wchar_t *wchar_name_or_ip = NULL; | |
| gulong ip_result = 0; | |
| NET_IFINDEX if_index; | |
| /* | |
| * For Windows OS only - return adapter IPv4 address in network byte order. | |
| * | |
| * Input string can be either friendly name of adapter, IP address of adapter, | |
| * indextoname, or fullname of adapter. | |
| * Example: | |
| * 192.168.1.109 ===> IP address given directly, | |
| * convert directly with inet_addr() function | |
| * Wi-Fi ===> Adapter friendly name "Wi-Fi", | |
| * scan with GetAdapterAddresses and adapter->FriendlyName | |
| * ethernet_32774 ===> Adapter name as returned by if_indextoname | |
| * {33E8F5CD-BAEA-4214-BE13-B79AB8080CAB} ===> Adaptername, | |
| * as returned in GetAdapterAddresses and adapter->AdapterName | |
| */ | |
| /* Step 1: Check if string is an IP address: */ | |
| if (inet_pton (AF_INET, name_or_ip, &ip_result) == 1) | |
| return ip_result; /* Success, IP address string was given directly */ | |
| /* | |
| * Step 2: Check if name represents a valid Interface index (e.g. ethernet_75521) | |
| * function if_nametoindex will return >=1 if a valid index, or 0=no match | |
| * valid index will be used later in GetAdaptersAddress loop for lookup of adapter IP address | |
| */ | |
| if_index = if_nametoindex (name_or_ip); | |
| /* Step 3: Prepare wchar string for friendly name comparison */ | |
| if (if_index == 0) | |
| { | |
| size_t if_name_len = strlen (name_or_ip); | |
| if (if_name_len >= MAX_ADAPTER_NAME_LENGTH + 4) | |
| return INADDR_NONE; | |
| /* Name-check only needed if index=0... */ | |
| wchar_name_or_ip = (wchar_t *) g_try_malloc ((if_name_len + 1) * sizeof(wchar_t)); | |
| if (wchar_name_or_ip) | |
| mbstowcs (wchar_name_or_ip, name_or_ip, if_name_len + 1); | |
| /* NOTE: Even if malloc fails here, some comparisons can still be done later... so no exit here! */ | |
| } | |
| /* | |
| * Step 4: Allocate memory and get adapter addresses. | |
| * Buffer allocation loop recommended by MS, since size can be dynamic | |
| * https://docs.microsoft.com/en-us/windows/desktop/api/iphlpapi/nf-iphlpapi-getadaptersaddresses | |
| */ | |
| #define MAX_ALLOC_ITERATIONS 3 | |
| do | |
| { | |
| malloc_iterations++; | |
| addr_buf = (PIP_ADAPTER_ADDRESSES) g_try_realloc (addr_buf, bufsize); | |
| if (addr_buf) | |
| ret = GetAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, addr_buf, &bufsize); | |
| } | |
| while (addr_buf && | |
| ret == ERROR_BUFFER_OVERFLOW && | |
| malloc_iterations < MAX_ALLOC_ITERATIONS); | |
| #undef MAX_ALLOC_ITERATIONS | |
| if (addr_buf == 0 || ret != NO_ERROR) | |
| { | |
| g_free (addr_buf); | |
| g_free (wchar_name_or_ip); | |
| return INADDR_NONE; | |
| } | |
| /* Step 5: Loop through adapters and check match for index or name */ | |
| for (eth_adapter = addr_buf; eth_adapter != NULL; eth_adapter = eth_adapter->Next) | |
| { | |
| /* Check if match for interface index/name: */ | |
| gboolean any_match = (if_index > 0) && (eth_adapter->IfIndex == if_index); | |
| /* Check if match for friendly name - but only if NO if_index! */ | |
| if (!any_match && if_index == 0 && eth_adapter->FriendlyName && | |
| eth_adapter->FriendlyName[0] != 0 && wchar_name_or_ip != NULL) | |
| any_match = (_wcsicmp (eth_adapter->FriendlyName, wchar_name_or_ip) == 0); | |
| /* Check if match for adapter low level name - but only if NO if_index: */ | |
| if (!any_match && if_index == 0 && eth_adapter->AdapterName && | |
| eth_adapter->AdapterName[0] != 0) | |
| any_match = (stricmp (eth_adapter->AdapterName, name_or_ip) == 0); | |
| if (any_match) | |
| { | |
| /* We have match for this adapter, lets get its local unicast IP address! */ | |
| PIP_ADAPTER_UNICAST_ADDRESS uni_addr; | |
| for (uni_addr = eth_adapter->FirstUnicastAddress; | |
| uni_addr != NULL; uni_addr = uni_addr->Next) | |
| { | |
| if (uni_addr->Address.lpSockaddr->sa_family == AF_INET) | |
| { | |
| ip_result = ((PSOCKADDR_IN) uni_addr->Address.lpSockaddr)->sin_addr.S_un.S_addr; | |
| break; /* finished, exit unicast addr loop */ | |
| } | |
| } | |
| } | |
| } | |
| g_free (addr_buf); | |
| g_free (wchar_name_or_ip); | |
| return ip_result; | |
| } | |
| #elif defined(HAVE_SIOCGIFADDR) | |
| static gulong | |
| g_socket_get_adapter_ipv4_addr (GSocket *socket, | |
| const char *iface, | |
| GError **error) | |
| { | |
| int ret; | |
| struct ifreq ifr; | |
| struct sockaddr_in *iface_addr; | |
| size_t if_name_len = strlen (iface); | |
| memset (&ifr, 0, sizeof (ifr)); | |
| if (if_name_len >= sizeof (ifr.ifr_name)) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_FILENAME_TOO_LONG, | |
| _("Interface name too long")); | |
| return ULONG_MAX; | |
| } | |
| memcpy (ifr.ifr_name, iface, if_name_len); | |
| /* Get the IPv4 address of the given network interface name. */ | |
| ret = ioctl (socket->priv->fd, SIOCGIFADDR, &ifr); | |
| if (ret < 0) | |
| { | |
| int errsv = errno; | |
| g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), | |
| _("Interface not found: %s"), g_strerror (errsv)); | |
| return ULONG_MAX; | |
| } | |
| iface_addr = (struct sockaddr_in *) &ifr.ifr_addr; | |
| return iface_addr->sin_addr.s_addr; | |
| } | |
| #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); | |
| g_return_val_if_fail (error == NULL || *error == NULL, 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) | |
| { | |
| #if defined(HAVE_IP_MREQN) && !defined(__APPLE__) | |
| 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)); | |
| /* mc_req.imr_ifindex is not used correctly by the XNU kernel, and | |
| * causes us to bind to the default interface; so fallback to ip_mreq | |
| * and set the iface source address (not SSM). | |
| * See: https://gitlab.gnome.org/GNOME/glib/-/issues/3489 */ | |
| #if defined(HAVE_IP_MREQN) && !defined(__APPLE__) | |
| 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_socket_w32_get_adapter_ipv4_addr (iface); | |
| else | |
| mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY); | |
| #elif defined(HAVE_SIOCGIFADDR) | |
| if (iface) | |
| { | |
| GError *local_error = NULL; | |
| mc_req.imr_interface.s_addr = g_socket_get_adapter_ipv4_addr (socket, iface, &local_error); | |
| if (local_error != NULL) | |
| { | |
| g_propagate_error (error, g_steal_pointer (&local_error)); | |
| return FALSE; | |
| } | |
| } | |
| 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: (nullable): 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. | |
| * | |
| * To bind to a given source-specific multicast address, use | |
| * g_socket_join_multicast_group_ssm() instead. | |
| * | |
| * 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: (nullable): 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. | |
| * | |
| * To unbind to a given source-specific multicast address, use | |
| * g_socket_leave_multicast_group_ssm() instead. | |
| * | |
| * 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); | |
| } | |
| static gboolean | |
| g_socket_multicast_group_operation_ssm (GSocket *socket, | |
| GInetAddress *group, | |
| GInetAddress *source_specific, | |
| const gchar *iface, | |
| gboolean join_group, | |
| GError **error) | |
| { | |
| gint 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); | |
| g_return_val_if_fail (iface == NULL || *iface != '\0', FALSE); | |
| g_return_val_if_fail (error == NULL || *error == NULL, FALSE); | |
| if (!source_specific) | |
| { | |
| return g_socket_multicast_group_operation (socket, group, FALSE, iface, | |
| join_group, error); | |
| } | |
| if (!check_socket (socket, error)) | |
| return FALSE; | |
| switch (g_inet_address_get_family (group)) | |
| { | |
| case G_SOCKET_FAMILY_INVALID: | |
| case G_SOCKET_FAMILY_UNIX: | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, | |
| join_group ? | |
| _("Error joining multicast group: %s") : | |
| _("Error leaving multicast group: %s"), | |
| _("Unsupported socket family")); | |
| return FALSE; | |
| } | |
| break; | |
| case G_SOCKET_FAMILY_IPV4: | |
| { | |
| #ifdef IP_ADD_SOURCE_MEMBERSHIP | |
| #ifdef BROKEN_IP_MREQ_SOURCE_STRUCT | |
| #define S_ADDR_FIELD(src) src.imr_interface | |
| #else | |
| #define S_ADDR_FIELD(src) src.imr_interface.s_addr | |
| #endif | |
| gint optname; | |
| struct ip_mreq_source mc_req_src; | |
| if (g_inet_address_get_family (source_specific) != | |
| G_SOCKET_FAMILY_IPV4) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, | |
| join_group ? | |
| _("Error joining multicast group: %s") : | |
| _("Error leaving multicast group: %s"), | |
| _("source-specific not an IPv4 address")); | |
| return FALSE; | |
| } | |
| memset (&mc_req_src, 0, sizeof (mc_req_src)); | |
| /* By default use the default IPv4 multicast interface. */ | |
| S_ADDR_FIELD(mc_req_src) = g_htonl (INADDR_ANY); | |
| if (iface) | |
| { | |
| #if defined(G_OS_WIN32) | |
| S_ADDR_FIELD(mc_req_src) = g_socket_w32_get_adapter_ipv4_addr (iface); | |
| #elif defined(HAVE_SIOCGIFADDR) | |
| GError *local_error = NULL; | |
| S_ADDR_FIELD(mc_req_src) = g_socket_get_adapter_ipv4_addr (socket, iface, &local_error); | |
| if (local_error != NULL) | |
| { | |
| g_propagate_error (error, g_steal_pointer (&local_error)); | |
| return FALSE; | |
| } | |
| #endif /* defined(G_OS_WIN32) && defined (HAVE_IF_NAMETOINDEX) */ | |
| } | |
| g_assert (g_inet_address_get_native_size (group) == sizeof (mc_req_src.imr_multiaddr)); | |
| memcpy (&mc_req_src.imr_multiaddr, g_inet_address_to_bytes (group), | |
| g_inet_address_get_native_size (group)); | |
| g_assert (g_inet_address_get_native_size (source_specific) == sizeof (mc_req_src.imr_sourceaddr)); | |
| memcpy (&mc_req_src.imr_sourceaddr, | |
| g_inet_address_to_bytes (source_specific), | |
| g_inet_address_get_native_size (source_specific)); | |
| optname = | |
| join_group ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP; | |
| result = setsockopt (socket->priv->fd, IPPROTO_IP, optname, | |
| &mc_req_src, sizeof (mc_req_src)); | |
| #undef S_ADDR_FIELD | |
| #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 IPv4 source-specific multicast")); | |
| return FALSE; | |
| #endif /* IP_ADD_SOURCE_MEMBERSHIP */ | |
| } | |
| break; | |
| case G_SOCKET_FAMILY_IPV6: | |
| { | |
| #ifdef MCAST_JOIN_SOURCE_GROUP | |
| gboolean res; | |
| gint optname; | |
| struct group_source_req mc_req_src; | |
| GSocketAddress *saddr_group, *saddr_source_specific; | |
| guint iface_index = 0; | |
| #if defined (HAVE_IF_NAMETOINDEX) | |
| if (iface) | |
| { | |
| iface_index = if_nametoindex (iface); | |
| if (iface_index == 0) | |
| { | |
| int errsv = errno; | |
| g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), | |
| _("Interface not found: %s"), g_strerror (errsv)); | |
| return FALSE; | |
| } | |
| } | |
| #endif /* defined (HAVE_IF_NAMETOINDEX) */ | |
| mc_req_src.gsr_interface = iface_index; | |
| saddr_group = g_inet_socket_address_new (group, 0); | |
| res = g_socket_address_to_native (saddr_group, &mc_req_src.gsr_group, | |
| sizeof (mc_req_src.gsr_group), | |
| error); | |
| g_object_unref (saddr_group); | |
| if (!res) | |
| return FALSE; | |
| saddr_source_specific = g_inet_socket_address_new (source_specific, 0); | |
| res = g_socket_address_to_native (saddr_source_specific, | |
| &mc_req_src.gsr_source, | |
| sizeof (mc_req_src.gsr_source), | |
| error); | |
| g_object_unref (saddr_source_specific); | |
| if (!res) | |
| return FALSE; | |
| optname = | |
| join_group ? MCAST_JOIN_SOURCE_GROUP : MCAST_LEAVE_SOURCE_GROUP; | |
| result = setsockopt (socket->priv->fd, IPPROTO_IPV6, optname, | |
| &mc_req_src, sizeof (mc_req_src)); | |
| #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 IPv6 source-specific multicast")); | |
| return FALSE; | |
| #endif /* MCAST_JOIN_SOURCE_GROUP */ | |
| } | |
| break; | |
| default: | |
| 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_ssm: | |
| * @socket: a #GSocket. | |
| * @group: a #GInetAddress specifying the group address to join. | |
| * @source_specific: (nullable): a #GInetAddress specifying the | |
| * source-specific multicast address or %NULL to ignore. | |
| * @iface: (nullable): Name of the interface to use, or %NULL | |
| * @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 not %NULL, use source-specific multicast as | |
| * defined in RFC 4604. Note that on older platforms this may fail | |
| * with a %G_IO_ERROR_NOT_SUPPORTED error. | |
| * | |
| * Note that this function can be called multiple times for the same | |
| * @group with different @source_specific in order to receive multicast | |
| * packets from more than one source. | |
| * | |
| * Returns: %TRUE on success, %FALSE on error. | |
| * | |
| * Since: 2.56 | |
| */ | |
| gboolean | |
| g_socket_join_multicast_group_ssm (GSocket *socket, | |
| GInetAddress *group, | |
| GInetAddress *source_specific, | |
| const gchar *iface, | |
| GError **error) | |
| { | |
| return g_socket_multicast_group_operation_ssm (socket, group, | |
| source_specific, iface, TRUE, error); | |
| } | |
| /** | |
| * g_socket_leave_multicast_group_ssm: | |
| * @socket: a #GSocket. | |
| * @group: a #GInetAddress specifying the group address to leave. | |
| * @source_specific: (nullable): a #GInetAddress specifying the | |
| * source-specific multicast address or %NULL to ignore. | |
| * @iface: (nullable): Name of the interface to use, or %NULL | |
| * @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.56 | |
| */ | |
| gboolean | |
| g_socket_leave_multicast_group_ssm (GSocket *socket, | |
| GInetAddress *group, | |
| GInetAddress *source_specific, | |
| const gchar *iface, | |
| GError **error) | |
| { | |
| return g_socket_multicast_group_operation_ssm (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; | |
| case G_SOCKET_FAMILY_IPV6: | |
| #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) | |
| { | |
| gint v6_only; | |
| if (!g_socket_get_option (socket, | |
| IPPROTO_IPV6, IPV6_V6ONLY, | |
| &v6_only, NULL)) | |
| return FALSE; | |
| return !v6_only; | |
| } | |
| #else | |
| return FALSE; | |
| #endif | |
| default: | |
| return FALSE; | |
| } | |
| } | |
| /** | |
| * g_socket_accept: | |
| * @socket: a #GSocket. | |
| * @cancellable: (nullable): a %GCancellable or %NULL | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * Accept incoming connections on a connection-based socket. This removes | |
| * the first outstanding connection request from the listening socket and | |
| * creates a #GSocket object for it. | |
| * | |
| * The @socket must be bound to a local address with g_socket_bind() and | |
| * must be listening for incoming connections (g_socket_listen()). | |
| * | |
| * If there are no outstanding connections then the operation will block | |
| * 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: (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) | |
| { | |
| #ifdef HAVE_ACCEPT4 | |
| gboolean try_accept4 = TRUE; | |
| #endif | |
| GSocket *new_socket; | |
| gint ret; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), NULL); | |
| if (!check_socket (socket, error)) | |
| return NULL; | |
| if (!check_timeout (socket, error)) | |
| return NULL; | |
| while (TRUE) | |
| { | |
| gboolean try_accept = TRUE; | |
| #ifdef HAVE_ACCEPT4 | |
| if (try_accept4) | |
| { | |
| ret = accept4 (socket->priv->fd, NULL, 0, SOCK_CLOEXEC); | |
| if (ret < 0 && errno == ENOSYS) | |
| { | |
| try_accept4 = FALSE; | |
| } | |
| else | |
| { | |
| try_accept = FALSE; | |
| } | |
| } | |
| g_assert (try_accept4 || try_accept); | |
| #endif | |
| if (try_accept) | |
| ret = accept (socket->priv->fd, NULL, 0); | |
| if (ret < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| #ifdef WSAEWOULDBLOCK | |
| if (errsv == WSAEWOULDBLOCK) | |
| #else | |
| if (errsv == EWOULDBLOCK || | |
| errsv == EAGAIN) | |
| #endif | |
| { | |
| win32_unset_event_mask (socket, FD_ACCEPT); | |
| if (socket->priv->blocking) | |
| { | |
| if (!g_socket_condition_wait (socket, | |
| G_IO_IN, cancellable, error)) | |
| return NULL; | |
| continue; | |
| } | |
| } | |
| socket_set_error_lazy (error, errsv, _("Error accepting connection: %s")); | |
| return NULL; | |
| } | |
| break; | |
| } | |
| win32_unset_event_mask (socket, FD_ACCEPT); | |
| #ifdef G_OS_WIN32 | |
| { | |
| /* The socket inherits the accepting sockets event mask and even object, | |
| we need to remove that */ | |
| WSAEventSelect (ret, NULL, 0); | |
| } | |
| #else | |
| { | |
| 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 (ret, F_GETFD, 0); | |
| if (flags != -1 && | |
| (flags & FD_CLOEXEC) == 0) | |
| { | |
| flags |= FD_CLOEXEC; | |
| fcntl (ret, F_SETFD, flags); | |
| } | |
| } | |
| #endif | |
| new_socket = g_socket_new_from_fd (ret, error); | |
| if (new_socket == NULL) | |
| { | |
| #ifdef G_OS_WIN32 | |
| closesocket (ret); | |
| #else | |
| 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: (nullable): a %GCancellable or %NULL | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * Connect the socket to the specified remote address. | |
| * | |
| * For connection oriented socket this generally means we attempt to make | |
| * a connection to the @address. For a connection-less socket it sets | |
| * 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. | |
| * | |
| * 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 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) | |
| { | |
| union { | |
| struct sockaddr_storage storage; | |
| struct sockaddr sa; | |
| } buffer; | |
| 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, &buffer.storage, 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 (connect (socket->priv->fd, &buffer.sa, | |
| g_socket_address_get_native_size (address)) < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| #ifndef G_OS_WIN32 | |
| if (errsv == EINPROGRESS) | |
| #else | |
| if (errsv == WSAEWOULDBLOCK) | |
| #endif | |
| { | |
| win32_unset_event_mask (socket, FD_CONNECT); | |
| if (socket->priv->blocking) | |
| { | |
| if (g_socket_condition_wait (socket, G_IO_OUT, cancellable, error)) | |
| { | |
| if (g_socket_check_connect_result (socket, error)) | |
| break; | |
| } | |
| } | |
| else | |
| { | |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING, | |
| _("Connection in progress")); | |
| socket->priv->connect_pending = TRUE; | |
| } | |
| } | |
| else | |
| g_set_error_literal (error, G_IO_ERROR, | |
| socket_io_error_from_errno (errsv), | |
| socket_strerror (errsv)); | |
| return FALSE; | |
| } | |
| break; | |
| } | |
| win32_unset_event_mask (socket, FD_CONNECT); | |
| socket->priv->connected_read = TRUE; | |
| socket->priv->connected_write = TRUE; | |
| return TRUE; | |
| } | |
| /** | |
| * g_socket_check_connect_result: | |
| * @socket: a #GSocket | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * 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_connect_result (GSocket *socket, | |
| GError **error) | |
| { | |
| int value; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); | |
| 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_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_read = TRUE; | |
| socket->priv->connected_write = TRUE; | |
| return TRUE; | |
| } | |
| /** | |
| * g_socket_get_available_bytes: | |
| * @socket: a #GSocket | |
| * | |
| * Get the amount of data pending in the OS input buffer, without blocking. | |
| * | |
| * 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) | |
| { | |
| #ifndef SO_NREAD | |
| const gint bufsize = 64 * 1024; | |
| static guchar *buf = NULL; | |
| #endif | |
| #ifdef G_OS_WIN32 | |
| u_long avail; | |
| #else | |
| gint avail; | |
| #endif | |
| g_return_val_if_fail (G_IS_SOCKET (socket), -1); | |
| if (!check_socket (socket, NULL)) | |
| return -1; | |
| #ifdef SO_NREAD | |
| if (!g_socket_get_option (socket, SOL_SOCKET, SO_NREAD, &avail, NULL)) | |
| return -1; | |
| #else | |
| if (socket->priv->type == G_SOCKET_TYPE_DATAGRAM) | |
| { | |
| if (G_UNLIKELY (g_once_init_enter_pointer (&buf))) | |
| g_once_init_leave_pointer (&buf, g_malloc (bufsize)); | |
| /* On datagram sockets, FIONREAD ioctl is not reliable because many | |
| * systems add internal header size to the reported size, making it | |
| * unusable for this function. */ | |
| avail = recv (socket->priv->fd, buf, bufsize, MSG_PEEK); | |
| if ((gint) avail == -1) | |
| { | |
| int errsv = get_socket_errno (); | |
| #ifdef G_OS_WIN32 | |
| if (errsv == WSAEWOULDBLOCK) | |
| #else | |
| if (errsv == EWOULDBLOCK || errsv == EAGAIN) | |
| #endif | |
| avail = 0; | |
| } | |
| } | |
| else | |
| { | |
| #ifdef G_OS_WIN32 | |
| if (ioctlsocket (socket->priv->fd, FIONREAD, &avail) < 0) | |
| #else | |
| if (ioctl (socket->priv->fd, FIONREAD, &avail) < 0) | |
| #endif | |
| avail = -1; | |
| } | |
| #endif | |
| return avail; | |
| } | |
| /* Block on a timed wait for @condition until (@start_time + @timeout). | |
| * Return %G_IO_ERROR_TIMED_OUT if the timeout is reached; otherwise %TRUE. | |
| */ | |
| static gboolean | |
| block_on_timeout (GSocket *socket, | |
| GIOCondition condition, | |
| gint64 timeout_us, | |
| gint64 start_time, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| gint64 wait_timeout = -1; | |
| g_return_val_if_fail (timeout_us != 0, TRUE); | |
| /* check if we've timed out or how much time to wait at most */ | |
| if (timeout_us >= 0) | |
| { | |
| gint64 elapsed = g_get_monotonic_time () - start_time; | |
| if (elapsed >= timeout_us) | |
| { | |
| g_set_error_literal (error, | |
| G_IO_ERROR, G_IO_ERROR_TIMED_OUT, | |
| _("Socket I/O timed out")); | |
| return FALSE; | |
| } | |
| wait_timeout = timeout_us - elapsed; | |
| } | |
| return g_socket_condition_timed_wait (socket, condition, wait_timeout, | |
| cancellable, error); | |
| } | |
| static gssize | |
| g_socket_receive_with_timeout (GSocket *socket, | |
| guint8 *buffer, | |
| gsize size, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| gssize ret; | |
| gint64 start_time; | |
| g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, -1); | |
| start_time = g_get_monotonic_time (); | |
| 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 ((ret = recv (socket->priv->fd, buffer, size, 0)) < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| #ifdef WSAEWOULDBLOCK | |
| if (errsv == WSAEWOULDBLOCK) | |
| #else | |
| if (errsv == EWOULDBLOCK || | |
| errsv == EAGAIN) | |
| #endif | |
| { | |
| win32_unset_event_mask (socket, FD_READ); | |
| if (timeout_us != 0) | |
| { | |
| if (!block_on_timeout (socket, G_IO_IN, timeout_us, start_time, | |
| cancellable, error)) | |
| return -1; | |
| continue; | |
| } | |
| } | |
| win32_unset_event_mask (socket, FD_READ); | |
| socket_set_error_lazy (error, errsv, _("Error receiving data: %s")); | |
| return -1; | |
| } | |
| win32_unset_event_mask (socket, FD_READ); | |
| break; | |
| } | |
| return ret; | |
| } | |
| /** | |
| * g_socket_receive_bytes: | |
| * @socket: a #GSocket | |
| * @size: the number of bytes you want to read from the socket | |
| * @timeout_us: the timeout to wait for, in microseconds, or `-1` to block | |
| * indefinitely | |
| * @cancellable: (nullable): a %GCancellable, or `NULL` | |
| * @error: return location for a #GError, or `NULL` | |
| * | |
| * Receives data (up to @size bytes) from a socket. | |
| * | |
| * This function is a variant of [method@Gio.Socket.receive] which returns a | |
| * [struct@GLib.Bytes] rather than a plain buffer. | |
| * | |
| * Pass `-1` to @timeout_us to block indefinitely until data is received (or | |
| * the connection is closed, or there is an error). Pass `0` to use the default | |
| * timeout from [property@Gio.Socket:timeout], or pass a positive number to wait | |
| * for that many microseconds for data before returning `G_IO_ERROR_TIMED_OUT`. | |
| * | |
| * Returns: (transfer full): a bytes buffer containing the | |
| * received bytes, or `NULL` on error | |
| * Since: 2.80 | |
| */ | |
| GBytes * | |
| g_socket_receive_bytes (GSocket *socket, | |
| gsize size, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| guint8 *data; | |
| gssize res; | |
| GBytes *buf; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), NULL); | |
| g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); | |
| g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
| data = g_new0 (guint8, size); | |
| res = g_socket_receive_with_timeout (socket, data, size, timeout_us, cancellable, error); | |
| if (res < 0) | |
| { | |
| g_free (data); | |
| return NULL; | |
| } | |
| if ((gsize) res == size) | |
| { | |
| buf = g_bytes_new_take (g_steal_pointer (&data), (gsize) res); | |
| } | |
| else | |
| { | |
| GBytes *sub_buf; | |
| buf = g_bytes_new_take (g_steal_pointer (&data), size); | |
| sub_buf = g_bytes_new_from_bytes (buf, 0, (gsize) res); | |
| g_bytes_unref (buf); | |
| buf = g_steal_pointer (&sub_buf); | |
| } | |
| return g_steal_pointer (&buf); | |
| } | |
| /** | |
| * g_socket_receive: | |
| * @socket: a #GSocket | |
| * @buffer: (array length=size) (element-type guint8) (out caller-allocates): | |
| * a buffer to read data into (which should be at least @size bytes long). | |
| * @size: (in): the number of bytes you want to read from the socket | |
| * @cancellable: (nullable): 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() | |
| * with @address set to %NULL. | |
| * | |
| * 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. | |
| * | |
| * 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 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_timeout (socket, (guint8 *) buffer, size, | |
| socket->priv->blocking ? -1 : 0, | |
| cancellable, error); | |
| } | |
| /** | |
| * g_socket_receive_with_blocking: | |
| * @socket: a #GSocket | |
| * @buffer: (array length=size) (element-type guint8) (out caller-allocates): | |
| * a buffer to read data into (which should be at least @size bytes long). | |
| * @size: (in): the number of bytes you want to read from the socket | |
| * @blocking: whether to do blocking or non-blocking I/O | |
| * @cancellable: (nullable): 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) | |
| { | |
| return g_socket_receive_with_timeout (socket, (guint8 *) buffer, size, | |
| blocking ? -1 : 0, cancellable, error); | |
| } | |
| /** | |
| * g_socket_receive_bytes_from: | |
| * @socket: a #GSocket | |
| * @address: (out) (optional): return location for a #GSocketAddress | |
| * @size: the number of bytes you want to read from the socket | |
| * @timeout_us: the timeout to wait for, in microseconds, or `-1` to block | |
| * indefinitely | |
| * @cancellable: (nullable): a #GCancellable, or `NULL` | |
| * @error: return location for a #GError, or `NULL` | |
| * | |
| * Receive data (up to @size bytes) from a socket. | |
| * | |
| * This function is a variant of [method@Gio.Socket.receive_from] which returns | |
| * a [struct@GLib.Bytes] rather than a plain buffer. | |
| * | |
| * If @address is non-%NULL then @address will be set equal to the | |
| * source address of the received packet. | |
| * | |
| * The @address is owned by the caller. | |
| * | |
| * Pass `-1` to @timeout_us to block indefinitely until data is received (or | |
| * the connection is closed, or there is an error). Pass `0` to use the default | |
| * timeout from [property@Gio.Socket:timeout], or pass a positive number to wait | |
| * for that many microseconds for data before returning `G_IO_ERROR_TIMED_OUT`. | |
| * | |
| * Returns: (transfer full): a bytes buffer containing the | |
| * received bytes, or `NULL` on error | |
| * Since: 2.80 | |
| */ | |
| GBytes * | |
| g_socket_receive_bytes_from (GSocket *socket, | |
| GSocketAddress **address, | |
| gsize size, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GInputVector v; | |
| gssize res; | |
| GBytes *buf; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), NULL); | |
| g_return_val_if_fail (address == NULL || *address == NULL, NULL); | |
| g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); | |
| g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
| v.buffer = g_new0 (guint8, size); | |
| v.size = size; | |
| res = g_socket_receive_message_with_timeout (socket, | |
| address, | |
| &v, 1, | |
| NULL, 0, NULL, | |
| timeout_us, | |
| cancellable, | |
| error); | |
| if (res < 0) | |
| { | |
| g_free (v.buffer); | |
| return NULL; | |
| } | |
| if ((gsize) res == size) | |
| { | |
| buf = g_bytes_new_take (g_steal_pointer (&v.buffer), (gsize) res); | |
| } | |
| else | |
| { | |
| GBytes *sub_buf; | |
| buf = g_bytes_new_take (g_steal_pointer (&v.buffer), size); | |
| sub_buf = g_bytes_new_from_bytes (buf, 0, (gsize) res); | |
| g_bytes_unref (buf); | |
| buf = g_steal_pointer (&sub_buf); | |
| } | |
| return g_steal_pointer (&buf); | |
| } | |
| /** | |
| * g_socket_receive_from: | |
| * @socket: a #GSocket | |
| * @address: (out) (optional): a pointer to a #GSocketAddress | |
| * pointer, or %NULL | |
| * @buffer: (array length=size) (element-type guint8) (out caller-allocates): | |
| * a buffer to read data into (which should be at least @size bytes long). | |
| * @size: (in): the number of bytes you want to read from the socket | |
| * @cancellable: (nullable): a %GCancellable or %NULL | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * Receive data (up to @size bytes) from a socket. | |
| * | |
| * If @address is non-%NULL then @address will be set equal to the | |
| * source address of the received packet. | |
| * @address is owned by the caller. | |
| * | |
| * See g_socket_receive() for additional information. | |
| * | |
| * 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, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GInputVector v; | |
| v.buffer = buffer; | |
| v.size = size; | |
| return g_socket_receive_message (socket, | |
| 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 | |
| static gssize | |
| g_socket_send_with_timeout (GSocket *socket, | |
| const guint8 *buffer, | |
| gsize size, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| gssize ret; | |
| gint64 start_time; | |
| g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, -1); | |
| start_time = g_get_monotonic_time (); | |
| 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 ((ret = send (socket->priv->fd, (const char *)buffer, size, G_SOCKET_DEFAULT_SEND_FLAGS)) < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| #ifdef WSAEWOULDBLOCK | |
| if (errsv == WSAEWOULDBLOCK) | |
| #else | |
| if (errsv == EWOULDBLOCK || | |
| errsv == EAGAIN) | |
| #endif | |
| { | |
| win32_unset_event_mask (socket, FD_WRITE); | |
| if (timeout_us != 0) | |
| { | |
| if (!block_on_timeout (socket, G_IO_OUT, timeout_us, start_time, | |
| cancellable, error)) | |
| return -1; | |
| continue; | |
| } | |
| } | |
| socket_set_error_lazy (error, errsv, _("Error sending data: %s")); | |
| return -1; | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| /** | |
| * g_socket_send: | |
| * @socket: a #GSocket | |
| * @buffer: (array length=size) (element-type guint8): the buffer | |
| * containing the data to send. | |
| * @size: the number of bytes to send | |
| * @cancellable: (nullable): 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. | |
| * | |
| * 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 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 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: (nullable): 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_with_blocking (GSocket *socket, | |
| const gchar *buffer, | |
| gsize size, | |
| gboolean blocking, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| return g_socket_send_with_timeout (socket, (const guint8 *) buffer, size, | |
| blocking ? -1 : 0, cancellable, error); | |
| } | |
| /** | |
| * g_socket_send_to: | |
| * @socket: a #GSocket | |
| * @address: (nullable): 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: (nullable): 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()). | |
| * | |
| * See g_socket_send() for additional information. | |
| * | |
| * 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, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GOutputVector v; | |
| v.buffer = buffer; | |
| v.size = size; | |
| return g_socket_send_message (socket, | |
| address, | |
| &v, 1, | |
| NULL, 0, | |
| 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 or all 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 it is useful to shut down only one side of a connection 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) | |
| socket->priv->connected_read = FALSE; | |
| if (shutdown_write) | |
| socket->priv->connected_write = FALSE; | |
| return TRUE; | |
| } | |
| /** | |
| * g_socket_close: | |
| * @socket: a #GSocket | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * 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. | |
| * | |
| * 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) | |
| { | |
| int res; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), TRUE); | |
| if (socket->priv->closed) | |
| return TRUE; /* Multiple close not an error */ | |
| if (!check_socket (socket, error)) | |
| return FALSE; | |
| while (1) | |
| { | |
| #ifdef G_OS_WIN32 | |
| res = closesocket (socket->priv->fd); | |
| #else | |
| res = close (socket->priv->fd); | |
| #endif | |
| if (res == -1) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| g_set_error (error, G_IO_ERROR, | |
| socket_io_error_from_errno (errsv), | |
| _("Error closing socket: %s"), | |
| socket_strerror (errsv)); | |
| return FALSE; | |
| } | |
| break; | |
| } | |
| socket->priv->fd = -1; | |
| socket->priv->connected_read = FALSE; | |
| socket->priv->connected_write = FALSE; | |
| socket->priv->closed = TRUE; | |
| if (socket->priv->remote_address) | |
| { | |
| g_object_unref (socket->priv->remote_address); | |
| socket->priv->remote_address = NULL; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| * g_socket_is_closed: | |
| * @socket: a #GSocket | |
| * | |
| * Checks whether a socket is closed. | |
| * | |
| * Returns: %TRUE if socket is closed, %FALSE otherwise | |
| * | |
| * Since: 2.22 | |
| */ | |
| gboolean | |
| g_socket_is_closed (GSocket *socket) | |
| { | |
| return socket->priv->closed; | |
| } | |
| /* Broken source, used on errors */ | |
| static gboolean | |
| broken_dispatch (GSource *source, | |
| GSourceFunc callback, | |
| gpointer user_data) | |
| { | |
| return TRUE; | |
| } | |
| static GSourceFuncs broken_funcs = | |
| { | |
| NULL, | |
| NULL, | |
| broken_dispatch, | |
| NULL, | |
| NULL, | |
| NULL, | |
| }; | |
| #ifdef G_OS_WIN32 | |
| static gint | |
| network_events_for_condition (GIOCondition condition) | |
| { | |
| int event_mask = 0; | |
| if (condition & G_IO_IN) | |
| event_mask |= (FD_READ | FD_ACCEPT); | |
| if (condition & G_IO_OUT) | |
| event_mask |= (FD_WRITE | FD_CONNECT); | |
| event_mask |= FD_CLOSE; | |
| return event_mask; | |
| } | |
| static void | |
| ensure_event (GSocket *socket) | |
| { | |
| if (socket->priv->event == WSA_INVALID_EVENT) | |
| socket->priv->event = WSACreateEvent(); | |
| } | |
| static void | |
| update_select_events (GSocket *socket) | |
| { | |
| int event_mask; | |
| GIOCondition *ptr; | |
| GList *l; | |
| WSAEVENT event; | |
| if (socket->priv->closed) | |
| return; | |
| ensure_event (socket); | |
| event_mask = 0; | |
| for (l = socket->priv->requested_conditions; l != NULL; l = l->next) | |
| { | |
| ptr = l->data; | |
| event_mask |= network_events_for_condition (*ptr); | |
| } | |
| if (event_mask != socket->priv->selected_events) | |
| { | |
| /* If no events selected, disable event so we can unset | |
| nonblocking mode */ | |
| if (event_mask == 0) | |
| event = NULL; | |
| else | |
| event = socket->priv->event; | |
| if (WSAEventSelect (socket->priv->fd, event, event_mask) == 0) | |
| socket->priv->selected_events = event_mask; | |
| } | |
| } | |
| static void | |
| 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, | |
| 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 | |
| update_condition_unlocked (GSocket *socket) | |
| { | |
| WSANETWORKEVENTS events; | |
| GIOCondition condition; | |
| if (!socket->priv->closed && | |
| WSAEnumNetworkEvents (socket->priv->fd, | |
| socket->priv->event, | |
| &events) == 0) | |
| { | |
| socket->priv->current_events |= events.lNetworkEvents; | |
| if (events.lNetworkEvents & FD_WRITE && | |
| events.iErrorCode[FD_WRITE_BIT] != 0) | |
| socket->priv->current_errors |= FD_WRITE; | |
| if (events.lNetworkEvents & FD_CONNECT && | |
| events.iErrorCode[FD_CONNECT_BIT] != 0) | |
| socket->priv->current_errors |= FD_CONNECT; | |
| } | |
| condition = 0; | |
| if (socket->priv->current_events & (FD_READ | FD_ACCEPT)) | |
| condition |= G_IO_IN; | |
| if (socket->priv->current_events & FD_CLOSE) | |
| { | |
| int r, errsv = NO_ERROR, 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 | |
| mutually exclusive (can't write to a closed socket) */ | |
| if ((condition & G_IO_HUP) == 0 && | |
| socket->priv->current_events & FD_WRITE) | |
| { | |
| if (socket->priv->current_errors & FD_WRITE) | |
| condition |= G_IO_ERR; | |
| else | |
| condition |= G_IO_OUT; | |
| } | |
| else | |
| { | |
| if (socket->priv->current_events & FD_CONNECT) | |
| { | |
| if (socket->priv->current_errors & FD_CONNECT) | |
| condition |= (G_IO_HUP | G_IO_ERR); | |
| else | |
| condition |= G_IO_OUT; | |
| } | |
| } | |
| return condition; | |
| } | |
| static GIOCondition | |
| update_condition (GSocket *socket) | |
| { | |
| GIOCondition res; | |
| g_mutex_lock (&socket->priv->win32_source_lock); | |
| res = update_condition_unlocked (socket); | |
| g_mutex_unlock (&socket->priv->win32_source_lock); | |
| return res; | |
| } | |
| #endif | |
| typedef struct { | |
| GSource source; | |
| #ifdef G_OS_WIN32 | |
| GPollFD pollfd; | |
| #else | |
| gpointer fd_tag; | |
| #endif | |
| GSocket *socket; | |
| GIOCondition condition; | |
| } GSocketSource; | |
| static gboolean | |
| socket_source_prepare (GSource *source, | |
| gint *timeout) | |
| { | |
| GSocketSource *socket_source = (GSocketSource *)source; | |
| #ifdef G_OS_WIN32 | |
| if ((socket_source->pollfd.revents & G_IO_NVAL) != 0) | |
| return TRUE; | |
| if (g_socket_is_closed (socket_source->socket)) | |
| { | |
| g_source_remove_poll (source, &socket_source->pollfd); | |
| socket_source->pollfd.revents = G_IO_NVAL; | |
| return TRUE; | |
| } | |
| return (update_condition (socket_source->socket) & socket_source->condition) != 0; | |
| #else | |
| return g_socket_is_closed (socket_source->socket) && socket_source->fd_tag != NULL; | |
| #endif | |
| } | |
| #ifdef G_OS_WIN32 | |
| static gboolean | |
| socket_source_check_win32 (GSource *source) | |
| { | |
| int timeout; | |
| return socket_source_prepare (source, &timeout); | |
| } | |
| #endif | |
| static gboolean | |
| socket_source_dispatch (GSource *source, | |
| GSourceFunc callback, | |
| gpointer user_data) | |
| { | |
| GSocketSourceFunc func = (GSocketSourceFunc)callback; | |
| GSocketSource *socket_source = (GSocketSource *)source; | |
| GSocket *socket = socket_source->socket; | |
| gint64 timeout; | |
| guint events; | |
| gboolean ret; | |
| #ifdef G_OS_WIN32 | |
| if ((socket_source->pollfd.revents & G_IO_NVAL) != 0) | |
| events = G_IO_NVAL; | |
| else | |
| events = update_condition (socket_source->socket); | |
| #else | |
| if (g_socket_is_closed (socket_source->socket)) | |
| { | |
| if (socket_source->fd_tag) | |
| g_source_remove_unix_fd (source, socket_source->fd_tag); | |
| socket_source->fd_tag = NULL; | |
| events = G_IO_NVAL; | |
| } | |
| 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) && | |
| !g_socket_is_closed (socket_source->socket)) | |
| { | |
| socket->priv->timed_out = TRUE; | |
| events |= (G_IO_IN | G_IO_OUT); | |
| } | |
| ret = (*func) (socket, events & socket_source->condition, user_data); | |
| if (socket->priv->timeout && !g_socket_is_closed (socket_source->socket)) | |
| 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; | |
| socket = socket_source->socket; | |
| #ifdef G_OS_WIN32 | |
| remove_condition_watch (socket, &socket_source->condition); | |
| #endif | |
| g_object_unref (socket); | |
| } | |
| static gboolean | |
| socket_source_closure_callback (GSocket *socket, | |
| GIOCondition condition, | |
| gpointer data) | |
| { | |
| GClosure *closure = data; | |
| GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT }; | |
| GValue result_value = G_VALUE_INIT; | |
| gboolean result; | |
| g_value_init (&result_value, G_TYPE_BOOLEAN); | |
| 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); | |
| 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]); | |
| return result; | |
| } | |
| static GSourceFuncs socket_source_funcs = | |
| { | |
| socket_source_prepare, | |
| #ifdef G_OS_WIN32 | |
| socket_source_check_win32, | |
| #else | |
| NULL, | |
| #endif | |
| socket_source_dispatch, | |
| socket_source_finalize, | |
| (GSourceFunc)socket_source_closure_callback, | |
| NULL, | |
| }; | |
| static GSource * | |
| socket_source_new (GSocket *socket, | |
| GIOCondition condition, | |
| GCancellable *cancellable) | |
| { | |
| GSource *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 | |
| if (!check_socket (socket, NULL)) | |
| { | |
| g_warning ("Socket check failed"); | |
| return g_source_new (&broken_funcs, sizeof (GSource)); | |
| } | |
| condition |= G_IO_HUP | G_IO_ERR | G_IO_NVAL; | |
| source = g_source_new (&socket_source_funcs, sizeof (GSocketSource)); | |
| g_source_set_static_name (source, "GSocket"); | |
| socket_source = (GSocketSource *)source; | |
| socket_source->socket = g_object_ref (socket); | |
| socket_source->condition = condition; | |
| if (cancellable) | |
| { | |
| 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); | |
| } | |
| #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; | |
| } | |
| /** | |
| * g_socket_create_source: (skip) | |
| * @socket: a #GSocket | |
| * @condition: a #GIOCondition mask to monitor | |
| * @cancellable: (nullable): a %GCancellable or %NULL | |
| * | |
| * 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; | |
| * 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 (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: (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) | |
| { | |
| g_return_val_if_fail (G_IS_SOCKET (socket) && (cancellable == NULL || G_IS_CANCELLABLE (cancellable)), NULL); | |
| return socket_source_new (socket, condition, cancellable); | |
| } | |
| /** | |
| * g_socket_condition_check: | |
| * @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 | |
| * 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. | |
| * | |
| * This call never blocks. | |
| * | |
| * Returns: the @GIOCondition mask of the current state | |
| * | |
| * Since: 2.22 | |
| */ | |
| GIOCondition | |
| 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; | |
| #ifdef G_OS_WIN32 | |
| { | |
| GIOCondition current_condition; | |
| condition |= G_IO_ERR | G_IO_HUP; | |
| add_condition_watch (socket, &condition); | |
| current_condition = update_condition (socket); | |
| remove_condition_watch (socket, &condition); | |
| return condition & current_condition; | |
| } | |
| #else | |
| { | |
| GPollFD poll_fd; | |
| gint result; | |
| poll_fd.fd = socket->priv->fd; | |
| poll_fd.events = condition; | |
| poll_fd.revents = 0; | |
| do | |
| result = g_poll (&poll_fd, 1, 0); | |
| while (result == -1 && get_socket_errno () == EINTR); | |
| return poll_fd.revents; | |
| } | |
| #endif | |
| } | |
| /** | |
| * g_socket_condition_wait: | |
| * @socket: a #GSocket | |
| * @condition: a #GIOCondition mask to wait for | |
| * @cancellable: (nullable): a #GCancellable, or %NULL | |
| * @error: a #GError pointer, or %NULL | |
| * | |
| * Waits for @condition to become true on @socket. When the condition | |
| * is met, %TRUE is returned. | |
| * | |
| * 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_us: the maximum time (in microseconds) to wait, or -1 | |
| * @cancellable: (nullable): a #GCancellable, or %NULL | |
| * @error: a #GError pointer, or %NULL | |
| * | |
| * Waits for up to @timeout_us 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_us (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_us.) | |
| * | |
| * Note that although @timeout_us is in microseconds for consistency with | |
| * other GLib APIs, this function actually only has millisecond | |
| * resolution, and the behavior is undefined if @timeout_us 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_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| gint64 start_time; | |
| gint64 timeout_ms; | |
| 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_us < 0 || socket->priv->timeout < timeout_us / G_USEC_PER_SEC)) | |
| timeout_ms = (gint64) socket->priv->timeout * 1000; | |
| else if (timeout_us != -1) | |
| timeout_ms = timeout_us / 1000; | |
| else | |
| timeout_ms = -1; | |
| start_time = g_get_monotonic_time (); | |
| #ifdef G_OS_WIN32 | |
| { | |
| GIOCondition current_condition; | |
| WSAEVENT events[2]; | |
| DWORD res; | |
| GPollFD cancel_fd; | |
| int num_events; | |
| /* Always check these */ | |
| condition |= G_IO_ERR | G_IO_HUP; | |
| add_condition_watch (socket, &condition); | |
| num_events = 0; | |
| events[num_events++] = socket->priv->event; | |
| if (g_cancellable_make_pollfd (cancellable, &cancel_fd)) | |
| events[num_events++] = (WSAEVENT)cancel_fd.fd; | |
| if (timeout_ms == -1) | |
| timeout_ms = WSA_INFINITE; | |
| g_mutex_lock (&socket->priv->win32_source_lock); | |
| current_condition = update_condition_unlocked (socket); | |
| while ((condition & current_condition) == 0) | |
| { | |
| if (!socket->priv->waiting) | |
| { | |
| socket->priv->waiting = TRUE; | |
| socket->priv->waiting_result = 0; | |
| g_mutex_unlock (&socket->priv->win32_source_lock); | |
| res = WSAWaitForMultipleEvents (num_events, events, FALSE, timeout_ms, FALSE); | |
| g_mutex_lock (&socket->priv->win32_source_lock); | |
| socket->priv->waiting = FALSE; | |
| socket->priv->waiting_result = res; | |
| g_cond_broadcast (&socket->priv->win32_source_cond); | |
| } | |
| else | |
| { | |
| if (timeout_ms != WSA_INFINITE) | |
| { | |
| if (!g_cond_wait_until (&socket->priv->win32_source_cond, &socket->priv->win32_source_lock, timeout_ms)) | |
| { | |
| res = WSA_WAIT_TIMEOUT; | |
| break; | |
| } | |
| else | |
| { | |
| res = socket->priv->waiting_result; | |
| } | |
| } | |
| else | |
| { | |
| g_cond_wait (&socket->priv->win32_source_cond, &socket->priv->win32_source_lock); | |
| res = socket->priv->waiting_result; | |
| } | |
| } | |
| if (res == WSA_WAIT_FAILED) | |
| { | |
| int errsv = get_socket_errno (); | |
| g_set_error (error, G_IO_ERROR, | |
| socket_io_error_from_errno (errsv), | |
| _("Waiting for socket condition: %s"), | |
| 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_unlocked (socket); | |
| if (timeout_ms != WSA_INFINITE) | |
| { | |
| timeout_ms -= (g_get_monotonic_time () - start_time) * 1000; | |
| if (timeout_ms < 0) | |
| timeout_ms = 0; | |
| } | |
| } | |
| g_mutex_unlock (&socket->priv->win32_source_lock); | |
| remove_condition_watch (socket, &condition); | |
| if (num_events > 1) | |
| g_cancellable_release_fd (cancellable); | |
| return (condition & current_condition) != 0; | |
| } | |
| #else | |
| { | |
| GPollFD poll_fd[2]; | |
| gint result; | |
| gint num; | |
| poll_fd[0].fd = socket->priv->fd; | |
| poll_fd[0].events = condition; | |
| num = 1; | |
| if (g_cancellable_make_pollfd (cancellable, &poll_fd[1])) | |
| num++; | |
| while (TRUE) | |
| { | |
| int errsv; | |
| result = g_poll (poll_fd, num, timeout_ms); | |
| errsv = errno; | |
| if (result != -1 || errsv != EINTR) | |
| break; | |
| if (timeout_ms != -1) | |
| { | |
| timeout_ms -= (g_get_monotonic_time () - start_time) / 1000; | |
| if (timeout_ms < 0) | |
| timeout_ms = 0; | |
| } | |
| } | |
| if (num > 1) | |
| g_cancellable_release_fd (cancellable); | |
| if (result == 0) | |
| { | |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, | |
| _("Socket I/O timed out")); | |
| return FALSE; | |
| } | |
| return !g_cancellable_set_error_if_cancelled (cancellable, error); | |
| } | |
| #endif | |
| } | |
| #ifndef G_OS_WIN32 | |
| #ifdef HAVE_QNX | |
| /* QNX has this weird upper limit, or at least used to back in the 6.x days. | |
| * This was discovered empirically and doesn't appear to be mentioned in any | |
| * of the official documentation. */ | |
| # define G_SOCKET_CONTROL_BUFFER_SIZE_BYTES 2016 | |
| #else | |
| # define G_SOCKET_CONTROL_BUFFER_SIZE_BYTES 2048 | |
| #endif | |
| /* Unfortunately these have to be macros rather than inline functions due to | |
| * using alloca(). */ | |
| #define output_message_to_msghdr(message, prev_message, msg, prev_msg, error) \ | |
| G_STMT_START { \ | |
| const GOutputMessage *_message = (message); \ | |
| const GOutputMessage *_prev_message = (prev_message); \ | |
| struct msghdr *_msg = (msg); \ | |
| const struct msghdr *_prev_msg = (prev_msg); \ | |
| GError **_error = (error); \ | |
| \ | |
| _msg->msg_flags = 0; \ | |
| \ | |
| /* name */ \ | |
| if (_prev_message != NULL && _prev_message->address == _message->address) \ | |
| { \ | |
| _msg->msg_name = _prev_msg->msg_name; \ | |
| _msg->msg_namelen = _prev_msg->msg_namelen; \ | |
| } \ | |
| else if (_message->address != NULL) \ | |
| { \ | |
| _msg->msg_namelen = g_socket_address_get_native_size (_message->address); \ | |
| _msg->msg_name = g_alloca (_msg->msg_namelen); \ | |
| if (!g_socket_address_to_native (_message->address, _msg->msg_name, \ | |
| _msg->msg_namelen, _error)) \ | |
| break; \ | |
| } \ | |
| else \ | |
| { \ | |
| _msg->msg_name = NULL; \ | |
| _msg->msg_namelen = 0; \ | |
| } \ | |
| \ | |
| /* iov */ \ | |
| { \ | |
| /* this entire expression will be evaluated at compile time */ \ | |
| if (sizeof *_msg->msg_iov == sizeof *_message->vectors && \ | |
| sizeof _msg->msg_iov->iov_base == sizeof _message->vectors->buffer && \ | |
| G_STRUCT_OFFSET (struct iovec, iov_base) == \ | |
| G_STRUCT_OFFSET (GOutputVector, buffer) && \ | |
| sizeof _msg->msg_iov->iov_len == sizeof _message->vectors->size && \ | |
| G_STRUCT_OFFSET (struct iovec, iov_len) == \ | |
| G_STRUCT_OFFSET (GOutputVector, size)) \ | |
| /* ABI is compatible */ \ | |
| { \ | |
| _msg->msg_iov = (struct iovec *) _message->vectors; \ | |
| _msg->msg_iovlen = _message->num_vectors; \ | |
| } \ | |
| else \ | |
| /* ABI is incompatible */ \ | |
| { \ | |
| guint i; \ | |
| \ | |
| _msg->msg_iov = g_newa (struct iovec, _message->num_vectors); \ | |
| for (i = 0; i < _message->num_vectors; i++) \ | |
| { \ | |
| _msg->msg_iov[i].iov_base = (void *) _message->vectors[i].buffer; \ | |
| _msg->msg_iov[i].iov_len = _message->vectors[i].size; \ | |
| } \ | |
| _msg->msg_iovlen = _message->num_vectors; \ | |
| } \ | |
| } \ | |
| \ | |
| /* control */ \ | |
| { \ | |
| struct cmsghdr *cmsg; \ | |
| guint i; \ | |
| \ | |
| _msg->msg_controllen = 0; \ | |
| for (i = 0; i < _message->num_control_messages; i++) \ | |
| _msg->msg_controllen += CMSG_SPACE (g_socket_control_message_get_size (_message->control_messages[i])); \ | |
| \ | |
| if (_msg->msg_controllen == 0) \ | |
| _msg->msg_control = NULL; \ | |
| else \ | |
| { \ | |
| _msg->msg_control = g_alloca0 (_msg->msg_controllen); \ | |
| } \ | |
| \ | |
| cmsg = CMSG_FIRSTHDR (_msg); \ | |
| for (i = 0; i < _message->num_control_messages; i++) \ | |
| { \ | |
| cmsg->cmsg_level = g_socket_control_message_get_level (_message->control_messages[i]); \ | |
| cmsg->cmsg_type = g_socket_control_message_get_msg_type (_message->control_messages[i]); \ | |
| cmsg->cmsg_len = CMSG_LEN (g_socket_control_message_get_size (_message->control_messages[i])); \ | |
| g_socket_control_message_serialize (_message->control_messages[i], \ | |
| CMSG_DATA (cmsg)); \ | |
| cmsg = CMSG_NXTHDR (_msg, cmsg); \ | |
| } \ | |
| g_assert (cmsg == NULL); \ | |
| } \ | |
| } G_STMT_END | |
| #define input_message_to_msghdr(message, msg) \ | |
| G_STMT_START { \ | |
| const GInputMessage *_message = (message); \ | |
| struct msghdr *_msg = (msg); \ | |
| \ | |
| /* name */ \ | |
| if (_message->address) \ | |
| { \ | |
| _msg->msg_namelen = sizeof (struct sockaddr_storage); \ | |
| _msg->msg_name = g_alloca (_msg->msg_namelen); \ | |
| } \ | |
| else \ | |
| { \ | |
| _msg->msg_name = NULL; \ | |
| _msg->msg_namelen = 0; \ | |
| } \ | |
| \ | |
| /* iov */ \ | |
| /* this entire expression will be evaluated at compile time */ \ | |
| if (sizeof *_msg->msg_iov == sizeof *_message->vectors && \ | |
| sizeof _msg->msg_iov->iov_base == sizeof _message->vectors->buffer && \ | |
| G_STRUCT_OFFSET (struct iovec, iov_base) == \ | |
| G_STRUCT_OFFSET (GInputVector, buffer) && \ | |
| sizeof _msg->msg_iov->iov_len == sizeof _message->vectors->size && \ | |
| G_STRUCT_OFFSET (struct iovec, iov_len) == \ | |
| G_STRUCT_OFFSET (GInputVector, size)) \ | |
| /* ABI is compatible */ \ | |
| { \ | |
| _msg->msg_iov = (struct iovec *) _message->vectors; \ | |
| _msg->msg_iovlen = _message->num_vectors; \ | |
| } \ | |
| else \ | |
| /* ABI is incompatible */ \ | |
| { \ | |
| guint i; \ | |
| \ | |
| _msg->msg_iov = g_newa (struct iovec, _message->num_vectors); \ | |
| for (i = 0; i < _message->num_vectors; i++) \ | |
| { \ | |
| _msg->msg_iov[i].iov_base = _message->vectors[i].buffer; \ | |
| _msg->msg_iov[i].iov_len = _message->vectors[i].size; \ | |
| } \ | |
| _msg->msg_iovlen = _message->num_vectors; \ | |
| } \ | |
| \ | |
| /* control */ \ | |
| if (_message->control_messages == NULL) \ | |
| { \ | |
| _msg->msg_controllen = 0; \ | |
| _msg->msg_control = NULL; \ | |
| } \ | |
| else \ | |
| { \ | |
| _msg->msg_controllen = G_SOCKET_CONTROL_BUFFER_SIZE_BYTES; \ | |
| _msg->msg_control = g_alloca (_msg->msg_controllen); \ | |
| } \ | |
| \ | |
| /* flags */ \ | |
| _msg->msg_flags = _message->flags; \ | |
| } G_STMT_END | |
| static void | |
| input_message_from_msghdr (const struct msghdr *msg, | |
| GInputMessage *message, | |
| GSocket *socket) | |
| { | |
| /* decode address */ | |
| if (message->address != NULL) | |
| { | |
| *message->address = cache_recv_address (socket, msg->msg_name, | |
| msg->msg_namelen); | |
| } | |
| /* decode control messages */ | |
| { | |
| GPtrArray *my_messages = NULL; | |
| struct cmsghdr *cmsg; | |
| if (msg->msg_controllen >= (socklen_t) sizeof (struct cmsghdr)) | |
| { | |
| g_assert (message->control_messages != NULL); | |
| for (cmsg = CMSG_FIRSTHDR (msg); | |
| cmsg != NULL; | |
| cmsg = CMSG_NXTHDR ((struct msghdr *) msg, cmsg)) | |
| { | |
| GSocketControlMessage *control_message; | |
| control_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 (control_message == NULL) | |
| /* We've already spewed about the problem in the | |
| deserialization code, so just continue */ | |
| continue; | |
| if (my_messages == NULL) | |
| my_messages = g_ptr_array_new (); | |
| g_ptr_array_add (my_messages, control_message); | |
| } | |
| } | |
| if (message->num_control_messages) | |
| *message->num_control_messages = my_messages != NULL ? my_messages->len : 0; | |
| if (message->control_messages) | |
| { | |
| if (my_messages == NULL) | |
| { | |
| *message->control_messages = NULL; | |
| } | |
| else | |
| { | |
| g_ptr_array_add (my_messages, NULL); | |
| *message->control_messages = (GSocketControlMessage **) g_ptr_array_free (my_messages, FALSE); | |
| } | |
| } | |
| else | |
| { | |
| g_assert (my_messages == NULL); | |
| } | |
| } | |
| /* capture the flags */ | |
| message->flags = msg->msg_flags; | |
| } | |
| #endif | |
| /** | |
| * g_socket_send_message: | |
| * @socket: a #GSocket | |
| * @address: (nullable): 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: (array length=num_messages) (nullable): a pointer to an | |
| * array of #GSocketControlMessages, or %NULL. | |
| * @num_messages: number of elements in @messages, or -1. | |
| * @flags: an int containing #GSocketMsgFlags flags, which may additionally | |
| * contain [other platform specific flags](http://man7.org/linux/man-pages/man2/recv.2.html) | |
| * @cancellable: (nullable): a %GCancellable or %NULL | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * Send data to @address on @socket. For sending multiple messages see | |
| * g_socket_send_messages(); for easier use, see | |
| * g_socket_send() and g_socket_send_to(). | |
| * | |
| * If @address is %NULL then the message is sent to the default receiver | |
| * (set by g_socket_connect()). | |
| * | |
| * @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 | |
| * messages to be sent on the socket. | |
| * If @num_messages is -1 then @messages is treated as a %NULL-terminated | |
| * array. | |
| * | |
| * @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. | |
| * | |
| * 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 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.) | |
| * | |
| * The sum of the sizes of each #GOutputVector in vectors must not be | |
| * greater than %G_MAXSSIZE. If the message can be larger than this, | |
| * then it is mandatory to use the g_socket_send_message_with_timeout() | |
| * function. | |
| * | |
| * On error -1 is returned and @error is set accordingly. | |
| * | |
| * 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, | |
| GOutputVector *vectors, | |
| gint num_vectors, | |
| GSocketControlMessage **messages, | |
| gint num_messages, | |
| gint flags, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GPollableReturn res; | |
| gsize bytes_written = 0; | |
| gsize vectors_size = 0; | |
| if (num_vectors != -1) | |
| { | |
| for (gint i = 0; i < num_vectors; i++) | |
| { | |
| /* No wrap-around for vectors_size */ | |
| if (vectors_size > vectors_size + vectors[i].size) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, | |
| _("Unable to send message: %s"), | |
| _("Message vectors too large")); | |
| return -1; | |
| } | |
| vectors_size += vectors[i].size; | |
| } | |
| } | |
| else | |
| { | |
| for (gsize i = 0; vectors[i].buffer != NULL; i++) | |
| { | |
| /* No wrap-around for vectors_size */ | |
| if (vectors_size > vectors_size + vectors[i].size) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, | |
| _("Unable to send message: %s"), | |
| _("Message vectors too large")); | |
| return -1; | |
| } | |
| vectors_size += vectors[i].size; | |
| } | |
| } | |
| /* Check if vector's buffers are too big for gssize */ | |
| if (vectors_size > G_MAXSSIZE) | |
| { | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, | |
| _("Unable to send message: %s"), | |
| _("Message vectors too large")); | |
| return -1; | |
| } | |
| res = g_socket_send_message_with_timeout (socket, address, | |
| vectors, num_vectors, | |
| messages, num_messages, flags, | |
| socket->priv->blocking ? -1 : 0, | |
| &bytes_written, | |
| cancellable, error); | |
| g_assert (res != G_POLLABLE_RETURN_OK || bytes_written <= G_MAXSSIZE); | |
| if (res == G_POLLABLE_RETURN_WOULD_BLOCK) | |
| { | |
| #ifndef G_OS_WIN32 | |
| socket_set_error_lazy (error, EWOULDBLOCK, _("Error sending message: %s")); | |
| #else | |
| socket_set_error_lazy (error, WSAEWOULDBLOCK, _("Error sending message: %s")); | |
| #endif | |
| } | |
| return res == G_POLLABLE_RETURN_OK ? (gssize) bytes_written : -1; | |
| } | |
| /** | |
| * g_socket_send_message_with_timeout: | |
| * @socket: a #GSocket | |
| * @address: (nullable): 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: (array length=num_messages) (nullable): a pointer to an | |
| * array of #GSocketControlMessages, or %NULL. | |
| * @num_messages: number of elements in @messages, or -1. | |
| * @flags: an int containing #GSocketMsgFlags flags, which may additionally | |
| * contain [other platform specific flags](http://man7.org/linux/man-pages/man2/recv.2.html) | |
| * @timeout_us: the maximum time (in microseconds) to wait, or -1 | |
| * @bytes_written: (out) (optional): location to store the number of bytes that were written to the socket | |
| * @cancellable: (nullable): a %GCancellable or %NULL | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * This behaves exactly the same as g_socket_send_message(), except that | |
| * the choice of timeout behavior is determined by the @timeout_us argument | |
| * rather than by @socket's properties. | |
| * | |
| * On error %G_POLLABLE_RETURN_FAILED is returned and @error is set accordingly, or | |
| * if the socket is currently not writable %G_POLLABLE_RETURN_WOULD_BLOCK is | |
| * returned. @bytes_written will contain 0 in both cases. | |
| * | |
| * Returns: %G_POLLABLE_RETURN_OK if all data was successfully written, | |
| * %G_POLLABLE_RETURN_WOULD_BLOCK if the socket is currently not writable, or | |
| * %G_POLLABLE_RETURN_FAILED if an error happened and @error is set. | |
| * | |
| * Since: 2.60 | |
| */ | |
| GPollableReturn | |
| g_socket_send_message_with_timeout (GSocket *socket, | |
| GSocketAddress *address, | |
| const GOutputVector *vectors, | |
| gint num_vectors, | |
| GSocketControlMessage **messages, | |
| gint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| gsize *bytes_written, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GOutputVector one_vector; | |
| char zero; | |
| gint64 start_time; | |
| if (bytes_written) | |
| *bytes_written = 0; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), G_POLLABLE_RETURN_FAILED); | |
| g_return_val_if_fail (address == NULL || G_IS_SOCKET_ADDRESS (address), G_POLLABLE_RETURN_FAILED); | |
| g_return_val_if_fail (num_vectors == 0 || vectors != NULL, G_POLLABLE_RETURN_FAILED); | |
| g_return_val_if_fail (num_messages == 0 || messages != NULL, G_POLLABLE_RETURN_FAILED); | |
| g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_POLLABLE_RETURN_FAILED); | |
| g_return_val_if_fail (error == NULL || *error == NULL, G_POLLABLE_RETURN_FAILED); | |
| start_time = g_get_monotonic_time (); | |
| if (!check_socket (socket, error)) | |
| return G_POLLABLE_RETURN_FAILED; | |
| if (!check_timeout (socket, error)) | |
| return G_POLLABLE_RETURN_FAILED; | |
| if (g_cancellable_set_error_if_cancelled (cancellable, error)) | |
| return G_POLLABLE_RETURN_FAILED; | |
| if (num_vectors == -1) | |
| { | |
| for (num_vectors = 0; | |
| vectors[num_vectors].buffer != NULL; | |
| num_vectors++) | |
| ; | |
| } | |
| if (num_messages == -1) | |
| { | |
| for (num_messages = 0; | |
| messages != NULL && messages[num_messages] != NULL; | |
| num_messages++) | |
| ; | |
| } | |
| if (num_vectors == 0) | |
| { | |
| zero = '\0'; | |
| one_vector.buffer = &zero; | |
| one_vector.size = 1; | |
| num_vectors = 1; | |
| vectors = &one_vector; | |
| } | |
| #ifndef G_OS_WIN32 | |
| { | |
| GOutputMessage output_message; | |
| struct msghdr msg; | |
| gssize result; | |
| GError *child_error = NULL; | |
| output_message.address = address; | |
| output_message.vectors = (GOutputVector *) vectors; | |
| output_message.num_vectors = num_vectors; | |
| output_message.bytes_sent = 0; | |
| output_message.control_messages = messages; | |
| output_message.num_control_messages = num_messages; | |
| output_message_to_msghdr (&output_message, NULL, &msg, NULL, &child_error); | |
| if (child_error != NULL) | |
| { | |
| g_propagate_error (error, child_error); | |
| return G_POLLABLE_RETURN_FAILED; | |
| } | |
| while (1) | |
| { | |
| result = sendmsg (socket->priv->fd, &msg, flags | G_SOCKET_DEFAULT_SEND_FLAGS); | |
| if (result < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| if (errsv == EWOULDBLOCK || errsv == EAGAIN) | |
| { | |
| if (timeout_us != 0) | |
| { | |
| if (!block_on_timeout (socket, G_IO_OUT, timeout_us, start_time, | |
| cancellable, error)) | |
| return G_POLLABLE_RETURN_FAILED; | |
| continue; | |
| } | |
| return G_POLLABLE_RETURN_WOULD_BLOCK; | |
| } | |
| socket_set_error_lazy (error, errsv, _("Error sending message: %s")); | |
| return G_POLLABLE_RETURN_FAILED; | |
| } | |
| break; | |
| } | |
| if (bytes_written) | |
| *bytes_written = result; | |
| return G_POLLABLE_RETURN_OK; | |
| } | |
| #else | |
| { | |
| struct sockaddr_storage addr; | |
| guint addrlen; | |
| DWORD bytes_sent; | |
| int result; | |
| WSABUF *bufs; | |
| gint i; | |
| /* Win32 doesn't support control messages. | |
| Actually this is possible for raw and datagram sockets | |
| via WSASendMessage on Vista or later, but that doesn't | |
| seem very useful */ | |
| if (num_messages != 0) | |
| { | |
| g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, | |
| _("GSocketControlMessage not supported on Windows")); | |
| return G_POLLABLE_RETURN_FAILED; | |
| } | |
| /* iov */ | |
| bufs = g_newa (WSABUF, num_vectors); | |
| for (i = 0; i < num_vectors; i++) | |
| { | |
| bufs[i].buf = (char *)vectors[i].buffer; | |
| bufs[i].len = (gulong)vectors[i].size; | |
| } | |
| /* name */ | |
| addrlen = 0; /* Avoid warning */ | |
| if (address) | |
| { | |
| addrlen = g_socket_address_get_native_size (address); | |
| if (!g_socket_address_to_native (address, &addr, sizeof addr, error)) | |
| return G_POLLABLE_RETURN_FAILED; | |
| } | |
| while (1) | |
| { | |
| if (address) | |
| result = WSASendTo (socket->priv->fd, | |
| bufs, num_vectors, | |
| &bytes_sent, flags, | |
| (const struct sockaddr *)&addr, addrlen, | |
| NULL, NULL); | |
| else | |
| result = WSASend (socket->priv->fd, | |
| bufs, num_vectors, | |
| &bytes_sent, flags, | |
| NULL, NULL); | |
| if (result != 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == WSAEINTR) | |
| continue; | |
| if (errsv == WSAEWOULDBLOCK) | |
| { | |
| win32_unset_event_mask (socket, FD_WRITE); | |
| if (timeout_us != 0) | |
| { | |
| if (!block_on_timeout (socket, G_IO_OUT, timeout_us, | |
| start_time, cancellable, error)) | |
| return G_POLLABLE_RETURN_FAILED; | |
| continue; | |
| } | |
| return G_POLLABLE_RETURN_WOULD_BLOCK; | |
| } | |
| socket_set_error_lazy (error, errsv, _("Error sending message: %s")); | |
| return G_POLLABLE_RETURN_FAILED; | |
| } | |
| break; | |
| } | |
| if (bytes_written) | |
| *bytes_written = bytes_sent; | |
| return G_POLLABLE_RETURN_OK; | |
| } | |
| #endif | |
| } | |
| /** | |
| * g_socket_send_messages: | |
| * @socket: a #GSocket | |
| * @messages: (array length=num_messages): an array of #GOutputMessage structs | |
| * @num_messages: the number of elements in @messages | |
| * @flags: an int containing #GSocketMsgFlags flags, which may additionally | |
| * contain [other platform specific flags](http://man7.org/linux/man-pages/man2/recv.2.html) | |
| * @cancellable: (nullable): a %GCancellable or %NULL | |
| * @error: #GError for error reporting, or %NULL to ignore. | |
| * | |
| * Send multiple data messages from @socket in one go. This is the most | |
| * complicated and fully-featured version of this call. For easier use, see | |
| * g_socket_send(), g_socket_send_to(), and g_socket_send_message(). | |
| * | |
| * @messages must point to an array of #GOutputMessage structs and | |
| * @num_messages must be the length of this array. Each #GOutputMessage | |
| * contains an address to send the data to, and a pointer to an array of | |
| * #GOutputVector structs to describe the buffers that the data to be sent | |
| * for each message 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(). Sending multiple messages in one go avoids the | |
| * overhead of making a lot of syscalls in scenarios where a lot of data | |
| * packets need to be sent (e.g. high-bandwidth video streaming over RTP/UDP), | |
| * or where the same data needs to be sent to multiple recipients. | |
| * | |
| * @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. | |
| * | |
| * If the socket is in blocking mode the call will block until there is | |
| * space for all 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 if no data was written at all, otherwise the number of | |
| * messages sent 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. An error will only | |
| * be returned if zero messages could be sent; otherwise the number of messages | |
| * successfully sent before the error will be returned. | |
| * | |
| * Returns: number of messages sent, or -1 on error. Note that the number of | |
| * messages sent may be smaller than @num_messages if the socket is | |
| * non-blocking or if @num_messages was larger than UIO_MAXIOV (1024), | |
| * in which case the caller may re-try to send the remaining messages. | |
| * | |
| * Since: 2.44 | |
| */ | |
| gint | |
| g_socket_send_messages (GSocket *socket, | |
| GOutputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| return g_socket_send_messages_with_timeout (socket, messages, num_messages, | |
| flags, | |
| socket->priv->blocking ? -1 : 0, | |
| cancellable, error); | |
| } | |
| static gint | |
| g_socket_send_messages_with_timeout (GSocket *socket, | |
| GOutputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| gint64 start_time; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), -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); | |
| start_time = g_get_monotonic_time (); | |
| 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_messages == 0) | |
| return 0; | |
| #if !defined (G_OS_WIN32) && defined (HAVE_SENDMMSG) | |
| { | |
| struct mmsghdr *msgvec; | |
| guint i, num_sent; | |
| /* Clamp the number of vectors if more given than we can write in one go. | |
| * The caller has to handle short writes anyway. | |
| */ | |
| if (num_messages > G_IOV_MAX) | |
| num_messages = G_IOV_MAX; | |
| msgvec = g_newa (struct mmsghdr, num_messages); | |
| for (i = 0; i < num_messages; ++i) | |
| { | |
| GOutputMessage *msg = &messages[i]; | |
| struct msghdr *msg_hdr = &msgvec[i].msg_hdr; | |
| GError *child_error = NULL; | |
| msgvec[i].msg_len = 0; | |
| output_message_to_msghdr (msg, (i > 0) ? &messages[i - 1] : NULL, | |
| msg_hdr, (i > 0) ? &msgvec[i - 1].msg_hdr : NULL, | |
| &child_error); | |
| if (child_error != NULL) | |
| { | |
| g_propagate_error (error, child_error); | |
| return -1; | |
| } | |
| } | |
| for (num_sent = 0; num_sent < num_messages;) | |
| { | |
| gint ret; | |
| ret = sendmmsg (socket->priv->fd, msgvec + num_sent, num_messages - num_sent, | |
| flags | G_SOCKET_DEFAULT_SEND_FLAGS); | |
| if (ret < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| if (timeout_us != 0 && | |
| (errsv == EWOULDBLOCK || | |
| errsv == EAGAIN)) | |
| { | |
| if (!block_on_timeout (socket, G_IO_OUT, timeout_us, start_time, | |
| cancellable, error)) | |
| { | |
| if (num_sent > 0) | |
| { | |
| g_clear_error (error); | |
| break; | |
| } | |
| return -1; | |
| } | |
| continue; | |
| } | |
| /* If any messages were successfully sent, do not error. */ | |
| if (num_sent > 0) | |
| break; | |
| socket_set_error_lazy (error, errsv, _("Error sending message: %s")); | |
| return -1; | |
| } | |
| num_sent += ret; | |
| } | |
| for (i = 0; i < num_sent; ++i) | |
| messages[i].bytes_sent = msgvec[i].msg_len; | |
| return num_sent; | |
| } | |
| #else | |
| { | |
| gssize result; | |
| guint i; | |
| gint64 wait_timeout; | |
| wait_timeout = timeout_us; | |
| for (i = 0; i < num_messages; ++i) | |
| { | |
| GOutputMessage *msg = &messages[i]; | |
| GError *msg_error = NULL; | |
| GPollableReturn pollable_result; | |
| gsize bytes_written = 0; | |
| pollable_result = g_socket_send_message_with_timeout (socket, msg->address, | |
| msg->vectors, | |
| msg->num_vectors, | |
| msg->control_messages, | |
| msg->num_control_messages, | |
| flags, wait_timeout, | |
| &bytes_written, | |
| cancellable, &msg_error); | |
| if (pollable_result == G_POLLABLE_RETURN_WOULD_BLOCK) | |
| { | |
| #ifndef G_OS_WIN32 | |
| socket_set_error_lazy (&msg_error, EWOULDBLOCK, _("Error sending message: %s")); | |
| #else | |
| socket_set_error_lazy (&msg_error, WSAEWOULDBLOCK, _("Error sending message: %s")); | |
| #endif | |
| } | |
| if (G_MAXSSIZE > bytes_written && | |
| pollable_result == G_POLLABLE_RETURN_OK) | |
| result = (gssize) bytes_written; | |
| else | |
| result = -1; | |
| /* check if we've timed out or how much time to wait at most */ | |
| if (timeout_us > 0) | |
| { | |
| gint64 elapsed = g_get_monotonic_time () - start_time; | |
| wait_timeout = MAX (timeout_us - elapsed, 1); | |
| } | |
| if (result < 0) | |
| { | |
| /* if we couldn't send all messages, just return how many we did | |
| * manage to send, provided we managed to send at least one */ | |
| if (i > 0) | |
| { | |
| g_error_free (msg_error); | |
| return i; | |
| } | |
| else | |
| { | |
| g_propagate_error (error, msg_error); | |
| return -1; | |
| } | |
| } | |
| msg->bytes_sent = result; | |
| } | |
| return i; | |
| } | |
| #endif | |
| } | |
| static GSocketAddress * | |
| cache_recv_address (GSocket *socket, struct sockaddr *native, size_t 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; | |
| gsize 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_memdup2 (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; | |
| } | |
| static gssize | |
| g_socket_receive_message_with_timeout (GSocket *socket, | |
| GSocketAddress **address, | |
| GInputVector *vectors, | |
| gint num_vectors, | |
| GSocketControlMessage ***messages, | |
| gint *num_messages, | |
| gint *flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GInputVector one_vector; | |
| char one_byte; | |
| gint64 start_time; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), -1); | |
| start_time = g_get_monotonic_time (); | |
| 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; | |
| vectors[num_vectors].buffer != NULL; | |
| num_vectors++) | |
| ; | |
| } | |
| if (num_vectors == 0) | |
| { | |
| one_vector.buffer = &one_byte; | |
| one_vector.size = 1; | |
| num_vectors = 1; | |
| vectors = &one_vector; | |
| } | |
| #ifndef G_OS_WIN32 | |
| { | |
| GInputMessage input_message; | |
| struct msghdr msg; | |
| gssize result; | |
| input_message.address = address; | |
| input_message.vectors = vectors; | |
| input_message.num_vectors = num_vectors; | |
| input_message.bytes_received = 0; | |
| input_message.flags = (flags != NULL) ? *flags : 0; | |
| input_message.control_messages = messages; | |
| input_message.num_control_messages = (guint *) num_messages; | |
| /* 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 | |
| input_message.flags |= MSG_CMSG_CLOEXEC; | |
| #endif | |
| input_message_to_msghdr (&input_message, &msg); | |
| /* do it */ | |
| while (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) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| if (timeout_us != 0 && | |
| (errsv == EWOULDBLOCK || | |
| errsv == EAGAIN)) | |
| { | |
| if (!block_on_timeout (socket, G_IO_IN, timeout_us, start_time, | |
| cancellable, error)) | |
| return -1; | |
| continue; | |
| } | |
| socket_set_error_lazy (error, errsv, _("Error receiving message: %s")); | |
| return -1; | |
| } | |
| break; | |
| } | |
| input_message_from_msghdr (&msg, &input_message, socket); | |
| if (flags != NULL) | |
| *flags = input_message.flags; | |
| return result; | |
| } | |
| #else | |
| { | |
| struct sockaddr_storage addr; | |
| int addrlen; | |
| DWORD bytes_received; | |
| DWORD win_flags; | |
| int result; | |
| WSABUF *bufs; | |
| gint i; | |
| /* iov */ | |
| bufs = g_newa (WSABUF, num_vectors); | |
| for (i = 0; i < num_vectors; i++) | |
| { | |
| bufs[i].buf = (char *)vectors[i].buffer; | |
| bufs[i].len = (gulong)vectors[i].size; | |
| } | |
| /* flags */ | |
| if (flags != NULL) | |
| win_flags = *flags; | |
| else | |
| win_flags = 0; | |
| /* do it */ | |
| while (1) | |
| { | |
| /* addrlen has to be of type int because that’s how WSARecvFrom() is defined */ | |
| G_STATIC_ASSERT (sizeof addr <= G_MAXINT); | |
| addrlen = sizeof addr; | |
| if (address) | |
| result = WSARecvFrom (socket->priv->fd, | |
| bufs, num_vectors, | |
| &bytes_received, &win_flags, | |
| (struct sockaddr *)&addr, &addrlen, | |
| NULL, NULL); | |
| else | |
| result = WSARecv (socket->priv->fd, | |
| bufs, num_vectors, | |
| &bytes_received, &win_flags, | |
| NULL, NULL); | |
| if (result != 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == WSAEINTR) | |
| continue; | |
| win32_unset_event_mask (socket, FD_READ); | |
| if (errsv == WSAEWOULDBLOCK) | |
| { | |
| if (timeout_us != 0) | |
| { | |
| if (!block_on_timeout (socket, G_IO_IN, timeout_us, | |
| start_time, cancellable, error)) | |
| return -1; | |
| continue; | |
| } | |
| } | |
| socket_set_error_lazy (error, errsv, _("Error receiving message: %s")); | |
| return -1; | |
| } | |
| win32_unset_event_mask (socket, FD_READ); | |
| break; | |
| } | |
| /* decode address */ | |
| if (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 | |
| } | |
| /** | |
| * g_socket_receive_messages: | |
| * @socket: a #GSocket | |
| * @messages: (array length=num_messages): an array of #GInputMessage structs | |
| * @num_messages: the number of elements in @messages | |
| * @flags: an int containing #GSocketMsgFlags flags for the overall operation, | |
| * which may additionally contain | |
| * [other platform specific flags](http://man7.org/linux/man-pages/man2/recv.2.html) | |
| * @cancellable: (nullable): a %GCancellable or %NULL | |
| * @error: #GError for error reporting, or %NULL to ignore | |
| * | |
| * Receive multiple data messages from @socket in one go. This is the most | |
| * complicated and fully-featured version of this call. For easier use, see | |
| * g_socket_receive(), g_socket_receive_from(), and g_socket_receive_message(). | |
| * | |
| * @messages must point to an array of #GInputMessage structs and | |
| * @num_messages must be the length of this array. Each #GInputMessage | |
| * contains a pointer to an array of #GInputVector structs describing the | |
| * buffers that the data received in each message will be written to. Using | |
| * multiple #GInputVectors is more memory-efficient than manually copying data | |
| * out of a single buffer to multiple sources, and more system-call-efficient | |
| * than making multiple calls to g_socket_receive(), such as in scenarios where | |
| * a lot of data packets need to be received (e.g. high-bandwidth video | |
| * streaming over RTP/UDP). | |
| * | |
| * @flags modify how all messages are received. 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. These | |
| * flags affect the overall receive operation. Flags affecting individual | |
| * messages are returned in #GInputMessage.flags. | |
| * | |
| * The other members of #GInputMessage are treated as described in its | |
| * documentation. | |
| * | |
| * If #GSocket:blocking is %TRUE the call will block until @num_messages have | |
| * been received, or the end of the stream is reached. | |
| * | |
| * If #GSocket:blocking is %FALSE the call will return up to @num_messages | |
| * without blocking, or %G_IO_ERROR_WOULD_BLOCK if no messages are queued in the | |
| * operating system to be received. | |
| * | |
| * In blocking mode, if #GSocket:timeout is positive and is reached before any | |
| * messages are received, %G_IO_ERROR_TIMED_OUT is returned, otherwise up to | |
| * @num_messages are returned. (Note: This is effectively the | |
| * behaviour of `MSG_WAITFORONE` with recvmmsg().) | |
| * | |
| * To be notified when messages are available, wait for the | |
| * %G_IO_IN condition. Note though that you may still receive | |
| * %G_IO_ERROR_WOULD_BLOCK from g_socket_receive_messages() even if you were | |
| * previously notified of a %G_IO_IN condition. | |
| * | |
| * If the remote peer closes the connection, any messages queued in the | |
| * operating system will be returned, and subsequent calls to | |
| * g_socket_receive_messages() will return 0 (with no error set). | |
| * | |
| * On error -1 is returned and @error is set accordingly. An error will only | |
| * be returned if zero messages could be received; otherwise the number of | |
| * messages successfully received before the error will be returned. | |
| * | |
| * Returns: number of messages received, or -1 on error. Note that the number | |
| * of messages received may be smaller than @num_messages if in non-blocking | |
| * mode, if the peer closed the connection, or if @num_messages | |
| * was larger than `UIO_MAXIOV` (1024), in which case the caller may re-try | |
| * to receive the remaining messages. | |
| * | |
| * Since: 2.48 | |
| */ | |
| gint | |
| g_socket_receive_messages (GSocket *socket, | |
| GInputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| if (!check_socket (socket, error) || | |
| !check_timeout (socket, error)) | |
| return -1; | |
| return g_socket_receive_messages_with_timeout (socket, messages, num_messages, | |
| flags, | |
| socket->priv->blocking ? -1 : 0, | |
| cancellable, error); | |
| } | |
| static gint | |
| g_socket_receive_messages_with_timeout (GSocket *socket, | |
| GInputMessage *messages, | |
| guint num_messages, | |
| gint flags, | |
| gint64 timeout_us, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| gint64 start_time; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), -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); | |
| start_time = g_get_monotonic_time (); | |
| 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_messages == 0) | |
| return 0; | |
| #if !defined (G_OS_WIN32) && defined (HAVE_RECVMMSG) | |
| { | |
| struct mmsghdr *msgvec; | |
| guint i, num_received; | |
| /* Clamp the number of vectors if more given than we can write in one go. | |
| * The caller has to handle short writes anyway. | |
| */ | |
| if (num_messages > G_IOV_MAX) | |
| num_messages = G_IOV_MAX; | |
| msgvec = g_newa (struct mmsghdr, num_messages); | |
| for (i = 0; i < num_messages; ++i) | |
| { | |
| GInputMessage *msg = &messages[i]; | |
| struct msghdr *msg_hdr = &msgvec[i].msg_hdr; | |
| input_message_to_msghdr (msg, msg_hdr); | |
| msgvec[i].msg_len = 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 | |
| flags |= MSG_CMSG_CLOEXEC; | |
| #endif | |
| for (num_received = 0; num_received < num_messages;) | |
| { | |
| gint ret; | |
| /* We operate in non-blocking mode and handle the timeout ourselves. */ | |
| ret = recvmmsg (socket->priv->fd, | |
| msgvec + num_received, | |
| num_messages - num_received, | |
| flags | G_SOCKET_DEFAULT_SEND_FLAGS, NULL); | |
| #ifdef MSG_CMSG_CLOEXEC | |
| if (ret < 0 && get_socket_errno () == EINVAL) | |
| { | |
| /* We must be running on an old kernel. Call without the flag. */ | |
| flags &= ~(MSG_CMSG_CLOEXEC); | |
| ret = recvmmsg (socket->priv->fd, | |
| msgvec + num_received, | |
| num_messages - num_received, | |
| flags | G_SOCKET_DEFAULT_SEND_FLAGS, NULL); | |
| } | |
| #endif | |
| if (ret < 0) | |
| { | |
| int errsv = get_socket_errno (); | |
| if (errsv == EINTR) | |
| continue; | |
| if (timeout_us != 0 && | |
| (errsv == EWOULDBLOCK || | |
| errsv == EAGAIN)) | |
| { | |
| if (!block_on_timeout (socket, G_IO_IN, timeout_us, start_time, | |
| cancellable, error)) | |
| { | |
| if (num_received > 0) | |
| { | |
| g_clear_error (error); | |
| break; | |
| } | |
| return -1; | |
| } | |
| continue; | |
| } | |
| /* If any messages were successfully received, do not error. */ | |
| if (num_received > 0) | |
| break; | |
| socket_set_error_lazy (error, errsv, | |
| _("Error receiving message: %s")); | |
| return -1; | |
| } | |
| else if (ret == 0) | |
| { | |
| /* EOS. */ | |
| break; | |
| } | |
| num_received += ret; | |
| } | |
| for (i = 0; i < num_received; ++i) | |
| { | |
| input_message_from_msghdr (&msgvec[i].msg_hdr, &messages[i], socket); | |
| messages[i].bytes_received = msgvec[i].msg_len; | |
| } | |
| return num_received; | |
| } | |
| #else | |
| { | |
| guint i; | |
| gint64 wait_timeout; | |
| wait_timeout = timeout_us; | |
| for (i = 0; i < num_messages; i++) | |
| { | |
| GInputMessage *msg = &messages[i]; | |
| gssize len; | |
| GError *msg_error = NULL; | |
| msg->flags = flags; /* in-out parameter */ | |
| len = g_socket_receive_message_with_timeout (socket, | |
| msg->address, | |
| msg->vectors, | |
| msg->num_vectors, | |
| msg->control_messages, | |
| (gint *) msg->num_control_messages, | |
| &msg->flags, | |
| wait_timeout, | |
| cancellable, | |
| &msg_error); | |
| /* check if we've timed out or how much time to wait at most */ | |
| if (timeout_us > 0) | |
| { | |
| gint64 elapsed = g_get_monotonic_time () - start_time; | |
| wait_timeout = MAX (timeout_us - elapsed, 1); | |
| } | |
| if (len >= 0) | |
| msg->bytes_received = len; | |
| if (i != 0 && | |
| (g_error_matches (msg_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) || | |
| g_error_matches (msg_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))) | |
| { | |
| g_clear_error (&msg_error); | |
| break; | |
| } | |
| if (msg_error != NULL) | |
| { | |
| g_propagate_error (error, msg_error); | |
| return -1; | |
| } | |
| if (len == 0) | |
| break; | |
| } | |
| return i; | |
| } | |
| #endif | |
| } | |
| /** | |
| * g_socket_receive_message: | |
| * @socket: a #GSocket | |
| * @address: (out) (optional): 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: (array length=num_messages) (out) (optional) (nullable): a pointer | |
| * which may be filled with an array of #GSocketControlMessages, or %NULL | |
| * @num_messages: (out): a pointer which will be filled with the number of | |
| * elements in @messages, or %NULL | |
| * @flags: (inout): a pointer to an int containing #GSocketMsgFlags flags, | |
| * which may additionally contain | |
| * [other platform specific flags](http://man7.org/linux/man-pages/man2/recv.2.html) | |
| * @cancellable: a %GCancellable or %NULL | |
| * @error: a #GError pointer, or %NULL | |
| * | |
| * Receive data from a socket. For receiving multiple messages, see | |
| * g_socket_receive_messages(); for easier use, see | |
| * g_socket_receive() and g_socket_receive_from(). | |
| * | |
| * If @address is non-%NULL then @address will be set equal to the | |
| * source address of the received packet. | |
| * @address is owned by the caller. | |
| * | |
| * @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_vectors is -1, then @vectors is assumed to be terminated | |
| * by a #GInputVector with a %NULL buffer pointer. | |
| * | |
| * 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, 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. | |
| * | |
| * If both @messages and @num_messages are non-%NULL, then | |
| * @num_messages gives the number of #GSocketControlMessage instances | |
| * in @messages (ie: not including the %NULL terminator). | |
| * | |
| * @flags is an in/out parameter. 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 | |
| * (and g_socket_receive_message() may pass system-specific flags out). | |
| * Flags passed in to the parameter affect the receive operation; flags returned | |
| * out of it are relevant to the specific returned message. | |
| * | |
| * 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 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, | |
| GInputVector *vectors, | |
| gint num_vectors, | |
| GSocketControlMessage ***messages, | |
| gint *num_messages, | |
| gint *flags, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| return g_socket_receive_message_with_timeout (socket, address, vectors, | |
| num_vectors, messages, | |
| num_messages, flags, | |
| socket->priv->blocking ? -1 : 0, | |
| cancellable, error); | |
| } | |
| /** | |
| * 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. | |
| * | |
| * This method can be expected to be available on the following platforms: | |
| * | |
| * - Linux since GLib 2.26 | |
| * - OpenBSD since GLib 2.30 | |
| * - Solaris, Illumos and OpenSolaris since GLib 2.40 | |
| * - NetBSD since GLib 2.42 | |
| * - macOS, tvOS, iOS since GLib 2.66 | |
| * | |
| * 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); | |
| if (!check_socket (socket, error)) | |
| return 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_APPLE_XUCRED | |
| { | |
| struct xucred cred; | |
| socklen_t optlen = sizeof (cred); | |
| if (getsockopt (socket->priv->fd, | |
| SOL_LOCAL, | |
| LOCAL_PEERCRED, | |
| &cred, | |
| &optlen) == 0 | |
| && optlen != 0) | |
| { | |
| if (cred.cr_version == XUCRED_VERSION) | |
| { | |
| pid_t pid; | |
| socklen_t optlen = sizeof (pid); | |
| ret = g_credentials_new (); | |
| g_credentials_set_native (ret, | |
| G_CREDENTIALS_NATIVE_TYPE, | |
| &cred); | |
| #ifdef LOCAL_PEERPID | |
| if (getsockopt (socket->priv->fd, | |
| SOL_LOCAL, | |
| LOCAL_PEERPID, | |
| &pid, | |
| &optlen) == 0) | |
| _g_credentials_set_local_peerid (ret, pid); | |
| #endif | |
| } | |
| else | |
| { | |
| g_set_error (error, | |
| G_IO_ERROR, | |
| G_IO_ERROR_NOT_SUPPORTED, | |
| /* No point in translating this! */ | |
| "struct xucred cr_version %u != %u", | |
| cred.cr_version, XUCRED_VERSION); | |
| /* Reuse a translatable string we already have */ | |
| g_prefix_error (error, | |
| _("Unable to read socket credentials: %s"), | |
| ""); | |
| return NULL; | |
| } | |
| } | |
| else if (optlen == 0 || errno == EINVAL) | |
| { | |
| g_set_error (error, | |
| G_IO_ERROR, | |
| G_IO_ERROR_NOT_SUPPORTED, | |
| _("Unable to read socket credentials: %s"), | |
| "unsupported socket type"); | |
| return NULL; | |
| } | |
| } | |
| #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); | |
| } | |
| } | |
| #elif G_CREDENTIALS_USE_WIN32_PID | |
| { | |
| DWORD peerid, drc; | |
| if (WSAIoctl (socket->priv->fd, SIO_AF_UNIX_GETPEERPID, | |
| NULL, 0U, | |
| &peerid, sizeof(peerid), | |
| /* Windows bug: always 0 https://github.com/microsoft/WSL/issues/4676 */ | |
| &drc, | |
| NULL, NULL) == 0) | |
| { | |
| ret = g_credentials_new (); | |
| g_credentials_set_native (ret, | |
| G_CREDENTIALS_TYPE_WIN32_PID, | |
| &peerid); | |
| } | |
| } | |
| #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>`](networking.html) | |
| * 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) | |
| { | |
| socklen_t size; | |
| g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); | |
| /* g_socket_get_option() is called during socket init, so skip the init checks | |
| * in check_socket() */ | |
| if (socket->priv->inited && !check_socket (socket, error)) | |
| return 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>`](networking.html) | |
| * 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); | |
| /* g_socket_set_option() is called during socket init, so skip the init checks | |
| * in check_socket() */ | |
| if (socket->priv->inited && !check_socket (socket, error)) | |
| return 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; | |
| } |