Added SNI support (OpenSSL)
This commit is contained in:
parent
0b79494ec9
commit
074e23e4e2
17 changed files with 422 additions and 28 deletions
|
@ -557,3 +557,17 @@ modules {
|
||||||
path = "modules";
|
path = "modules";
|
||||||
path = "modules/autoload";
|
path = "modules/autoload";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
vhost "selfsigned.hades.arpa" {
|
||||||
|
ssl_private_key = "etc/selfssl.key";
|
||||||
|
ssl_cert = "etc/selfssl.pem";
|
||||||
|
};
|
||||||
|
|
||||||
|
vhost "oldca.hades.arpa" {
|
||||||
|
ssl_private_key = "etc/oldssl.key";
|
||||||
|
ssl_cert = "etc/oldssl2.pem";
|
||||||
|
ssl_dh_params = "etc/olddh.pem";
|
||||||
|
ssl_cipher_list = "kEECDH+HIGH:kEDH+HIGH:HIGH:!RC4:!aNULL";;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
|
@ -1360,3 +1360,17 @@ modules {
|
||||||
/* module: the name of a module to load on startup/rehash */
|
/* module: the name of a module to load on startup/rehash */
|
||||||
#module = "some_module.so";
|
#module = "some_module.so";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
vhost "selfsigned.hades.arpa" {
|
||||||
|
ssl_private_key = "etc/selfssl.key";
|
||||||
|
ssl_cert = "etc/selfssl.pem";
|
||||||
|
};
|
||||||
|
|
||||||
|
vhost "oldca.hades.arpa" {
|
||||||
|
ssl_private_key = "etc/oldssl.key";
|
||||||
|
ssl_cert = "etc/oldssl2.pem";
|
||||||
|
ssl_dh_params = "etc/olddh.pem";
|
||||||
|
ssl_cipher_list = "kEECDH+HIGH:kEDH+HIGH:HIGH:!RC4:!aNULL";
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
|
@ -53,6 +53,7 @@ extern rb_dlink_list xline_conf_list;
|
||||||
extern rb_dlink_list resv_conf_list;
|
extern rb_dlink_list resv_conf_list;
|
||||||
extern rb_dlink_list nd_list;
|
extern rb_dlink_list nd_list;
|
||||||
extern rb_dlink_list tgchange_list;
|
extern rb_dlink_list tgchange_list;
|
||||||
|
extern rb_dlink_list vhost_conf_list;
|
||||||
|
|
||||||
extern struct _rb_patricia_tree_t *tgchange_tree;
|
extern struct _rb_patricia_tree_t *tgchange_tree;
|
||||||
|
|
||||||
|
@ -245,5 +246,16 @@ extern void add_nd_entry(const char *name);
|
||||||
extern void free_nd_entry(struct nd_entry *);
|
extern void free_nd_entry(struct nd_entry *);
|
||||||
extern unsigned long get_nd_count(void);
|
extern unsigned long get_nd_count(void);
|
||||||
|
|
||||||
#endif
|
struct vhost_conf
|
||||||
|
{
|
||||||
|
char *hostname;
|
||||||
|
char *ssl_private_key;
|
||||||
|
char *ssl_cert;
|
||||||
|
char *ssl_dh_params;
|
||||||
|
char *ssl_cipher_list;
|
||||||
|
rb_dlink_node node;
|
||||||
|
};
|
||||||
|
extern struct vhost_conf *make_vhost_conf(void);
|
||||||
|
extern void free_vhost_conf(struct vhost_conf *);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -39,10 +39,10 @@ int start_ssldaemon(int count, const char *ssl_cert, const char *ssl_private_key
|
||||||
ssl_ctl_t *start_ssld_accept(rb_fde_t *sslF, rb_fde_t *plainF, uint32_t id);
|
ssl_ctl_t *start_ssld_accept(rb_fde_t *sslF, rb_fde_t *plainF, uint32_t id);
|
||||||
ssl_ctl_t *start_ssld_connect(rb_fde_t *sslF, rb_fde_t *plainF, uint32_t id);
|
ssl_ctl_t *start_ssld_connect(rb_fde_t *sslF, rb_fde_t *plainF, uint32_t id);
|
||||||
void start_zlib_session(void *data);
|
void start_zlib_session(void *data);
|
||||||
void send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list, const int method);
|
void send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list, const int method, const char *hostname);
|
||||||
|
void send_remove_ssl_vhost(const char *hostname);
|
||||||
void ssld_decrement_clicount(ssl_ctl_t *ctl);
|
void ssld_decrement_clicount(ssl_ctl_t *ctl);
|
||||||
int get_ssld_count(void);
|
int get_ssld_count(void);
|
||||||
void ssld_foreach_info(void (*func)(void *data, pid_t pid, int cli_count, enum ssld_status status), void *data);
|
void ssld_foreach_info(void (*func)(void *data, pid_t pid, int cli_count, enum ssld_status status), void *data);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#ifndef _COMMIO_SSL_H
|
#ifndef _COMMIO_SSL_H
|
||||||
#define _COMMIO_SSL_H
|
#define _COMMIO_SSL_H
|
||||||
|
|
||||||
int rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile, const char *cipher_list);
|
int rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile, const char *cipher_list, const char *hostname);
|
||||||
int rb_init_ssl(void);
|
int rb_init_ssl(void);
|
||||||
|
|
||||||
int rb_ssl_listen(rb_fde_t *F, int backlog, int defer_accept);
|
int rb_ssl_listen(rb_fde_t *F, int backlog, int defer_accept);
|
||||||
|
|
|
@ -152,9 +152,10 @@ ssize_t rb_writev(rb_fde_t *, struct rb_iovec *vector, int count);
|
||||||
ssize_t rb_read(rb_fde_t *, void *buf, int count);
|
ssize_t rb_read(rb_fde_t *, void *buf, int count);
|
||||||
int rb_pipe(rb_fde_t **, rb_fde_t **, const char *desc);
|
int rb_pipe(rb_fde_t **, rb_fde_t **, const char *desc);
|
||||||
|
|
||||||
int rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile, const char *cipher_list);
|
int rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile, const char *cipher_list, const char *hostname);
|
||||||
int rb_ssl_listen(rb_fde_t *, int backlog, int defer_accept);
|
int rb_ssl_listen(rb_fde_t *, int backlog, int defer_accept);
|
||||||
int rb_listen(rb_fde_t *, int backlog, int defer_accept);
|
int rb_listen(rb_fde_t *, int backlog, int defer_accept);
|
||||||
|
int rb_remove_ssl_vserver(const char *hostname);
|
||||||
|
|
||||||
const char *rb_inet_ntop(int af, const void *src, char *dst, unsigned int size);
|
const char *rb_inet_ntop(int af, const void *src, char *dst, unsigned int size);
|
||||||
int rb_inet_pton(int af, const char *src, void *dst);
|
int rb_inet_pton(int af, const char *src, void *dst);
|
||||||
|
|
|
@ -48,6 +48,8 @@ rb_setselect
|
||||||
rb_settimeout
|
rb_settimeout
|
||||||
rb_setup_fd
|
rb_setup_fd
|
||||||
rb_setup_ssl_server
|
rb_setup_ssl_server
|
||||||
|
rb_setup_ssl_server_hostname
|
||||||
|
rb_remove_ssl_vserver
|
||||||
rb_socket
|
rb_socket
|
||||||
rb_socketpair
|
rb_socketpair
|
||||||
rb_ssl_listen
|
rb_ssl_listen
|
||||||
|
|
|
@ -498,7 +498,7 @@ rb_init_ssl(void)
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
||||||
const char *const dhfile, const char *cipherlist)
|
const char *const dhfile, const char *cipherlist, char *hostname)
|
||||||
{
|
{
|
||||||
if(certfile == NULL)
|
if(certfile == NULL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -457,7 +457,7 @@ rb_init_ssl(void)
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
||||||
const char *const dhfile, const char *const cipherlist)
|
const char *const dhfile, const char *const cipherlist, const char *hostname)
|
||||||
{
|
{
|
||||||
if(certfile == NULL)
|
if(certfile == NULL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include <commio-ssl.h>
|
#include <commio-ssl.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile, const char *cipher_list)
|
rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile, const char *cipher_list, const char *hostname)
|
||||||
{
|
{
|
||||||
errno = ENOSYS;
|
errno = ENOSYS;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -41,9 +41,31 @@ typedef enum
|
||||||
|
|
||||||
#define SSL_P(x) ((SSL *)((x)->ssl))
|
#define SSL_P(x) ((SSL *)((x)->ssl))
|
||||||
|
|
||||||
|
typedef struct ssl_vhost_s
|
||||||
|
{
|
||||||
|
char *hostname;
|
||||||
|
SSL_CTX *ctx;
|
||||||
|
rb_dlink_node node;
|
||||||
|
} ssl_vhost;
|
||||||
|
|
||||||
static SSL_CTX *ssl_ctx = NULL;
|
static SSL_CTX *ssl_ctx = NULL;
|
||||||
|
rb_dlink_list ssl_vhosts;
|
||||||
|
|
||||||
|
rb_dlink_node *
|
||||||
|
find_ssl_vhost(const char *hostname)
|
||||||
|
{
|
||||||
|
ssl_vhost *vhost_p;
|
||||||
|
rb_dlink_node *ptr;
|
||||||
|
|
||||||
|
RB_DLINK_FOREACH(ptr, ssl_vhosts.head)
|
||||||
|
{
|
||||||
|
vhost_p = ptr->data;
|
||||||
|
|
||||||
|
if(!strcmp(hostname,vhost_p->hostname))
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct ssl_connect
|
struct ssl_connect
|
||||||
{
|
{
|
||||||
|
@ -337,9 +359,65 @@ rb_init_ssl(void)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rb_ssl_servername_cb(SSL *s, int *ad, void *arg)
|
||||||
|
{
|
||||||
|
ssl_vhost *vhost_p;
|
||||||
|
rb_dlink_node *ptr;
|
||||||
|
rb_dlink_node *next_ptr;
|
||||||
|
|
||||||
|
const char * servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
|
||||||
|
|
||||||
|
rb_lib_log("Hostname in TLS extension?");
|
||||||
|
if (servername)
|
||||||
|
{
|
||||||
|
rb_lib_log("Hostname in TLS extension %s", servername);
|
||||||
|
|
||||||
|
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, ssl_vhosts.head)
|
||||||
|
{
|
||||||
|
vhost_p = ptr->data;
|
||||||
|
|
||||||
|
if (strcmp(servername, vhost_p->hostname) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (vhost_p->ctx)
|
||||||
|
{
|
||||||
|
rb_lib_log("Switching server context %s", servername);
|
||||||
|
SSL_set_SSL_CTX(s,vhost_p->ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
free_ssl_vhost(ssl_vhost *vhost_p)
|
||||||
|
{
|
||||||
|
if(vhost_p == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rb_free(vhost_p->hostname);
|
||||||
|
|
||||||
|
SSL_CTX_free(vhost_p->ctx);
|
||||||
|
rb_free(vhost_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
rb_remove_ssl_vserver(const char *hostname)
|
||||||
|
{
|
||||||
|
rb_dlink_node *ptr = find_ssl_vhost(hostname);
|
||||||
|
if(ptr != NULL && ptr) {
|
||||||
|
rb_dlinkDelete(ptr, &ssl_vhosts);
|
||||||
|
free_ssl_vhost(ptr->data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
||||||
const char *const dhfile, const char *cipherlist)
|
const char *const dhfile, const char *cipherlist,
|
||||||
|
const char *hostname)
|
||||||
{
|
{
|
||||||
if(certfile == NULL)
|
if(certfile == NULL)
|
||||||
{
|
{
|
||||||
|
@ -419,6 +497,7 @@ rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
||||||
SSL_CTX_free(ssl_ctx_new);
|
SSL_CTX_free(ssl_ctx_new);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
SSL_CTX_set_tlsext_servername_callback(ssl_ctx_new, rb_ssl_servername_cb);
|
||||||
|
|
||||||
SSL_CTX_set_session_cache_mode(ssl_ctx_new, SSL_SESS_CACHE_OFF);
|
SSL_CTX_set_session_cache_mode(ssl_ctx_new, SSL_SESS_CACHE_OFF);
|
||||||
SSL_CTX_set_verify(ssl_ctx_new, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb);
|
SSL_CTX_set_verify(ssl_ctx_new, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb);
|
||||||
|
@ -472,14 +551,33 @@ rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
rb_lib_log("%s: TLS configuration successful", __func__);
|
||||||
|
if(hostname == NULL) {
|
||||||
if(ssl_ctx)
|
if(ssl_ctx)
|
||||||
SSL_CTX_free(ssl_ctx);
|
SSL_CTX_free(ssl_ctx);
|
||||||
|
|
||||||
ssl_ctx = ssl_ctx_new;
|
ssl_ctx = ssl_ctx_new;
|
||||||
|
} else {
|
||||||
|
ssl_vhost *vhost_p = NULL;
|
||||||
|
|
||||||
|
rb_lib_log("rb_setup_ssl_server: SETUP [%s]", hostname);
|
||||||
|
rb_dlink_node *ptr = find_ssl_vhost(hostname);
|
||||||
|
if(ptr != NULL && ptr)
|
||||||
|
vhost_p = ptr->data;
|
||||||
|
|
||||||
|
if(vhost_p == NULL) {
|
||||||
|
vhost_p = rb_malloc(sizeof(ssl_vhost));
|
||||||
|
vhost_p->hostname = rb_strdup(hostname);
|
||||||
|
|
||||||
|
rb_dlinkAdd(vhost_p, &vhost_p->node, &ssl_vhosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vhost_p->ctx)
|
||||||
|
SSL_CTX_free(vhost_p->ctx);
|
||||||
|
|
||||||
|
vhost_p->ctx = ssl_ctx_new;
|
||||||
|
}
|
||||||
|
|
||||||
rb_lib_log("%s: TLS configuration successful", __func__);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
src/ircd.c
17
src/ircd.c
|
@ -535,6 +535,9 @@ int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
|
struct vhost_conf *vhost_p;
|
||||||
|
rb_dlink_node *ptr;
|
||||||
|
rb_dlink_node *next_ptr;
|
||||||
|
|
||||||
/* Check to see if the user is running us as root, which is a nono */
|
/* Check to see if the user is running us as root, which is a nono */
|
||||||
if(geteuid() == 0)
|
if(geteuid() == 0)
|
||||||
|
@ -712,13 +715,25 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
/* just do the rb_setup_ssl_server to validate the config */
|
/* just do the rb_setup_ssl_server to validate the config */
|
||||||
if(!rb_setup_ssl_server(ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list))
|
if(!rb_setup_ssl_server(ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list, NULL))
|
||||||
{
|
{
|
||||||
ilog(L_MAIN, "WARNING: Unable to setup SSL.");
|
ilog(L_MAIN, "WARNING: Unable to setup SSL.");
|
||||||
ircd_ssl_ok = 0;
|
ircd_ssl_ok = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ircd_ssl_ok = 1;
|
ircd_ssl_ok = 1;
|
||||||
|
|
||||||
|
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, vhost_conf_list.head)
|
||||||
|
{
|
||||||
|
vhost_p = ptr->data;
|
||||||
|
ilog(L_MAIN, "WARNING: h:%s c:%s k:%s d:%s ci:%s", vhost_p->hostname, vhost_p->ssl_cert, vhost_p->ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list);
|
||||||
|
if(rb_setup_ssl_server(vhost_p->ssl_cert, vhost_p->ssl_private_key,
|
||||||
|
vhost_p->ssl_dh_params ? vhost_p->ssl_dh_params : ServerInfo.ssl_dh_params,
|
||||||
|
vhost_p->ssl_cipher_list ? vhost_p->ssl_cipher_list : ServerInfo.ssl_cipher_list, vhost_p->hostname))
|
||||||
|
{
|
||||||
|
ircd_ssl_ok = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testing_conf)
|
if (testing_conf)
|
||||||
|
|
|
@ -46,6 +46,7 @@ static struct Class *yy_class = NULL;
|
||||||
|
|
||||||
static struct remote_conf *yy_shared = NULL;
|
static struct remote_conf *yy_shared = NULL;
|
||||||
static struct server_conf *yy_server = NULL;
|
static struct server_conf *yy_server = NULL;
|
||||||
|
static struct vhost_conf *yy_vhost = NULL;
|
||||||
|
|
||||||
static rb_dlink_list yy_aconf_list;
|
static rb_dlink_list yy_aconf_list;
|
||||||
static rb_dlink_list yy_oper_list;
|
static rb_dlink_list yy_oper_list;
|
||||||
|
@ -1439,6 +1440,71 @@ conf_set_connect_class(void *data)
|
||||||
yy_server->class_name = rb_strdup(data);
|
yy_server->class_name = rb_strdup(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
conf_begin_vhost(struct TopConf *tc)
|
||||||
|
{
|
||||||
|
if(yy_vhost)
|
||||||
|
free_vhost_conf(yy_vhost);
|
||||||
|
|
||||||
|
yy_vhost = make_vhost_conf();
|
||||||
|
|
||||||
|
if(conf_cur_block_name != NULL) {
|
||||||
|
yy_vhost->hostname = rb_strdup(conf_cur_block_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
conf_end_vhost(struct TopConf *tc)
|
||||||
|
{
|
||||||
|
if(EmptyString(yy_vhost->hostname))
|
||||||
|
{
|
||||||
|
conf_report_error("Ignoring vhost block -- missing hostname.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(EmptyString(yy_vhost->ssl_cert) || EmptyString(yy_vhost->ssl_private_key))
|
||||||
|
{
|
||||||
|
conf_report_error("Ignoring vhost block for %s -- no ssl_cert or ssl_private_key file provided.",
|
||||||
|
yy_vhost->hostname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_dlinkAdd(yy_vhost, &yy_vhost->node, &vhost_conf_list);
|
||||||
|
yy_vhost = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
conf_set_vhost_ssl_private_key(void *data)
|
||||||
|
{
|
||||||
|
if (yy_vhost->ssl_private_key)
|
||||||
|
rb_free(yy_vhost->ssl_private_key);
|
||||||
|
yy_vhost->ssl_private_key = rb_strdup((char *) data);
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
conf_set_vhost_ssl_cert(void *data)
|
||||||
|
{
|
||||||
|
if (yy_vhost->ssl_cert)
|
||||||
|
rb_free(yy_vhost->ssl_cert);
|
||||||
|
yy_vhost->ssl_cert = rb_strdup((char *) data);
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
conf_set_vhost_ssl_dh_params(void *data)
|
||||||
|
{
|
||||||
|
if (yy_vhost->ssl_dh_params)
|
||||||
|
rb_free(yy_vhost->ssl_dh_params);
|
||||||
|
yy_vhost->ssl_dh_params = rb_strdup((char *) data);
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
conf_set_vhost_ssl_cipher_list(void *data)
|
||||||
|
{
|
||||||
|
if (yy_vhost->ssl_cipher_list)
|
||||||
|
rb_free(yy_vhost->ssl_cipher_list);
|
||||||
|
yy_vhost->ssl_cipher_list = rb_strdup((char *) data);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
conf_set_exempt_ip(void *data)
|
conf_set_exempt_ip(void *data)
|
||||||
{
|
{
|
||||||
|
@ -1457,6 +1523,15 @@ conf_set_exempt_ip(void *data)
|
||||||
add_conf_by_address(yy_tmp->host, CONF_EXEMPTDLINE, NULL, NULL, yy_tmp);
|
add_conf_by_address(yy_tmp->host, CONF_EXEMPTDLINE, NULL, NULL, yy_tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct ConfEntry conf_vhost_table[] =
|
||||||
|
{
|
||||||
|
{ "ssl_private_key", CF_QSTRING, conf_set_vhost_ssl_private_key, 0, NULL },
|
||||||
|
{ "ssl_cert", CF_QSTRING, conf_set_vhost_ssl_cert, 0, NULL },
|
||||||
|
{ "ssl_dh_params", CF_QSTRING, conf_set_vhost_ssl_dh_params, 0, NULL },
|
||||||
|
{ "ssl_cipher_list", CF_QSTRING, conf_set_vhost_ssl_cipher_list, 0, NULL },
|
||||||
|
{ "\0", 0, NULL, 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
conf_cleanup_cluster(struct TopConf *tc)
|
conf_cleanup_cluster(struct TopConf *tc)
|
||||||
{
|
{
|
||||||
|
@ -2592,6 +2667,7 @@ newconf_init()
|
||||||
add_conf_item("shared", "flags", CF_STRING | CF_FLIST, conf_set_shared_flags);
|
add_conf_item("shared", "flags", CF_STRING | CF_FLIST, conf_set_shared_flags);
|
||||||
|
|
||||||
add_top_conf("connect", conf_begin_connect, conf_end_connect, conf_connect_table);
|
add_top_conf("connect", conf_begin_connect, conf_end_connect, conf_connect_table);
|
||||||
|
add_top_conf("vhost", conf_begin_vhost, conf_end_vhost, conf_vhost_table);
|
||||||
|
|
||||||
add_top_conf("exempt", NULL, NULL, NULL);
|
add_top_conf("exempt", NULL, NULL, NULL);
|
||||||
add_conf_item("exempt", "ip", CF_QSTRING, conf_set_exempt_ip);
|
add_conf_item("exempt", "ip", CF_QSTRING, conf_set_exempt_ip);
|
||||||
|
|
31
src/s_conf.c
31
src/s_conf.c
|
@ -860,6 +860,10 @@ read_conf(void)
|
||||||
static void
|
static void
|
||||||
validate_conf(void)
|
validate_conf(void)
|
||||||
{
|
{
|
||||||
|
struct vhost_conf *vhost_p;
|
||||||
|
rb_dlink_node *ptr;
|
||||||
|
rb_dlink_node *next_ptr;
|
||||||
|
|
||||||
if(ConfigFileEntry.default_ident_timeout < 1)
|
if(ConfigFileEntry.default_ident_timeout < 1)
|
||||||
ConfigFileEntry.default_ident_timeout = IDENT_TIMEOUT_DEFAULT;
|
ConfigFileEntry.default_ident_timeout = IDENT_TIMEOUT_DEFAULT;
|
||||||
|
|
||||||
|
@ -875,13 +879,27 @@ validate_conf(void)
|
||||||
if(ServerInfo.ssld_count < 1)
|
if(ServerInfo.ssld_count < 1)
|
||||||
ServerInfo.ssld_count = 1;
|
ServerInfo.ssld_count = 1;
|
||||||
|
|
||||||
if(!rb_setup_ssl_server(ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list))
|
if(!rb_setup_ssl_server(ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list, NULL))
|
||||||
{
|
{
|
||||||
ilog(L_MAIN, "WARNING: Unable to setup SSL.");
|
ilog(L_MAIN, "WARNING: Unable to setup SSL.");
|
||||||
ircd_ssl_ok = 0;
|
ircd_ssl_ok = 0;
|
||||||
} else {
|
} else {
|
||||||
ircd_ssl_ok = 1;
|
ircd_ssl_ok = 1;
|
||||||
send_new_ssl_certs(ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list, ConfigFileEntry.certfp_method);
|
send_new_ssl_certs(ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list, ConfigFileEntry.certfp_method, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, vhost_conf_list.head)
|
||||||
|
{
|
||||||
|
vhost_p = ptr->data;
|
||||||
|
if(rb_setup_ssl_server(vhost_p->ssl_cert, vhost_p->ssl_private_key,
|
||||||
|
vhost_p->ssl_dh_params,
|
||||||
|
vhost_p->ssl_cipher_list, vhost_p->hostname))
|
||||||
|
{
|
||||||
|
ircd_ssl_ok = 1;
|
||||||
|
send_new_ssl_certs(vhost_p->ssl_cert, vhost_p->ssl_private_key,
|
||||||
|
vhost_p->ssl_dh_params ? vhost_p->ssl_dh_params : ServerInfo.ssl_dh_params,
|
||||||
|
vhost_p->ssl_cipher_list ? vhost_p->ssl_cipher_list : ServerInfo.ssl_cipher_list, ConfigFileEntry.certfp_method, vhost_p->hostname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ServerInfo.ssld_count > get_ssld_count())
|
if(ServerInfo.ssld_count > get_ssld_count())
|
||||||
|
@ -1462,6 +1480,7 @@ static void
|
||||||
clear_out_old_conf(void)
|
clear_out_old_conf(void)
|
||||||
{
|
{
|
||||||
struct Class *cltmp;
|
struct Class *cltmp;
|
||||||
|
struct vhost_conf *vhost_p;
|
||||||
rb_dlink_node *ptr;
|
rb_dlink_node *ptr;
|
||||||
rb_dlink_node *next_ptr;
|
rb_dlink_node *next_ptr;
|
||||||
|
|
||||||
|
@ -1547,6 +1566,14 @@ clear_out_old_conf(void)
|
||||||
rb_dlinkDestroy(ptr, &service_list);
|
rb_dlinkDestroy(ptr, &service_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, vhost_conf_list.head)
|
||||||
|
{
|
||||||
|
vhost_p = ptr->data;
|
||||||
|
send_remove_ssl_vhost(vhost_p->hostname);
|
||||||
|
rb_dlinkDelete(ptr, &vhost_conf_list);
|
||||||
|
free_vhost_conf(ptr->data);
|
||||||
|
}
|
||||||
|
|
||||||
/* remove any aliases... -- nenolod */
|
/* remove any aliases... -- nenolod */
|
||||||
if (alias_dict != NULL)
|
if (alias_dict != NULL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,6 +56,7 @@ rb_dlink_list xline_conf_list;
|
||||||
rb_dlink_list resv_conf_list; /* nicks only! */
|
rb_dlink_list resv_conf_list; /* nicks only! */
|
||||||
rb_dlink_list nd_list; /* nick delay */
|
rb_dlink_list nd_list; /* nick delay */
|
||||||
rb_dlink_list tgchange_list;
|
rb_dlink_list tgchange_list;
|
||||||
|
rb_dlink_list vhost_conf_list;
|
||||||
|
|
||||||
rb_patricia_tree_t *tgchange_tree;
|
rb_patricia_tree_t *tgchange_tree;
|
||||||
|
|
||||||
|
@ -805,3 +806,42 @@ find_tgchange(const char *host)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct vhost_conf *
|
||||||
|
find_ssl_vhost(const char *hostname)
|
||||||
|
{
|
||||||
|
struct vhost_conf *vhost_p;
|
||||||
|
rb_dlink_node *ptr;
|
||||||
|
|
||||||
|
RB_DLINK_FOREACH(ptr, vhost_conf_list.head)
|
||||||
|
{
|
||||||
|
vhost_p = ptr->data;
|
||||||
|
|
||||||
|
if(!strcmp(hostname,vhost_p->hostname))
|
||||||
|
return vhost_p;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vhost_conf *
|
||||||
|
make_vhost_conf(void)
|
||||||
|
{
|
||||||
|
struct vhost_conf *vhost_p = rb_malloc(sizeof(struct vhost_conf));
|
||||||
|
return vhost_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
free_vhost_conf(struct vhost_conf *vhost_p)
|
||||||
|
{
|
||||||
|
s_assert(vhost_p != NULL);
|
||||||
|
if(vhost_p == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rb_free(vhost_p->hostname);
|
||||||
|
rb_free(vhost_p->ssl_private_key);
|
||||||
|
rb_free(vhost_p->ssl_cert);
|
||||||
|
rb_free(vhost_p->ssl_dh_params);
|
||||||
|
rb_free(vhost_p->ssl_cipher_list);
|
||||||
|
|
||||||
|
rb_free(vhost_p);
|
||||||
|
}
|
||||||
|
// TODO: Free vhost_conf_list
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "stdinc.h"
|
#include "stdinc.h"
|
||||||
|
|
||||||
#include "s_conf.h"
|
#include "s_conf.h"
|
||||||
|
#include "s_newconf.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "listener.h"
|
#include "listener.h"
|
||||||
#include "sslproc.h"
|
#include "sslproc.h"
|
||||||
|
@ -70,7 +71,9 @@ struct _ssl_ctl
|
||||||
|
|
||||||
static void send_new_ssl_certs_one(ssl_ctl_t * ctl, const char *ssl_cert,
|
static void send_new_ssl_certs_one(ssl_ctl_t * ctl, const char *ssl_cert,
|
||||||
const char *ssl_private_key, const char *ssl_dh_params,
|
const char *ssl_private_key, const char *ssl_dh_params,
|
||||||
const char *ssl_cipher_list);
|
const char *ssl_cipher_list, const char *hostname);
|
||||||
|
static void send_remove_ssl_vhost_one(ssl_ctl_t * ctl, const char *hostname);
|
||||||
|
|
||||||
static void send_init_prng(ssl_ctl_t * ctl, prng_seed_t seedtype, const char *path);
|
static void send_init_prng(ssl_ctl_t * ctl, prng_seed_t seedtype, const char *path);
|
||||||
static void send_certfp_method(ssl_ctl_t *ctl, int method);
|
static void send_certfp_method(ssl_ctl_t *ctl, int method);
|
||||||
|
|
||||||
|
@ -256,6 +259,10 @@ start_ssldaemon(int count, const char *ssl_cert, const char *ssl_private_key, co
|
||||||
char s_pid[10];
|
char s_pid[10];
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int started = 0, i;
|
int started = 0, i;
|
||||||
|
struct vhost_conf *vhost_p;
|
||||||
|
rb_dlink_node *ptr;
|
||||||
|
rb_dlink_node *next_ptr;
|
||||||
|
|
||||||
|
|
||||||
if(ssld_wait)
|
if(ssld_wait)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -341,8 +348,16 @@ start_ssldaemon(int count, const char *ssl_cert, const char *ssl_private_key, co
|
||||||
send_init_prng(ctl, RB_PRNG_DEFAULT, NULL);
|
send_init_prng(ctl, RB_PRNG_DEFAULT, NULL);
|
||||||
send_certfp_method(ctl, ConfigFileEntry.certfp_method);
|
send_certfp_method(ctl, ConfigFileEntry.certfp_method);
|
||||||
|
|
||||||
if(ssl_cert != NULL)
|
if(ssl_cert != NULL) {
|
||||||
send_new_ssl_certs_one(ctl, ssl_cert, ssl_private_key, ssl_dh_params, ssl_cipher_list);
|
send_new_ssl_certs_one(ctl, ssl_cert, ssl_private_key, ssl_dh_params, ssl_cipher_list, NULL);
|
||||||
|
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, vhost_conf_list.head)
|
||||||
|
{
|
||||||
|
vhost_p = ptr->data;
|
||||||
|
send_new_ssl_certs_one(ctl, vhost_p->ssl_cert, vhost_p->ssl_private_key,
|
||||||
|
vhost_p->ssl_dh_params ? vhost_p->ssl_dh_params : ssl_dh_params,
|
||||||
|
vhost_p->ssl_cipher_list ? vhost_p->ssl_cipher_list : ssl_cipher_list, vhost_p->hostname);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ssl_read_ctl(ctl->F, ctl);
|
ssl_read_ctl(ctl->F, ctl);
|
||||||
ssl_do_pipe(P2, ctl);
|
ssl_do_pipe(P2, ctl);
|
||||||
|
@ -671,7 +686,7 @@ ssl_cmd_write_queue(ssl_ctl_t * ctl, rb_fde_t ** F, int count, const void *buf,
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_new_ssl_certs_one(ssl_ctl_t * ctl, const char *ssl_cert, const char *ssl_private_key,
|
send_new_ssl_certs_one(ssl_ctl_t * ctl, const char *ssl_cert, const char *ssl_private_key,
|
||||||
const char *ssl_dh_params, const char *ssl_cipher_list)
|
const char *ssl_dh_params, const char *ssl_cipher_list, const char *hostname)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
|
@ -684,7 +699,10 @@ send_new_ssl_certs_one(ssl_ctl_t * ctl, const char *ssl_cert, const char *ssl_pr
|
||||||
if (ssl_cipher_list == NULL)
|
if (ssl_cipher_list == NULL)
|
||||||
ssl_cipher_list = "";
|
ssl_cipher_list = "";
|
||||||
|
|
||||||
len = strlen(ssl_cert) + strlen(ssl_private_key) + strlen(ssl_dh_params) + strlen(ssl_cipher_list) + 6;
|
if (hostname == NULL)
|
||||||
|
hostname = "";
|
||||||
|
|
||||||
|
len = strlen(ssl_cert) + strlen(ssl_private_key) + strlen(ssl_dh_params) + strlen(ssl_cipher_list) + strlen(hostname) + 6;
|
||||||
if(len > sizeof(tmpbuf))
|
if(len > sizeof(tmpbuf))
|
||||||
{
|
{
|
||||||
sendto_realops_snomask(SNO_GENERAL, L_ALL,
|
sendto_realops_snomask(SNO_GENERAL, L_ALL,
|
||||||
|
@ -695,8 +713,47 @@ send_new_ssl_certs_one(ssl_ctl_t * ctl, const char *ssl_cert, const char *ssl_pr
|
||||||
len, sizeof(tmpbuf));
|
len, sizeof(tmpbuf));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
len = rb_snprintf(tmpbuf, sizeof(tmpbuf), "K%c%s%c%s%c%s%c%s%c", nul, ssl_cert,
|
len = rb_snprintf(tmpbuf, sizeof(tmpbuf), "K%c%s%c%s%c%s%c%s%c%s%c", nul, ssl_cert,
|
||||||
nul, ssl_private_key, nul, ssl_dh_params, nul, ssl_cipher_list, nul);
|
nul, ssl_private_key, nul, ssl_dh_params, nul, ssl_cipher_list, nul, hostname, nul);
|
||||||
|
ssl_cmd_write_queue(ctl, NULL, 0, tmpbuf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
send_remove_ssl_vhost(const char *hostname)
|
||||||
|
{
|
||||||
|
rb_dlink_node *ptr;
|
||||||
|
if(hostname == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RB_DLINK_FOREACH(ptr, ssl_daemons.head)
|
||||||
|
{
|
||||||
|
ssl_ctl_t *ctl = ptr->data;
|
||||||
|
send_remove_ssl_vhost_one(ctl, hostname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_remove_ssl_vhost_one(ssl_ctl_t * ctl, const char *hostname)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if(hostname == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
len = strlen(hostname) + 3;
|
||||||
|
if(len > sizeof(tmpbuf))
|
||||||
|
{
|
||||||
|
sendto_realops_snomask(SNO_GENERAL, L_ALL,
|
||||||
|
"Parameters for send_init_prng too long (%zd > %zd) to pass to ssld, not sending...",
|
||||||
|
len, sizeof(tmpbuf));
|
||||||
|
ilog(L_MAIN,
|
||||||
|
"Parameters for send_init_prng too long (%zd > %zd) to pass to ssld, not sending...",
|
||||||
|
len, sizeof(tmpbuf));
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
len = rb_snprintf(tmpbuf, sizeof(tmpbuf), "D%c%s%c", nul, hostname, nul);
|
||||||
ssl_cmd_write_queue(ctl, NULL, 0, tmpbuf, len);
|
ssl_cmd_write_queue(ctl, NULL, 0, tmpbuf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,7 +796,7 @@ send_certfp_method(ssl_ctl_t *ctl, int method)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list, const int method)
|
send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list, const int method, const char *hostname)
|
||||||
{
|
{
|
||||||
rb_dlink_node *ptr;
|
rb_dlink_node *ptr;
|
||||||
if(ssl_cert == NULL)
|
if(ssl_cert == NULL)
|
||||||
|
@ -755,7 +812,7 @@ send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
send_certfp_method(ctl, method);
|
send_certfp_method(ctl, method);
|
||||||
send_new_ssl_certs_one(ctl, ssl_cert, ssl_private_key, ssl_dh_params, ssl_cipher_list);
|
send_new_ssl_certs_one(ctl, ssl_cert, ssl_private_key, ssl_dh_params, ssl_cipher_list, hostname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
42
ssld/ssld.c
42
ssld/ssld.c
|
@ -925,7 +925,7 @@ static void
|
||||||
ssl_new_keys(mod_ctl_t * ctl, mod_ctl_buf_t * ctl_buf)
|
ssl_new_keys(mod_ctl_t * ctl, mod_ctl_buf_t * ctl_buf)
|
||||||
{
|
{
|
||||||
char *buf;
|
char *buf;
|
||||||
char *cert, *key, *dhparam, *cipher_list;
|
char *cert, *key, *dhparam, *cipher_list, *hostname;
|
||||||
|
|
||||||
buf = (char *) &ctl_buf->buf[2];
|
buf = (char *) &ctl_buf->buf[2];
|
||||||
cert = buf;
|
cert = buf;
|
||||||
|
@ -935,6 +935,8 @@ ssl_new_keys(mod_ctl_t * ctl, mod_ctl_buf_t * ctl_buf)
|
||||||
dhparam = buf;
|
dhparam = buf;
|
||||||
buf += strlen(dhparam) + 1;
|
buf += strlen(dhparam) + 1;
|
||||||
cipher_list = buf;
|
cipher_list = buf;
|
||||||
|
buf += strlen(cipher_list) + 1;
|
||||||
|
hostname = buf;
|
||||||
if(strlen(key) == 0)
|
if(strlen(key) == 0)
|
||||||
key = cert;
|
key = cert;
|
||||||
if(strlen(dhparam) == 0)
|
if(strlen(dhparam) == 0)
|
||||||
|
@ -942,7 +944,15 @@ ssl_new_keys(mod_ctl_t * ctl, mod_ctl_buf_t * ctl_buf)
|
||||||
if(strlen(cipher_list) == 0)
|
if(strlen(cipher_list) == 0)
|
||||||
cipher_list = NULL;
|
cipher_list = NULL;
|
||||||
|
|
||||||
if(!rb_setup_ssl_server(cert, key, dhparam, cipher_list))
|
int ret;
|
||||||
|
if(strlen(hostname) == 0) {
|
||||||
|
ret = rb_setup_ssl_server(cert, key, dhparam, cipher_list, NULL);
|
||||||
|
} else {
|
||||||
|
ret = rb_setup_ssl_server(cert, key, dhparam, cipher_list, hostname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ret)
|
||||||
{
|
{
|
||||||
const char *invalid = "I";
|
const char *invalid = "I";
|
||||||
mod_cmd_write_queue(ctl, invalid, strlen(invalid));
|
mod_cmd_write_queue(ctl, invalid, strlen(invalid));
|
||||||
|
@ -950,6 +960,24 @@ ssl_new_keys(mod_ctl_t * ctl, mod_ctl_buf_t * ctl_buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ssl_remove_ssl_vhost(mod_ctl_t * ctl, mod_ctl_buf_t * ctl_buf)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
char *hostname;
|
||||||
|
|
||||||
|
buf = (char *) &ctl_buf->buf[2];
|
||||||
|
hostname = buf;
|
||||||
|
|
||||||
|
if(!rb_remove_ssl_vserver(hostname))
|
||||||
|
{
|
||||||
|
const char *invalid = "I";
|
||||||
|
mod_cmd_write_queue(ctl, invalid, strlen(invalid));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_nossl_support(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
send_nossl_support(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
||||||
{
|
{
|
||||||
|
@ -1054,6 +1082,16 @@ mod_process_cmd_recv(mod_ctl_t * ctl)
|
||||||
ssl_new_keys(ctl, ctl_buf);
|
ssl_new_keys(ctl, ctl_buf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'D':
|
||||||
|
{
|
||||||
|
if(!ssld_ssl_ok)
|
||||||
|
{
|
||||||
|
send_nossl_support(ctl, ctl_buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ssl_remove_ssl_vhost(ctl, ctl_buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'I':
|
case 'I':
|
||||||
init_prng(ctl, ctl_buf);
|
init_prng(ctl, ctl_buf);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue