2016-10-26 13:57:37 -07:00
|
|
|
#define EFL_NET_SERVER_UNIX_PROTECTED 1
|
|
|
|
#define EFL_NET_SERVER_FD_PROTECTED 1
|
|
|
|
#define EFL_NET_SERVER_PROTECTED 1
|
|
|
|
#define EFL_LOOP_FD_PROTECTED 1
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "Ecore.h"
|
|
|
|
#include "Ecore_Con.h"
|
|
|
|
#include "ecore_con_private.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
|
|
# include <sys/socket.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_UN_H
|
|
|
|
#include <sys/un.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* no include EVIL as it's not supposed to be compiled on Windows */
|
|
|
|
|
|
|
|
#define MY_CLASS EFL_NET_SERVER_UNIX_CLASS
|
|
|
|
|
|
|
|
typedef struct _Efl_Net_Server_Unix_Data
|
|
|
|
{
|
2016-11-25 12:01:29 -08:00
|
|
|
unsigned int leading_directories_create_mode;
|
|
|
|
Eina_Bool leading_directories_create;
|
2016-10-26 13:57:37 -07:00
|
|
|
Eina_Bool unlink_before_bind;
|
|
|
|
} Efl_Net_Server_Unix_Data;
|
|
|
|
|
|
|
|
EOLIAN static void
|
|
|
|
_efl_net_server_unix_efl_object_destructor(Eo *o, Efl_Net_Server_Unix_Data *pd EINA_UNUSED)
|
|
|
|
{
|
|
|
|
SOCKET fd = efl_loop_fd_get(o);
|
|
|
|
|
|
|
|
if (fd != INVALID_SOCKET)
|
|
|
|
{
|
|
|
|
const char *address = efl_net_server_address_get(o);
|
|
|
|
if ((address) && (strncmp(address, "abstract:", strlen("abstract:")) != 0))
|
|
|
|
unlink(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
efl_destructor(efl_super(o, MY_CLASS));
|
|
|
|
}
|
|
|
|
|
2016-11-23 12:38:24 -08:00
|
|
|
static Eina_Error
|
|
|
|
_efl_net_server_unix_bind(Eo *o, Efl_Net_Server_Unix_Data *pd)
|
2016-10-26 13:57:37 -07:00
|
|
|
{
|
|
|
|
const char *address = efl_net_server_address_get(o);
|
|
|
|
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
|
|
|
socklen_t addrlen;
|
2017-12-20 04:10:53 -08:00
|
|
|
SOCKET fd = INVALID_SOCKET;
|
2016-10-26 13:57:37 -07:00
|
|
|
Eina_Error err = 0;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
efl_net_server_fd_family_set(o, AF_UNIX);
|
|
|
|
|
2016-12-09 07:43:59 -08:00
|
|
|
if ((pd->leading_directories_create) &&
|
|
|
|
(strncmp(address, "abstract:", strlen("abstract:")) != 0))
|
2016-11-25 12:01:29 -08:00
|
|
|
_ecore_con_local_mkpath(address, pd->leading_directories_create_mode);
|
|
|
|
|
2016-10-26 13:57:37 -07:00
|
|
|
do
|
|
|
|
{
|
|
|
|
fd = efl_net_socket4(AF_UNIX, SOCK_STREAM, 0,
|
|
|
|
efl_net_server_fd_close_on_exec_get(o));
|
|
|
|
if (fd == INVALID_SOCKET)
|
|
|
|
{
|
|
|
|
err = efl_net_socket_error_get();
|
|
|
|
ERR("socket(AF_UNIX, SOCK_STREAM, 0): %s", eina_error_msg_get(err));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(address, "abstract:", strlen("abstract:")) == 0)
|
|
|
|
{
|
|
|
|
const char *path = address + strlen("abstract:");
|
|
|
|
if (strlen(path) + 2 > sizeof(addr.sun_path))
|
|
|
|
{
|
|
|
|
ERR("abstract path is too long: %s", path);
|
2016-12-11 16:17:15 -08:00
|
|
|
err = EFL_NET_ERROR_COULDNT_RESOLVE_HOST;
|
2016-10-26 13:57:37 -07:00
|
|
|
}
|
|
|
|
addr.sun_path[0] = '\0';
|
|
|
|
memcpy(addr.sun_path + 1, path, strlen(path) + 1);
|
|
|
|
addrlen = strlen(path) + 2 + offsetof(struct sockaddr_un, sun_path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char *path = address;
|
|
|
|
if (strlen(path) + 1 > sizeof(addr.sun_path))
|
|
|
|
{
|
|
|
|
ERR("path is too long: %s", path);
|
2016-12-11 16:17:15 -08:00
|
|
|
err = EFL_NET_ERROR_COULDNT_RESOLVE_HOST;
|
2016-10-26 13:57:37 -07:00
|
|
|
}
|
|
|
|
memcpy(addr.sun_path, path, strlen(path) + 1);
|
|
|
|
addrlen = strlen(path) + 1 + offsetof(struct sockaddr_un, sun_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((pd->unlink_before_bind) && (addr.sun_path[0] != '\0'))
|
|
|
|
{
|
|
|
|
DBG("unlinking AF_UNIX path '%s'", addr.sun_path);
|
|
|
|
unlink(addr.sun_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = bind(fd, (struct sockaddr *)&addr, addrlen);
|
|
|
|
if (r != 0)
|
|
|
|
{
|
|
|
|
err = efl_net_socket_error_get();
|
|
|
|
if ((err == EADDRINUSE) && (pd->unlink_before_bind) && (addr.sun_path[0] != '\0'))
|
|
|
|
{
|
|
|
|
closesocket(fd);
|
2017-12-20 04:10:53 -08:00
|
|
|
fd = INVALID_SOCKET;
|
|
|
|
err = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((err == EADDRINUSE) && (addr.sun_path[0] != '\0') &&
|
|
|
|
(connect(fd, (struct sockaddr *)&addr, addrlen) != 0))
|
|
|
|
{
|
|
|
|
DBG("bind(" SOCKET_FMT ", %s): failed with EADDRINUSE but connect also failed, so unlink socket file and try again", fd, address);
|
|
|
|
closesocket(fd);
|
|
|
|
unlink(addr.sun_path);
|
|
|
|
fd = INVALID_SOCKET;
|
2016-10-26 13:57:37 -07:00
|
|
|
err = 0;
|
|
|
|
continue;
|
|
|
|
}
|
2016-11-18 06:17:08 -08:00
|
|
|
DBG("bind(" SOCKET_FMT ", %s): %s", fd, address, eina_error_msg_get(err));
|
2016-10-26 13:57:37 -07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
while (pd->unlink_before_bind);
|
|
|
|
|
2017-12-20 04:10:53 -08:00
|
|
|
if (fd == INVALID_SOCKET) goto error;
|
2016-10-26 13:57:37 -07:00
|
|
|
efl_loop_fd_set(o, fd);
|
|
|
|
|
|
|
|
r = listen(fd, 0);
|
|
|
|
if (r != 0)
|
|
|
|
{
|
|
|
|
err = efl_net_socket_error_get();
|
2016-11-18 06:17:08 -08:00
|
|
|
DBG("listen(" SOCKET_FMT "): %s", fd, eina_error_msg_get(err));
|
2016-10-26 13:57:37 -07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
addrlen = sizeof(addr);
|
|
|
|
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
|
|
|
{
|
|
|
|
err = efl_net_socket_error_get();
|
2016-11-18 06:17:08 -08:00
|
|
|
ERR("getsockname(" SOCKET_FMT "): %s", fd, eina_error_msg_get(err));
|
2016-10-26 13:57:37 -07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char str[sizeof(addr) + sizeof("abstract:")];
|
|
|
|
if (!efl_net_unix_fmt(str, sizeof(str), fd, &addr, addrlen))
|
|
|
|
ERR("could not format unix address");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
efl_net_server_address_set(o, str);
|
|
|
|
address = efl_net_server_address_get(o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-18 06:17:08 -08:00
|
|
|
DBG("fd=" SOCKET_FMT " serving at %s", fd, address);
|
2016-10-26 13:57:37 -07:00
|
|
|
efl_net_server_serving_set(o, EINA_TRUE);
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
|
2016-11-09 11:25:27 -08:00
|
|
|
if (fd != INVALID_SOCKET) closesocket(fd);
|
2016-11-18 06:17:08 -08:00
|
|
|
efl_loop_fd_set(o, SOCKET_TO_LOOP_FD(INVALID_SOCKET));
|
2016-10-26 13:57:37 -07:00
|
|
|
}
|
2016-11-23 12:38:24 -08:00
|
|
|
return err;
|
2016-10-26 13:57:37 -07:00
|
|
|
}
|
|
|
|
|
2016-11-01 11:01:57 -07:00
|
|
|
EOLIAN static Eina_Error
|
|
|
|
_efl_net_server_unix_efl_net_server_fd_socket_activate(Eo *o, Efl_Net_Server_Unix_Data *pd EINA_UNUSED, const char *address)
|
|
|
|
{
|
2016-11-18 06:17:08 -08:00
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL((SOCKET)efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
2016-11-01 11:01:57 -07:00
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
|
|
|
|
|
|
|
#ifndef HAVE_SYSTEMD
|
|
|
|
return efl_net_server_fd_socket_activate(efl_super(o, MY_CLASS), address);
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
|
|
|
|
Eina_Bool listening;
|
|
|
|
Eina_Error err;
|
2017-07-28 18:35:39 -07:00
|
|
|
struct sockaddr_storage addr;
|
2016-11-01 11:01:57 -07:00
|
|
|
socklen_t addrlen;
|
2016-11-18 06:17:08 -08:00
|
|
|
SOCKET fd;
|
2016-11-01 11:01:57 -07:00
|
|
|
|
|
|
|
err = efl_net_ip_socket_activate_check(address, AF_UNIX, SOCK_STREAM, &listening);
|
|
|
|
if (err) return err;
|
|
|
|
|
|
|
|
err = efl_net_server_fd_socket_activate(efl_super(o, MY_CLASS), address);
|
|
|
|
if (err) return err;
|
|
|
|
|
|
|
|
fd = efl_loop_fd_get(o);
|
|
|
|
|
|
|
|
if (!listening)
|
|
|
|
{
|
|
|
|
if (listen(fd, 0) != 0)
|
|
|
|
{
|
|
|
|
err = efl_net_socket_error_get();
|
2016-11-18 06:17:08 -08:00
|
|
|
DBG("listen(" SOCKET_FMT "): %s", fd, eina_error_msg_get(err));
|
2016-11-01 11:01:57 -07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addrlen = sizeof(addr);
|
|
|
|
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
|
|
|
{
|
|
|
|
err = efl_net_socket_error_get();
|
2016-11-18 06:17:08 -08:00
|
|
|
ERR("getsockname(" SOCKET_FMT "): %s", fd, eina_error_msg_get(err));
|
2016-11-01 11:01:57 -07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
else if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
|
|
|
|
efl_net_server_address_set(o, buf);
|
|
|
|
|
2016-11-18 06:17:08 -08:00
|
|
|
DBG("fd=" SOCKET_FMT " serving at %s", fd, address);
|
2016-11-01 11:01:57 -07:00
|
|
|
efl_net_server_serving_set(o, EINA_TRUE);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
efl_net_server_fd_family_set(o, AF_UNSPEC);
|
2016-11-18 06:17:08 -08:00
|
|
|
efl_loop_fd_set(o, SOCKET_TO_LOOP_FD(INVALID_SOCKET));
|
2016-11-01 11:01:57 -07:00
|
|
|
closesocket(fd);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-10-26 13:57:37 -07:00
|
|
|
EOLIAN static Eina_Error
|
|
|
|
_efl_net_server_unix_efl_net_server_serve(Eo *o, Efl_Net_Server_Unix_Data *pd, const char *address)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(address[0] == '\0', EINVAL);
|
|
|
|
|
|
|
|
efl_net_server_address_set(o, address);
|
|
|
|
|
2016-11-23 12:38:24 -08:00
|
|
|
return _efl_net_server_unix_bind(o, pd);
|
2016-10-26 13:57:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_server_unix_efl_net_server_fd_client_add(Eo *o, Efl_Net_Server_Unix_Data *pd EINA_UNUSED, int client_fd)
|
|
|
|
{
|
|
|
|
Eo *client = efl_add(EFL_NET_SOCKET_UNIX_CLASS, o,
|
|
|
|
efl_io_closer_close_on_exec_set(efl_added, efl_net_server_fd_close_on_exec_get(o)),
|
|
|
|
efl_io_closer_close_on_destructor_set(efl_added, EINA_TRUE),
|
|
|
|
efl_loop_fd_set(efl_added, client_fd));
|
|
|
|
if (!client)
|
|
|
|
{
|
2016-11-18 06:17:08 -08:00
|
|
|
ERR("could not create client object fd=" SOCKET_FMT, (SOCKET)client_fd);
|
2016-10-26 13:57:37 -07:00
|
|
|
closesocket(client_fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
efl_net_server: add 'client_announce', share logic and fix a bug.
I just realized that if a client is not referenced it would leak in
the 'ssl' server as we must del it.
However, if we del the SSL socket, we're going to close the underlying
TCP. But we're from the TCP "client,add" callback and this causes
issues since "closed" will be emitted, our close callback will
unparent the client, which lead to it being deleted.
The proper solution is to only monitor "closed" if the client is
accepted. Otherwise we just check if it was closed, if we're the
parent, etc...
Fixing this in all servers were painful, we could share since most
inherit from Efl.Net.Server.Fd. Then add the "client_announce"
protected method to do it, and document how it should work.
2016-11-24 23:32:16 -08:00
|
|
|
efl_net_server_client_announce(o, client);
|
2016-10-26 13:57:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_server_unix_efl_net_server_fd_client_reject(Eo *o, Efl_Net_Server_Unix_Data *pd EINA_UNUSED, int client_fd)
|
|
|
|
{
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
socklen_t addrlen;
|
|
|
|
char str[sizeof(addr) + sizeof("abstract:")] = "";
|
|
|
|
|
|
|
|
addrlen = sizeof(addr);
|
|
|
|
if (getpeername(client_fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
|
|
|
ERR("getpeername(%d): %s", client_fd, eina_error_msg_get(efl_net_socket_error_get()));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!efl_net_unix_fmt(str, sizeof(str), client_fd, &addr, addrlen))
|
2016-11-18 06:17:08 -08:00
|
|
|
ERR("could not format rejected client unix address fd=" SOCKET_FMT, (SOCKET)client_fd);
|
2016-10-26 13:57:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
closesocket(client_fd);
|
|
|
|
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_server_unix_unlink_before_bind_set(Eo *o EINA_UNUSED, Efl_Net_Server_Unix_Data *pd, Eina_Bool unlink_before_bind)
|
|
|
|
{
|
|
|
|
pd->unlink_before_bind = unlink_before_bind;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_server_unix_unlink_before_bind_get(const Eo *o EINA_UNUSED, Efl_Net_Server_Unix_Data *pd)
|
2016-10-26 13:57:37 -07:00
|
|
|
{
|
|
|
|
return pd->unlink_before_bind;
|
|
|
|
}
|
|
|
|
|
2016-11-25 12:01:29 -08:00
|
|
|
|
|
|
|
static void
|
|
|
|
_efl_net_server_unix_leading_directories_create_set(Eo *o EINA_UNUSED, Efl_Net_Server_Unix_Data *pd, Eina_Bool do_it, unsigned int mode)
|
|
|
|
{
|
|
|
|
pd->leading_directories_create = do_it;
|
|
|
|
pd->leading_directories_create_mode = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-04-17 11:09:44 -07:00
|
|
|
_efl_net_server_unix_leading_directories_create_get(const Eo *o EINA_UNUSED, Efl_Net_Server_Unix_Data *pd, Eina_Bool *do_it, unsigned int *mode)
|
2016-11-25 12:01:29 -08:00
|
|
|
{
|
|
|
|
if (do_it) *do_it = pd->leading_directories_create;
|
|
|
|
if (mode) *mode = pd->leading_directories_create_mode;
|
|
|
|
}
|
|
|
|
|
2016-10-26 13:57:37 -07:00
|
|
|
#include "efl_net_server_unix.eo.c"
|