solanum/ircd/sslproc.c
William Pitcock de7cf7e009 ircd: client: substantially rework the connid registry system
now connid's are allocated on demand and clients may have as many connid's as necessary.
this allows us to build chains of helpers while ensuring the ircd properly tracks and GCs the resources.
2016-03-25 19:50:29 -05:00

955 lines
22 KiB
C

/*
* sslproc.c: An interface to ssld
* Copyright (C) 2007 Aaron Sethman <androsyn@ratbox.org>
* Copyright (C) 2007 ircd-ratbox development team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <rb_lib.h>
#include "stdinc.h"
#include "s_conf.h"
#include "logger.h"
#include "listener.h"
#include "sslproc.h"
#include "s_serv.h"
#include "ircd.h"
#include "hash.h"
#include "client.h"
#include "send.h"
#include "packet.h"
#define ZIPSTATS_TIME 60
static void collect_zipstats(void *unused);
static void ssl_read_ctl(rb_fde_t * F, void *data);
static int ssld_count;
static char tmpbuf[READBUF_SIZE];
static char nul = '\0';
#define MAXPASSFD 4
#define READSIZE 1024
typedef struct _ssl_ctl_buf
{
rb_dlink_node node;
char *buf;
size_t buflen;
rb_fde_t *F[MAXPASSFD];
int nfds;
} ssl_ctl_buf_t;
struct _ssl_ctl
{
rb_dlink_node node;
int cli_count;
rb_fde_t *F;
rb_fde_t *P;
pid_t pid;
rb_dlink_list readq;
rb_dlink_list writeq;
uint8_t shutdown;
uint8_t dead;
char version[256];
};
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_cipher_list);
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 rb_dlink_list ssl_daemons;
static inline uint32_t
buf_to_uint32(char *buf)
{
uint32_t x;
memcpy(&x, buf, sizeof(x));
return x;
}
static inline void
uint32_to_buf(char *buf, uint32_t x)
{
memcpy(buf, &x, sizeof(x));
return;
}
static ssl_ctl_t *
allocate_ssl_daemon(rb_fde_t * F, rb_fde_t * P, int pid)
{
ssl_ctl_t *ctl;
if(F == NULL || pid < 0)
return NULL;
ctl = rb_malloc(sizeof(ssl_ctl_t));
ctl->F = F;
ctl->P = P;
ctl->pid = pid;
ssld_count++;
rb_dlinkAdd(ctl, &ctl->node, &ssl_daemons);
return ctl;
}
static void
free_ssl_daemon(ssl_ctl_t * ctl)
{
rb_dlink_node *ptr;
ssl_ctl_buf_t *ctl_buf;
int x;
if(ctl->cli_count)
return;
RB_DLINK_FOREACH(ptr, ctl->readq.head)
{
ctl_buf = ptr->data;
for(x = 0; x < ctl_buf->nfds; x++)
rb_close(ctl_buf->F[x]);
rb_free(ctl_buf->buf);
rb_free(ctl_buf);
}
RB_DLINK_FOREACH(ptr, ctl->writeq.head)
{
ctl_buf = ptr->data;
for(x = 0; x < ctl_buf->nfds; x++)
rb_close(ctl_buf->F[x]);
rb_free(ctl_buf->buf);
rb_free(ctl_buf);
}
rb_close(ctl->F);
rb_close(ctl->P);
rb_dlinkDelete(&ctl->node, &ssl_daemons);
rb_free(ctl);
}
static char *ssld_path;
static int ssld_spin_count = 0;
static time_t last_spin;
static int ssld_wait = 0;
void
restart_ssld(void)
{
rb_dlink_node *ptr, *next;
ssl_ctl_t *ctl;
RB_DLINK_FOREACH_SAFE(ptr, next, ssl_daemons.head)
{
ctl = ptr->data;
if(ctl->dead)
continue;
if(ctl->shutdown)
continue;
ctl->shutdown = 1;
ssld_count--;
if(!ctl->cli_count)
{
rb_kill(ctl->pid, SIGKILL);
free_ssl_daemon(ctl);
}
}
start_ssldaemon(ServerInfo.ssld_count, ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list);
}
static void
ssl_killall(void)
{
rb_dlink_node *ptr, *next;
ssl_ctl_t *ctl;
RB_DLINK_FOREACH_SAFE(ptr, next, ssl_daemons.head)
{
ctl = ptr->data;
if(ctl->dead)
continue;
ctl->dead = 1;
if(!ctl->shutdown)
ssld_count--;
rb_kill(ctl->pid, SIGKILL);
if(!ctl->cli_count)
free_ssl_daemon(ctl);
}
}
static void
ssl_dead(ssl_ctl_t * ctl)
{
if(ctl->dead)
return;
ctl->dead = 1;
rb_kill(ctl->pid, SIGKILL); /* make sure the process is really gone */
if(!ctl->shutdown)
{
ssld_count--;
ilog(L_MAIN, "ssld helper died - attempting to restart");
sendto_realops_snomask(SNO_GENERAL, L_ALL, "ssld helper died - attempting to restart");
start_ssldaemon(1, ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list);
}
}
static void
ssl_do_pipe(rb_fde_t * F, void *data)
{
int retlen;
ssl_ctl_t *ctl = data;
retlen = rb_write(F, "0", 1);
if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
{
ssl_dead(ctl);
return;
}
rb_setselect(F, RB_SELECT_READ, ssl_do_pipe, data);
}
static void
restart_ssld_event(void *unused)
{
ssld_spin_count = 0;
last_spin = 0;
ssld_wait = 0;
if(ServerInfo.ssld_count > get_ssld_count())
{
int start = ServerInfo.ssld_count - get_ssld_count();
ilog(L_MAIN, "Attempting to restart ssld processes");
sendto_realops_snomask(SNO_GENERAL, L_ALL, "Attempt to restart ssld processes");
start_ssldaemon(start, ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list);
}
}
int
start_ssldaemon(int count, const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list)
{
rb_fde_t *F1, *F2;
rb_fde_t *P1, *P2;
#ifdef _WIN32
const char *suffix = ".exe";
#else
const char *suffix = "";
#endif
char fullpath[PATH_MAX + 1];
char fdarg[6];
const char *parv[2];
char buf[128];
char s_pid[10];
pid_t pid;
int started = 0, i;
if(ssld_wait)
return 0;
if(ssld_spin_count > 20 && (rb_current_time() - last_spin < 5))
{
ilog(L_MAIN, "ssld helper is spinning - will attempt to restart in 1 minute");
sendto_realops_snomask(SNO_GENERAL, L_ALL,
"ssld helper is spinning - will attempt to restart in 1 minute");
rb_event_add("restart_ssld_event", restart_ssld_event, NULL, 60);
ssld_wait = 1;
return 0;
}
ssld_spin_count++;
last_spin = rb_current_time();
if(ssld_path == NULL)
{
snprintf(fullpath, sizeof(fullpath), "%s%cssld%s", ircd_paths[IRCD_PATH_LIBEXEC], RB_PATH_SEPARATOR, suffix);
if(access(fullpath, X_OK) == -1)
{
snprintf(fullpath, sizeof(fullpath), "%s%cbin%cssld%s",
ConfigFileEntry.dpath, RB_PATH_SEPARATOR, RB_PATH_SEPARATOR, suffix);
if(access(fullpath, X_OK) == -1)
{
ilog(L_MAIN,
"Unable to execute ssld%s in %s or %s/bin",
suffix, ircd_paths[IRCD_PATH_LIBEXEC], ConfigFileEntry.dpath);
return 0;
}
}
ssld_path = rb_strdup(fullpath);
}
rb_strlcpy(buf, "-ircd ssld daemon", sizeof(buf));
parv[0] = buf;
parv[1] = NULL;
for(i = 0; i < count; i++)
{
ssl_ctl_t *ctl;
if(rb_socketpair(AF_UNIX, SOCK_DGRAM, 0, &F1, &F2, "SSL/TLS handle passing socket") == -1)
{
ilog(L_MAIN, "Unable to create ssld - rb_socketpair failed: %s", strerror(errno));
return started;
}
rb_set_buffers(F1, READBUF_SIZE);
rb_set_buffers(F2, READBUF_SIZE);
snprintf(fdarg, sizeof(fdarg), "%d", rb_get_fd(F2));
rb_setenv("CTL_FD", fdarg, 1);
if(rb_pipe(&P1, &P2, "SSL/TLS pipe") == -1)
{
ilog(L_MAIN, "Unable to create ssld - rb_pipe failed: %s", strerror(errno));
return started;
}
snprintf(fdarg, sizeof(fdarg), "%d", rb_get_fd(P1));
rb_setenv("CTL_PIPE", fdarg, 1);
snprintf(s_pid, sizeof(s_pid), "%d", (int)getpid());
rb_setenv("CTL_PPID", s_pid, 1);
#ifdef _WIN32
SetHandleInformation((HANDLE) rb_get_fd(F2), HANDLE_FLAG_INHERIT, 1);
SetHandleInformation((HANDLE) rb_get_fd(P1), HANDLE_FLAG_INHERIT, 1);
#endif
pid = rb_spawn_process(ssld_path, (const char **) parv);
if(pid == -1)
{
ilog(L_MAIN, "Unable to create ssld: %s\n", strerror(errno));
rb_close(F1);
rb_close(F2);
rb_close(P1);
rb_close(P2);
return started;
}
started++;
rb_close(F2);
rb_close(P1);
ctl = allocate_ssl_daemon(F1, P2, pid);
if(ircd_ssl_ok)
{
send_init_prng(ctl, RB_PRNG_DEFAULT, NULL);
send_certfp_method(ctl, ConfigFileEntry.certfp_method);
if(ssl_cert != NULL && ssl_private_key != NULL)
send_new_ssl_certs_one(ctl, ssl_cert, ssl_private_key,
ssl_dh_params != NULL ? ssl_dh_params : "",
ssl_cipher_list != NULL ? ssl_cipher_list : "");
}
ssl_read_ctl(ctl->F, ctl);
ssl_do_pipe(P2, ctl);
}
return started;
}
static void
ssl_process_zipstats(ssl_ctl_t * ctl, ssl_ctl_buf_t * ctl_buf)
{
struct Client *server;
struct ZipStats *zips;
char *parv[7];
(void) rb_string_to_array(ctl_buf->buf, parv, 6);
server = find_server(NULL, parv[1]);
if(server == NULL || server->localClient == NULL || !IsCapable(server, CAP_ZIP))
return;
if(server->localClient->zipstats == NULL)
server->localClient->zipstats = rb_malloc(sizeof(struct ZipStats));
zips = server->localClient->zipstats;
zips->in += strtoull(parv[2], NULL, 10);
zips->in_wire += strtoull(parv[3], NULL, 10);
zips->out += strtoull(parv[4], NULL, 10);
zips->out_wire += strtoull(parv[5], NULL, 10);
if(zips->in > 0)
zips->in_ratio = ((double) (zips->in - zips->in_wire) / (double) zips->in) * 100.00;
else
zips->in_ratio = 0;
if(zips->out > 0)
zips->out_ratio = ((double) (zips->out - zips->out_wire) / (double) zips->out) * 100.00;
else
zips->out_ratio = 0;
}
static void
ssl_process_dead_fd(ssl_ctl_t * ctl, ssl_ctl_buf_t * ctl_buf)
{
struct Client *client_p;
char reason[256];
uint32_t fd;
if(ctl_buf->buflen < 6)
return; /* bogus message..drop it.. XXX should warn here */
fd = buf_to_uint32(&ctl_buf->buf[1]);
rb_strlcpy(reason, &ctl_buf->buf[5], sizeof(reason));
client_p = find_cli_connid_hash(fd);
if(client_p == NULL)
return;
if(IsAnyServer(client_p) || IsRegistered(client_p))
{
/* read any last moment ERROR, QUIT or the like -- jilles */
if (!strcmp(reason, "Remote host closed the connection"))
read_packet(client_p->localClient->F, client_p);
if (IsAnyDead(client_p))
return;
}
if(IsAnyServer(client_p))
{
sendto_realops_snomask(SNO_GENERAL, is_remote_connect(client_p) && !IsServer(client_p) ? L_NETWIDE : L_ALL, "ssld error for %s: %s", client_p->name, reason);
ilog(L_SERVER, "ssld error for %s: %s", log_client_name(client_p, SHOW_IP), reason);
}
exit_client(client_p, client_p, &me, reason);
}
static void
ssl_process_cipher_string(ssl_ctl_t *ctl, ssl_ctl_buf_t *ctl_buf)
{
struct Client *client_p;
const char *cstring;
uint32_t fd;
if(ctl_buf->buflen < 6)
return; /* bogus message..drop it.. XXX should warn here */
fd = buf_to_uint32(&ctl_buf->buf[1]);
cstring = (const char *)&ctl_buf->buf[5];
if(EmptyString(cstring))
return;
client_p = find_cli_connid_hash(fd);
if(client_p != NULL && client_p->localClient != NULL)
{
rb_free(client_p->localClient->cipher_string);
client_p->localClient->cipher_string = rb_strdup(cstring);
}
}
static void
ssl_process_certfp(ssl_ctl_t * ctl, ssl_ctl_buf_t * ctl_buf)
{
struct Client *client_p;
uint32_t fd;
uint32_t len;
uint8_t *certfp;
char *certfp_string;
if(ctl_buf->buflen > 5 + RB_SSL_CERTFP_LEN)
return; /* bogus message..drop it.. XXX should warn here */
fd = buf_to_uint32(&ctl_buf->buf[1]);
len = buf_to_uint32(&ctl_buf->buf[5]);
certfp = (uint8_t *)&ctl_buf->buf[9];
client_p = find_cli_connid_hash(fd);
if(client_p == NULL)
return;
rb_free(client_p->certfp);
certfp_string = rb_malloc(len * 2 + 1);
for(uint32_t i = 0; i < len; i++)
snprintf(certfp_string + 2 * i, 3, "%02x",
certfp[i]);
client_p->certfp = certfp_string;
}
static void
ssl_process_cmd_recv(ssl_ctl_t * ctl)
{
static const char *cannot_setup_ssl = "ssld cannot setup ssl, check your certificates and private key";
static const char *no_ssl_or_zlib = "ssld has neither SSL/TLS or zlib support killing all sslds";
rb_dlink_node *ptr, *next;
ssl_ctl_buf_t *ctl_buf;
unsigned long len;
if(ctl->dead)
return;
RB_DLINK_FOREACH_SAFE(ptr, next, ctl->readq.head)
{
ctl_buf = ptr->data;
switch (*ctl_buf->buf)
{
case 'N':
ircd_ssl_ok = false; /* ssld says it can't do ssl/tls */
break;
case 'D':
ssl_process_dead_fd(ctl, ctl_buf);
break;
case 'C':
ssl_process_cipher_string(ctl, ctl_buf);
break;
case 'F':
ssl_process_certfp(ctl, ctl_buf);
break;
case 'S':
ssl_process_zipstats(ctl, ctl_buf);
break;
case 'I':
ircd_ssl_ok = false;
ilog(L_MAIN, "%s", cannot_setup_ssl);
sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s", cannot_setup_ssl);
break;
case 'U':
ircd_zlib_ok = 0;
ircd_ssl_ok = false;
ilog(L_MAIN, "%s", no_ssl_or_zlib);
sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s", no_ssl_or_zlib);
ssl_killall();
return;
case 'V':
len = ctl_buf->buflen - 1;
if (len > sizeof(ctl->version) - 1)
len = sizeof(ctl->version) - 1;
strncpy(ctl->version, &ctl_buf->buf[1], len);
case 'z':
ircd_zlib_ok = 0;
break;
default:
ilog(L_MAIN, "Received invalid command from ssld: %s", ctl_buf->buf);
sendto_realops_snomask(SNO_GENERAL, L_ALL, "Received invalid command from ssld");
break;
}
rb_dlinkDelete(ptr, &ctl->readq);
rb_free(ctl_buf->buf);
rb_free(ctl_buf);
}
}
static void
ssl_read_ctl(rb_fde_t * F, void *data)
{
ssl_ctl_buf_t *ctl_buf;
ssl_ctl_t *ctl = data;
int retlen;
if(ctl->dead)
return;
do
{
ctl_buf = rb_malloc(sizeof(ssl_ctl_buf_t));
ctl_buf->buf = rb_malloc(READSIZE);
retlen = rb_recv_fd_buf(ctl->F, ctl_buf->buf, READSIZE, ctl_buf->F, 4);
ctl_buf->buflen = retlen;
if(retlen <= 0)
{
rb_free(ctl_buf->buf);
rb_free(ctl_buf);
}
else
rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->readq);
}
while(retlen > 0);
if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
{
ssl_dead(ctl);
return;
}
ssl_process_cmd_recv(ctl);
rb_setselect(ctl->F, RB_SELECT_READ, ssl_read_ctl, ctl);
}
static ssl_ctl_t *
which_ssld(void)
{
ssl_ctl_t *ctl, *lowest = NULL;
rb_dlink_node *ptr;
RB_DLINK_FOREACH(ptr, ssl_daemons.head)
{
ctl = ptr->data;
if(ctl->dead)
continue;
if(ctl->shutdown)
continue;
if(lowest == NULL)
{
lowest = ctl;
continue;
}
if(ctl->cli_count < lowest->cli_count)
lowest = ctl;
}
return (lowest);
}
static void
ssl_write_ctl(rb_fde_t * F, void *data)
{
ssl_ctl_t *ctl = data;
ssl_ctl_buf_t *ctl_buf;
rb_dlink_node *ptr, *next;
int retlen, x;
if(ctl->dead)
return;
RB_DLINK_FOREACH_SAFE(ptr, next, ctl->writeq.head)
{
ctl_buf = ptr->data;
/* in theory unix sock_dgram shouldn't ever short write this.. */
retlen = rb_send_fd_buf(ctl->F, ctl_buf->F, ctl_buf->nfds, ctl_buf->buf, ctl_buf->buflen, ctl->pid);
if(retlen > 0)
{
rb_dlinkDelete(ptr, &ctl->writeq);
for(x = 0; x < ctl_buf->nfds; x++)
rb_close(ctl_buf->F[x]);
rb_free(ctl_buf->buf);
rb_free(ctl_buf);
}
if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
{
ssl_dead(ctl);
return;
}
else
{
rb_setselect(ctl->F, RB_SELECT_WRITE, ssl_write_ctl, ctl);
}
}
}
static void
ssl_cmd_write_queue(ssl_ctl_t * ctl, rb_fde_t ** F, int count, const void *buf, size_t buflen)
{
ssl_ctl_buf_t *ctl_buf;
int x;
/* don't bother */
if(ctl->dead)
return;
ctl_buf = rb_malloc(sizeof(ssl_ctl_buf_t));
ctl_buf->buf = rb_malloc(buflen);
memcpy(ctl_buf->buf, buf, buflen);
ctl_buf->buflen = buflen;
for(x = 0; x < count && x < MAXPASSFD; x++)
{
ctl_buf->F[x] = F[x];
}
ctl_buf->nfds = count;
rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->writeq);
ssl_write_ctl(ctl->F, ctl);
}
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_cipher_list)
{
size_t len;
len = strlen(ssl_cert) + strlen(ssl_private_key) + strlen(ssl_dh_params) + 5;
if(len > sizeof(tmpbuf))
{
sendto_realops_snomask(SNO_GENERAL, L_ALL,
"Parameters for send_new_ssl_certs_one too long (%zu > %zu) to pass to ssld, not sending...",
len, sizeof(tmpbuf));
ilog(L_MAIN,
"Parameters for send_new_ssl_certs_one too long (%zu > %zu) to pass to ssld, not sending...",
len, sizeof(tmpbuf));
return;
}
len = snprintf(tmpbuf, sizeof(tmpbuf), "K%c%s%c%s%c%s%c%s%c", nul, ssl_cert, nul,
ssl_private_key, nul, ssl_dh_params, nul,
ssl_cipher_list != NULL ? ssl_cipher_list : "", nul);
ssl_cmd_write_queue(ctl, NULL, 0, tmpbuf, len);
}
static void
send_init_prng(ssl_ctl_t * ctl, prng_seed_t seedtype, const char *path)
{
size_t len;
const char *s;
uint8_t seed = (uint8_t) seedtype;
if(path == NULL)
s = "";
else
s = path;
len = strlen(s) + 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 = snprintf(tmpbuf, sizeof(tmpbuf), "I%c%s%c", seed, s, nul);
ssl_cmd_write_queue(ctl, NULL, 0, tmpbuf, len);
}
static void
send_certfp_method(ssl_ctl_t *ctl, int method)
{
char buf[5];
buf[0] = 'F';
uint32_to_buf(&buf[1], method);
ssl_cmd_write_queue(ctl, NULL, 0, buf, sizeof(buf));
}
void
send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list)
{
rb_dlink_node *ptr;
if(ssl_cert == NULL || ssl_private_key == NULL || ssl_dh_params == NULL)
{
ircd_ssl_ok = false;
return;
}
RB_DLINK_FOREACH(ptr, ssl_daemons.head)
{
ssl_ctl_t *ctl = ptr->data;
send_new_ssl_certs_one(ctl, ssl_cert, ssl_private_key, ssl_dh_params, ssl_cipher_list);
}
}
ssl_ctl_t *
start_ssld_accept(rb_fde_t * sslF, rb_fde_t * plainF, uint32_t id)
{
rb_fde_t *F[2];
ssl_ctl_t *ctl;
char buf[5];
F[0] = sslF;
F[1] = plainF;
buf[0] = 'A';
uint32_to_buf(&buf[1], id);
ctl = which_ssld();
if(!ctl)
return NULL;
ctl->cli_count++;
ssl_cmd_write_queue(ctl, F, 2, buf, sizeof(buf));
return ctl;
}
ssl_ctl_t *
start_ssld_connect(rb_fde_t * sslF, rb_fde_t * plainF, uint32_t id)
{
rb_fde_t *F[2];
ssl_ctl_t *ctl;
char buf[5];
F[0] = sslF;
F[1] = plainF;
buf[0] = 'C';
uint32_to_buf(&buf[1], id);
ctl = which_ssld();
if(!ctl)
return NULL;
ctl->cli_count++;
ssl_cmd_write_queue(ctl, F, 2, buf, sizeof(buf));
return ctl;
}
void
ssld_decrement_clicount(ssl_ctl_t * ctl)
{
if(ctl == NULL)
return;
ctl->cli_count--;
if(ctl->shutdown && !ctl->cli_count)
{
ctl->dead = 1;
rb_kill(ctl->pid, SIGKILL);
}
if(ctl->dead && !ctl->cli_count)
{
free_ssl_daemon(ctl);
}
}
/*
* what we end up sending to the ssld process for ziplinks is the following
* Z[ourfd][level][RECVQ]
* Z = ziplinks command = buf[0]
* ourfd = Our end of the socketpair = buf[1..4]
* level = zip level buf[5]
* recvqlen = our recvq len = buf[6-7]
* recvq = any data we read prior to starting ziplinks
*/
void
start_zlib_session(void *data)
{
struct Client *server = (struct Client *) data;
uint16_t recvqlen;
uint8_t level;
void *xbuf;
rb_fde_t *F[2];
rb_fde_t *xF1, *xF2;
char *buf;
char buf2[9];
void *recvq_start;
size_t hdr = (sizeof(uint8_t) * 2) + sizeof(uint32_t);
size_t len;
int cpylen, left;
server->localClient->event = NULL;
recvqlen = rb_linebuf_len(&server->localClient->buf_recvq);
len = recvqlen + hdr;
if(len > READBUF_SIZE)
{
sendto_realops_snomask(SNO_GENERAL, L_ALL,
"ssld - attempted to pass message of %zd len, max len %d, giving up",
len, READBUF_SIZE);
ilog(L_MAIN, "ssld - attempted to pass message of %zd len, max len %d, giving up", len, READBUF_SIZE);
exit_client(server, server, server, "ssld readbuf exceeded");
return;
}
buf = rb_malloc(len);
level = ConfigFileEntry.compression_level;
uint32_to_buf(&buf[1], rb_get_fd(server->localClient->F));
buf[5] = (char) level;
recvq_start = &buf[6];
server->localClient->zipstats = rb_malloc(sizeof(struct ZipStats));
xbuf = recvq_start;
left = recvqlen;
do
{
cpylen = rb_linebuf_get(&server->localClient->buf_recvq, xbuf, left, LINEBUF_PARTIAL, LINEBUF_RAW);
left -= cpylen;
xbuf = (void *) (((uintptr_t) xbuf) + cpylen);
}
while(cpylen > 0);
/* Pass the socket to ssld. */
*buf = 'Z';
if(rb_socketpair(AF_UNIX, SOCK_STREAM, 0, &xF1, &xF2, "Initial zlib socketpairs") == -1)
{
sendto_realops_snomask(SNO_GENERAL, L_ALL, "Error creating zlib socketpair - %s", strerror(errno));
ilog(L_MAIN, "Error creating zlib socketpairs - %s", strerror(errno));
exit_client(server, server, server, "Error creating zlib socketpair");
rb_free(buf);
return;
}
F[0] = server->localClient->F;
F[1] = xF1;
server->localClient->F = xF2;
/* need to redo as what we did before isn't valid now */
uint32_to_buf(&buf[1], connid_get(server));
server->localClient->z_ctl = which_ssld();
if(!server->localClient->z_ctl)
{
exit_client(server, server, server, "Error finding available ssld");
rb_free(buf);
return;
}
server->localClient->z_ctl->cli_count++;
ssl_cmd_write_queue(server->localClient->z_ctl, F, 2, buf, len);
rb_free(buf);
}
static void
collect_zipstats(void *unused)
{
rb_dlink_node *ptr;
struct Client *target_p;
char buf[sizeof(uint8_t) + sizeof(uint32_t) + HOSTLEN];
void *odata;
size_t len;
uint32_t id;
buf[0] = 'S';
odata = buf + sizeof(uint8_t) + sizeof(uint32_t);
RB_DLINK_FOREACH(ptr, serv_list.head)
{
target_p = ptr->data;
if(IsCapable(target_p, CAP_ZIP))
{
len = sizeof(uint8_t) + sizeof(uint32_t);
id = rb_get_fd(target_p->localClient->F);
uint32_to_buf(&buf[1], id);
rb_strlcpy(odata, target_p->name, (sizeof(buf) - len));
len += strlen(odata) + 1; /* Get the \0 as well */
ssl_cmd_write_queue(target_p->localClient->z_ctl, NULL, 0, buf, len);
}
}
}
static void
cleanup_dead_ssl(void *unused)
{
rb_dlink_node *ptr, *next;
ssl_ctl_t *ctl;
RB_DLINK_FOREACH_SAFE(ptr, next, ssl_daemons.head)
{
ctl = ptr->data;
if(ctl->dead && !ctl->cli_count)
{
free_ssl_daemon(ctl);
}
}
}
int
get_ssld_count(void)
{
return ssld_count;
}
void
ssld_foreach_info(void (*func)(void *data, pid_t pid, int cli_count, enum ssld_status status, const char *version), void *data)
{
rb_dlink_node *ptr, *next;
ssl_ctl_t *ctl;
RB_DLINK_FOREACH_SAFE(ptr, next, ssl_daemons.head)
{
ctl = ptr->data;
func(data, ctl->pid, ctl->cli_count,
ctl->dead ? SSLD_DEAD :
(ctl->shutdown ? SSLD_SHUTDOWN : SSLD_ACTIVE),
ctl->version);
}
}
void
init_ssld(void)
{
rb_event_addish("collect_zipstats", collect_zipstats, NULL, ZIPSTATS_TIME);
rb_event_addish("cleanup_dead_ssld", cleanup_dead_ssl, NULL, 60);
}