X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgsocket.c;h=f06e49bccda6b683a75493a9c7756b76779de149;hb=e608ec7b2e47d29fa189fca6e97f484f41c115a4;hp=0b8d91bf2fd81d29ee09ddef162ed3ddc1e71d72;hpb=87cc77a1985efa18bcd8fc3318c90d8557ae9129;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gsocket.c b/gio/gsocket.c index 0b8d91b..f06e49b 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 @@ -44,7 +42,11 @@ # include #endif -#ifdef HAVE_SYS_UIO_H +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#ifdef G_OS_UNIX #include #endif @@ -59,13 +61,14 @@ #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. @@ -100,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 @@ -114,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 */ @@ -122,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, @@ -145,6 +148,9 @@ enum PROP_MULTICAST_TTL }; +/* Size of the receiver cache for g_socket_receive_from() */ +#define RECV_ADDR_CACHE_SIZE 8 + struct _GSocketPrivate { GSocketFamily family; @@ -169,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) { @@ -185,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 } @@ -246,6 +242,20 @@ _win32_unset_event_mask (GSocket *socket, int mask) #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 void set_fd_nonblocking (int fd) { @@ -298,6 +308,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; @@ -315,19 +332,11 @@ g_socket_details_from_fd (GSocket *socket) struct sockaddr_storage address; gint fd; guint addrlen; - guint optlen; 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 (); @@ -351,7 +360,6 @@ g_socket_details_from_fd (GSocket *socket) goto err; } - g_assert (optlen == sizeof value); switch (value) { case SOCK_STREAM: @@ -390,8 +398,7 @@ g_socket_details_from_fd (GSocket *socket) * But we can use SO_DOMAIN as a workaround there. */ #ifdef SO_DOMAIN - optlen = sizeof family; - if (getsockopt (fd, SOL_SOCKET, SO_DOMAIN, (void *)&family, &optlen) != 0) + if (!g_socket_get_option (socket, SOL_SOCKET, SO_DOMAIN, &family, NULL)) { errsv = get_socket_errno (); goto err; @@ -444,19 +451,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 { @@ -473,6 +470,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, @@ -480,7 +526,6 @@ g_socket_create_socket (GSocketFamily family, GError **error) { gint native_type; - gint fd; switch (type) { @@ -514,39 +559,7 @@ g_socket_create_socket (GSocketFamily family, 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) - { - 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)); - } - -#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 @@ -714,6 +727,7 @@ static void g_socket_finalize (GObject *object) { GSocket *socket = G_SOCKET (object); + gint i; g_clear_error (&socket->priv->construct_error); @@ -732,8 +746,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); } @@ -742,11 +766,6 @@ 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 (); - (type); /* To avoid -Wunused-but-set-variable */ #ifdef SIGPIPE /* There is no portable, thread-safe way to avoid having the process @@ -756,8 +775,6 @@ g_socket_class_init (GSocketClass *klass) 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; @@ -866,14 +883,14 @@ g_socket_class_init (GSocketClass *klass) /** * GSocket:broadcast: * - * Whether the socket should allow sending to and receiving from broadcast addresses. + * 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 and receiving from broadcast addresses"), + P_("Whether to allow sending to broadcast addresses"), FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -933,7 +950,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; @@ -941,6 +958,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 } @@ -1120,7 +1138,7 @@ void g_socket_set_keepalive (GSocket *socket, gboolean keepalive) { - int value; + GError *error = NULL; g_return_if_fail (G_IS_SOCKET (socket)); @@ -1128,12 +1146,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; } @@ -1282,34 +1299,29 @@ g_socket_set_timeout (GSocket *socket, guint g_socket_get_ttl (GSocket *socket) { - int result; - guint value, optlen; + GError *error = NULL; + gint value; - g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + g_return_val_if_fail (G_IS_SOCKET (socket), 0); if (socket->priv->family == G_SOCKET_FAMILY_IPV4) { - guchar optval; - - optlen = sizeof (optval); - result = getsockopt (socket->priv->fd, IPPROTO_IP, IP_TTL, - &optval, &optlen); - value = optval; + g_socket_get_option (socket, IPPROTO_IP, IP_TTL, + &value, &error); } else if (socket->priv->family == G_SOCKET_FAMILY_IPV6) { - optlen = sizeof (value); - result = getsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, - &value, &optlen); + g_socket_get_option (socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + &value, &error); } else - g_return_val_if_reached (FALSE); + g_return_val_if_reached (0); - if (result < 0) + if (error) { - int errsv = get_socket_errno (); - g_warning ("error getting unicast ttl: %s", socket_strerror (errsv)); - return FALSE; + g_warning ("error getting unicast ttl: %s", error->message); + g_error_free (error); + return 0; } return value; @@ -1329,29 +1341,29 @@ void g_socket_set_ttl (GSocket *socket, guint ttl) { - int result; + GError *error = NULL; g_return_if_fail (G_IS_SOCKET (socket)); if (socket->priv->family == G_SOCKET_FAMILY_IPV4) { - guchar optval = (guchar)ttl; - - result = setsockopt (socket->priv->fd, IPPROTO_IP, IP_TTL, - &optval, sizeof (optval)); + g_socket_set_option (socket, IPPROTO_IP, IP_TTL, + ttl, &error); } else if (socket->priv->family == G_SOCKET_FAMILY_IPV6) { - result = setsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, - &ttl, sizeof (ttl)); + 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 (result < 0) + if (error) { - int errsv = get_socket_errno (); - g_warning ("error setting unicast ttl: %s", socket_strerror (errsv)); + g_warning ("error setting unicast ttl: %s", error->message); + g_error_free (error); return; } @@ -1364,7 +1376,7 @@ g_socket_set_ttl (GSocket *socket, * * Gets the broadcast setting on @socket; if %TRUE, * it is possible to send packets to broadcast - * addresses or receive from broadcast addresses. + * addresses. * * Returns: the broadcast setting on @socket * @@ -1373,19 +1385,16 @@ g_socket_set_ttl (GSocket *socket, gboolean g_socket_get_broadcast (GSocket *socket) { - int result; - guint value = 0, optlen; + GError *error = NULL; + gint value; g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); - optlen = sizeof (guchar); - result = getsockopt (socket->priv->fd, SOL_SOCKET, SO_BROADCAST, - &value, &optlen); - - if (result < 0) + if (!g_socket_get_option (socket, SOL_SOCKET, SO_BROADCAST, + &value, &error)) { - int errsv = get_socket_errno (); - g_warning ("error getting broadcast: %s", socket_strerror (errsv)); + g_warning ("error getting broadcast: %s", error->message); + g_error_free (error); return FALSE; } @@ -1395,11 +1404,11 @@ g_socket_get_broadcast (GSocket *socket) /** * g_socket_set_broadcast: * @socket: a #GSocket. - * @broadcast: whether @socket should allow sending to and receiving - * from broadcast addresses + * @broadcast: whether @socket should allow sending to broadcast + * addresses * - * Sets whether @socket should allow sending to and receiving from - * broadcast addresses. This is %FALSE by default. + * Sets whether @socket should allow sending to broadcast addresses. + * This is %FALSE by default. * * Since: 2.32 */ @@ -1407,21 +1416,17 @@ void g_socket_set_broadcast (GSocket *socket, gboolean broadcast) { - int result; - gint value; + GError *error = NULL; g_return_if_fail (G_IS_SOCKET (socket)); broadcast = !!broadcast; - value = (guchar)broadcast; - - result = setsockopt (socket->priv->fd, SOL_SOCKET, SO_BROADCAST, - &value, sizeof (value)); - if (result < 0) + if (!g_socket_set_option (socket, SOL_SOCKET, SO_BROADCAST, + broadcast, &error)) { - int errsv = get_socket_errno (); - g_warning ("error setting broadcast: %s", socket_strerror (errsv)); + g_warning ("error setting broadcast: %s", error->message); + g_error_free (error); return; } @@ -1443,30 +1448,28 @@ g_socket_set_broadcast (GSocket *socket, gboolean g_socket_get_multicast_loopback (GSocket *socket) { - int result; - guint value = 0, optlen; + GError *error = NULL; + gint value; g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); if (socket->priv->family == G_SOCKET_FAMILY_IPV4) { - optlen = sizeof (guchar); - result = getsockopt (socket->priv->fd, IPPROTO_IP, IP_MULTICAST_LOOP, - &value, &optlen); + g_socket_get_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP, + &value, &error); } else if (socket->priv->family == G_SOCKET_FAMILY_IPV6) { - optlen = sizeof (guint); - result = getsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, - &value, &optlen); + g_socket_get_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + &value, &error); } else g_return_val_if_reached (FALSE); - if (result < 0) + if (error) { - int errsv = get_socket_errno (); - g_warning ("error getting multicast loopback: %s", socket_strerror (errsv)); + g_warning ("error getting multicast loopback: %s", error->message); + g_error_free (error); return FALSE; } @@ -1489,7 +1492,7 @@ void g_socket_set_multicast_loopback (GSocket *socket, gboolean loopback) { - int result; + GError *error = NULL; g_return_if_fail (G_IS_SOCKET (socket)); @@ -1497,25 +1500,23 @@ g_socket_set_multicast_loopback (GSocket *socket, if (socket->priv->family == G_SOCKET_FAMILY_IPV4) { - guchar value = (guchar)loopback; - - result = setsockopt (socket->priv->fd, IPPROTO_IP, IP_MULTICAST_LOOP, - &value, sizeof (value)); + g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_LOOP, + loopback, &error); } else if (socket->priv->family == G_SOCKET_FAMILY_IPV6) { - guint value = (guint)loopback; - - result = setsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, - &value, sizeof (value)); + 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 (result < 0) + if (error) { - int errsv = get_socket_errno (); - g_warning ("error setting multicast loopback: %s", socket_strerror (errsv)); + g_warning ("error setting multicast loopback: %s", error->message); + g_error_free (error); return; } @@ -1536,33 +1537,28 @@ g_socket_set_multicast_loopback (GSocket *socket, guint g_socket_get_multicast_ttl (GSocket *socket) { - int result; - guint value, optlen; + GError *error = NULL; + gint value; - g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + g_return_val_if_fail (G_IS_SOCKET (socket), 0); if (socket->priv->family == G_SOCKET_FAMILY_IPV4) { - guchar optval; - - optlen = sizeof (optval); - result = getsockopt (socket->priv->fd, IPPROTO_IP, IP_MULTICAST_TTL, - &optval, &optlen); - value = optval; + g_socket_get_option (socket, IPPROTO_IP, IP_MULTICAST_TTL, + &value, &error); } else if (socket->priv->family == G_SOCKET_FAMILY_IPV6) { - optlen = sizeof (value); - result = getsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - &value, &optlen); + g_socket_get_option (socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &value, &error); } else g_return_val_if_reached (FALSE); - if (result < 0) + if (error) { - int errsv = get_socket_errno (); - g_warning ("error getting multicast ttl: %s", socket_strerror (errsv)); + g_warning ("error getting multicast ttl: %s", error->message); + g_error_free (error); return FALSE; } @@ -1584,29 +1580,29 @@ void g_socket_set_multicast_ttl (GSocket *socket, guint ttl) { - int result; + GError *error = NULL; g_return_if_fail (G_IS_SOCKET (socket)); if (socket->priv->family == G_SOCKET_FAMILY_IPV4) { - guchar optval = (guchar)ttl; - - result = setsockopt (socket->priv->fd, IPPROTO_IP, IP_MULTICAST_TTL, - &optval, sizeof (optval)); + g_socket_set_option (socket, IPPROTO_IP, IP_MULTICAST_TTL, + ttl, &error); } else if (socket->priv->family == G_SOCKET_FAMILY_IPV6) { - result = setsockopt (socket->priv->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - &ttl, sizeof (ttl)); + 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 (result < 0) + if (error) { - int errsv = get_socket_errno (); - g_warning ("error setting multicast ttl: %s", socket_strerror (errsv)); + g_warning ("error setting multicast ttl: %s", error->message); + g_error_free (error); return; } @@ -1673,7 +1669,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. @@ -1709,7 +1705,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); @@ -1742,7 +1738,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); @@ -1846,14 +1842,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. * @@ -1866,28 +1868,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) @@ -1902,6 +1923,60 @@ 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, @@ -1916,13 +1991,12 @@ g_socket_multicast_group_operation (GSocket *socket, 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 (g_inet_address_get_family (group) == socket->priv->family, FALSE); if (!check_socket (socket, error)) return FALSE; native_addr = g_inet_address_to_bytes (group); - if (socket->priv->family == G_SOCKET_FAMILY_IPV4) + if (g_inet_address_get_family (group) == G_SOCKET_FAMILY_IPV4) { #ifdef HAVE_IP_MREQN struct ip_mreqn mc_req; @@ -1930,6 +2004,7 @@ g_socket_multicast_group_operation (GSocket *socket, 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 @@ -1937,6 +2012,11 @@ g_socket_multicast_group_operation (GSocket *socket, 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 @@ -1959,10 +2039,11 @@ g_socket_multicast_group_operation (GSocket *socket, result = setsockopt (socket->priv->fd, IPPROTO_IP, optname, &mc_req, sizeof (mc_req)); } - else if (socket->priv->family == G_SOCKET_FAMILY_IPV6) + 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) @@ -2085,12 +2166,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; @@ -2139,6 +2219,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 && @@ -2328,7 +2411,6 @@ gboolean g_socket_check_connect_result (GSocket *socket, GError **error) { - guint optlen; int value; g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); @@ -2336,13 +2418,12 @@ g_socket_check_connect_result (GSocket *socket, 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; } @@ -2368,28 +2449,56 @@ g_socket_check_connect_result (GSocket *socket, * * 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 -1 on error. + * without blocking or truncating, or -1 on error. * * Since: 2.32 */ gssize g_socket_get_available_bytes (GSocket *socket) { -#ifndef G_OS_WIN32 - gulong avail = 0; +#ifdef G_OS_WIN32 + const gint bufsize = 64 * 1024; + static guchar *buf = NULL; + u_long avail; #else - gint avail = 0; + gint avail; #endif g_return_val_if_fail (G_IS_SOCKET (socket), -1); -#ifndef G_OS_WIN32 +#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) - return -1; + avail = -1; #else - if (ioctlsocket (socket->priv->fd, FIONREAD, &avail) == SOCKET_ERROR) - return -1; + 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; @@ -2398,8 +2507,8 @@ g_socket_get_available_bytes (GSocket *socket) /** * 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. @@ -2448,8 +2557,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 @@ -2479,6 +2588,9 @@ g_socket_receive_with_blocking (GSocket *socket, 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; @@ -2654,6 +2766,9 @@ g_socket_send_with_blocking (GSocket *socket, 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; @@ -2920,19 +3035,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) @@ -2942,8 +3044,8 @@ broken_dispatch (GSource *source, static GSourceFuncs broken_funcs = { - broken_prepare, - broken_check, + NULL, + NULL, broken_dispatch, NULL }; @@ -3005,24 +3107,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 @@ -3097,57 +3203,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; - *timeout = 0; - return TRUE; - } - } - else - *timeout = -1; - -#ifdef G_OS_WIN32 - socket_source->pollfd.revents = update_condition (socket_source->socket); -#endif + *timeout = -1; - if ((socket_source->condition & socket_source->pollfd.revents) != 0) - return TRUE; - - 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, @@ -3157,24 +3241,29 @@ 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 - if (socket_source->socket->priv->timed_out) - socket_source->pollfd.revents |= socket_source->condition & (G_IO_IN | G_IO_OUT); - ret = (*func) (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); + } - if (socket->priv->timeout) - socket_source->timeout_time = g_get_monotonic_time () + - socket->priv->timeout * 1000000; + 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 - socket_source->timeout_time = 0; + g_source_set_ready_time (source, -1); return ret; } @@ -3192,12 +3281,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 @@ -3230,12 +3313,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)g_cclosure_marshal_generic, }; static GSource * @@ -3256,7 +3342,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"); @@ -3265,30 +3351,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; } @@ -3299,8 +3385,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. * @@ -3386,6 +3473,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); @@ -3567,7 +3655,7 @@ g_socket_condition_timed_wait (GSocket *socket, if (timeout != -1) { - timeout -= (g_get_monotonic_time () - start_time) * 1000; + timeout -= (g_get_monotonic_time () - start_time) / 1000; if (timeout < 0) timeout = 0; } @@ -3613,7 +3701,7 @@ g_socket_condition_timed_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(). * @@ -3659,10 +3747,18 @@ g_socket_send_message (GSocket *socket, 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; @@ -3820,7 +3916,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; } @@ -3888,6 +3984,60 @@ 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 @@ -3985,6 +4135,9 @@ g_socket_receive_message (GSocket *socket, 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; @@ -4110,11 +4263,7 @@ 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 */ @@ -4122,33 +4271,36 @@ g_socket_receive_message (GSocket *socket, GPtrArray *my_messages = NULL; struct cmsghdr *cmsg; - 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; @@ -4247,10 +4399,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 */ @@ -4301,42 +4450,72 @@ g_socket_get_credentials (GSocket *socket, ret = NULL; -#if defined(__linux__) || defined(__OpenBSD__) +#if G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED + +#ifdef SO_PEERCRED { - socklen_t optlen; -#if defined(__linux__) - struct ucred native_creds; - optlen = sizeof (struct ucred); -#elif defined(__OpenBSD__) - struct sockpeercred native_creds; - optlen = sizeof (struct sockpeercred); -#endif + 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) { - 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, + native_creds_buf); } - else + } +#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, -#if defined(__linux__) - G_CREDENTIALS_TYPE_LINUX_UCRED, -#elif defined(__OpenBSD__) - G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED, -#endif - &native_creds); + G_CREDENTIALS_NATIVE_TYPE, + &cred); + } + } +#elif G_CREDENTIALS_USE_SOLARIS_UCRED + { + ucred_t *ucred = NULL; + + if (getpeerucred (socket->priv->fd, &ucred) == 0) + { + ret = g_credentials_new (); + g_credentials_set_native (ret, + G_CREDENTIALS_TYPE_SOLARIS_UCRED, + ucred); + ucred_free (ucred); } } #else + #error "G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED is set but this is no code for this platform" +#endif + + if (!ret) + { + int errsv = get_socket_errno (); + + g_set_error (error, + G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Unable to read socket credentials: %s"), + socket_strerror (errsv)); + } + +#else + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, @@ -4345,3 +4524,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; +} +