forked from enlightenment/efl
efl_net_server support systemd socket activation.
It includes extensive verifications to avoid mistakes and usage of incorrect sockets.
This commit is contained in:
parent
3ac1812a1f
commit
c2630c829f
|
@ -504,6 +504,8 @@ static const Ecore_Getopt options = {
|
||||||
{
|
{
|
||||||
ECORE_GETOPT_STORE_TRUE('e', "echo",
|
ECORE_GETOPT_STORE_TRUE('e', "echo",
|
||||||
"Behave as 'echo' server, send back to client all the data receive"),
|
"Behave as 'echo' server, send back to client all the data receive"),
|
||||||
|
ECORE_GETOPT_STORE_TRUE(0, "socket-activated",
|
||||||
|
"Try to use $LISTEN_FDS from systemd, if not do a regular serve()"),
|
||||||
ECORE_GETOPT_STORE_UINT('l', "clients-limit",
|
ECORE_GETOPT_STORE_UINT('l', "clients-limit",
|
||||||
"If set will limit number of clients to accept"),
|
"If set will limit number of clients to accept"),
|
||||||
ECORE_GETOPT_STORE_TRUE('r', "clients-reject-excess",
|
ECORE_GETOPT_STORE_TRUE('r', "clients-reject-excess",
|
||||||
|
@ -564,9 +566,11 @@ main(int argc, char **argv)
|
||||||
Eina_List *crls = NULL;
|
Eina_List *crls = NULL;
|
||||||
Eina_List *cas = NULL;
|
Eina_List *cas = NULL;
|
||||||
char *cipher_choice = NULL;
|
char *cipher_choice = NULL;
|
||||||
|
Eina_Bool socket_activated = EINA_FALSE;
|
||||||
Eina_Bool quit_option = EINA_FALSE;
|
Eina_Bool quit_option = EINA_FALSE;
|
||||||
Ecore_Getopt_Value values[] = {
|
Ecore_Getopt_Value values[] = {
|
||||||
ECORE_GETOPT_VALUE_BOOL(echo),
|
ECORE_GETOPT_VALUE_BOOL(echo),
|
||||||
|
ECORE_GETOPT_VALUE_BOOL(socket_activated),
|
||||||
ECORE_GETOPT_VALUE_UINT(clients_limit),
|
ECORE_GETOPT_VALUE_UINT(clients_limit),
|
||||||
ECORE_GETOPT_VALUE_BOOL(clients_reject_excess),
|
ECORE_GETOPT_VALUE_BOOL(clients_reject_excess),
|
||||||
ECORE_GETOPT_VALUE_BOOL(ipv6_only),
|
ECORE_GETOPT_VALUE_BOOL(ipv6_only),
|
||||||
|
@ -659,6 +663,8 @@ main(int argc, char **argv)
|
||||||
efl_net_server_fd_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
efl_net_server_fd_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
||||||
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
||||||
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
||||||
|
|
||||||
|
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
|
||||||
}
|
}
|
||||||
else if (cls == EFL_NET_SERVER_UDP_CLASS)
|
else if (cls == EFL_NET_SERVER_UDP_CLASS)
|
||||||
{
|
{
|
||||||
|
@ -677,6 +683,7 @@ main(int argc, char **argv)
|
||||||
efl_net_server_fd_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
efl_net_server_fd_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
||||||
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
||||||
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
||||||
|
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
|
||||||
}
|
}
|
||||||
else if (cls == EFL_NET_SERVER_SSL_CLASS)
|
else if (cls == EFL_NET_SERVER_SSL_CLASS)
|
||||||
{
|
{
|
||||||
|
@ -708,11 +715,13 @@ main(int argc, char **argv)
|
||||||
efl_net_server_ssl_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
efl_net_server_ssl_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
||||||
efl_net_server_ssl_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
efl_net_server_ssl_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
||||||
efl_net_server_ssl_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
efl_net_server_ssl_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
||||||
|
if (socket_activated) efl_net_server_ssl_socket_activate(server, address);
|
||||||
}
|
}
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
else if (cls == EFL_NET_SERVER_UNIX_CLASS)
|
else if (cls == EFL_NET_SERVER_UNIX_CLASS)
|
||||||
{
|
{
|
||||||
efl_net_server_unix_unlink_before_bind_set(server, EINA_TRUE); /* makes testing easier */
|
efl_net_server_unix_unlink_before_bind_set(server, EINA_TRUE); /* makes testing easier */
|
||||||
|
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -721,12 +730,18 @@ main(int argc, char **argv)
|
||||||
* with the object to add more properties that couldn't be done
|
* with the object to add more properties that couldn't be done
|
||||||
* during efl_add().
|
* during efl_add().
|
||||||
*/
|
*/
|
||||||
err = efl_net_server_serve(server, address);
|
if (!efl_net_server_serving_get(server))
|
||||||
if (err)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "ERROR: could not serve(%s): %s\n",
|
if (socket_activated)
|
||||||
address, eina_error_msg_get(err));
|
fprintf(stderr, "WARNING: --socket-activated, but not able to use $LISTEN_FDS descriptors. Try to start the server...\n");
|
||||||
goto end_server;
|
|
||||||
|
err = efl_net_server_serve(server, address);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: could not serve(%s): %s\n",
|
||||||
|
address, eina_error_msg_get(err));
|
||||||
|
goto end_server;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ecore_main_loop_begin();
|
ecore_main_loop_begin();
|
||||||
|
|
|
@ -3175,6 +3175,220 @@ efl_net_ip_port_split(char *buf, const char **p_host, const char **p_port)
|
||||||
return EINA_TRUE;
|
return EINA_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSTEMD
|
||||||
|
Eina_Error
|
||||||
|
efl_net_ip_socket_activate_check(const char *address, int family, int type, Eina_Bool *listening)
|
||||||
|
{
|
||||||
|
SOCKET fd = SD_LISTEN_FDS_START + sd_fd_index;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (sd_fd_index >= sd_fd_max) return ENOENT;
|
||||||
|
|
||||||
|
if (family == AF_UNIX)
|
||||||
|
{
|
||||||
|
char buf[sizeof(struct sockaddr_un)] = "";
|
||||||
|
const char *sun_path;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (strncmp(address, "abstract:", strlen("abstract:")) == 0)
|
||||||
|
{
|
||||||
|
const char *path = address + strlen("abstract:");
|
||||||
|
if (strlen(path) + 2 > sizeof(buf))
|
||||||
|
{
|
||||||
|
ERR("abstract path is too long: %s", path);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
buf[0] = '\0';
|
||||||
|
memcpy(buf + 1, path, strlen(path) + 1);
|
||||||
|
sun_path = buf;
|
||||||
|
len = strlen(path) + 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strlen(address) + 1 > sizeof(buf))
|
||||||
|
{
|
||||||
|
ERR("path is too long: %s", address);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
sun_path = address;
|
||||||
|
len = strlen(address) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_is_socket_unix(fd, type, 0, sun_path, len);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
ERR("socket %d is not of family=%d, type=%d", fd, family, type);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (listening) *listening = (r == 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if ((family == AF_UNSPEC) || (family == AF_INET) || (family == AF_INET6))
|
||||||
|
{
|
||||||
|
char *str;
|
||||||
|
const char *host, *port;
|
||||||
|
struct sockaddr_storage sock_addr;
|
||||||
|
struct sockaddr_storage want_addr = { .ss_family = family };
|
||||||
|
socklen_t addrlen;
|
||||||
|
Eina_Error err;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
r = sd_is_socket(fd, family, type, (type == SOCK_DGRAM) ? -1 : 0);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
ERR("socket %d is not of family=%d, type=%d", fd, family, type);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if ((type == SOCK_DGRAM) && (listening)) *listening = EINA_FALSE;
|
||||||
|
else if (listening) *listening = (r == 1);
|
||||||
|
|
||||||
|
addrlen = sizeof(sock_addr);
|
||||||
|
if (getsockname(fd, (struct sockaddr *)&sock_addr, &addrlen) != 0)
|
||||||
|
{
|
||||||
|
err = efl_net_socket_error_get();
|
||||||
|
ERR("could not query socket=%d name: %s", fd, eina_error_msg_get(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = strdup(address);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(str, ENOMEM);
|
||||||
|
if (!efl_net_ip_port_split(str, &host, &port))
|
||||||
|
{
|
||||||
|
ERR("invalid IP:PORT address: %s", address);
|
||||||
|
free(str);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (!port) port = "0";
|
||||||
|
|
||||||
|
if ((family == AF_UNSPEC) && (strchr(host, ':'))) family = AF_INET6;
|
||||||
|
|
||||||
|
if (family == AF_INET6)
|
||||||
|
{
|
||||||
|
struct sockaddr_in6 *a = (struct sockaddr_in6 *)&want_addr;
|
||||||
|
x = inet_pton(AF_INET6, host, &a->sin6_addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct sockaddr_in *a = (struct sockaddr_in *)&want_addr;
|
||||||
|
x = inet_pton(AF_INET, host, &a->sin_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FAST PATH: numbers were provided */
|
||||||
|
if (x == 1)
|
||||||
|
{
|
||||||
|
char *endptr;
|
||||||
|
unsigned long p;
|
||||||
|
Eina_Bool matches;
|
||||||
|
|
||||||
|
want_addr.ss_family = family;
|
||||||
|
if (want_addr.ss_family != sock_addr.ss_family)
|
||||||
|
{
|
||||||
|
ERR("socket %d family=%d differs from wanted %d", fd, sock_addr.ss_family, want_addr.ss_family);
|
||||||
|
free(str);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
p = strtoul(port, &endptr, 10);
|
||||||
|
if ((errno) || (endptr == port) || (*endptr != '\0'))
|
||||||
|
{
|
||||||
|
ERR("invalid port number '%s'", port);
|
||||||
|
free(str);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
else if (p > UINT16_MAX)
|
||||||
|
{
|
||||||
|
ERR("invalid port number %lu (out of range)", p);
|
||||||
|
free(str);
|
||||||
|
return ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (family == AF_INET6)
|
||||||
|
{
|
||||||
|
struct sockaddr_in6 *a = (struct sockaddr_in6 *)&want_addr;
|
||||||
|
a->sin6_port = htons(p);
|
||||||
|
matches = memcmp(a, &sock_addr, sizeof(*a)) == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct sockaddr_in *a = (struct sockaddr_in *)&want_addr;
|
||||||
|
x = inet_pton(AF_INET, host, &a->sin_addr);
|
||||||
|
a->sin_port = htons(p);
|
||||||
|
matches = memcmp(a, &sock_addr, sizeof(*a)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matches)
|
||||||
|
{
|
||||||
|
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")] = "";
|
||||||
|
|
||||||
|
efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&sock_addr);
|
||||||
|
ERR("socket %d address %s differs from wanted %s", fd, buf, address);
|
||||||
|
free(str);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: this may block, but users should be using the IP:PORT
|
||||||
|
* as numbers, getting into the fast path above.
|
||||||
|
*
|
||||||
|
* This is best-try to help API to be usable, but may
|
||||||
|
* impact the main loop execution for a while. However
|
||||||
|
* people doing bind are expected to do so on a local
|
||||||
|
* address, usually resolves faster without too many DNS
|
||||||
|
* lookups.
|
||||||
|
*/
|
||||||
|
struct addrinfo hints = {
|
||||||
|
.ai_socktype = type,
|
||||||
|
.ai_family = family,
|
||||||
|
.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED,
|
||||||
|
};
|
||||||
|
struct addrinfo *results, *itr;
|
||||||
|
|
||||||
|
DBG("resolving '%s', it may block main loop! Consider using IP:PORT", address);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
x = getaddrinfo(host, port, &hints, &results);
|
||||||
|
}
|
||||||
|
while ((r == EAI_AGAIN) || ((r == EAI_SYSTEM) && (errno == EINTR)));
|
||||||
|
|
||||||
|
if (x != 0)
|
||||||
|
{
|
||||||
|
ERR("couldn't resolve host='%s', port='%s': %s",
|
||||||
|
host, port, gai_strerror(x));
|
||||||
|
free(str);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = EINVAL;
|
||||||
|
for (itr = results; itr != NULL; itr = itr->ai_next)
|
||||||
|
{
|
||||||
|
if (sock_addr.ss_family != itr->ai_family) continue;
|
||||||
|
if (memcmp(itr->ai_addr, &sock_addr, itr->ai_addrlen) == 0)
|
||||||
|
{
|
||||||
|
err = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeaddrinfo(results);
|
||||||
|
free(str);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (listening) *listening = EINA_FALSE;
|
||||||
|
ERR("unsupported family=%d", family);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_cleanup_close(void *data)
|
_cleanup_close(void *data)
|
||||||
{
|
{
|
||||||
|
|
|
@ -411,6 +411,30 @@ Eina_Bool efl_net_unix_fmt(char *buf, size_t buflen, SOCKET fd, const struct soc
|
||||||
#endif
|
#endif
|
||||||
Eina_Bool efl_net_ip_port_fmt(char *buf, size_t buflen, const struct sockaddr *addr);
|
Eina_Bool efl_net_ip_port_fmt(char *buf, size_t buflen, const struct sockaddr *addr);
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSTEMD
|
||||||
|
/**
|
||||||
|
* Checks if the next FD in the sd_fd_index:sd_fd_max is of the
|
||||||
|
* expected family, protocol and if it's listening.
|
||||||
|
*
|
||||||
|
* This is similar to sd_is_socket()/sd_is_socket_inet(), but will
|
||||||
|
* also parse address in our standard format "IP:PORT", including IPv6
|
||||||
|
* within braces, and then will validate the address with
|
||||||
|
* getsockaddr() for INET.
|
||||||
|
*
|
||||||
|
* @param address the address to validate
|
||||||
|
* @param family AF_UNIX or AF_UNSPEC for INET, in that case AF_INET
|
||||||
|
* or AF_INET6 will be inferred from @a address.
|
||||||
|
* @param type SOCK_STREAM or SOCK_DGRAM
|
||||||
|
* @param[out] listening where to return listening state, should be
|
||||||
|
* NULL for @a type SOCK_DGRAM
|
||||||
|
*
|
||||||
|
* @return 0 on success, error otherwise.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
Eina_Error efl_net_ip_socket_activate_check(const char *address, int family, int type, Eina_Bool *listening);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief splits an address in the format "host:port" in two
|
* @brief splits an address in the format "host:port" in two
|
||||||
* null-terminated strings.
|
* null-terminated strings.
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
# include <Evil.h>
|
# include <Evil.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSTEMD
|
||||||
|
# include <systemd/sd-daemon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MY_CLASS EFL_NET_SERVER_FD_CLASS
|
#define MY_CLASS EFL_NET_SERVER_FD_CLASS
|
||||||
|
|
||||||
typedef struct _Efl_Net_Server_Fd_Data
|
typedef struct _Efl_Net_Server_Fd_Data
|
||||||
|
@ -196,6 +200,58 @@ _efl_net_server_fd_efl_net_server_serving_get(Eo *o EINA_UNUSED, Efl_Net_Server_
|
||||||
return pd->serving;
|
return pd->serving;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EOLIAN static Eina_Error
|
||||||
|
_efl_net_server_fd_socket_activate(Eo *o, Efl_Net_Server_Fd_Data *pd EINA_UNUSED, const char *address)
|
||||||
|
{
|
||||||
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
||||||
|
|
||||||
|
#ifndef HAVE_SYSTEMD
|
||||||
|
DBG("systemd support is disabled");
|
||||||
|
return ENOENT;
|
||||||
|
#else
|
||||||
|
if (!sd_fd_max)
|
||||||
|
{
|
||||||
|
DBG("This service was not socket-activated, no $LISTEN_FDS");
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
else if (sd_fd_index >= sd_fd_max)
|
||||||
|
{
|
||||||
|
WRN("No more systemd sockets available. Configuration mismatch?");
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SOCKET fd = SD_LISTEN_FDS_START + sd_fd_index;
|
||||||
|
int family;
|
||||||
|
socklen_t len = sizeof(family);
|
||||||
|
|
||||||
|
if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &family, &len) != 0)
|
||||||
|
{
|
||||||
|
WRN("socket %d failed to return family: %s", fd, eina_error_msg_get(efl_net_socket_error_get()));
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_fd_index++;
|
||||||
|
efl_net_server_fd_family_set(o, family);
|
||||||
|
efl_loop_fd_set(o, fd);
|
||||||
|
if (efl_loop_fd_get(o) == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
sd_fd_index--;
|
||||||
|
WRN("socket %d could not be used by %p (%s)",
|
||||||
|
fd, o, efl_class_name_get(efl_class_get(o)));
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* by default they all come with close_on_exec set
|
||||||
|
* and we must apply our local conf.
|
||||||
|
*/
|
||||||
|
efl_net_server_fd_close_on_exec_set(o, pd->close_on_exec);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
EOLIAN static Eina_Bool
|
EOLIAN static Eina_Bool
|
||||||
_efl_net_server_fd_close_on_exec_set(Eo *o, Efl_Net_Server_Fd_Data *pd, Eina_Bool close_on_exec)
|
_efl_net_server_fd_close_on_exec_set(Eo *o, Efl_Net_Server_Fd_Data *pd, Eina_Bool close_on_exec)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,53 @@ class Efl.Net.Server.Fd (Efl.Loop.Fd, Efl.Net.Server) {
|
||||||
]]
|
]]
|
||||||
|
|
||||||
methods {
|
methods {
|
||||||
|
socket_activate {
|
||||||
|
[[If this method is called use an already activated socket.
|
||||||
|
|
||||||
|
This method allows a server to use an existing socket
|
||||||
|
received from systemd or similar system.
|
||||||
|
|
||||||
|
It will replace @Efl.Net.Server.serve, thus if this is
|
||||||
|
used, that method will return EALREADY.
|
||||||
|
|
||||||
|
\@note The parameter 'address' given to this function is
|
||||||
|
only used to validate the next socket available, it
|
||||||
|
doesn't search for a socket with the given address. Thus
|
||||||
|
the socket to be used is the next unused and orders
|
||||||
|
matter is using multiple servers!
|
||||||
|
|
||||||
|
\@note subclasses must validate the socket and return
|
||||||
|
EINVAL prior to call the base class with
|
||||||
|
Efl.Object.super. They must also emit "serving" when
|
||||||
|
ready, for instance stream protocols may need to check
|
||||||
|
for listening and if not try to listen. Usually they
|
||||||
|
will also query getsockname() and set
|
||||||
|
@Efl.Net.Server.address.
|
||||||
|
|
||||||
|
Errors:
|
||||||
|
|
||||||
|
- EALREADY: already have a socket, either from
|
||||||
|
previous @.socket_activate or
|
||||||
|
@Efl.Net.Server.serve. Usually represents a
|
||||||
|
programming error.
|
||||||
|
|
||||||
|
- ENOENT: no sockets received from process manager
|
||||||
|
(ie: systemd). Usually this is not a fatal error,
|
||||||
|
just proceed by calling @Efl.Net.Server.serve
|
||||||
|
|
||||||
|
- EINVAL: the socket received is not of the correct
|
||||||
|
family, type or protocol. Usually this means a
|
||||||
|
configuration mismatch with the order of server
|
||||||
|
creation and calls to socket_activate. The
|
||||||
|
systemd.socket entries must match the order in your
|
||||||
|
application.
|
||||||
|
]]
|
||||||
|
params {
|
||||||
|
address: string; [[The address to validate the next available socket. It doesn't serve as search, only as validation!]]
|
||||||
|
}
|
||||||
|
return: Eina.Error; [[0 on success, ENOENT if no socket is available or EALREADY if already have a socket]]
|
||||||
|
}
|
||||||
|
|
||||||
@property family {
|
@property family {
|
||||||
[[The address family (AF_*) family of this socket.
|
[[The address family (AF_*) family of this socket.
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,14 @@ _efl_net_server_ssl_ssl_context_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *
|
||||||
return pd->ssl_ctx;
|
return pd->ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EOLIAN static Eina_Error
|
||||||
|
_efl_net_server_ssl_socket_activate(Eo *o, Efl_Net_Server_Ssl_Data *pd, const char *address)
|
||||||
|
{
|
||||||
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_server_serving_get(o), EALREADY);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
||||||
|
return efl_net_server_fd_socket_activate(pd->server, address);
|
||||||
|
}
|
||||||
|
|
||||||
EOLIAN static Eina_Error
|
EOLIAN static Eina_Error
|
||||||
_efl_net_server_ssl_efl_net_server_serve(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, const char *address)
|
_efl_net_server_ssl_efl_net_server_serve(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, const char *address)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,14 +18,49 @@ class Efl.Net.Server.Ssl (Efl.Loop_User, Efl.Net.Server) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
socket_activate {
|
||||||
|
[[If this method is called use an already activated socket.
|
||||||
|
|
||||||
|
This method allows a server to use an existing socket
|
||||||
|
received from systemd or similar system.
|
||||||
|
|
||||||
|
It will replace @Efl.Net.Server.serve, thus if this is
|
||||||
|
used, that method will return EALREADY.
|
||||||
|
|
||||||
|
\@note The parameter 'address' given to this function is
|
||||||
|
only used to validate the next socket available, it
|
||||||
|
doesn't search for a socket with the given address. Thus
|
||||||
|
the socket to be used is the next unused and orders
|
||||||
|
matter is using multiple servers!
|
||||||
|
|
||||||
|
Errors:
|
||||||
|
|
||||||
|
- EALREADY: already have a socket, either from
|
||||||
|
previous @.socket_activate or
|
||||||
|
@Efl.Net.Server.serve. Usually represents a
|
||||||
|
programming error.
|
||||||
|
|
||||||
|
- ENOENT: no sockets received from process manager
|
||||||
|
(ie: systemd). Usually this is not a fatal error,
|
||||||
|
just proceed by calling @Efl.Net.Server.serve
|
||||||
|
|
||||||
|
- EINVAL: the socket received is not of the correct
|
||||||
|
family, type or protocol. Usually this means a
|
||||||
|
configuration mismatch with the order of server
|
||||||
|
creation and calls to socket_activate. The
|
||||||
|
systemd.socket entries must match the order in your
|
||||||
|
application.
|
||||||
|
]]
|
||||||
|
params {
|
||||||
|
address: string; [[The address to validate the next available socket. It doesn't serve as search, only as validation!]]
|
||||||
|
}
|
||||||
|
return: Eina.Error; [[0 on success, ENOENT if no socket is available or EALREADY if already have a socket]]
|
||||||
|
}
|
||||||
|
|
||||||
@property family {
|
@property family {
|
||||||
[[The address family (AF_*) family of this socket.
|
[[The address family (AF_*) family of this socket.
|
||||||
|
|
||||||
It will be one of AF_INET (IPv4), AF_INET6 (IPv6),
|
It will be one of AF_INET (IPv4) or AF_INET6 (IPv6).
|
||||||
AF_UNIX...
|
|
||||||
|
|
||||||
It must be set before the @Efl.Loop.Fd.fd.set is called
|
|
||||||
with a valid file descriptor.
|
|
||||||
]]
|
]]
|
||||||
get { }
|
get { }
|
||||||
values {
|
values {
|
||||||
|
|
|
@ -154,6 +154,64 @@ _efl_net_server_tcp_resolved(void *data, const char *host EINA_UNUSED, const cha
|
||||||
efl_unref(o);
|
efl_unref(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EOLIAN static Eina_Error
|
||||||
|
_efl_net_server_tcp_efl_net_server_fd_socket_activate(Eo *o, Efl_Net_Server_Tcp_Data *pd EINA_UNUSED, const char *address)
|
||||||
|
{
|
||||||
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
||||||
|
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;
|
||||||
|
struct sockaddr_storage *addr;
|
||||||
|
socklen_t addrlen;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
err = efl_net_ip_socket_activate_check(address, AF_UNSPEC, 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();
|
||||||
|
DBG("listen(%d): %s", fd, eina_error_msg_get(err));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addrlen = sizeof(addr);
|
||||||
|
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
||||||
|
{
|
||||||
|
err = efl_net_socket_error_get();
|
||||||
|
ERR("getsockname(%d): %s", fd, eina_error_msg_get(err));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
|
||||||
|
efl_net_server_address_set(o, buf);
|
||||||
|
|
||||||
|
DBG("fd=%d serving at %s", fd, address);
|
||||||
|
efl_net_server_serving_set(o, EINA_TRUE);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
efl_net_server_fd_family_set(o, AF_UNSPEC);
|
||||||
|
efl_loop_fd_set(o, INVALID_SOCKET);
|
||||||
|
closesocket(fd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
EOLIAN static Eina_Error
|
EOLIAN static Eina_Error
|
||||||
_efl_net_server_tcp_efl_net_server_serve(Eo *o, Efl_Net_Server_Tcp_Data *pd, const char *address)
|
_efl_net_server_tcp_efl_net_server_serve(Eo *o, Efl_Net_Server_Tcp_Data *pd, const char *address)
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,5 +39,6 @@ class Efl.Net.Server.Tcp (Efl.Net.Server.Fd) {
|
||||||
Efl.Net.Server.serve;
|
Efl.Net.Server.serve;
|
||||||
Efl.Net.Server.Fd.client_add;
|
Efl.Net.Server.Fd.client_add;
|
||||||
Efl.Net.Server.Fd.client_reject;
|
Efl.Net.Server.Fd.client_reject;
|
||||||
|
Efl.Net.Server.Fd.socket_activate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,53 @@ _efl_net_server_udp_resolved(void *data, const char *host EINA_UNUSED, const cha
|
||||||
efl_unref(o);
|
efl_unref(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EOLIAN static Eina_Error
|
||||||
|
_efl_net_server_udp_efl_net_server_fd_socket_activate(Eo *o, Efl_Net_Server_Udp_Data *pd EINA_UNUSED, const char *address)
|
||||||
|
{
|
||||||
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
||||||
|
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_Error err;
|
||||||
|
struct sockaddr_storage *addr;
|
||||||
|
socklen_t addrlen;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
err = efl_net_ip_socket_activate_check(address, AF_UNSPEC, SOCK_DGRAM, NULL);
|
||||||
|
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);
|
||||||
|
|
||||||
|
addrlen = sizeof(addr);
|
||||||
|
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
||||||
|
{
|
||||||
|
err = efl_net_socket_error_get();
|
||||||
|
ERR("getsockname(%d): %s", fd, eina_error_msg_get(err));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
|
||||||
|
efl_net_server_address_set(o, buf);
|
||||||
|
|
||||||
|
DBG("fd=%d serving at %s", fd, address);
|
||||||
|
efl_net_server_serving_set(o, EINA_TRUE);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
efl_net_server_fd_family_set(o, AF_UNSPEC);
|
||||||
|
efl_loop_fd_set(o, INVALID_SOCKET);
|
||||||
|
closesocket(fd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
EOLIAN static Eina_Error
|
EOLIAN static Eina_Error
|
||||||
_efl_net_server_udp_efl_net_server_serve(Eo *o, Efl_Net_Server_Udp_Data *pd, const char *address)
|
_efl_net_server_udp_efl_net_server_serve(Eo *o, Efl_Net_Server_Udp_Data *pd, const char *address)
|
||||||
{
|
{
|
||||||
|
|
|
@ -125,5 +125,6 @@ class Efl.Net.Server.Udp (Efl.Net.Server.Fd) {
|
||||||
Efl.Object.destructor;
|
Efl.Object.destructor;
|
||||||
Efl.Net.Server.serve;
|
Efl.Net.Server.serve;
|
||||||
Efl.Net.Server.Fd.process_incoming_data;
|
Efl.Net.Server.Fd.process_incoming_data;
|
||||||
|
Efl.Net.Server.Fd.socket_activate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,6 +162,64 @@ _efl_net_server_unix_bind_job(void *data, const Efl_Event *event EINA_UNUSED)
|
||||||
efl_unref(o);
|
efl_unref(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
||||||
|
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;
|
||||||
|
struct sockaddr_storage *addr;
|
||||||
|
socklen_t addrlen;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
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();
|
||||||
|
DBG("listen(%d): %s", fd, eina_error_msg_get(err));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addrlen = sizeof(addr);
|
||||||
|
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
||||||
|
{
|
||||||
|
err = efl_net_socket_error_get();
|
||||||
|
ERR("getsockname(%d): %s", fd, eina_error_msg_get(err));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
|
||||||
|
efl_net_server_address_set(o, buf);
|
||||||
|
|
||||||
|
DBG("fd=%d serving at %s", fd, address);
|
||||||
|
efl_net_server_serving_set(o, EINA_TRUE);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
efl_net_server_fd_family_set(o, AF_UNSPEC);
|
||||||
|
efl_loop_fd_set(o, INVALID_SOCKET);
|
||||||
|
closesocket(fd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
EOLIAN static Eina_Error
|
EOLIAN static Eina_Error
|
||||||
_efl_net_server_unix_efl_net_server_serve(Eo *o, Efl_Net_Server_Unix_Data *pd, const char *address)
|
_efl_net_server_unix_efl_net_server_serve(Eo *o, Efl_Net_Server_Unix_Data *pd, const char *address)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,5 +24,6 @@ class Efl.Net.Server.Unix (Efl.Net.Server.Fd) {
|
||||||
Efl.Net.Server.serve;
|
Efl.Net.Server.serve;
|
||||||
Efl.Net.Server.Fd.client_add;
|
Efl.Net.Server.Fd.client_add;
|
||||||
Efl.Net.Server.Fd.client_reject;
|
Efl.Net.Server.Fd.client_reject;
|
||||||
|
Efl.Net.Server.Fd.socket_activate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue