diff --git a/include/maxscale/listener.h b/include/maxscale/listener.h index a5530eb85..f40a7b310 100644 --- a/include/maxscale/listener.h +++ b/include/maxscale/listener.h @@ -49,6 +49,7 @@ typedef struct servlistener unsigned short port; /**< Port to listen on */ char *address; /**< Address to listen with */ char *authenticator; /**< Name of authenticator */ + char *auth_options; /**< Authenticator options */ void *auth_instance; /**< Authenticator instance created in GWAUTHENTICATOR::initialize() */ SSL_LISTENER *ssl; /**< Structure of SSL data or NULL */ struct dcb *listener; /**< The DCB for the listener */ @@ -59,6 +60,18 @@ typedef struct servlistener struct servlistener *next; /**< Next service protocol */ } SERV_LISTENER; +/** + * @brief Serialize a listener to a file + * + * This converts @c listener into an INI format file. This allows created listeners + * to be persisted to disk. This will replace any existing files with the same + * name. + * + * @param listener Listener to serialize + * @return True if the serialization of the listener was successful, false if it fails + */ +bool listener_serialize(const SERV_LISTENER *listener); + SERV_LISTENER* listener_alloc(struct service* service, const char* name, const char *protocol, const char *address, unsigned short port, const char *authenticator, const char* auth_options, SSL_LISTENER *ssl); diff --git a/include/maxscale/service.h b/include/maxscale/service.h index 73c282142..fab8f3128 100644 --- a/include/maxscale/service.h +++ b/include/maxscale/service.h @@ -192,9 +192,9 @@ extern SERVICE *service_alloc(const char *, const char *); extern int service_free(SERVICE *); extern SERVICE *service_find(const char *); extern int service_isvalid(SERVICE *); -extern bool serviceAddProtocol(SERVICE *service, const char *name, const char *protocol, - const char *address, unsigned short port, const char *authenticator, - const char *options, SSL_LISTENER *ssl); +extern SERV_LISTENER* serviceCreateListener(SERVICE *service, const char *name, const char *protocol, + const char *address, unsigned short port, const char *authenticator, + const char *options, SSL_LISTENER *ssl); extern int serviceHasProtocol(SERVICE *service, const char *protocol, const char* address, unsigned short port); extern void serviceAddBackend(SERVICE *, SERVER *); @@ -204,7 +204,7 @@ extern void serviceAddRouterOption(SERVICE *, char *); extern void serviceClearRouterOptions(SERVICE *); extern int serviceStart(SERVICE *); extern int serviceStartAll(); -extern bool serviceListen(SERVICE *service, unsigned short port); +extern bool serviceListen(SERVICE *service, SERV_LISTENER *port); extern int serviceStop(SERVICE *); extern int serviceRestart(SERVICE *); extern int serviceSetUser(SERVICE *, char *, char *); diff --git a/server/core/config.c b/server/core/config.c index 65d22e9af..58d01c8cb 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -3121,8 +3121,8 @@ int create_new_listener(CONFIG_CONTEXT *obj) } else { - serviceAddProtocol(service, obj->object, protocol, socket, 0, - authenticator, authenticator_options, ssl_info); + serviceCreateListener(service, obj->object, protocol, socket, 0, + authenticator, authenticator_options, ssl_info); } } @@ -3138,8 +3138,8 @@ int create_new_listener(CONFIG_CONTEXT *obj) } else { - serviceAddProtocol(service, obj->object, protocol, address, atoi(port), - authenticator, authenticator_options, ssl_info); + serviceCreateListener(service, obj->object, protocol, address, atoi(port), + authenticator, authenticator_options, ssl_info); } } diff --git a/server/core/config_runtime.c b/server/core/config_runtime.c index 84740391a..54c60550d 100644 --- a/server/core/config_runtime.c +++ b/server/core/config_runtime.c @@ -411,9 +411,10 @@ bool runtime_create_listener(SERVICE *service, const char *name, const char *add if (rval) { const char *print_addr = addr ? addr : "0.0.0.0"; + SERV_LISTENER *listener = serviceCreateListener(service, name, proto, addr, + u_port, auth, auth_opt, ssl); - if (serviceAddProtocol(service, name, proto, addr, u_port, auth, auth_opt, ssl) && - serviceListen(service, u_port)) + if (listener && listener_serialize(listener) && serviceListen(service, listener)) { MXS_NOTICE("Listener '%s' at %s:%s for service '%s' created", name, print_addr, port, service->name); diff --git a/server/core/listener.c b/server/core/listener.c index a669d2f30..1d811290b 100644 --- a/server/core/listener.c +++ b/server/core/listener.c @@ -30,13 +30,16 @@ #include #include #include +#include #include +#include #include #include #include #include #include #include +#include static RSA *rsa_512 = NULL; static RSA *rsa_1024 = NULL; @@ -69,6 +72,14 @@ listener_alloc(struct service* service, const char* name, const char *protocol, } } + char *my_auth_options = NULL; + + if (auth_options && (my_auth_options = MXS_STRDUP(auth_options)) == NULL) + { + MXS_FREE(my_address); + return NULL; + } + char *my_authenticator = NULL; if (authenticator) @@ -116,6 +127,7 @@ listener_alloc(struct service* service, const char* name, const char *protocol, proto->address = my_address; proto->port = port; proto->authenticator = my_authenticator; + proto->auth_options = my_auth_options; proto->ssl = ssl; proto->users = NULL; proto->resources = NULL; @@ -146,6 +158,8 @@ void listener_free(SERV_LISTENER* listener) MXS_FREE(listener->address); MXS_FREE(listener->authenticator); + MXS_FREE(listener->auth_options); + MXS_FREE(listener->name); MXS_FREE(listener->protocol); MXS_FREE(listener); } @@ -376,3 +390,132 @@ tmp_rsa_callback(SSL *s, int is_export, int keylength) } return(rsa_tmp); } + +/** + * Creates a listener configuration at the location pointed by @c filename + * + * @param listener Listener to serialize into a configuration + * @param filename Filename where configuration is written + * @return True on success, false on error + */ +static bool create_listener_config(const SERV_LISTENER *listener, const char *filename) +{ + int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if (file == -1) + { + char errbuf[MXS_STRERROR_BUFLEN]; + MXS_ERROR("Failed to open file '%s' when serializing listener '%s': %d, %s", + filename, listener->name, errno, strerror_r(errno, errbuf, sizeof(errbuf))); + return false; + } + + // TODO: Check for return values on all of the dprintf calls + dprintf(file, "[%s]\n", listener->name); + dprintf(file, "type=listener\n"); + dprintf(file, "protocol=%s\n", listener->protocol); + dprintf(file, "service=%s\n", listener->service->name); + dprintf(file, "address=%s\n", listener->address); + dprintf(file, "port=%u\n", listener->port); + dprintf(file, "authenticator=%s\n", listener->authenticator); + + if (listener->auth_options) + { + dprintf(file, "authenticator_options=%s\n", listener->auth_options); + } + + if (listener->ssl) + { + dprintf(file, "ssl=required\n"); + + if (listener->ssl->ssl_cert) + { + dprintf(file, "ssl_cert=%s\n", listener->ssl->ssl_cert); + } + + if (listener->ssl->ssl_key) + { + dprintf(file, "ssl_key=%s\n", listener->ssl->ssl_key); + } + + if (listener->ssl->ssl_ca_cert) + { + dprintf(file, "ssl_ca_cert=%s\n", listener->ssl->ssl_ca_cert); + } + if (listener->ssl->ssl_cert_verify_depth) + { + dprintf(file, "ssl_cert_verify_depth=%d\n", listener->ssl->ssl_cert_verify_depth); + } + + const char *version = NULL; + + switch (listener->ssl->ssl_method_type) + { + case SERVICE_TLS10: + version = "TLSV10"; + break; + +#ifdef OPENSSL_1_0 + case SERVICE_TLS11: + version = "TLSV11"; + break; + + case SERVICE_TLS12: + version = "TLSV12"; + break; +#endif + case SERVICE_SSL_TLS_MAX: + version = "MAX"; + break; + + default: + break; + } + + if (version) + { + dprintf(file, "ssl_version=%s\n", version); + } + } + + close(file); + + return true; +} + +bool listener_serialize(const SERV_LISTENER *listener) +{ + bool rval = false; + char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "%s/%s.cnf.tmp", get_config_persistdir(), + listener->name); + + if (unlink(filename) == -1 && errno != ENOENT) + { + char err[MXS_STRERROR_BUFLEN]; + MXS_ERROR("Failed to remove temporary listener configuration at '%s': %d, %s", + filename, errno, strerror_r(errno, err, sizeof(err))); + } + else if (create_listener_config(listener, filename)) + { + char final_filename[PATH_MAX]; + strcpy(final_filename, filename); + + char *dot = strrchr(final_filename, '.'); + ss_dassert(dot); + *dot = '\0'; + + if (rename(filename, final_filename) == 0) + { + rval = true; + } + else + { + char err[MXS_STRERROR_BUFLEN]; + MXS_ERROR("Failed to rename temporary listener configuration at '%s': %d, %s", + filename, errno, strerror_r(errno, err, sizeof(err))); + } + } + + return rval; +} diff --git a/server/core/service.c b/server/core/service.c index 2c04f8e54..66e3da276 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -508,22 +508,9 @@ serviceStart(SERVICE *service) * @param service The service to start the listener for * @param port The port number */ -bool serviceListen(SERVICE *service, unsigned short port) +bool serviceListen(SERVICE *service, SERV_LISTENER *port) { - bool rval = false; - for (SERV_LISTENER *ptr = service->ports; ptr; ptr = ptr->next) - { - if (ptr->port == port) - { - if (serviceStartPort(service, ptr)) - { - rval = true; - } - break; - } - } - - return rval; + return serviceStartPort(service, port); } /** @@ -678,7 +665,7 @@ service_free(SERVICE *service) } /** - * Add a protocol/port pair to the service + * Create a listener for the service * * @param service The service * @param protocol The name of the protocol module @@ -686,13 +673,13 @@ service_free(SERVICE *service) * @param port The port to listen on * @param authenticator Name of the authenticator to be used * @param ssl SSL configuration - * @return TRUE if the protocol/port could be added + * + * @return Created listener or NULL on error */ -bool serviceAddProtocol(SERVICE *service, const char *name, const char *protocol, +SERV_LISTENER* serviceCreateListener(SERVICE *service, const char *name, const char *protocol, const char *address, unsigned short port, const char *authenticator, const char *options, SSL_LISTENER *ssl) { - bool rval = false; SERV_LISTENER *proto = listener_alloc(service, name, protocol, address, port, authenticator, options, ssl); @@ -702,10 +689,9 @@ bool serviceAddProtocol(SERVICE *service, const char *name, const char *protocol proto->next = service->ports; service->ports = proto; spinlock_release(&service->spin); - rval = true; } - return rval; + return proto; } /** diff --git a/server/core/test/testservice.c b/server/core/test/testservice.c index e6aa0bd5b..cef230b21 100644 --- a/server/core/test/testservice.c +++ b/server/core/test/testservice.c @@ -69,8 +69,8 @@ test1() ss_info_dassert(0 == strcmp("MyService", service_get_name(service)), "Service must have given name"); ss_dfprintf(stderr, "\t..done\nAdding protocol testprotocol."); set_libdir(MXS_STRDUP_A("../../modules/authenticator/MySQLAuth/")); - ss_info_dassert(0 != serviceAddProtocol(service, "TestProtocol", "testprotocol", - "localhost", 9876, "MySQLAuth", NULL, NULL), + ss_info_dassert(serviceCreateListener(service, "TestProtocol", "testprotocol", + "localhost", 9876, "MySQLAuth", NULL, NULL), "Add Protocol should succeed"); ss_info_dassert(0 != serviceHasProtocol(service, "testprotocol", "localhost", 9876), "Service should have new protocol as requested");