X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgsocket.c;h=4d863cecdf56e8bb9b2be7ef081136746aefe909;hb=e55a953642a9e402f4363f9fa347b6061dd78990;hp=a28546f322274e6392325449bb38d524a3c9e8af;hpb=c5b6f774c4c133b5c1a9ebbd046a54ef6f177f84;p=platform%2Fupstream%2Fglib.git
diff --git a/gio/gsocket.c b/gio/gsocket.c
index a28546f..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
@@ -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,31 +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
- GError *error = NULL;
-#else
- gulong arg;
-#endif
-
-#ifndef G_OS_WIN32
- if (!g_unix_set_fd_nonblocking (fd, TRUE, &error))
- {
- g_warning ("Error setting socket nonblocking: %s", error->message);
- g_clear_error (&error);
- }
-#else
- arg = TRUE;
-
- if (ioctlsocket (fd, FIONBIO, &arg) == SOCKET_ERROR)
- {
- int errsv = get_socket_errno ();
- g_warning ("Error setting socket status flags: %s", socket_strerror (errsv));
- }
+/* 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,
@@ -298,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;
@@ -315,19 +306,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 +334,6 @@ g_socket_details_from_fd (GSocket *socket)
goto err;
}
- g_assert (optlen == sizeof value);
switch (value)
{
case SOCK_STREAM:
@@ -390,8 +372,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 +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
{
@@ -473,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,
@@ -480,7 +500,6 @@ g_socket_create_socket (GSocketFamily family,
GError **error)
{
gint native_type;
- gint fd;
switch (type)
{
@@ -500,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
@@ -558,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
@@ -707,6 +728,7 @@ static void
g_socket_finalize (GObject *object)
{
GSocket *socket = G_SOCKET (object);
+ gint i;
g_clear_error (&socket->priv->construct_error);
@@ -725,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);
}
@@ -735,22 +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 ();
- (type); /* To avoid -Wunused-but-set-variable */
#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;
@@ -859,14 +889,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));
@@ -926,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;
@@ -934,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
}
@@ -1113,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));
@@ -1121,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;
}
@@ -1275,34 +1305,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;
@@ -1322,29 +1347,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;
}
@@ -1357,7 +1382,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
*
@@ -1366,19 +1391,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;
}
@@ -1388,11 +1410,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
*/
@@ -1400,21 +1422,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;
}
@@ -1436,30 +1454,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;
}
@@ -1482,7 +1498,7 @@ void
g_socket_set_multicast_loopback (GSocket *socket,
gboolean loopback)
{
- int result;
+ GError *error = NULL;
g_return_if_fail (G_IS_SOCKET (socket));
@@ -1490,25 +1506,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;
}
@@ -1529,33 +1543,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;
}
@@ -1577,29 +1586,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;
}
@@ -1666,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.
@@ -1702,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);
@@ -1735,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);
@@ -1839,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.
*
@@ -1859,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)
@@ -1895,6 +1929,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,
@@ -1909,13 +1997,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;
@@ -1923,6 +2010,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
@@ -1930,6 +2018,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
@@ -1952,10 +2045,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)
@@ -2078,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;
@@ -2132,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 &&
@@ -2321,7 +2417,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);
@@ -2329,13 +2424,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;
}
@@ -2361,28 +2455,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;
@@ -2391,8 +2513,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.
@@ -2441,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
@@ -2472,6 +2594,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;
@@ -2562,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
@@ -2647,6 +2769,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;
@@ -2695,7 +2820,7 @@ g_socket_send_with_blocking (GSocket *socket,
/**
* g_socket_send_to:
* @socket: a #GSocket
- * @address: a #GSocketAddress, or %NULL
+ * @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
@@ -2913,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)
@@ -2935,8 +3047,8 @@ broken_dispatch (GSource *source,
static GSourceFuncs broken_funcs =
{
- broken_prepare,
- broken_check,
+ NULL,
+ NULL,
broken_dispatch,
NULL
};
@@ -2998,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
@@ -3090,57 +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;
- *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,
@@ -3150,24 +3244,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;
}
@@ -3185,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
@@ -3223,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)g_cclosure_marshal_generic,
};
static GSource *
@@ -3249,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");
@@ -3258,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;
}
@@ -3292,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.
*
@@ -3379,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);
@@ -3560,7 +3658,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;
}
@@ -3584,7 +3682,7 @@ g_socket_condition_timed_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
@@ -3606,7 +3704,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().
*
@@ -3652,10 +3750,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;
@@ -3813,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;
}
@@ -3881,6 +3987,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
@@ -3978,6 +4138,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;
@@ -4103,11 +4266,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 */
@@ -4115,33 +4274,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;
@@ -4240,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 */
@@ -4294,42 +4453,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,
@@ -4338,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;
+}
+