Combine socket creation code

The client connection and the server listener sockets used largely similar
code. Combining them allows for simpler protocol code.

Cleaned up parts of the DCB listener creation and moved the parsing of the
network binding configuration to a higher level.
This commit is contained in:
Markus Mäkelä 2017-03-07 10:59:02 +02:00
parent 7bc47fd5a4
commit e8ef701409
4 changed files with 109 additions and 185 deletions

View File

@ -35,31 +35,39 @@ MXS_BEGIN_DECLS
*/
#define MXS_PTR(a, b) (((uint8_t*)(a)) + (b))
/** The type of the socket */
enum mxs_socket_type
{
MXS_SOCKET_LISTENER, /**< */
MXS_SOCKET_NETWORK,
};
bool utils_init(); /*< Call this first before using any other function */
void utils_end();
/**
* Parse the bind configuration data.
*
* The configuration is passed as string in the `address|port` format.
*
* @param config The bind address and port separated by a '|'
* @param addr The struct sockaddr_storage in which the data is written
* @return True on success, false on failure
*/
bool parse_bindconfig(const char *config, struct sockaddr_storage *addr);
/**
* @brief Create a network socket and a socket configuration
*
* @param dest Pointer to a struct sockaddr_storage where the configuration is stored
* This helper function can be used to open both listener socket and network
* connection sockets. For listener sockets, the @c host and @c port parameters
* tell where the socket will bind to. For network sockets, the parameters tell
* where the connection is created.
*
* After calling this function, the only thing that needs to be done is to
* give @c addr and the return value of this function as the parameters to
* either bind() (for listeners) or connect() (for outbound network connections).
*
* @param type Type of the socket, either MXS_SOCKET_LISTENER for a listener
* socket or MXS_SOCKET_NETWORK for a network connection socket
* @param addr Pointer to a struct sockaddr_storage where the socket
* configuration is stored
* @param host The target host for which the socket is created
* @param port The target port on the host
*
* @return The opened socket or -1 on failure
*/
int open_network_socket(struct sockaddr_storage *dest, char *host, uint16_t port);
int open_network_socket(enum mxs_socket_type type, struct sockaddr_storage *addr,
const char *host, uint16_t port);
int setnonblocking(int fd);
char *gw_strend(register const char *s);

View File

