efl/src/lib/ecore_con/ecore_con_private.h

496 lines
15 KiB
C
Raw Normal View History

#ifndef _ECORE_CON_PRIVATE_H
#define _ECORE_CON_PRIVATE_H
#include "ecore_private.h"
#include "Ecore_Con.h"
#define ECORE_MAGIC_CON_SERVER 0x77665544
#define ECORE_MAGIC_CON_CLIENT 0x77556677
#define ECORE_MAGIC_CON_URL 0x77074255
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include <inttypes.h>
2004-04-03 07:03:33 -08:00
#define READBUFSIZ 65536
extern int _ecore_con_log_dom;
#ifdef ECORE_CON_DEFAULT_LOG_COLOR
#undef ECORE_LOG_DEFAULT_LOG_COLOR
#endif
#define ECORE_CON_DEFAULT_LOG_COLOR EINA_COLOR_BLUE
#ifdef ERR
# undef ERR
#endif
#define ERR(...) EINA_LOG_DOM_ERR(_ecore_con_log_dom, __VA_ARGS__)
#ifdef DBG
# undef DBG
#endif
#define DBG(...) EINA_LOG_DOM_DBG(_ecore_con_log_dom, __VA_ARGS__)
#ifdef INF
# undef INF
#endif
#define INF(...) EINA_LOG_DOM_INFO(_ecore_con_log_dom, __VA_ARGS__)
#ifdef WRN
# undef WRN
#endif
#define WRN(...) EINA_LOG_DOM_WARN(_ecore_con_log_dom, __VA_ARGS__)
#ifdef CRI
# undef CRI
#endif
#define CRI(...) EINA_LOG_DOM_CRIT(_ecore_con_log_dom, __VA_ARGS__)
typedef struct Ecore_Con_Socks Ecore_Con_Socks_v4;
typedef struct Ecore_Con_Socks_v5 Ecore_Con_Socks_v5;
struct Ecore_Con_Socks /* v4 */
{
unsigned char version;
const char *ip;
int port;
const char *username;
unsigned int ulen;
Eina_Bool lookup : 1;
Eina_Bool bind : 1;
};
struct Ecore_Con_Socks_v5
{
unsigned char version;
const char *ip;
int port;
const char *username;
unsigned int ulen;
Eina_Bool lookup : 1;
Eina_Bool bind : 1;
/* v5 only */
unsigned char method;
const char *password;
unsigned int plen;
};
#ifdef HAVE_SYSTEMD
extern int sd_fd_index;
extern int sd_fd_max;
#endif
/* init must be called from main thread */
void ecore_con_libproxy_proxies_free(char **proxies);
/* BLOCKING! should be called from a worker thread */
char **ecore_con_libproxy_proxies_get(const char *url, Ecore_Thread *eth);
Eina_Bool ecore_con_server_check(const Ecore_Con_Server *svr);
extern Ecore_Con_Socks *_ecore_con_proxy_once;
extern Ecore_Con_Socks *_ecore_con_proxy_global;
void ecore_con_socks_init(void);
void ecore_con_socks_shutdown(void);
void ecore_con_mempool_init(void);
void ecore_con_mempool_shutdown(void);
void ecore_con_legacy_init(void);
void ecore_con_legacy_shutdown(void);
void _ecore_con_local_mkpath(const char *path, mode_t mode);
/* allow windows and posix to use the same error comparison */
#ifndef SOCKET_ERROR
#define SOCKET_ERROR -1
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#ifndef SOCKET_TO_LOOP_FD
#define SOCKET_TO_LOOP_FD(sock) ((int)sock)
#endif
#ifndef _WIN32
#define closesocket(fd) close(fd)
#define SOCKET int
#define SOCKET_FMT "%d"
#else
#define SOCKET_FMT "%"PRIuPTR
#endif
/*
* This define will force SOCKET to be 'unsigned long', this will
* force compile to emit errors when assuming "int"/"%d", which is the
* case on UNIX but not on Windows.
*/
//#define EFL_NET_CHECK_SOCKET_TYPE 1
#if EFL_NET_CHECK_SOCKET_TYPE
#undef SOCKET
#undef SOCKET_FMT
#undef INVALID_SOCKET
#define SOCKET unsigned long
#define SOCKET_FMT "%lu"
#define INVALID_SOCKET ((SOCKET)-1)
#endif
/* some platforms do not have AI_V4MAPPED, then define to 0 so bitwise OR won't be changed */
#ifndef AI_V4MAPPED
#define AI_V4MAPPED 0
#endif
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0
#endif
#ifndef AI_CANONNAME
#define AI_CANONNAME 0
#endif
/* Windows do not define EAI_SYSTEM, so just define to some number
* that won't be matched, effectively disabling the subsequent
* checks/usage
*/
#ifndef EAI_SYSTEM
#define EAI_SYSTEM 254 /* number that won't match anything in EAI_* */
#endif
void _efl_net_server_udp_client_init(Eo *client, SOCKET fd, const struct sockaddr *addr, socklen_t addrlen, const char *str);
void _efl_net_server_udp_client_feed(Eo *client, Eina_Rw_Slice slice);
implement efl_net_{socket,dialer,server}_windows This is the local socket for windows, analogous to AF_UNIX. `Efl_Net_Socket_Windows` is the base class doing `ReadFile()` and `WriteFile()` using overlapped I/O, as well as the close procedure (`FlushFileBuffers()`, `DisconnectNamedPipe()` and `CloseHandle()`). These are done on top of an existing HANDLE that is set by `Efl_Net_Dialer_Windows` (from `CreateFile()`) or `Efl_Net_Server_Windows` (from `CreateNamedPipe()`). The overlapped I/O will return immediately, either with operation completed or `ERROR_IO_PENDING`, which means the kernel will execute that asynchronously and will later `SetEvent(overlapped.hEvent)` which is an event we wait on our main loop. That `overlapped` handle must exist during the call lifetime, thus cannot be bound to `pd`, as we may call `CancelIo()` but there is no guarantee the memory won't be touched, in that case we keep the overlapped around, but without an associated object. Windows provides no notification "can read without blocking" or non-blocking calls that returns partial data. The way to go is to use these overlapped I/O, with an initial `ReadFile()` to an internal buffer, once that operation finishes, we callback the user to says there is something to read (`efl_io_reader_can_read_set()`) and wait until `efl_io_reader_read()` is called to consume the available data, then `ReadFile()` is called again to read more data to the same internal buffer. Likewise, there is no "can write without blocking" or non-blocking calls that sends only partial data. The way to go is to get user bytes in `efl_io_writer_write()` and copy them in an internal buffer, then call `WriteFile()` on that and inform the user nothing else can be written until that operation completes (`efl_io_writer_can_write_set()`). This is cumbersome since we say we "sent" stuff when we actually didn't, it's still in our internal buffer (`pd->send.bytes`), but nonetheless the kernel and the other peer may be adding even more buffers, in this case we need to do a best effort to get it delivery. A particular case is troublesome: `write() -> close()`, this may result in `WriteFile()` pending, in this case we wait using `GetOverlappedResult()`, *this is nasty and may block*, but it's the only way I see to cope with such common use case. Other operations, like ongoing `ReadFile()` or `ConnectNamedPipe()` will be canceled using `CancelIo()`. Q: Why no I/O Completion Port (IOCP) was used? Why no CreateThreadpoolIo()? These perform much better! A: These will call back from secondary threads, but in EFL we must report back to the user in order to process incoming data or get more data to send. That is, we serialize everything to the main thread, making it impossible to use the benefits of IOCP and similar such as CreateThreadpoolIo(). Since we'd need to wakeup the main thread anyways, using `OVERLAPPED.hEvent` with `ecore_main_win32_handler_add()` does the job as we expect. Thanks to Vincent Torri (vtorri) for his help getting this code done with an example on how to do the NamedPipe handling on Windows.
2017-03-22 00:29:16 -07:00
#ifdef EFL_NET_SOCKET_WINDOWS_CLASS
#define PIPE_NS "\\\\.\\pipe\\"
char *_efl_net_windows_error_msg_get(DWORD win32err);
Eina_Error _efl_net_socket_windows_init(Eo *o, HANDLE h);
Eina_Error _efl_net_socket_windows_io_start(Eo *o);
HANDLE _efl_net_socket_windows_handle_get(const Eo *o);
typedef struct _Efl_Net_Socket_Windows_Operation Efl_Net_Socket_Windows_Operation;
typedef Eina_Error (*Efl_Net_Socket_Windows_Operation_Success_Cb)(void *data, Eo *sock, DWORD used_size);
typedef Eina_Error (*Efl_Net_Socket_Windows_Operation_Failure_Cb)(void *data, Eo *sock, DWORD win32err);
Efl_Net_Socket_Windows_Operation *_efl_net_socket_windows_operation_new(Eo *sock, Efl_Net_Socket_Windows_Operation_Success_Cb success_cb, Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb, const void *data);
Eina_Error _efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, DWORD win32err);
Eina_Error _efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, DWORD used_size);
static inline OVERLAPPED *
_efl_net_socket_windows_operation_overlapped_get(Efl_Net_Socket_Windows_Operation *op)
{
return (OVERLAPPED *)op;
}
#endif
#ifdef EFL_NET_SOCKET_UNIX_CLASS
Eina_Bool efl_net_unix_fmt(char *buf, size_t buflen, SOCKET fd, const struct sockaddr_un *addr, socklen_t addrlen);
#endif
Eina_Bool efl_net_ip_port_parse(const char *address, struct sockaddr_storage *storage);
Eina_Bool efl_net_ip_port_parse_split(const char *host, const char *port, struct sockaddr_storage *storage);
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
* null-terminated strings.
*
* The address may be 'server.com:1234', 'server.com:http',
* 'server.com' (@c *p_port will be NULL), IPv4 127.0.0.1:456 or
* IPv6 [::1]:456
*
* @param[inout] buf contains the string to be split and will be modified.
* @param[out] p_host returns a pointer inside @a buf with
* null-terminated host part.
* @param[out] p_port returns a pointer with null-terminated port
* part. The pointer may be inside @a buf if port was
* specified or #NULL if it wasn't specified.
*
* @return #EINA_TRUE on success, #EINA_FALSE on errors.
*
* @internal
*/
Eina_Bool efl_net_ip_port_split(char *buf, const char **p_host, const char **p_port);
SOCKET efl_net_socket4(int domain, int type, int protocol, Eina_Bool close_on_exec);
/**
* @brief callback to notify of resolved address.
*
* The callback is given the ownership of the result, thus must free
* it with freeaddrinfo().
*
* @internal
*/
typedef void (*Efl_Net_Ip_Resolve_Async_Cb)(void *data, const char *host, const char *port, const struct addrinfo *hints, struct addrinfo *result, int gai_error);
/**
* @brief asynchronously resolve a host and port using getaddrinfo().
*
* This will call getaddrinfo() in a thread, taking care to return the
* result to the main loop and calling @a cb with given user @a data.
*
* @internal
*/
Ecore_Thread *efl_net_ip_resolve_async_new(const char *host, const char *port, const struct addrinfo *hints, Efl_Net_Ip_Resolve_Async_Cb cb, const void *data);
/**
* @brief callback to notify of connection.
*
* The callback is given the ownership of the socket (sockfd), thus
* must close().
*
* @internal
*/
typedef void (*Efl_Net_Connect_Async_Cb)(void *data, const struct sockaddr *addr, socklen_t addrlen, SOCKET sockfd, Eina_Error error);
/**
* @brief asynchronously create a socket and connect to the address.
*
* This will call socket() and connect() in a thread, taking care to
* return the result to the main loop and calling @a cb with given
* user @a data.
*
* For name resolution and proxy support use
* efl_net_ip_connect_async_new()
*
* @internal
*/
Ecore_Thread *efl_net_connect_async_new(const struct sockaddr *addr, socklen_t addrlen, int type, int protocol, Eina_Bool close_on_exec, Efl_Net_Connect_Async_Cb cb, const void *data);
/**
* @brief asynchronously create a socket and connect to the IP address.
*
* This wil resolve the address using getaddrinfo(), create a socket
* and connect in a thread.
*
* If a @a proxy is given, then it's always used. Otherwise the
* environment variable @a proxy_env is used unless it matches @a
* no_proxy_env. Some systems may do special queries for proxy from
* the thread.
*
* @param address the host:port to connect. Host may be a name or an
* IP address, IPv6 addresses should be enclosed in braces.
* @param proxy a mandatory proxy to use. If "" (empty string), it's
* disabled. If NULL, then @a proxy_env is used unless it
* matches @a no_proxy_env.
* @param proxy_env if @a proxy is NULL, then this will be used as the
* proxy unless it matches @a no_proxy_env.
* @param no_proxy_env a comma-separated list of matches that will
* avoid using @a proxy_env. "server.com" will inhibit proxy
* for "server.com", "host.server.com" but not "xserver.com".
* @param type the socket type, such as SOCK_STREAM or SOCK_DGRAM.
* @param protocol the socket protocol, such as IPPROTO_TCP.
* @param close_on_exec if EINA_TRUE, will set SOCK_CLOEXEC.
* @param cb the callback to report connection
* @param data data to give to callback
*
* @return an Ecore_Thread that will execute the connection.
*
* @internal
*/
Ecore_Thread *efl_net_ip_connect_async_new(const char *address, const char *proxy, const char *proxy_env, const char *no_proxy_env, int type, int protocol, Eina_Bool close_on_exec, Efl_Net_Connect_Async_Cb cb, const void *data);
static inline Eina_Error
efl_net_socket_error_get(void)
{
#ifndef _WIN32
return errno;
#else
Eina_Error err = WSAGetLastError();
if (0) { }
/* used by send() */
#if defined(WSAEACCES) && (WSAEACCES != EACCES)
else if (err == WSAEACCES) err = EACCES;
#endif
#if defined(WSAEWOULDBLOCK) && (WSAEWOULDBLOCK != EAGAIN)
else if (err == WSAEWOULDBLOCK) err = EAGAIN;
#endif
#if defined(WSAEBADF) && (WSAEBADF != EBADF)
else if (err == WSAEBADF) err = EBADF;
#endif
#if defined(WSAECONNRESET) && (WSAECONNRESET != ECONNRESET)
else if (err == WSAECONNRESET) err = ECONNRESET;
#endif
#if defined(WSAEDESTADDRREQ) && (WSAEDESTADDRREQ != EDESTADDRREQ)
else if (err == WSAEDESTADDRREQ) err = EDESTADDRREQ;
#endif
#if defined(WSAEFAULT) && (WSAEFAULT != EFAULT)
else if (err == WSAEFAULT) err = EFAULT;
#endif
#if defined(WSAEINTR) && (WSAEINTR != EINTR)
else if (err == WSAEINTR) err = EINTR;
#endif
#if defined(WSAEINVAL) && (WSAEINVAL != EINVAL)
else if (err == WSAEINVAL) err = EINVAL;
#endif
#if defined(WSAEISCONN) && (WSAEISCONN != EISCONN)
else if (err == WSAEISCONN) err = EISCONN;
#endif
#if defined(WSAEMSGSIZE) && (WSAEMSGSIZE != EMSGSIZE)
else if (err == WSAEMSGSIZE) err = EMSGSIZE;
#endif
#if defined(WSAENOBUFS) && (WSAENOBUFS != ENOBUFS)
else if (err == WSAENOBUFS) err = ENOBUFS;
#endif
#if defined(WSA_NOT_ENOUGH_MEMORY) && (WSA_NOT_ENOUGH_MEMORY != ENOMEM)
else if (err == WSA_NOT_ENOUGH_MEMORY) err = ENOMEM;
#endif
#if defined(WSAENOTCONN) && (WSAENOTCONN != ENOTCONN)
else if (err == WSAENOTCONN) err = ENOTCONN;
#endif
#if defined(WSAENOTSOCK) && (WSAENOTSOCK != ENOTSOCK)
else if (err == WSAENOTSOCK) err = ENOTSOCK;
#endif
#if defined(WSAEOPNOTSUPP) && (WSAEOPNOTSUPP != EOPNOTSUPP)
else if (err == WSAEOPNOTSUPP) err = EOPNOTSUPP;
#endif
#if defined(WSAESHUTDOWN) && (WSAESHUTDOWN != EPIPE)
else if (err == WSAESHUTDOWN) err = EPIPE;
#endif
/* extras used by recv() */
#if defined(WSAECONNREFUSED) && (WSAECONNREFUSED != ECONNREFUSED)
else if (err == WSAECONNREFUSED) err = ECONNREFUSED;
#endif
/* extras used by getsockopt() */
#if defined(WSAENOPROTOOPT) && (WSAENOPROTOOPT != ENOPROTOOPT)
else if (err == WSAENOPROTOOPT) err = ENOPROTOOPT;
#endif
return err;
#endif
}
/**
* Join a multicast group specified by address.
*
* Address must be an IPv4 or IPv6 depending on @a fd and will be
* parsed using inet_pton() with corresponding @a family. The address
* may contain an '@@' delimiter to specify the local interface IP
* address to use. No interface means '0.0.0.0'.
*
* @param fd socket to operate on.
* @param family the socket family of fd, AF_INET or AF_INET6.
* @param address the address in the format IP[@@IFACE]
*
* @return 0 on success, errno mapping otherwise.
* @internal
*/
Eina_Error efl_net_multicast_join(SOCKET fd, int family, const char *address);
/**
* Leave a multicast group specified by address.
*
* This reverses the effect of efl_net_multicast_join().
*
* @param fd socket to operate on.
* @param family the socket family of fd, AF_INET or AF_INET6.
* @param address the address in the format IP[@@IFACE]
*
* @return 0 on success, errno mapping otherwise.
* @internal
*/
Eina_Error efl_net_multicast_leave(SOCKET fd, int family, const char *address);
/**
* Sets the Time-To-Live of multicast packets. <= 1 disables going
* outside of local network.
*
* @param fd socket to operate on.
* @param family the socket family of fd, AF_INET or AF_INET6.
* @param ttl the time-to-live in units.
*
* @return 0 on success, errno mapping otherwise.
* @internal
*/
Eina_Error efl_net_multicast_ttl_set(SOCKET fd, int family, uint8_t ttl);
/**
* Retrieves the current time-to-live of multicast packets.
*
* @param fd socket to operate on.
* @param family the socket family of fd, AF_INET or AF_INET6.
* @param[out] ttl returns the time-to-live in units.
*
* @return 0 on success, errno mapping otherwise.
* @internal
*/
Eina_Error efl_net_multicast_ttl_get(SOCKET fd, int family, uint8_t *ttl);
/**
* Sets if the current local address should get a copy of the packets sent.
*
* @param fd socket to operate on.
* @param family the socket family of fd, AF_INET or AF_INET6.
* @param loopback if #EINA_TRUE, enables receive of local copy. #EINA_FALSE means only remote peers will do.
*
* @return 0 on success, errno mapping otherwise.
* @internal
*/
Eina_Error efl_net_multicast_loopback_set(SOCKET fd, int family, Eina_Bool loopback);
/**
* Gets if the current local address should get a copy of the packets sent.
*
* @param fd socket to operate on.
* @param family the socket family of fd, AF_INET or AF_INET6.
* @param[out] loopback returns if #EINA_TRUE, enables receive of local copy. #EINA_FALSE means only remote peers will do.
*
* @return 0 on success, errno mapping otherwise.
* @internal
*/
Eina_Error efl_net_multicast_loopback_get(SOCKET fd, int family, Eina_Bool *loopback);
/**
* Query the size of the next UDP datagram pending on queue.
*
* @param fd socket to operate on.
* @return the size in bytes.
* @internal
*/
size_t efl_net_udp_datagram_size_query(SOCKET fd);
/* SSL abstraction API */
extern void *efl_net_ssl_context_connection_new(Efl_Net_Ssl_Context *context);
#define EFL_NET_DIALER_HTTP_BUFFER_RECEIVE_SIZE (1U << 14) /* 16Kb to receive */
#endif