#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include "gsksocketaddress.h"
#include "gskerrno.h"
#include "gskerror.h"
#include "gskmacros.h"
#include "gskghelpers.h"
#include "gskutils.h"
/* implementation of the stuff which creates connecting file-descriptor,
* and also finishing the connection if the connection process blocks.
*/
/* --- connecting to a socket-address --- */
/**
* gsk_socket_address_connect_fd:
* @address: the address to connect to.
* @is_connected: whether the connection succeeded completely.
* @error: an optional error return.
*
* Begin connecting to a location by address.
*
* If the connection is fully established before returning to
* the caller, then *@is_connected will be set to TRUE
* and a non-negative file descriptor will be returned.
*
* Sometimes connections only partially succeed,
* in which case *@is_connected will be set to FALSE,
* and you must call gsk_socket_address_finish_fd() whenever the
* file-description polls ready to input or output.
*
* If the connect fails immediately, -1 will be returned
* and *@error will be set if @error is non-NULL.
*
* returns: the connecting or connected file-descriptor, or -1 on error.
*/
int
gsk_socket_address_connect_fd (GskSocketAddress *address,
gboolean *is_connected,
GError **error)
{
guint size = gsk_socket_address_sizeof_native (address);
struct sockaddr *addr = alloca (size);
int fd;
if (!gsk_socket_address_to_native (address, addr, error))
return -1;
retry:
fd = socket (gsk_socket_address_protocol_family (address), SOCK_STREAM, 0);
if (fd < 0)
{
if (gsk_errno_is_ignorable (errno))
goto retry;
gsk_errno_fd_creation_failed ();
if (error != NULL && *error == NULL)
{
char *addr_str = gsk_socket_address_to_string (address);
int e = errno;
*error = g_error_new (GSK_G_ERROR_DOMAIN,
gsk_error_code_from_errno (e),
_("socket(2) failed when creating a connection (%s): %s"),
addr_str,
g_strerror (e));
g_free (addr_str);
}
return -1;
}
gsk_fd_set_nonblocking (fd);
gsk_fd_set_close_on_exec (fd, TRUE);
if (connect (fd, addr, size) < 0)
{
int e = errno;
if (e == EINPROGRESS)
{
/* neither success nor failure: the connection process
* itself is blocking */
*is_connected = FALSE;
return fd;
}
if (error != NULL && *error == NULL)
{
char *addr_str = gsk_socket_address_to_string (address);
*error = g_error_new (GSK_G_ERROR_DOMAIN,
gsk_error_code_from_errno (e),
_("connect(2) failed when creating a connection (%s): %s"),
addr_str,
g_strerror (e));
g_free (addr_str);
}
close (fd);
return -1;
}
*is_connected = TRUE;
return fd;
}
/**
* gsk_socket_address_finish_fd:
* @fd: a file descriptor which may be done connecting.
* @error: an optional error return.
*
* Finish connecting a partially connected file-descriptor.
*
* returns: TRUE if the connection is now established,
* otherwise it returns FALSE and will set *@error if an error occurred.
*/
gboolean
gsk_socket_address_finish_fd (int fd,
GError **error)
{
int err = gsk_errno_from_fd (fd);
if (err == 0)
return TRUE;
if (!gsk_errno_is_ignorable (err))
g_set_error (error, GSK_G_ERROR_DOMAIN,
gsk_error_code_from_errno (err),
_("error finishing connection: %s"),
g_strerror (err));
return FALSE;
}
syntax highlighted by Code2HTML, v. 0.9.1