@ -153,8 +153,8 @@ static int gw_write(DCB *dcb, GWBUF *writeq, bool *stop_writing);
static int gw_write_SSL(DCB *dcb, GWBUF *writeq, bool *stop_writing);
static int dcb_log_errors_SSL (DCB *dcb, const char *called_by, int ret);
static int dcb_accept_one_connection(DCB *listener, struct sockaddr *client_conn);
static int dcb_listen_create_socket_inet(const char *config_bind);
static int dcb_listen_create_socket_unix(const char *config_bind);
static int dcb_listen_create_socket_inet(const char *host, uint16_t port);
static int dcb_listen_create_socket_unix(const char *path);
static int dcb_set_socket_option(int sockfd, int level, int optname, void *optval, socklen_t optlen);
static void dcb_add_to_all_list(DCB *dcb);
static DCB *dcb_find_free();
@ -3044,22 +3044,38 @@ dcb_accept_one_connection(DCB *listener, struct sockaddr *client_conn)
* @param protocol_name Name of protocol that is listening
* @return 0 if new listener created successfully, otherwise -1
*/
int
dcb_listen(DCB *listener, const char *config, const char *protocol_name)
int dcb_listen(DCB *listener, const char *config, const char *protocol_name)
{
int listener_socket;
char host[strlen(config) + 1];
strcpy(host, config);
char *port_str = strrchr(host, '|');
uint16_t port = 0;
listener->fd = -1;
if (strchr(config, '/'))
if (port_str)
{
listener_socket = dcb_listen_create_socket_unix(config);
*port_str++ = 0;
port = atoi(port_str);
}
int listener_socket = -1;
if (strchr(host, '/'))
{
listener_socket = dcb_listen_create_socket_unix(host);
}
else if (port > 0)
{
listener_socket = dcb_listen_create_socket_inet(host, port);
}
else
{
listener_socket = dcb_listen_create_socket_inet(config);
// We don't have a socket path or a network port
ss_dassert(false);
}
if (listener_socket < 0)
{
ss_dassert(listener_socket == -1);
return -1;
}
@ -3072,12 +3088,8 @@ dcb_listen(DCB *listener, const char *config, const char *protocol_name)
*/
if (listen(listener_socket, INT_MAX) != 0)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to start listening on '%s' with protocol '%s': %d, %s",
config,
protocol_name,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)));
config, protocol_name, errno, mxs_strerror(errno));
close(listener_socket);
return -1;
}
@ -3098,108 +3110,59 @@ dcb_listen(DCB *listener, const char *config, const char *protocol_name)
}
/**
* @brief Create a listening socket, TCP
* @brief Create a network listener socket
*
* Parse the configuration provided and if valid create a socket.
* Set options, set non-blocking and bind to the socket.
*
* @param config_bind The configuration information
* @return socket if successful, -1 otherwise
* @param host The network address to listen on
* @param port The port to listen on
* @return The opened socket or -1 on error
*/
static int
dcb_listen_create_socket_inet(const char *config_bind)
static int dcb_listen_create_socket_inet(const char *host, uint16_t port)
{
struct sockaddr_storage server_address = {};
int listener_socket = open_network_socket(MXS_SOCKET_LISTENER, &server_address, host, port);
if (!parse_bindconfig(config_bind, &server_address))
if (listener_socket != -1)
{
MXS_ERROR("Error in parse_bindconfig for [%s]", config_bind);
return -1;
if (bind(listener_socket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0)
{
MXS_ERROR("Failed to bind on '%s:%u': %d, %s",
host, port, errno, mxs_strerror(errno));
close(listener_socket);
listener_socket = -1;
}
}
/** TODO: Move everything before the `bind` call to utils.c */
/** Create the TCP socket */
int listener_socket = socket(server_address.ss_family, SOCK_STREAM, 0);
if (listener_socket < 0)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Can't create socket: %d, %s", errno,
strerror_r(errno, errbuf, sizeof(errbuf)));
return -1;
}
int one = 1;
// socket options
if (dcb_set_socket_option(listener_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) != 0 ||
dcb_set_socket_option(listener_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != 0)
{
return -1;
}
// set NONBLOCKING mode
if (setnonblocking(listener_socket) != 0)
{
MXS_ERROR("Failed to set socket to non-blocking mode.");
close(listener_socket);
return -1;
}
if (bind(listener_socket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to bind on '%s': %i, %s",
config_bind,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)));
close(listener_socket);
return -1;
}
return listener_socket;
}
/**
* @brief Create a listening socket, Unix
* @brief Create a Unix domain socket
*
* Parse the configuration provided and if valid create a socket.
* Set options, set non-blocking and bind to the socket.
*
* @param config_bind The configuration information
* @return socket if successful, -1 otherwise
* @param path The socket path
* @return The opened socket or -1 on error
*/
static int
dcb_listen_create_socket_unix(const char *config_bind)
static int dcb_listen_create_socket_unix(const char *path)
{
int listener_socket;
struct sockaddr_un local_addr;
int one = 1;
char *tmp = strrchr(config_bind, ':');
if (tmp)
{
*tmp = '\0';
}
if (strlen(config_bind) > sizeof(local_addr.sun_path) - 1)
if (strlen(path) > sizeof(local_addr.sun_path) - 1)
{
MXS_ERROR("The path %s specified for the UNIX domain socket is too long. "
"The maximum length is %lu.", config_bind, sizeof(local_addr.sun_path) - 1);
"The maximum length is %lu.", path, sizeof(local_addr.sun_path) - 1);
return -1;
}
// UNIX socket create
if ((listener_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Can't create UNIX socket: %i, %s",
errno,
strerror_r(errno, errbuf, sizeof(errbuf)));
MXS_ERROR("Can't create UNIX socket: %d, %s", errno, mxs_strerror(errno));
return -1;
}
// socket options
if (dcb_set_socket_option(listener_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) != 0)
if (dcb_set_socket_option(listener_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) != 0)
{
return -1;
}
@ -3214,36 +3177,30 @@ dcb_listen_create_socket_unix(const char *config_bind)
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sun_family = AF_UNIX;
strcpy(local_addr.sun_path, config_bind);
strcpy(local_addr.sun_path, path);
if ((-1 == unlink(config_bind)) && (errno != ENOENT))
if ((-1 == unlink(path)) && (errno != ENOENT))
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to unlink Unix Socket %s: %d %s",
config_bind, errno, strerror_r(errno, errbuf, sizeof(errbuf)));
path, errno, mxs_strerror(errno));
}
/* Bind the socket to the Unix domain socket */
if (bind(listener_socket, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
if (bind(listener_socket, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to bind to UNIX Domain socket '%s': %i, %s",
config_bind,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)));
MXS_ERROR("Failed to bind to UNIX Domain socket '%s': %d, %s",
path, errno, mxs_strerror(errno));
close(listener_socket);
return -1;
}
/* set permission for all users */
if (chmod(config_bind, 0777) < 0)
if (chmod(path, 0777) < 0)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to change permissions on UNIX Domain socket '%s': %i, %s",
config_bind,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)));
MXS_ERROR("Failed to change permissions on UNIX Domain socket '%s': %d, %s",
path, errno, mxs_strerror(errno));
}
return listener_socket;
}

View File

