X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgsocket.c;h=4d863cecdf56e8bb9b2be7ef081136746aefe909;hb=2a53b4d0e2c98a14aedf31e38f0ad1fb2e8fe26f;hp=01792a7feafd1da36901b0c889724bc345753a1c;hpb=d8ca6404229e5b64d2bf2e1a3660ad9fe7feefdd;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gsocket.c b/gio/gsocket.c index 01792a7..4d863ce 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -15,9 +15,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . * * Authors: Christian Kellner * Samuel Cormier-Iijima @@ -29,6 +27,10 @@ #include "gsocket.h" +#ifdef G_OS_UNIX +#include "glib-unix.h" +#endif + #include #include #include @@ -37,9 +39,14 @@ #ifndef G_OS_WIN32 # include # include +# include +#endif + +#ifdef HAVE_SYS_FILIO_H +# include #endif -#ifdef HAVE_SYS_UIO_H +#ifdef G_OS_UNIX #include #endif @@ -50,18 +57,18 @@ #include "gioerror.h" #include "gioenums.h" #include "gioerror.h" -#include "gio-marshal.h" #include "gnetworkingprivate.h" #include "gsocketaddress.h" #include "gsocketcontrolmessage.h" #include "gcredentials.h" +#include "gcredentialsprivate.h" #include "glibintl.h" /** * SECTION:gsocket * @short_description: Low-level socket object * @include: gio/gio.h - * @see_also: #GInitable + * @see_also: #GInitable, [][gio-gnetworking.h] * * A #GSocket is a low-level networking primitive. It is a more or less * direct mapping of the BSD socket API in a portable GObject based API. @@ -96,7 +103,7 @@ * reasons. For instance, on Windows a socket is always seen as writable * until a write returns %G_IO_ERROR_WOULD_BLOCK. * - * #GSockets can be either connection oriented or datagram based. + * #GSockets can be either connection oriented or datagram based. * For connection oriented types you must first establish a connection by * either connecting to an address or accepting a connection from another * address. For connectionless socket types the target/source address is @@ -110,6 +117,10 @@ * account the fact that your program will not automatically be killed * if it tries to write to %stdout after it has been closed. * + * Like most other APIs in GLib, #GSocket is not inherently thread safe. To use + * a #GSocket concurrently from multiple threads, you must implement your own + * locking. + * * Since: 2.22 */ @@ -118,10 +129,6 @@ static gboolean g_socket_initable_init (GInitable *initable, GCancellable *cancellable, GError **error); -G_DEFINE_TYPE_WITH_CODE (GSocket, g_socket, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - g_socket_initable_iface_init)); - enum { PROP_0, @@ -134,9 +141,16 @@ enum PROP_KEEPALIVE, PROP_LOCAL_ADDRESS, PROP_REMOTE_ADDRESS, - PROP_TIMEOUT + 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; @@ -161,9 +175,23 @@ struct _GSocketPrivate int current_errors; int selected_events; GList *requested_conditions; /* list of requested GIOCondition * */ + GMutex win32_source_lock; #endif + + struct { + GSocketAddress *addr; + struct sockaddr *native; + gint native_len; + guint64 last_used; + } recv_addr_cache[RECV_ADDR_CACHE_SIZE]; }; +G_DEFINE_TYPE_WITH_CODE (GSocket, g_socket, G_TYPE_OBJECT, + G_ADD_PRIVATE (GSocket) + g_networking_init (); + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_socket_initable_iface_init)); + static int get_socket_errno (void) { @@ -177,34 +205,10 @@ get_socket_errno (void) static GIOErrorEnum socket_io_error_from_errno (int err) { -#ifndef G_OS_WIN32 - return g_io_error_from_errno (err); +#ifdef G_OS_WIN32 + return g_io_error_from_win32_error (err); #else - switch (err) - { - case WSAEADDRINUSE: - return G_IO_ERROR_ADDRESS_IN_USE; - case WSAEWOULDBLOCK: - return G_IO_ERROR_WOULD_BLOCK; - case WSAEACCES: - return G_IO_ERROR_PERMISSION_DENIED; - case WSA_INVALID_HANDLE: - case WSA_INVALID_PARAMETER: - case WSAEBADF: - case WSAENOTSOCK: - return G_IO_ERROR_INVALID_ARGUMENT; - case WSAEPROTONOSUPPORT: - return G_IO_ERROR_NOT_SUPPORTED; - case WSAECANCELLED: - return G_IO_ERROR_CANCELLED; - case WSAESOCKTNOSUPPORT: - case WSAEOPNOTSUPP: - case WSAEPFNOSUPPORT: - case WSAEAFNOSUPPORT: - return G_IO_ERROR_NOT_SUPPORTED; - default: - return G_IO_ERROR_FAILED; - } + return g_io_error_from_errno (err); #endif } @@ -214,13 +218,15 @@ socket_strerror (int err) #ifndef G_OS_WIN32 return g_strerror (err); #else - static GStaticPrivate last_msg = G_STATIC_PRIVATE_INIT; + const char *msg_ret; char *msg; msg = g_win32_error_message (err); - g_static_private_set (&last_msg, msg, g_free); - return msg; + msg_ret = g_intern_string (msg); + g_free (msg); + + return msg_ret; #endif } @@ -236,36 +242,19 @@ _win32_unset_event_mask (GSocket *socket, int mask) #define win32_unset_event_mask(_socket, _mask) #endif -static void -set_fd_nonblocking (int fd) -{ -#ifndef G_OS_WIN32 - glong arg; -#else - gulong arg; -#endif - -#ifndef G_OS_WIN32 - if ((arg = fcntl (fd, F_GETFL, NULL)) < 0) - { - g_warning ("Error getting socket status flags: %s", socket_strerror (errno)); - arg = 0; - } - - arg = arg | O_NONBLOCK; - - if (fcntl (fd, F_SETFL, arg) < 0) - g_warning ("Error setting socket status flags: %s", socket_strerror (errno)); -#else - arg = TRUE; - - if (ioctlsocket (fd, FIONBIO, &arg) == SOCKET_ERROR) - { - int errsv = get_socket_errno (); - g_warning ("Error setting socket status flags: %s", socket_strerror (errsv)); - } +/* Windows has broken prototypes... */ +#ifdef G_OS_WIN32 +#define getsockopt(sockfd, level, optname, optval, optlen) \ + getsockopt (sockfd, level, optname, (gpointer) optval, (int*) optlen) +#define setsockopt(sockfd, level, optname, optval, optlen) \ + setsockopt (sockfd, level, optname, (gpointer) optval, optlen) +#define getsockname(sockfd, addr, addrlen) \ + getsockname (sockfd, addr, (int *)addrlen) +#define getpeername(sockfd, addr, addrlen) \ + getpeername (sockfd, addr, (int *)addrlen) +#define recv(sockfd, buf, len, flags) \ + recv (sockfd, (gpointer)buf, len, flags) #endif -} static gboolean check_socket (GSocket *socket, @@ -293,6 +282,13 @@ check_socket (GSocket *socket, return FALSE; } + return TRUE; +} + +static gboolean +check_timeout (GSocket *socket, + GError **error) +{ if (socket->priv->timed_out) { socket->priv->timed_out = FALSE; @@ -310,19 +306,11 @@ g_socket_details_from_fd (GSocket *socket) struct sockaddr_storage address; gint fd; guint addrlen; - guint optlen; - int value; + int value, family; int errsv; -#ifdef G_OS_WIN32 - /* See bug #611756 */ - BOOL bool_val = FALSE; -#else - int bool_val; -#endif fd = socket->priv->fd; - optlen = sizeof value; - if (getsockopt (fd, SOL_SOCKET, SO_TYPE, (void *)&value, &optlen) != 0) + if (!g_socket_get_option (socket, SOL_SOCKET, SO_TYPE, &value, NULL)) { errsv = get_socket_errno (); @@ -330,10 +318,11 @@ g_socket_details_from_fd (GSocket *socket) { #ifdef ENOTSOCK case ENOTSOCK: -#endif +#else #ifdef WSAENOTSOCK case WSAENOTSOCK: #endif +#endif case EBADF: /* programmer error */ g_error ("creating GSocket from fd %d: %s\n", @@ -345,7 +334,6 @@ g_socket_details_from_fd (GSocket *socket) goto err; } - g_assert (optlen == sizeof value); switch (value) { case SOCK_STREAM: @@ -372,9 +360,31 @@ g_socket_details_from_fd (GSocket *socket) goto err; } - g_assert (G_STRUCT_OFFSET (struct sockaddr, sa_family) + - sizeof address.ss_family <= addrlen); - switch (address.ss_family) + if (addrlen > 0) + { + g_assert (G_STRUCT_OFFSET (struct sockaddr, sa_family) + + sizeof address.ss_family <= addrlen); + family = address.ss_family; + } + else + { + /* On Solaris, this happens if the socket is not yet connected. + * But we can use SO_DOMAIN as a workaround there. + */ +#ifdef SO_DOMAIN + if (!g_socket_get_option (socket, SOL_SOCKET, SO_DOMAIN, &family, NULL)) + { + errsv = get_socket_errno (); + goto err; + } +#else + /* This will translate to G_IO_ERROR_FAILED on either unix or windows */ + errsv = -1; + goto err; +#endif + } + + switch (family) { case G_SOCKET_FAMILY_IPV4: case G_SOCKET_FAMILY_IPV6: @@ -415,19 +425,9 @@ g_socket_details_from_fd (GSocket *socket) socket->priv->connected = TRUE; } - optlen = sizeof bool_val; - if (getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, - (void *)&bool_val, &optlen) == 0) + if (g_socket_get_option (socket, SOL_SOCKET, SO_KEEPALIVE, &value, NULL)) { -#ifndef G_OS_WIN32 - /* Experimentation indicates that the SO_KEEPALIVE value is - * actually a char on Windows, even if documentation claims it - * to be a BOOL which is a typedef for int. So this g_assert() - * fails. See bug #611756. - */ - g_assert (optlen == sizeof bool_val); -#endif - socket->priv->keepalive = !!bool_val; + socket->priv->keepalive = !!value; } else { @@ -444,6 +444,55 @@ g_socket_details_from_fd (GSocket *socket) socket_strerror (errsv)); } +/* Wrapper around socket() that is shared with gnetworkmonitornetlink.c */ +gint +g_socket (gint domain, + gint type, + gint protocol, + GError **error) +{ + int fd; + +#ifdef SOCK_CLOEXEC + fd = socket (domain, type | SOCK_CLOEXEC, protocol); + if (fd != -1) + return fd; + + /* It's possible that libc has SOCK_CLOEXEC but the kernel does not */ + if (fd < 0 && (errno == EINVAL || errno == EPROTOTYPE)) +#endif + fd = socket (domain, type, protocol); + + if (fd < 0) + { + int errsv = get_socket_errno (); + + g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), + _("Unable to create socket: %s"), socket_strerror (errsv)); + errno = errsv; + return -1; + } + +#ifndef G_OS_WIN32 + { + int flags; + + /* We always want to set close-on-exec to protect users. If you + need to so some weird inheritance to exec you can re-enable this + using lower level hacks with g_socket_get_fd(). */ + flags = fcntl (fd, F_GETFD, 0); + if (flags != -1 && + (flags & FD_CLOEXEC) == 0) + { + flags |= FD_CLOEXEC; + fcntl (fd, F_SETFD, flags); + } + } +#endif + + return fd; +} + static gint g_socket_create_socket (GSocketFamily family, GSocketType type, @@ -451,7 +500,6 @@ g_socket_create_socket (GSocketFamily family, GError **error) { gint native_type; - gint fd; switch (type) { @@ -471,46 +519,21 @@ g_socket_create_socket (GSocketFamily family, g_assert_not_reached (); } - if (protocol == -1) + if (family <= 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Unable to create socket: %s"), _("Unknown protocol was specified")); + _("Unable to create socket: %s"), _("Unknown family was specified")); return -1; } -#ifdef SOCK_CLOEXEC - fd = socket (family, native_type | SOCK_CLOEXEC, protocol); - /* It's possible that libc has SOCK_CLOEXEC but the kernel does not */ - if (fd < 0 && errno == EINVAL) -#endif - fd = socket (family, native_type, protocol); - - if (fd < 0) + if (protocol == -1) { - int errsv = get_socket_errno (); - - g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), - _("Unable to create socket: %s"), socket_strerror (errsv)); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + _("Unable to create socket: %s"), _("Unknown protocol was specified")); + return -1; } -#ifndef G_OS_WIN32 - { - int flags; - - /* We always want to set close-on-exec to protect users. If you - need to so some weird inheritance to exec you can re-enable this - using lower level hacks with g_socket_get_fd(). */ - flags = fcntl (fd, F_GETFD, 0); - if (flags != -1 && - (flags & FD_CLOEXEC) == 0) - { - flags |= FD_CLOEXEC; - fcntl (fd, F_SETFD, flags); - } - } -#endif - - return fd; + return g_socket (family, native_type, protocol, error); } static void @@ -529,12 +552,39 @@ g_socket_constructed (GObject *object) socket->priv->protocol, &socket->priv->construct_error); - /* Always use native nonblocking sockets, as - windows sets sockets to nonblocking automatically - in certain operations. This way we make things work - the same on all platforms */ if (socket->priv->fd != -1) - set_fd_nonblocking (socket->priv->fd); + { +#ifndef G_OS_WIN32 + GError *error = NULL; +#else + gulong arg; +#endif + + /* Always use native nonblocking sockets, as Windows sets sockets to + * nonblocking automatically in certain operations. This way we make + * things work the same on all platforms. + */ +#ifndef G_OS_WIN32 + if (!g_unix_set_fd_nonblocking (socket->priv->fd, TRUE, &error)) + { + g_warning ("Error setting socket nonblocking: %s", error->message); + g_clear_error (&error); + } +#else + arg = TRUE; + + if (ioctlsocket (socket->priv->fd, FIONBIO, &arg) == SOCKET_ERROR) + { + int errsv = get_socket_errno (); + g_warning ("Error setting socket status flags: %s", socket_strerror (errsv)); + } +#endif + +#ifdef SO_NOSIGPIPE + /* See note about SIGPIPE below. */ + g_socket_set_option (socket, SOL_SOCKET, SO_NOSIGPIPE, TRUE, NULL); +#endif + } } static void @@ -590,6 +640,22 @@ g_socket_get_property (GObject *object, 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); } @@ -637,6 +703,22 @@ g_socket_set_property (GObject *object, 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); } @@ -646,6 +728,7 @@ static void g_socket_finalize (GObject *object) { GSocket *socket = G_SOCKET (object); + gint i; g_clear_error (&socket->priv->construct_error); @@ -664,8 +747,18 @@ g_socket_finalize (GObject *object) } g_assert (socket->priv->requested_conditions == NULL); + g_mutex_clear (&socket->priv->win32_source_lock); #endif + for (i = 0; i < RECV_ADDR_CACHE_SIZE; i++) + { + 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); } @@ -674,21 +767,20 @@ static void g_socket_class_init (GSocketClass *klass) { GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass); - volatile GType type; - - /* Make sure winsock has been initialized */ - type = g_inet_address_get_type (); #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 - g_type_class_add_private (klass, sizeof (GSocketPrivate)); - gobject_class->finalize = g_socket_finalize; gobject_class->constructed = g_socket_constructed; gobject_class->set_property = g_socket_set_property; @@ -793,6 +885,66 @@ g_socket_class_init (GSocketClass *klass) 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GSocket:broadcast: + * + * Whether the socket should allow sending to broadcast addresses. + * + * Since: 2.32 + */ + g_object_class_install_property (gobject_class, PROP_BROADCAST, + g_param_spec_boolean ("broadcast", + P_("Broadcast"), + P_("Whether to allow sending to broadcast addresses"), + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * GSocket:ttl: + * + * Time-to-live for outgoing unicast packets + * + * Since: 2.32 + */ + g_object_class_install_property (gobject_class, PROP_TTL, + g_param_spec_uint ("ttl", + P_("TTL"), + P_("Time-to-live of outgoing unicast packets"), + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * GSocket:multicast-loopback: + * + * Whether outgoing multicast packets loop back to the local host. + * + * Since: 2.32 + */ + g_object_class_install_property (gobject_class, PROP_MULTICAST_LOOPBACK, + g_param_spec_boolean ("multicast-loopback", + P_("Multicast loopback"), + P_("Whether outgoing multicast packets loop back to the local host"), + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * GSocket:multicast-ttl: + * + * Time-to-live out outgoing multicast packets + * + * Since: 2.32 + */ + g_object_class_install_property (gobject_class, PROP_MULTICAST_TTL, + g_param_spec_uint ("multicast-ttl", + P_("Multicast TTL"), + P_("Time-to-live of outgoing multicast packets"), + 0, G_MAXUINT, 1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); } static void @@ -804,7 +956,7 @@ g_socket_initable_iface_init (GInitableIface *iface) static void g_socket_init (GSocket *socket) { - socket->priv = G_TYPE_INSTANCE_GET_PRIVATE (socket, G_TYPE_SOCKET, GSocketPrivate); + socket->priv = g_socket_get_instance_private (socket); socket->priv->fd = -1; socket->priv->blocking = TRUE; @@ -812,6 +964,7 @@ g_socket_init (GSocket *socket) socket->priv->construct_error = NULL; #ifdef G_OS_WIN32 socket->priv->event = WSA_INVALID_EVENT; + g_mutex_init (&socket->priv->win32_source_lock); #endif } @@ -991,7 +1144,7 @@ void g_socket_set_keepalive (GSocket *socket, gboolean keepalive) { - int value; + GError *error = NULL; g_return_if_fail (G_IS_SOCKET (socket)); @@ -999,12 +1152,11 @@ g_socket_set_keepalive (GSocket *socket, if (socket->priv->keepalive == keepalive) return; - value = (gint) keepalive; - if (setsockopt (socket->priv->fd, SOL_SOCKET, SO_KEEPALIVE, - (gpointer) &value, sizeof (value)) < 0) + if (!g_socket_set_option (socket, SOL_SOCKET, SO_KEEPALIVE, + keepalive, &error)) { - int errsv = get_socket_errno (); - g_warning ("error setting keepalive: %s", socket_strerror (errsv)); + g_warning ("error setting keepalive: %s", error->message); + g_error_free (error); return; } @@ -1140,47 +1292,371 @@ g_socket_set_timeout (GSocket *socket, } /** - * g_socket_get_family: + * g_socket_get_ttl: * @socket: a #GSocket. * - * Gets the socket family of the socket. + * Gets the unicast time-to-live setting on @socket; see + * g_socket_set_ttl() for more details. * - * Returns: a #GSocketFamily + * Returns: the time-to-live setting on @socket * - * Since: 2.22 + * Since: 2.32 */ -GSocketFamily -g_socket_get_family (GSocket *socket) +guint +g_socket_get_ttl (GSocket *socket) { - g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_FAMILY_INVALID); + GError *error = NULL; + gint value; - return socket->priv->family; + 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_get_socket_type: + * g_socket_set_ttl: * @socket: a #GSocket. + * @ttl: the time-to-live value for all unicast packets on @socket * - * Gets the socket type of the socket. - * - * Returns: a #GSocketType + * Sets the time-to-live for outgoing unicast packets on @socket. + * By default the platform-specific default value is used. * - * Since: 2.22 + * Since: 2.32 */ -GSocketType -g_socket_get_socket_type (GSocket *socket) +void +g_socket_set_ttl (GSocket *socket, + guint ttl) { - g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_TYPE_INVALID); + GError *error = NULL; - return socket->priv->type; + 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_protocol: + * g_socket_get_broadcast: * @socket: a #GSocket. * - * Gets the socket protocol id the socket was created with. - * In case the protocol is unknown, -1 is returned. + * 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 * @@ -1199,7 +1675,7 @@ g_socket_get_protocol (GSocket *socket) * @socket: a #GSocket. * * Returns the underlying OS socket object. On unix this - * is a socket file descriptor, and on windows this is + * is a socket file descriptor, and on Windows this is * a Winsock2 SOCKET handle. This may be useful for * doing platform specific or otherwise unusual operations * on the socket. @@ -1235,7 +1711,7 @@ g_socket_get_local_address (GSocket *socket, GError **error) { struct sockaddr_storage buffer; - guint32 len = sizeof (buffer); + guint len = sizeof (buffer); g_return_val_if_fail (G_IS_SOCKET (socket), NULL); @@ -1268,7 +1744,7 @@ g_socket_get_remote_address (GSocket *socket, GError **error) { struct sockaddr_storage buffer; - guint32 len = sizeof (buffer); + guint len = sizeof (buffer); g_return_val_if_fail (G_IS_SOCKET (socket), NULL); @@ -1372,14 +1848,20 @@ g_socket_listen (GSocket *socket, * In certain situations, you may also want to bind a socket that will be * used to initiate connections, though this is not normally required. * - * @allow_reuse should be %TRUE for server sockets (sockets that you will - * eventually call g_socket_accept() on), and %FALSE for client sockets. - * (Specifically, if it is %TRUE, then g_socket_bind() will set the - * %SO_REUSEADDR flag on the socket, allowing it to bind @address even if - * that address was previously used by another socket that has not yet been - * fully cleaned-up by the kernel. Failing to set this flag on a server - * socket may cause the bind call to return %G_IO_ERROR_ADDRESS_IN_USE if - * the server program is stopped and then immediately restarted.) + * 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. * @@ -1392,28 +1874,47 @@ g_socket_bind (GSocket *socket, GError **error) { struct sockaddr_storage addr; + gboolean so_reuseaddr; +#ifdef SO_REUSEPORT + gboolean so_reuseport; +#endif g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE); if (!check_socket (socket, error)) return FALSE; - /* SO_REUSEADDR on windows means something else and is not what we want. - It always allows the unix variant of SO_REUSEADDR anyway */ -#ifndef G_OS_WIN32 - { - int value; + if (!g_socket_address_to_native (address, &addr, sizeof addr, error)) + return FALSE; - value = (int) !!reuse_address; - /* Ignore errors here, the only likely error is "not supported", and - this is a "best effort" thing mainly */ - setsockopt (socket->priv->fd, SOL_SOCKET, SO_REUSEADDR, - (gpointer) &value, sizeof (value)); - } + /* 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 - if (!g_socket_address_to_native (address, &addr, sizeof addr, error)) - return FALSE; +#ifdef SO_REUSEPORT + so_reuseport = reuse_address && (socket->priv->type == G_SOCKET_TYPE_DATAGRAM); +#endif + + /* Ignore errors here, the only likely error is "not supported", and + * this is a "best effort" thing mainly. + */ + g_socket_set_option (socket, SOL_SOCKET, SO_REUSEADDR, so_reuseaddr, NULL); +#ifdef SO_REUSEPORT + g_socket_set_option (socket, SOL_SOCKET, SO_REUSEPORT, so_reuseport, NULL); +#endif if (bind (socket->priv->fd, (struct sockaddr *) &addr, g_socket_address_get_native_size (address)) < 0) @@ -1428,6 +1929,220 @@ g_socket_bind (GSocket *socket, return TRUE; } +#if !defined(HAVE_IF_NAMETOINDEX) && defined(G_OS_WIN32) +static guint +if_nametoindex (const gchar *iface) +{ + PIP_ADAPTER_ADDRESSES addresses = NULL, p; + gulong addresses_len = 0; + guint idx = 0; + DWORD res; + + res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, NULL, &addresses_len); + if (res != NO_ERROR && res != ERROR_BUFFER_OVERFLOW) + { + if (res == ERROR_NO_DATA) + errno = ENXIO; + else + errno = EINVAL; + return 0; + } + + addresses = g_malloc (addresses_len); + res = GetAdaptersAddresses (AF_UNSPEC, 0, NULL, addresses, &addresses_len); + + if (res != NO_ERROR) + { + g_free (addresses); + if (res == ERROR_NO_DATA) + errno = ENXIO; + else + errno = EINVAL; + return 0; + } + + p = addresses; + while (p) + { + if (strcmp (p->AdapterName, iface) == 0) + { + idx = p->IfIndex; + break; + } + p = p->Next; + } + + if (p == NULL) + errno = ENXIO; + + g_free (addresses); + + return idx; +} + +#define HAVE_IF_NAMETOINDEX 1 +#endif + +static gboolean +g_socket_multicast_group_operation (GSocket *socket, + GInetAddress *group, + gboolean source_specific, + const gchar *iface, + gboolean join_group, + GError **error) +{ + const guint8 *native_addr; + gint optname, result; + + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + g_return_val_if_fail (socket->priv->type == G_SOCKET_TYPE_DATAGRAM, FALSE); + g_return_val_if_fail (G_IS_INET_ADDRESS (group), FALSE); + + if (!check_socket (socket, error)) + return FALSE; + + native_addr = g_inet_address_to_bytes (group); + if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV4) + { +#ifdef HAVE_IP_MREQN + struct ip_mreqn mc_req; +#else + struct ip_mreq mc_req; +#endif + + memset (&mc_req, 0, sizeof (mc_req)); + memcpy (&mc_req.imr_multiaddr, native_addr, sizeof (struct in_addr)); + +#ifdef HAVE_IP_MREQN + if (iface) + mc_req.imr_ifindex = if_nametoindex (iface); + else + mc_req.imr_ifindex = 0; /* Pick any. */ +#elif defined(G_OS_WIN32) + if (iface) + mc_req.imr_interface.s_addr = g_htonl (if_nametoindex (iface)); + else + mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY); +#else + mc_req.imr_interface.s_addr = g_htonl (INADDR_ANY); +#endif + + if (source_specific) + { +#ifdef IP_ADD_SOURCE_MEMBERSHIP + optname = join_group ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP; +#else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + join_group ? + _("Error joining multicast group: %s") : + _("Error leaving multicast group: %s"), + _("No support for source-specific multicast")); + return FALSE; +#endif + } + else + optname = join_group ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + result = setsockopt (socket->priv->fd, IPPROTO_IP, optname, + &mc_req, sizeof (mc_req)); + } + else if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV6) + { + struct ipv6_mreq mc_req_ipv6; + + memset (&mc_req_ipv6, 0, sizeof (mc_req_ipv6)); + memcpy (&mc_req_ipv6.ipv6mr_multiaddr, native_addr, sizeof (struct in6_addr)); +#ifdef HAVE_IF_NAMETOINDEX + if (iface) + mc_req_ipv6.ipv6mr_interface = if_nametoindex (iface); + else +#endif + mc_req_ipv6.ipv6mr_interface = 0; + + optname = join_group ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP; + result = setsockopt (socket->priv->fd, IPPROTO_IPV6, optname, + &mc_req_ipv6, sizeof (mc_req_ipv6)); + } + else + g_return_val_if_reached (FALSE); + + if (result < 0) + { + int errsv = get_socket_errno (); + + g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), + join_group ? + _("Error joining multicast group: %s") : + _("Error leaving multicast group: %s"), + socket_strerror (errsv)); + return FALSE; + } + + return TRUE; +} + +/** + * g_socket_join_multicast_group: + * @socket: a #GSocket. + * @group: a #GInetAddress specifying the group address to join. + * @iface: (allow-none): Name of the interface to use, or %NULL + * @source_specific: %TRUE if source-specific multicast should be used + * @error: #GError for error reporting, or %NULL to ignore. + * + * Registers @socket to receive multicast messages sent to @group. + * @socket must be a %G_SOCKET_TYPE_DATAGRAM socket, and must have + * been bound to an appropriate interface and port with + * g_socket_bind(). + * + * If @iface is %NULL, the system will automatically pick an interface + * to bind to based on @group. + * + * If @source_specific is %TRUE, source-specific multicast as defined + * in RFC 4604 is used. Note that on older platforms this may fail + * with a %G_IO_ERROR_NOT_SUPPORTED error. + * + * Returns: %TRUE on success, %FALSE on error. + * + * Since: 2.32 + */ +gboolean +g_socket_join_multicast_group (GSocket *socket, + GInetAddress *group, + gboolean source_specific, + const gchar *iface, + GError **error) +{ + return g_socket_multicast_group_operation (socket, group, source_specific, iface, TRUE, error); +} + +/** + * g_socket_leave_multicast_group: + * @socket: a #GSocket. + * @group: a #GInetAddress specifying the group address to leave. + * @iface: (allow-none): Interface used + * @source_specific: %TRUE if source-specific multicast was used + * @error: #GError for error reporting, or %NULL to ignore. + * + * Removes @socket from the multicast group defined by @group, @iface, + * and @source_specific (which must all have the same values they had + * when you joined the group). + * + * @socket remains bound to its address and port, and can still receive + * unicast messages after calling this. + * + * Returns: %TRUE on success, %FALSE on error. + * + * Since: 2.32 + */ +gboolean +g_socket_leave_multicast_group (GSocket *socket, + GInetAddress *group, + gboolean source_specific, + const gchar *iface, + GError **error) +{ + return g_socket_multicast_group_operation (socket, group, source_specific, iface, FALSE, error); +} + /** * g_socket_speaks_ipv4: * @socket: a #GSocket @@ -1457,12 +2172,11 @@ g_socket_speaks_ipv4 (GSocket *socket) case G_SOCKET_FAMILY_IPV6: #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) { - guint sizeof_int = sizeof (int); gint v6_only; - if (getsockopt (socket->priv->fd, - IPPROTO_IPV6, IPV6_V6ONLY, - &v6_only, &sizeof_int) != 0) + if (!g_socket_get_option (socket, + IPPROTO_IPV6, IPV6_V6ONLY, + &v6_only, NULL)) return FALSE; return !v6_only; @@ -1511,6 +2225,9 @@ g_socket_accept (GSocket *socket, if (!check_socket (socket, error)) return NULL; + if (!check_timeout (socket, error)) + return NULL; + while (TRUE) { if (socket->priv->blocking && @@ -1608,7 +2325,7 @@ g_socket_accept (GSocket *socket, * If the connect call needs to do network I/O it will block, unless * non-blocking I/O is enabled. Then %G_IO_ERROR_PENDING is returned * and the user can be notified of the connection finishing by waiting - * for the G_IO_OUT condition. The result of the connection can then be + * 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. @@ -1658,7 +2375,6 @@ g_socket_connect (GSocket *socket, if (g_socket_check_connect_result (socket, error)) break; } - g_prefix_error (error, _("Error connecting: ")); } else { @@ -1668,9 +2384,9 @@ g_socket_connect (GSocket *socket, } } else - g_set_error (error, G_IO_ERROR, - socket_io_error_from_errno (errsv), - _("Error connecting: %s"), socket_strerror (errsv)); + g_set_error_literal (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + socket_strerror (errsv)); return FALSE; } @@ -1701,19 +2417,19 @@ gboolean g_socket_check_connect_result (GSocket *socket, GError **error) { - guint optlen; int value; + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + if (!check_socket (socket, error)) return FALSE; - optlen = sizeof (value); - if (getsockopt (socket->priv->fd, SOL_SOCKET, SO_ERROR, (void *)&value, &optlen) != 0) - { - int errsv = get_socket_errno (); + if (!check_timeout (socket, error)) + return FALSE; - g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), - _("Unable to get pending error: %s"), socket_strerror (errsv)); + if (!g_socket_get_option (socket, SOL_SOCKET, SO_ERROR, &value, error)) + { + g_prefix_error (error, _("Unable to get pending error: ")); return FALSE; } @@ -1728,14 +2444,77 @@ g_socket_check_connect_result (GSocket *socket, } return FALSE; } - return TRUE; + + socket->priv->connected = TRUE; + return TRUE; +} + +/** + * g_socket_get_available_bytes: + * @socket: a #GSocket + * + * Get the amount of data pending in the OS input buffer. + * + * If @socket is a UDP or SCTP socket, this will return the size of + * just the next packet, even if additional packets are buffered after + * that one. + * + * Note that on Windows, this function is rather inefficient in the + * UDP case, and so if you know any plausible upper bound on the size + * of the incoming packet, it is better to just do a + * g_socket_receive() with a buffer of that size, rather than calling + * g_socket_get_available_bytes() first and then doing a receive of + * exactly the right size. + * + * Returns: the number of bytes that can be read from the socket + * without blocking or truncating, or -1 on error. + * + * Since: 2.32 + */ +gssize +g_socket_get_available_bytes (GSocket *socket) +{ +#ifdef G_OS_WIN32 + const gint bufsize = 64 * 1024; + static guchar *buf = NULL; + u_long avail; +#else + gint avail; +#endif + + g_return_val_if_fail (G_IS_SOCKET (socket), -1); + +#if defined (SO_NREAD) + if (!g_socket_get_option (socket, SOL_SOCKET, SO_NREAD, &avail, NULL)) + return -1; +#elif !defined (G_OS_WIN32) + if (ioctl (socket->priv->fd, FIONREAD, &avail) < 0) + avail = -1; +#else + if (socket->priv->type == G_SOCKET_TYPE_DATAGRAM) + { + if (G_UNLIKELY (g_once_init_enter (&buf))) + g_once_init_leave (&buf, g_malloc (bufsize)); + + avail = recv (socket->priv->fd, buf, bufsize, MSG_PEEK); + if (avail == -1 && get_socket_errno () == WSAEWOULDBLOCK) + avail = 0; + } + else + { + if (ioctlsocket (socket->priv->fd, FIONREAD, &avail) < 0) + avail = -1; + } +#endif + + return avail; } /** * g_socket_receive: * @socket: a #GSocket - * @buffer: a buffer to read data into (which should be at least @size - * bytes long). + * @buffer: (array length=size) (element-type guint8): a buffer to + * read data into (which should be at least @size bytes long). * @size: the number of bytes you want to read from the socket * @cancellable: (allow-none): a %GCancellable or %NULL * @error: #GError for error reporting, or %NULL to ignore. @@ -1755,15 +2534,17 @@ g_socket_check_connect_result (GSocket *socket, * 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 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 + * If the socket is in blocking mode the call will block until there + * is some data to receive, the connection is closed, or there is an + * error. If there is no data available and the socket is in + * non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error will be + * returned. To be notified when data is available, wait for the * %G_IO_IN condition. * * On error -1 is returned and @error is set accordingly. * - * Returns: Number of bytes read, or -1 on error + * Returns: Number of bytes read, or 0 if the connection was closed by + * the peer, or -1 on error * * Since: 2.22 */ @@ -1782,8 +2563,8 @@ g_socket_receive (GSocket *socket, /** * g_socket_receive_with_blocking: * @socket: a #GSocket - * @buffer: a buffer to read data into (which should be at least @size - * bytes long). + * @buffer: (array length=size) (element-type guint8): a buffer to + * read data into (which should be at least @size bytes long). * @size: the number of bytes you want to read from the socket * @blocking: whether to do blocking or non-blocking I/O * @cancellable: (allow-none): a %GCancellable or %NULL @@ -1793,7 +2574,8 @@ g_socket_receive (GSocket *socket, * 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 -1 on error + * Returns: Number of bytes read, or 0 if the connection was closed by + * the peer, or -1 on error * * Since: 2.26 */ @@ -1807,11 +2589,14 @@ g_socket_receive_with_blocking (GSocket *socket, { gssize ret; - g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE); + g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, -1); if (!check_socket (socket, error)) return -1; + if (!check_timeout (socket, error)) + return -1; + if (g_cancellable_set_error_if_cancelled (cancellable, error)) return -1; @@ -1860,9 +2645,10 @@ g_socket_receive_with_blocking (GSocket *socket, /** * g_socket_receive_from: * @socket: a #GSocket - * @address: a pointer to a #GSocketAddress pointer, or %NULL - * @buffer: a buffer to read data into (which should be at least @size - * bytes long). + * @address: (out) (allow-none): a pointer to a #GSocketAddress + * pointer, or %NULL + * @buffer: (array length=size) (element-type guint8): a buffer to + * read data into (which should be at least @size bytes long). * @size: the number of bytes you want to read from the socket * @cancellable: (allow-none): a %GCancellable or %NULL * @error: #GError for error reporting, or %NULL to ignore. @@ -1875,7 +2661,8 @@ g_socket_receive_with_blocking (GSocket *socket, * * See g_socket_receive() for additional information. * - * Returns: Number of bytes read, or -1 on error + * Returns: Number of bytes read, or 0 if the connection was closed by + * the peer, or -1 on error * * Since: 2.22 */ @@ -1900,10 +2687,7 @@ g_socket_receive_from (GSocket *socket, error); } -/* Although we ignore SIGPIPE, gdb will still stop if the app receives - * one, which can be confusing and annoying. So if possible, we want - * to suppress the signal entirely. - */ +/* See the comment about SIGPIPE above. */ #ifdef MSG_NOSIGNAL #define G_SOCKET_DEFAULT_SEND_FLAGS MSG_NOSIGNAL #else @@ -1913,7 +2697,8 @@ g_socket_receive_from (GSocket *socket, /** * g_socket_send: * @socket: a #GSocket - * @buffer: (array length=size): the buffer containing the data to send. + * @buffer: (array length=size) (element-type guint8): the buffer + * containing the data to send. * @size: the number of bytes to send * @cancellable: (allow-none): a %GCancellable or %NULL * @error: #GError for error reporting, or %NULL to ignore. @@ -1953,7 +2738,8 @@ g_socket_send (GSocket *socket, /** * g_socket_send_with_blocking: * @socket: a #GSocket - * @buffer: (array length=size): the buffer containing the data to send. + * @buffer: (array length=size) (element-type guint8): the buffer + * containing the data to send. * @size: the number of bytes to send * @blocking: whether to do blocking or non-blocking I/O * @cancellable: (allow-none): a %GCancellable or %NULL @@ -1978,11 +2764,14 @@ g_socket_send_with_blocking (GSocket *socket, { gssize ret; - g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE); + g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, -1); if (!check_socket (socket, error)) return -1; + if (!check_timeout (socket, error)) + return -1; + if (g_cancellable_set_error_if_cancelled (cancellable, error)) return -1; @@ -2031,8 +2820,9 @@ g_socket_send_with_blocking (GSocket *socket, /** * g_socket_send_to: * @socket: a #GSocket - * @address: a #GSocketAddress, or %NULL - * @buffer: (array length=size): the buffer containing the data to send. + * @address: (allow-none): a #GSocketAddress, or %NULL + * @buffer: (array length=size) (element-type guint8): the buffer + * containing the data to send. * @size: the number of bytes to send * @cancellable: (allow-none): a %GCancellable or %NULL * @error: #GError for error reporting, or %NULL to ignore. @@ -2079,7 +2869,7 @@ g_socket_send_to (GSocket *socket, * * Shut down part of a full-duplex connection. * - * If @shutdown_read is %TRUE then the recieving side of the 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 @@ -2105,7 +2895,7 @@ g_socket_shutdown (GSocket *socket, g_return_val_if_fail (G_IS_SOCKET (socket), TRUE); - if (!check_socket (socket, NULL)) + if (!check_socket (socket, error)) return FALSE; /* Do nothing? */ @@ -2132,7 +2922,7 @@ g_socket_shutdown (GSocket *socket, { int errsv = get_socket_errno (); g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), - _("Unable to create socket: %s"), socket_strerror (errsv)); + _("Unable to shutdown socket: %s"), socket_strerror (errsv)); return FALSE; } @@ -2192,7 +2982,7 @@ g_socket_close (GSocket *socket, if (socket->priv->closed) return TRUE; /* Multiple close not an error */ - if (!check_socket (socket, NULL)) + if (!check_socket (socket, error)) return FALSE; while (1) @@ -2248,19 +3038,6 @@ g_socket_is_closed (GSocket *socket) #ifdef G_OS_WIN32 /* Broken source, used on errors */ static gboolean -broken_prepare (GSource *source, - gint *timeout) -{ - return FALSE; -} - -static gboolean -broken_check (GSource *source) -{ - return FALSE; -} - -static gboolean broken_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) @@ -2270,8 +3047,8 @@ broken_dispatch (GSource *source, static GSourceFuncs broken_funcs = { - broken_prepare, - broken_check, + NULL, + NULL, broken_dispatch, NULL }; @@ -2333,24 +3110,28 @@ 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 @@ -2376,8 +3157,26 @@ update_condition (GSocket *socket) if (socket->priv->current_events & (FD_READ | FD_ACCEPT)) condition |= G_IO_IN; - if (socket->priv->current_events & FD_CLOSE || - socket->priv->closed) + if (socket->priv->current_events & FD_CLOSE) + { + int r, errsv, buffer; + + r = recv (socket->priv->fd, &buffer, sizeof (buffer), MSG_PEEK); + if (r < 0) + errsv = get_socket_errno (); + + if (r > 0 || + (r < 0 && errsv == WSAENOTCONN)) + condition |= G_IO_IN; + else if (r == 0 || + (r < 0 && (errsv == WSAESHUTDOWN || errsv == WSAECONNRESET || + errsv == WSAECONNABORTED || errsv == WSAENETRESET))) + condition |= G_IO_HUP; + else + condition |= G_IO_ERR; + } + + if (socket->priv->closed) condition |= G_IO_HUP; /* Never report both G_IO_OUT and HUP, these are @@ -2407,58 +3206,35 @@ update_condition (GSocket *socket) typedef struct { GSource source; +#ifdef G_OS_WIN32 GPollFD pollfd; +#else + gpointer fd_tag; +#endif GSocket *socket; GIOCondition condition; - GCancellable *cancellable; - GPollFD cancel_pollfd; - gint64 timeout_time; } GSocketSource; +#ifdef G_OS_WIN32 static gboolean -socket_source_prepare (GSource *source, - gint *timeout) +socket_source_prepare_win32 (GSource *source, + gint *timeout) { GSocketSource *socket_source = (GSocketSource *)source; - if (g_cancellable_is_cancelled (socket_source->cancellable)) - return TRUE; - - if (socket_source->timeout_time) - { - gint64 now; - - now = g_source_get_time (source); - /* Round up to ensure that we don't try again too early */ - *timeout = (socket_source->timeout_time - now + 999) / 1000; - if (*timeout < 0) - { - socket_source->socket->priv->timed_out = TRUE; - socket_source->pollfd.revents = socket_source->condition & (G_IO_IN | G_IO_OUT); - *timeout = 0; - return TRUE; - } - } - else - *timeout = -1; - -#ifdef G_OS_WIN32 - socket_source->pollfd.revents = update_condition (socket_source->socket); -#endif - - if ((socket_source->condition & socket_source->pollfd.revents) != 0) - return TRUE; + *timeout = -1; - return FALSE; + return (update_condition (socket_source->socket) & socket_source->condition) != 0; } static gboolean -socket_source_check (GSource *source) +socket_source_check_win32 (GSource *source) { int timeout; - return socket_source_prepare (source, &timeout); + return socket_source_prepare_win32 (source, &timeout); } +#endif static gboolean socket_source_dispatch (GSource *source, @@ -2467,14 +3243,32 @@ socket_source_dispatch (GSource *source, { GSocketSourceFunc func = (GSocketSourceFunc)callback; GSocketSource *socket_source = (GSocketSource *)source; + GSocket *socket = socket_source->socket; + gint64 timeout; + guint events; + gboolean ret; #ifdef G_OS_WIN32 - socket_source->pollfd.revents = update_condition (socket_source->socket); + events = update_condition (socket_source->socket); +#else + events = g_source_query_unix_fd (source, socket_source->fd_tag); #endif - return (*func) (socket_source->socket, - socket_source->pollfd.revents & socket_source->condition, - user_data); + timeout = g_source_get_ready_time (source); + if (timeout >= 0 && timeout < g_source_get_time (source)) + { + socket->priv->timed_out = TRUE; + events |= (G_IO_IN | G_IO_OUT); + } + + ret = (*func) (socket, events & socket_source->condition, user_data); + + if (socket->priv->timeout) + g_source_set_ready_time (source, g_get_monotonic_time () + socket->priv->timeout * 1000000); + else + g_source_set_ready_time (source, -1); + + return ret; } static void @@ -2490,12 +3284,6 @@ socket_source_finalize (GSource *source) #endif g_object_unref (socket); - - if (socket_source->cancellable) - { - g_cancellable_release_fd (socket_source->cancellable); - g_object_unref (socket_source->cancellable); - } } static gboolean @@ -2505,8 +3293,8 @@ socket_source_closure_callback (GSocket *socket, { GClosure *closure = data; - GValue params[2] = { { 0, }, { 0, } }; - GValue result_value = { 0, }; + 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); @@ -2528,12 +3316,15 @@ socket_source_closure_callback (GSocket *socket, static GSourceFuncs socket_source_funcs = { - socket_source_prepare, - socket_source_check, +#ifdef G_OS_WIN32 + socket_source_prepare_win32, + socket_source_check_win32, +#else + NULL, NULL, /* check, prepare */ +#endif socket_source_dispatch, socket_source_finalize, (GSourceFunc)socket_source_closure_callback, - (GSourceDummyMarshal)_gio_marshal_BOOLEAN__FLAGS, }; static GSource * @@ -2554,7 +3345,7 @@ socket_source_new (GSocket *socket, } #endif - condition |= G_IO_HUP | G_IO_ERR; + condition |= G_IO_HUP | G_IO_ERR | G_IO_NVAL; source = g_source_new (&socket_source_funcs, sizeof (GSocketSource)); g_source_set_name (source, "GSocket"); @@ -2563,30 +3354,30 @@ socket_source_new (GSocket *socket, socket_source->socket = g_object_ref (socket); socket_source->condition = condition; - if (g_cancellable_make_pollfd (cancellable, - &socket_source->cancel_pollfd)) + if (cancellable) { - socket_source->cancellable = g_object_ref (cancellable); - g_source_add_poll (source, &socket_source->cancel_pollfd); + GSource *cancellable_source; + + cancellable_source = g_cancellable_source_new (cancellable); + g_source_add_child_source (source, cancellable_source); + g_source_set_dummy_callback (cancellable_source); + g_source_unref (cancellable_source); } #ifdef G_OS_WIN32 add_condition_watch (socket, &socket_source->condition); socket_source->pollfd.fd = (gintptr) socket->priv->event; -#else - socket_source->pollfd.fd = socket->priv->fd; -#endif - 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) - socket_source->timeout_time = g_get_monotonic_time () + - socket->priv->timeout * 1000000; - + g_source_set_ready_time (source, g_get_monotonic_time () + socket->priv->timeout * 1000000); else - socket_source->timeout_time = 0; + g_source_set_ready_time (source, -1); return source; } @@ -2597,8 +3388,9 @@ socket_source_new (GSocket *socket, * @condition: a #GIOCondition mask to monitor * @cancellable: (allow-none): a %GCancellable or %NULL * - * Creates a %GSource that can be attached to a %GMainContext to monitor - * for the availibility of the specified @condition on the socket. + * Creates a #GSource that can be attached to a %GMainContext to monitor + * for the availability of the specified @condition on the socket. The #GSource + * keeps a reference to the @socket. * * The callback on the source is of the #GSocketSourceFunc type. * @@ -2662,6 +3454,8 @@ 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; @@ -2682,6 +3476,7 @@ g_socket_condition_check (GSocket *socket, gint result; poll_fd.fd = socket->priv->fd; poll_fd.events = condition; + poll_fd.revents = 0; do result = g_poll (&poll_fd, 1, 0); @@ -2708,6 +3503,8 @@ g_socket_condition_check (GSocket *socket, * 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 @@ -2718,17 +3515,71 @@ g_socket_condition_wait (GSocket *socket, GCancellable *cancellable, GError **error) { + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + + return g_socket_condition_timed_wait (socket, condition, -1, + cancellable, error); +} + +/** + * g_socket_condition_timed_wait: + * @socket: a #GSocket + * @condition: a #GIOCondition mask to wait for + * @timeout: the maximum time (in microseconds) to wait, or -1 + * @cancellable: (allow-none): a #GCancellable, or %NULL + * @error: a #GError pointer, or %NULL + * + * Waits for up to @timeout microseconds for @condition to become true + * on @socket. If the condition is met, %TRUE is returned. + * + * If @cancellable is cancelled before the condition is met, or if + * @timeout (or the socket's #GSocket:timeout) is reached before the + * condition is met, then %FALSE is returned and @error, if non-%NULL, + * is set to the appropriate value (%G_IO_ERROR_CANCELLED or + * %G_IO_ERROR_TIMED_OUT). + * + * If you don't want a timeout, use g_socket_condition_wait(). + * (Alternatively, you can pass -1 for @timeout.) + * + * Note that although @timeout is in microseconds for consistency with + * other GLib APIs, this function actually only has millisecond + * resolution, and the behavior is undefined if @timeout is not an + * exact number of milliseconds. + * + * Returns: %TRUE if the condition was met, %FALSE otherwise + * + * Since: 2.32 + */ +gboolean +g_socket_condition_timed_wait (GSocket *socket, + GIOCondition condition, + gint64 timeout, + GCancellable *cancellable, + GError **error) +{ + gint64 start_time; + + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + if (!check_socket (socket, error)) return FALSE; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; + if (socket->priv->timeout && + (timeout < 0 || socket->priv->timeout < timeout / G_USEC_PER_SEC)) + timeout = socket->priv->timeout * 1000; + else if (timeout != -1) + timeout = timeout / 1000; + + start_time = g_get_monotonic_time (); + #ifdef G_OS_WIN32 { GIOCondition current_condition; WSAEVENT events[2]; - DWORD res, timeout; + DWORD res; GPollFD cancel_fd; int num_events; @@ -2743,16 +3594,14 @@ g_socket_condition_wait (GSocket *socket, if (g_cancellable_make_pollfd (cancellable, &cancel_fd)) events[num_events++] = (WSAEVENT)cancel_fd.fd; - if (socket->priv->timeout) - timeout = socket->priv->timeout * 1000; - else + if (timeout == -1) timeout = WSA_INFINITE; current_condition = update_condition (socket); while ((condition & current_condition) == 0) { - res = WSAWaitForMultipleEvents(num_events, events, - FALSE, timeout, FALSE); + res = WSAWaitForMultipleEvents (num_events, events, + FALSE, timeout, FALSE); if (res == WSA_WAIT_FAILED) { int errsv = get_socket_errno (); @@ -2774,6 +3623,13 @@ g_socket_condition_wait (GSocket *socket, break; current_condition = update_condition (socket); + + if (timeout != WSA_INFINITE) + { + timeout -= (g_get_monotonic_time () - start_time) * 1000; + if (timeout < 0) + timeout = 0; + } } remove_condition_watch (socket, &condition); if (num_events > 1) @@ -2786,7 +3642,6 @@ g_socket_condition_wait (GSocket *socket, GPollFD poll_fd[2]; gint result; gint num; - gint timeout; poll_fd[0].fd = socket->priv->fd; poll_fd[0].events = condition; @@ -2795,14 +3650,19 @@ g_socket_condition_wait (GSocket *socket, if (g_cancellable_make_pollfd (cancellable, &poll_fd[1])) num++; - if (socket->priv->timeout) - timeout = socket->priv->timeout * 1000; - else - timeout = -1; + while (TRUE) + { + result = g_poll (poll_fd, num, timeout); + if (result != -1 || errno != EINTR) + break; - do - result = g_poll (poll_fd, num, timeout); - while (result == -1 && get_socket_errno () == EINTR); + if (timeout != -1) + { + timeout -= (g_get_monotonic_time () - start_time) / 1000; + if (timeout < 0) + timeout = 0; + } + } if (num > 1) g_cancellable_release_fd (cancellable); @@ -2822,7 +3682,7 @@ g_socket_condition_wait (GSocket *socket, /** * g_socket_send_message: * @socket: a #GSocket - * @address: a #GSocketAddress, or %NULL + * @address: (allow-none): a #GSocketAddress, or %NULL * @vectors: (array length=num_vectors): an array of #GOutputVector structs * @num_vectors: the number of elements in @vectors, or -1 * @messages: (array length=num_messages) (allow-none): a pointer to an @@ -2844,7 +3704,7 @@ g_socket_condition_wait (GSocket *socket, * 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 + * #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(). * @@ -2889,9 +3749,19 @@ g_socket_send_message (GSocket *socket, GOutputVector one_vector; char zero; + g_return_val_if_fail (G_IS_SOCKET (socket), -1); + g_return_val_if_fail (address == NULL || G_IS_SOCKET_ADDRESS (address), -1); + g_return_val_if_fail (num_vectors == 0 || vectors != NULL, -1); + g_return_val_if_fail (num_messages == 0 || messages != NULL, -1); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), -1); + g_return_val_if_fail (error == NULL || *error == NULL, -1); + if (!check_socket (socket, error)) return -1; + if (!check_timeout (socket, error)) + return -1; + if (g_cancellable_set_error_if_cancelled (cancellable, error)) return -1; @@ -3049,7 +3919,7 @@ g_socket_send_message (GSocket *socket, if (num_messages != 0) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("GSocketControlMessage not supported on windows")); + _("GSocketControlMessage not supported on Windows")); return -1; } @@ -3117,10 +3987,65 @@ g_socket_send_message (GSocket *socket, #endif } +static GSocketAddress * +cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len) +{ + GSocketAddress *saddr; + gint i; + guint64 oldest_time = G_MAXUINT64; + gint oldest_index = 0; + + if (native_len <= 0) + return NULL; + + saddr = NULL; + for (i = 0; i < RECV_ADDR_CACHE_SIZE; i++) + { + GSocketAddress *tmp = socket->priv->recv_addr_cache[i].addr; + gpointer tmp_native = socket->priv->recv_addr_cache[i].native; + gint tmp_native_len = socket->priv->recv_addr_cache[i].native_len; + + if (!tmp) + continue; + + if (tmp_native_len != native_len) + continue; + + if (memcmp (tmp_native, native, native_len) == 0) + { + saddr = g_object_ref (tmp); + socket->priv->recv_addr_cache[i].last_used = g_get_monotonic_time (); + return saddr; + } + + if (socket->priv->recv_addr_cache[i].last_used < oldest_time) + { + oldest_time = socket->priv->recv_addr_cache[i].last_used; + oldest_index = i; + } + } + + saddr = g_socket_address_new_from_native (native, native_len); + + if (socket->priv->recv_addr_cache[oldest_index].addr) + { + g_object_unref (socket->priv->recv_addr_cache[oldest_index].addr); + g_free (socket->priv->recv_addr_cache[oldest_index].native); + } + + socket->priv->recv_addr_cache[oldest_index].native = g_memdup (native, native_len); + socket->priv->recv_addr_cache[oldest_index].native_len = native_len; + socket->priv->recv_addr_cache[oldest_index].addr = g_object_ref (saddr); + socket->priv->recv_addr_cache[oldest_index].last_used = g_get_monotonic_time (); + + return saddr; +} + /** * g_socket_receive_message: * @socket: a #GSocket - * @address: a pointer to a #GSocketAddress pointer, or %NULL + * @address: (out) (allow-none): a pointer to a #GSocketAddress + * pointer, or %NULL * @vectors: (array length=num_vectors): an array of #GInputVector structs * @num_vectors: the number of elements in @vectors, or -1 * @messages: (array length=num_messages) (allow-none): a pointer which @@ -3181,14 +4106,16 @@ g_socket_send_message (GSocket *socket, * sufficiently-large buffer. * * If the socket is in blocking mode the call will block until there - * is some data to receive or there is an error. If there is no data - * available and the socket is in non-blocking mode, a - * %G_IO_ERROR_WOULD_BLOCK error will be returned. To be notified when - * data is available, wait for the %G_IO_IN condition. + * is some data to receive, the connection is closed, or there is an + * error. If there is no data available and the socket is in + * non-blocking mode, a %G_IO_ERROR_WOULD_BLOCK error will be + * returned. To be notified when data is available, wait for the + * %G_IO_IN condition. * * On error -1 is returned and @error is set accordingly. * - * Returns: Number of bytes read, or -1 on error + * Returns: Number of bytes read, or 0 if the connection was closed by + * the peer, or -1 on error * * Since: 2.22 */ @@ -3206,9 +4133,14 @@ g_socket_receive_message (GSocket *socket, GInputVector one_vector; char one_byte; + g_return_val_if_fail (G_IS_SOCKET (socket), -1); + if (!check_socket (socket, error)) return -1; + if (!check_timeout (socket, error)) + return -1; + if (g_cancellable_set_error_if_cancelled (cancellable, error)) return -1; @@ -3284,6 +4216,14 @@ g_socket_receive_message (GSocket *socket, else msg.msg_flags = 0; + /* We always set the close-on-exec flag so we don't leak file + * descriptors into child processes. Note that gunixfdmessage.c + * will later call fcntl (fd, FD_CLOEXEC), but that isn't atomic. + */ +#ifdef MSG_CMSG_CLOEXEC + msg.msg_flags |= MSG_CMSG_CLOEXEC; +#endif + /* do it */ while (1) { @@ -3293,6 +4233,14 @@ g_socket_receive_message (GSocket *socket, return -1; result = recvmsg (socket->priv->fd, &msg, msg.msg_flags); +#ifdef MSG_CMSG_CLOEXEC + if (result < 0 && get_socket_errno () == EINVAL) + { + /* We must be running on an old kernel. Call without the flag. */ + msg.msg_flags &= ~(MSG_CMSG_CLOEXEC); + result = recvmsg (socket->priv->fd, &msg, msg.msg_flags); + } +#endif if (result < 0) { @@ -3318,50 +4266,44 @@ g_socket_receive_message (GSocket *socket, /* decode address */ if (address != NULL) { - if (msg.msg_namelen > 0) - *address = g_socket_address_new_from_native (msg.msg_name, - msg.msg_namelen); - else - *address = NULL; + *address = cache_recv_address (socket, msg.msg_name, msg.msg_namelen); } /* decode control messages */ { GPtrArray *my_messages = NULL; - const gchar *scm_pointer; struct cmsghdr *cmsg; - gsize scm_size; - - scm_pointer = (const gchar *) msg.msg_control; - scm_size = msg.msg_controllen; - for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg)) - { - GSocketControlMessage *message; - - message = g_socket_control_message_deserialize (cmsg->cmsg_level, - cmsg->cmsg_type, - cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg), - CMSG_DATA (cmsg)); - if (message == NULL) - /* We've already spewed about the problem in the - deserialization code, so just continue */ - continue; - - if (messages == NULL) - { - /* we have to do it this way if the user ignores the - * messages so that we will close any received fds. - */ - g_object_unref (message); - } - else - { - if (my_messages == NULL) - my_messages = g_ptr_array_new (); - g_ptr_array_add (my_messages, message); - } - } + if (msg.msg_controllen >= sizeof (struct cmsghdr)) + { + for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg)) + { + GSocketControlMessage *message; + + message = g_socket_control_message_deserialize (cmsg->cmsg_level, + cmsg->cmsg_type, + cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg), + CMSG_DATA (cmsg)); + if (message == NULL) + /* We've already spewed about the problem in the + deserialization code, so just continue */ + continue; + + if (messages == NULL) + { + /* we have to do it this way if the user ignores the + * messages so that we will close any received fds. + */ + g_object_unref (message); + } + else + { + if (my_messages == NULL) + my_messages = g_ptr_array_new (); + g_ptr_array_add (my_messages, message); + } + } + } if (num_messages) *num_messages = my_messages != NULL ? my_messages->len : 0; @@ -3460,10 +4402,7 @@ g_socket_receive_message (GSocket *socket, /* decode address */ if (address != NULL) { - if (addrlen > 0) - *address = g_socket_address_new_from_native (&addr, addrlen); - else - *address = NULL; + *address = cache_recv_address (socket, (struct sockaddr *)&addr, addrlen); } /* capture the flags */ @@ -3514,33 +4453,72 @@ g_socket_get_credentials (GSocket *socket, ret = NULL; -#ifdef __linux__ +#if G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED + +#ifdef SO_PEERCRED { - struct ucred native_creds; - socklen_t optlen; - optlen = sizeof (struct ucred); + guint8 native_creds_buf[G_CREDENTIALS_NATIVE_SIZE]; + socklen_t optlen = sizeof (native_creds_buf); + if (getsockopt (socket->priv->fd, SOL_SOCKET, SO_PEERCRED, - (void *)&native_creds, - &optlen) != 0) + native_creds_buf, + &optlen) == 0) + { + ret = g_credentials_new (); + g_credentials_set_native (ret, + G_CREDENTIALS_NATIVE_TYPE, + native_creds_buf); + } + } +#elif G_CREDENTIALS_USE_NETBSD_UNPCBID + { + struct unpcbid cred; + socklen_t optlen = sizeof (cred); + + if (getsockopt (socket->priv->fd, + 0, + LOCAL_PEEREID, + &cred, + &optlen) == 0) { - int errsv = get_socket_errno (); - g_set_error (error, - G_IO_ERROR, - socket_io_error_from_errno (errsv), - _("Unable to get pending error: %s"), - socket_strerror (errsv)); + ret = g_credentials_new (); + g_credentials_set_native (ret, + G_CREDENTIALS_NATIVE_TYPE, + &cred); } - else + } +#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_LINUX_UCRED, - &native_creds); + G_CREDENTIALS_TYPE_SOLARIS_UCRED, + ucred); + ucred_free (ucred); } } #else + #error "G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED is set but this is no code for this platform" +#endif + + if (!ret) + { + int errsv = get_socket_errno (); + + g_set_error (error, + G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Unable to read socket credentials: %s"), + socket_strerror (errsv)); + } + +#else + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, @@ -3549,3 +4527,135 @@ g_socket_get_credentials (GSocket *socket, 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] + * header pulls in system headers that will define most of the + * standard/portable socket options. For unusual socket protocols or + * platform-dependent options, you may need to include additional + * headers. + * + * Note that even for socket options that are a single byte in size, + * @value is still a pointer to a #gint variable, not a #guchar; + * g_socket_get_option() will handle the conversion internally. + * + * Returns: success or failure. On failure, @error will be set, and + * the system error value (`errno` or WSAGetLastError()) will still + * be set to the result of the getsockopt() call. + * + * Since: 2.36 + */ +gboolean +g_socket_get_option (GSocket *socket, + gint level, + gint optname, + gint *value, + GError **error) +{ + guint size; + + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + + *value = 0; + size = sizeof (gint); + if (getsockopt (socket->priv->fd, level, optname, value, &size) != 0) + { + int errsv = get_socket_errno (); + + g_set_error_literal (error, + G_IO_ERROR, + socket_io_error_from_errno (errsv), + socket_strerror (errsv)); +#ifndef G_OS_WIN32 + /* Reset errno in case the caller wants to look at it */ + errno = errsv; +#endif + return FALSE; + } + +#if G_BYTE_ORDER == G_BIG_ENDIAN + /* If the returned value is smaller than an int then we need to + * slide it over into the low-order bytes of *value. + */ + if (size != sizeof (gint)) + *value = *value >> (8 * (sizeof (gint) - size)); +#endif + + return TRUE; +} + +/** + * g_socket_set_option: + * @socket: a #GSocket + * @level: the "API level" of the option (eg, `SOL_SOCKET`) + * @optname: the "name" of the option (eg, `SO_BROADCAST`) + * @value: the value to set the option to + * @error: #GError for error reporting, or %NULL to ignore. + * + * Sets the value of an integer-valued option on @socket, as with + * setsockopt(). (If you need to set a non-integer-valued option, + * you will need to call setsockopt() directly.) + * + * The [][gio-gnetworking.h] + * header pulls in system headers that will define most of the + * standard/portable socket options. For unusual socket protocols or + * platform-dependent options, you may need to include additional + * headers. + * + * Returns: success or failure. On failure, @error will be set, and + * the system error value (`errno` or WSAGetLastError()) will still + * be set to the result of the setsockopt() call. + * + * Since: 2.36 + */ +gboolean +g_socket_set_option (GSocket *socket, + gint level, + gint optname, + gint value, + GError **error) +{ + gint errsv; + + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + + if (setsockopt (socket->priv->fd, level, optname, &value, sizeof (gint)) == 0) + return TRUE; + +#if !defined (__linux__) && !defined (G_OS_WIN32) + /* Linux and Windows let you set a single-byte value from an int, + * but most other platforms don't. + */ + if (errno == EINVAL && value >= SCHAR_MIN && value <= CHAR_MAX) + { +#if G_BYTE_ORDER == G_BIG_ENDIAN + value = value << (8 * (sizeof (gint) - 1)); +#endif + if (setsockopt (socket->priv->fd, level, optname, &value, 1) == 0) + return TRUE; + } +#endif + + errsv = get_socket_errno (); + + g_set_error_literal (error, + G_IO_ERROR, + socket_io_error_from_errno (errsv), + socket_strerror (errsv)); +#ifndef G_OS_WIN32 + errno = errsv; +#endif + return FALSE; +} +