@ -896,7 +896,7 @@ void utils_end()
SPINLOCK tmplock = SPINLOCK_INIT;
static bool configure_socket(int so)
static bool configure_network_socket(int so)
{
int sndbufsize = MXS_BACKEND_SO_SNDBUF;
int rcvbufsize = MXS_BACKEND_SO_RCVBUF;
@ -906,9 +906,21 @@ static bool configure_socket(int so)
setsockopt(so, SOL_SOCKET, SO_RCVBUF, &rcvbufsize, sizeof(rcvbufsize)) != 0 ||
setsockopt(so, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != 0)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to set socket option: %d, %s.",
errno, strerror_r(errno, errbuf, sizeof(errbuf)));
MXS_ERROR("Failed to set socket option: %d, %s.", errno, mxs_strerror(errno));
return false;
}
return setnonblocking(so) == 0;
}
static bool configure_listener_socket(int so)
{
int one = 1;
if (setsockopt(so, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0 ||
setsockopt(so, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != 0)
{
MXS_ERROR("Failed to set socket option: %d, %s.", errno, mxs_strerror(errno));
return false;
}
@ -934,8 +946,9 @@ static void set_port(struct sockaddr_storage *addr, uint16_t port)
}
}
int open_network_socket(struct sockaddr_storage *dest, char *host, uint16_t port)
int open_network_socket(enum mxs_socket_type type, struct sockaddr_storage *addr, const char *host, uint16_t port)
{
ss_dassert(type == MXS_SOCKET_NETWORK || type == MXS_SOCKET_LISTENER);
#ifdef __USE_POSIX
struct addrinfo *ai = NULL, hint = {};
int so, rc;
@ -945,28 +958,24 @@ int open_network_socket(struct sockaddr_storage *dest, char *host, uint16_t port
if ((rc = getaddrinfo(host, NULL, &hint, &ai)) != 0)
{
MXS_ERROR("Failed to obtain address for host %s, %s",
host, gai_strerror(rc));
MXS_ERROR("Failed to obtain address for host %s: %s", host, gai_strerror(rc));
return -1;
}
/* Take the first one */
if (ai)
{
so = socket(ai->ai_family, SOCK_STREAM, 0);
if (so < 0)
if ((so = socket(ai->ai_family, SOCK_STREAM, 0)) == -1)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Socket creation failed: %d, %s.",
errno, strerror_r(errno, errbuf, sizeof(errbuf)));
MXS_ERROR("Socket creation failed: %d, %s.", errno, mxs_strerror(errno));
}
else
{
memcpy(dest, ai->ai_addr, ai->ai_addrlen);
set_port(dest, port);
memcpy(addr, ai->ai_addr, ai->ai_addrlen);
set_port(addr, port);
if (!configure_socket(so))
if ((type == MXS_SOCKET_NETWORK && !configure_network_socket(so)) ||
(type == MXS_SOCKET_LISTENER && !configure_listener_socket(so)))
{
close(so);
so = -1;
@ -983,53 +992,6 @@ int open_network_socket(struct sockaddr_storage *dest, char *host, uint16_t port
return so;
}
bool parse_bindconfig(const char *config, struct sockaddr_storage *addr)
{
char buf[strlen(config) + 1];
strcpy(buf, config);
char *port = strrchr(buf, '|');
short pnum;
if (port)
{
*port = 0;
port++;
pnum = atoi(port);
}
else
{
ss_dassert(false);
return 0;
}
struct addrinfo *ai = NULL, hint = {};
hint.ai_flags = AI_ALL;
hint.ai_family = AF_UNSPEC;
int rc = getaddrinfo(buf, NULL, &hint, &ai);
if (rc == 0)
{
if (ai)
{
memcpy(addr, ai->ai_addr, ai->ai_addrlen);
set_port(addr, pnum);
freeaddrinfo(ai);
}
else
{
MXS_ERROR("Failed to find valid network address for '%s'.", config);
rc = -1;
}
}
else
{
MXS_ERROR("Failed to resolve network address for '%s': %s", config, gai_strerror(rc));
}
return rc == 0;
}
/**
* Return the number of processors available.
* @return Number of processors or 1 if the required definition of _SC_NPROCESSORS_CONF

View File

@ -276,9 +276,9 @@ static int gw_do_connect_to_backend(char *host, int port, int *fd)
int rv = -1;
/* prepare for connect */
int so = open_network_socket(&serv_addr, host, port);
int so = open_network_socket(MXS_SOCKET_NETWORK, &serv_addr, host, port);
if (so < 0)
if (so == -1)
{
MXS_ERROR("Establishing connection to backend server %s:%d failed.", host, port);
return rv;
@ -294,10 +294,8 @@ static int gw_do_connect_to_backend(char *host, int port, int *fd)
}
else
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to connect backend server %s:%d due to: %d, %s.",
host, port, errno, strerror_r(errno, errbuf, sizeof(errbuf)));
/** Close socket */
host, port, errno, mxs_strerror(errno));
close(so);
return rv;
}
@ -305,8 +303,7 @@ static int gw_do_connect_to_backend(char *host, int port, int *fd)
*fd = so;
MXS_DEBUG("%lu [gw_do_connect_to_backend] Connected to backend server "
"%s:%d, fd %d.",
pthread_self(), host, port, so);
"%s:%d, fd %d.", pthread_self(), host, port, so);
return rv;