de7cf7e009
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.
1299 lines
28 KiB
C
1299 lines
28 KiB
C
/*
|
|
* ssld.c: The ircd-ratbox ssl/zlib helper daemon thingy
|
|
* 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 "stdinc.h"
|
|
|
|
#ifdef HAVE_LIBZ
|
|
#include <zlib.h>
|
|
#endif
|
|
|
|
#define MAXPASSFD 4
|
|
#ifndef READBUF_SIZE
|
|
#define READBUF_SIZE 16384
|
|
#endif
|
|
|
|
static void setup_signals(void);
|
|
static pid_t ppid;
|
|
|
|
static inline uint32_t
|
|
buf_to_uint32(uint8_t *buf)
|
|
{
|
|
uint32_t x;
|
|
memcpy(&x, buf, sizeof(x));
|
|
return x;
|
|
}
|
|
|
|
static inline void
|
|
uint32_to_buf(uint8_t *buf, uint32_t x)
|
|
{
|
|
memcpy(buf, &x, sizeof(x));
|
|
return;
|
|
}
|
|
|
|
typedef struct _mod_ctl_buf
|
|
{
|
|
rb_dlink_node node;
|
|
uint8_t *buf;
|
|
size_t buflen;
|
|
rb_fde_t *F[MAXPASSFD];
|
|
int nfds;
|
|
} mod_ctl_buf_t;
|
|
|
|
typedef struct _mod_ctl
|
|
{
|
|
rb_dlink_node node;
|
|
int cli_count;
|
|
rb_fde_t *F;
|
|
rb_fde_t *F_pipe;
|
|
rb_dlink_list readq;
|
|
rb_dlink_list writeq;
|
|
} mod_ctl_t;
|
|
|
|
static mod_ctl_t *mod_ctl;
|
|
|
|
|
|
#ifdef HAVE_LIBZ
|
|
typedef struct _zlib_stream
|
|
{
|
|
z_stream instream;
|
|
z_stream outstream;
|
|
} zlib_stream_t;
|
|
#endif
|
|
|
|
typedef struct _conn
|
|
{
|
|
rb_dlink_node node;
|
|
mod_ctl_t *ctl;
|
|
rawbuf_head_t *modbuf_out;
|
|
rawbuf_head_t *plainbuf_out;
|
|
|
|
uint32_t id;
|
|
|
|
rb_fde_t *mod_fd;
|
|
rb_fde_t *plain_fd;
|
|
uint64_t mod_out;
|
|
uint64_t mod_in;
|
|
uint64_t plain_in;
|
|
uint64_t plain_out;
|
|
uint8_t flags;
|
|
void *stream;
|
|
} conn_t;
|
|
|
|
#define FLAG_SSL 0x01
|
|
#define FLAG_ZIP 0x02
|
|
#define FLAG_CORK 0x04
|
|
#define FLAG_DEAD 0x08
|
|
#define FLAG_SSL_W_WANTS_R 0x10 /* output needs to wait until input possible */
|
|
#define FLAG_SSL_R_WANTS_W 0x20 /* input needs to wait until output possible */
|
|
#define FLAG_ZIPSSL 0x40
|
|
|
|
#define IsSSL(x) ((x)->flags & FLAG_SSL)
|
|
#define IsZip(x) ((x)->flags & FLAG_ZIP)
|
|
#define IsCork(x) ((x)->flags & FLAG_CORK)
|
|
#define IsDead(x) ((x)->flags & FLAG_DEAD)
|
|
#define IsSSLWWantsR(x) ((x)->flags & FLAG_SSL_W_WANTS_R)
|
|
#define IsSSLRWantsW(x) ((x)->flags & FLAG_SSL_R_WANTS_W)
|
|
#define IsZipSSL(x) ((x)->flags & FLAG_ZIPSSL)
|
|
|
|
#define SetSSL(x) ((x)->flags |= FLAG_SSL)
|
|
#define SetZip(x) ((x)->flags |= FLAG_ZIP)
|
|
#define SetCork(x) ((x)->flags |= FLAG_CORK)
|
|
#define SetDead(x) ((x)->flags |= FLAG_DEAD)
|
|
#define SetSSLWWantsR(x) ((x)->flags |= FLAG_SSL_W_WANTS_R)
|
|
#define SetSSLRWantsW(x) ((x)->flags |= FLAG_SSL_R_WANTS_W)
|
|
#define SetZipSSL(x) ((x)->flags |= FLAG_ZIPSSL)
|
|
|
|
#define ClearSSL(x) ((x)->flags &= ~FLAG_SSL)
|
|
#define ClearZip(x) ((x)->flags &= ~FLAG_ZIP)
|
|
#define ClearCork(x) ((x)->flags &= ~FLAG_CORK)
|
|
#define ClearDead(x) ((x)->flags &= ~FLAG_DEAD)
|
|
#define ClearSSLWWantsR(x) ((x)->flags &= ~FLAG_SSL_W_WANTS_R)
|
|
#define ClearSSLRWantsW(x) ((x)->flags &= ~FLAG_SSL_R_WANTS_W)
|
|
#define ClearZipSSL(x) ((x)->flags &= ~FLAG_ZIPSSL)
|
|
|
|
#define NO_WAIT 0x0
|
|
#define WAIT_PLAIN 0x1
|
|
|
|
#define HASH_WALK_SAFE(i, max, ptr, next, table) for(i = 0; i < max; i++) { RB_DLINK_FOREACH_SAFE(ptr, next, table[i].head)
|
|
#define HASH_WALK_END }
|
|
#define CONN_HASH_SIZE 2000
|
|
#define connid_hash(x) (&connid_hash_table[(x % CONN_HASH_SIZE)])
|
|
|
|
|
|
|
|
static rb_dlink_list connid_hash_table[CONN_HASH_SIZE];
|
|
static rb_dlink_list dead_list;
|
|
|
|
static void conn_mod_read_cb(rb_fde_t *fd, void *data);
|
|
static void conn_mod_write_sendq(rb_fde_t *, void *data);
|
|
static void conn_plain_write_sendq(rb_fde_t *, void *data);
|
|
static void mod_write_ctl(rb_fde_t *, void *data);
|
|
static void conn_plain_read_cb(rb_fde_t *fd, void *data);
|
|
static void conn_plain_read_shutdown_cb(rb_fde_t *fd, void *data);
|
|
static void mod_cmd_write_queue(mod_ctl_t * ctl, const void *data, size_t len);
|
|
static const char *remote_closed = "Remote host closed the connection";
|
|
static bool ssld_ssl_ok;
|
|
static int certfp_method = RB_SSL_CERTFP_METH_SHA1;
|
|
#ifdef HAVE_LIBZ
|
|
static bool zlib_ok = true;
|
|
#else
|
|
static bool zlib_ok = false;
|
|
#endif
|
|
|
|
|
|
#ifdef HAVE_LIBZ
|
|
static void *
|
|
ssld_alloc(void *unused, size_t count, size_t size)
|
|
{
|
|
return rb_malloc(count * size);
|
|
}
|
|
|
|
static void
|
|
ssld_free(void *unused, void *ptr)
|
|
{
|
|
rb_free(ptr);
|
|
}
|
|
#endif
|
|
|
|
static conn_t *
|
|
conn_find_by_id(uint32_t id)
|
|
{
|
|
rb_dlink_node *ptr;
|
|
conn_t *conn;
|
|
|
|
RB_DLINK_FOREACH(ptr, (connid_hash(id))->head)
|
|
{
|
|
conn = ptr->data;
|
|
if(conn->id == id && !IsDead(conn))
|
|
return conn;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
conn_add_id_hash(conn_t * conn, uint32_t id)
|
|
{
|
|
conn->id = id;
|
|
rb_dlinkAdd(conn, &conn->node, connid_hash(id));
|
|
}
|
|
|
|
static void
|
|
free_conn(conn_t * conn)
|
|
{
|
|
rb_free_rawbuffer(conn->modbuf_out);
|
|
rb_free_rawbuffer(conn->plainbuf_out);
|
|
#ifdef HAVE_LIBZ
|
|
if(IsZip(conn))
|
|
{
|
|
zlib_stream_t *stream = conn->stream;
|
|
inflateEnd(&stream->instream);
|
|
deflateEnd(&stream->outstream);
|
|
rb_free(stream);
|
|
}
|
|
#endif
|
|
rb_free(conn);
|
|
}
|
|
|
|
static void
|
|
clean_dead_conns(void *unused)
|
|
{
|
|
conn_t *conn;
|
|
rb_dlink_node *ptr, *next;
|
|
RB_DLINK_FOREACH_SAFE(ptr, next, dead_list.head)
|
|
{
|
|
conn = ptr->data;
|
|
free_conn(conn);
|
|
}
|
|
dead_list.tail = dead_list.head = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
close_conn(conn_t * conn, int wait_plain, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char reason[128]; /* must always be under 250 bytes */
|
|
uint8_t buf[256];
|
|
int len;
|
|
if(IsDead(conn))
|
|
return;
|
|
|
|
rb_rawbuf_flush(conn->modbuf_out, conn->mod_fd);
|
|
rb_rawbuf_flush(conn->plainbuf_out, conn->plain_fd);
|
|
rb_close(conn->mod_fd);
|
|
SetDead(conn);
|
|
|
|
if(!IsZipSSL(conn))
|
|
rb_dlinkDelete(&conn->node, connid_hash(conn->id));
|
|
|
|
if(!wait_plain || fmt == NULL)
|
|
{
|
|
rb_close(conn->plain_fd);
|
|
rb_dlinkAdd(conn, &conn->node, &dead_list);
|
|
return;
|
|
}
|
|
rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_shutdown_cb, conn);
|
|
rb_setselect(conn->plain_fd, RB_SELECT_WRITE, NULL, NULL);
|
|
va_start(ap, fmt);
|
|
vsnprintf(reason, sizeof(reason), fmt, ap);
|
|
va_end(ap);
|
|
|
|
buf[0] = 'D';
|
|
uint32_to_buf(&buf[1], conn->id);
|
|
rb_strlcpy((char *) &buf[5], reason, sizeof(buf) - 5);
|
|
len = (strlen(reason) + 1) + 5;
|
|
mod_cmd_write_queue(conn->ctl, buf, len);
|
|
}
|
|
|
|
static conn_t *
|
|
make_conn(mod_ctl_t * ctl, rb_fde_t *mod_fd, rb_fde_t *plain_fd)
|
|
{
|
|
conn_t *conn = rb_malloc(sizeof(conn_t));
|
|
conn->ctl = ctl;
|
|
conn->modbuf_out = rb_new_rawbuffer();
|
|
conn->plainbuf_out = rb_new_rawbuffer();
|
|
conn->mod_fd = mod_fd;
|
|
conn->plain_fd = plain_fd;
|
|
conn->id = -1;
|
|
conn->stream = NULL;
|
|
rb_set_nb(mod_fd);
|
|
rb_set_nb(plain_fd);
|
|
return conn;
|
|
}
|
|
|
|
static void
|
|
check_handshake_flood(void *unused)
|
|
{
|
|
conn_t *conn;
|
|
rb_dlink_node *ptr, *next;
|
|
unsigned int count;
|
|
int i;
|
|
HASH_WALK_SAFE(i, CONN_HASH_SIZE, ptr, next, connid_hash_table)
|
|
{
|
|
conn = ptr->data;
|
|
if(!IsSSL(conn))
|
|
continue;
|
|
|
|
count = rb_ssl_handshake_count(conn->mod_fd);
|
|
/* nothing needs to do this more than twice in ten seconds i don't think */
|
|
if(count > 2)
|
|
close_conn(conn, WAIT_PLAIN, "Handshake flooding");
|
|
else
|
|
rb_ssl_clear_handshake_count(conn->mod_fd);
|
|
}
|
|
HASH_WALK_END}
|
|
|
|
static void
|
|
conn_mod_write_sendq(rb_fde_t *fd, void *data)
|
|
{
|
|
conn_t *conn = data;
|
|
const char *err;
|
|
int retlen;
|
|
if(IsDead(conn))
|
|
return;
|
|
|
|
if(IsSSLWWantsR(conn))
|
|
{
|
|
ClearSSLWWantsR(conn);
|
|
conn_mod_read_cb(conn->mod_fd, conn);
|
|
if(IsDead(conn))
|
|
return;
|
|
}
|
|
|
|
while((retlen = rb_rawbuf_flush(conn->modbuf_out, fd)) > 0)
|
|
conn->mod_out += retlen;
|
|
|
|
if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
|
|
{
|
|
if(retlen == 0)
|
|
close_conn(conn, WAIT_PLAIN, "%s", remote_closed);
|
|
if(IsSSL(conn) && retlen == RB_RW_SSL_ERROR)
|
|
err = rb_get_ssl_strerror(conn->mod_fd);
|
|
else
|
|
err = strerror(errno);
|
|
close_conn(conn, WAIT_PLAIN, "Write error: %s", err);
|
|
return;
|
|
}
|
|
if(rb_rawbuf_length(conn->modbuf_out) > 0)
|
|
{
|
|
if(retlen != RB_RW_SSL_NEED_READ)
|
|
rb_setselect(conn->mod_fd, RB_SELECT_WRITE, conn_mod_write_sendq, conn);
|
|
else
|
|
{
|
|
rb_setselect(conn->mod_fd, RB_SELECT_READ, conn_mod_write_sendq, conn);
|
|
rb_setselect(conn->mod_fd, RB_SELECT_WRITE, NULL, NULL);
|
|
SetSSLWWantsR(conn);
|
|
}
|
|
}
|
|
else
|
|
rb_setselect(conn->mod_fd, RB_SELECT_WRITE, NULL, NULL);
|
|
|
|
if(IsCork(conn) && rb_rawbuf_length(conn->modbuf_out) == 0)
|
|
{
|
|
ClearCork(conn);
|
|
conn_plain_read_cb(conn->plain_fd, conn);
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
conn_mod_write(conn_t * conn, void *data, size_t len)
|
|
{
|
|
if(IsDead(conn)) /* no point in queueing to a dead man */
|
|
return;
|
|
rb_rawbuf_append(conn->modbuf_out, data, len);
|
|
}
|
|
|
|
static void
|
|
conn_plain_write(conn_t * conn, void *data, size_t len)
|
|
{
|
|
if(IsDead(conn)) /* again no point in queueing to dead men */
|
|
return;
|
|
rb_rawbuf_append(conn->plainbuf_out, data, len);
|
|
}
|
|
|
|
static void
|
|
mod_cmd_write_queue(mod_ctl_t * ctl, const void *data, size_t len)
|
|
{
|
|
mod_ctl_buf_t *ctl_buf;
|
|
ctl_buf = rb_malloc(sizeof(mod_ctl_buf_t));
|
|
ctl_buf->buf = rb_malloc(len);
|
|
ctl_buf->buflen = len;
|
|
memcpy(ctl_buf->buf, data, len);
|
|
ctl_buf->nfds = 0;
|
|
rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->writeq);
|
|
mod_write_ctl(ctl->F, ctl);
|
|
}
|
|
|
|
#ifdef HAVE_LIBZ
|
|
static void
|
|
common_zlib_deflate(conn_t * conn, void *buf, size_t len)
|
|
{
|
|
char outbuf[READBUF_SIZE];
|
|
int ret, have;
|
|
z_stream *outstream = &((zlib_stream_t *) conn->stream)->outstream;
|
|
outstream->next_in = buf;
|
|
outstream->avail_in = len;
|
|
outstream->next_out = (Bytef *) outbuf;
|
|
outstream->avail_out = sizeof(outbuf);
|
|
|
|
ret = deflate(outstream, Z_SYNC_FLUSH);
|
|
if(ret != Z_OK)
|
|
{
|
|
/* deflate error */
|
|
close_conn(conn, WAIT_PLAIN, "Deflate failed: %s", zError(ret));
|
|
return;
|
|
}
|
|
if(outstream->avail_out == 0)
|
|
{
|
|
/* avail_out empty */
|
|
close_conn(conn, WAIT_PLAIN, "error compressing data, avail_out == 0");
|
|
return;
|
|
}
|
|
if(outstream->avail_in != 0)
|
|
{
|
|
/* avail_in isn't empty... */
|
|
close_conn(conn, WAIT_PLAIN, "error compressing data, avail_in != 0");
|
|
return;
|
|
}
|
|
have = sizeof(outbuf) - outstream->avail_out;
|
|
conn_mod_write(conn, outbuf, have);
|
|
}
|
|
|
|
static void
|
|
common_zlib_inflate(conn_t * conn, void *buf, size_t len)
|
|
{
|
|
char outbuf[READBUF_SIZE];
|
|
int ret, have = 0;
|
|
((zlib_stream_t *) conn->stream)->instream.next_in = buf;
|
|
((zlib_stream_t *) conn->stream)->instream.avail_in = len;
|
|
((zlib_stream_t *) conn->stream)->instream.next_out = (Bytef *) outbuf;
|
|
((zlib_stream_t *) conn->stream)->instream.avail_out = sizeof(outbuf);
|
|
|
|
while(((zlib_stream_t *) conn->stream)->instream.avail_in)
|
|
{
|
|
ret = inflate(&((zlib_stream_t *) conn->stream)->instream, Z_NO_FLUSH);
|
|
if(ret != Z_OK)
|
|
{
|
|
if(!strncmp("ERROR ", buf, 6))
|
|
{
|
|
close_conn(conn, WAIT_PLAIN, "Received uncompressed ERROR");
|
|
return;
|
|
}
|
|
close_conn(conn, WAIT_PLAIN, "Inflate failed: %s", zError(ret));
|
|
return;
|
|
}
|
|
have = sizeof(outbuf) - ((zlib_stream_t *) conn->stream)->instream.avail_out;
|
|
|
|
if(((zlib_stream_t *) conn->stream)->instream.avail_in)
|
|
{
|
|
conn_plain_write(conn, outbuf, have);
|
|
have = 0;
|
|
((zlib_stream_t *) conn->stream)->instream.next_out = (Bytef *) outbuf;
|
|
((zlib_stream_t *) conn->stream)->instream.avail_out = sizeof(outbuf);
|
|
}
|
|
}
|
|
if(have == 0)
|
|
return;
|
|
|
|
conn_plain_write(conn, outbuf, have);
|
|
}
|
|
#endif
|
|
|
|
static bool
|
|
plain_check_cork(conn_t * conn)
|
|
{
|
|
if(rb_rawbuf_length(conn->modbuf_out) >= 4096)
|
|
{
|
|
/* if we have over 4k pending outbound, don't read until
|
|
* we've cleared the queue */
|
|
SetCork(conn);
|
|
rb_setselect(conn->plain_fd, RB_SELECT_READ, NULL, NULL);
|
|
/* try to write */
|
|
conn_mod_write_sendq(conn->mod_fd, conn);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static void
|
|
conn_plain_read_cb(rb_fde_t *fd, void *data)
|
|
{
|
|
char inbuf[READBUF_SIZE];
|
|
conn_t *conn = data;
|
|
int length = 0;
|
|
if(conn == NULL)
|
|
return;
|
|
|
|
if(IsDead(conn))
|
|
return;
|
|
|
|
if(plain_check_cork(conn))
|
|
return;
|
|
|
|
while(1)
|
|
{
|
|
if(IsDead(conn))
|
|
return;
|
|
|
|
length = rb_read(conn->plain_fd, inbuf, sizeof(inbuf));
|
|
|
|
if(length == 0 || (length < 0 && !rb_ignore_errno(errno)))
|
|
{
|
|
close_conn(conn, NO_WAIT, NULL);
|
|
return;
|
|
}
|
|
|
|
if(length < 0)
|
|
{
|
|
rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_cb, conn);
|
|
conn_mod_write_sendq(conn->mod_fd, conn);
|
|
return;
|
|
}
|
|
conn->plain_in += length;
|
|
|
|
#ifdef HAVE_LIBZ
|
|
if(IsZip(conn))
|
|
common_zlib_deflate(conn, inbuf, length);
|
|
else
|
|
#endif
|
|
conn_mod_write(conn, inbuf, length);
|
|
if(IsDead(conn))
|
|
return;
|
|
if(plain_check_cork(conn))
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
conn_plain_read_shutdown_cb(rb_fde_t *fd, void *data)
|
|
{
|
|
char inbuf[READBUF_SIZE];
|
|
conn_t *conn = data;
|
|
int length = 0;
|
|
|
|
if(conn == NULL)
|
|
return;
|
|
|
|
while(1)
|
|
{
|
|
length = rb_read(conn->plain_fd, inbuf, sizeof(inbuf));
|
|
|
|
if(length == 0 || (length < 0 && !rb_ignore_errno(errno)))
|
|
{
|
|
rb_close(conn->plain_fd);
|
|
rb_dlinkAdd(conn, &conn->node, &dead_list);
|
|
return;
|
|
}
|
|
|
|
if(length < 0)
|
|
{
|
|
rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_shutdown_cb, conn);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
conn_mod_read_cb(rb_fde_t *fd, void *data)
|
|
{
|
|
char inbuf[READBUF_SIZE];
|
|
conn_t *conn = data;
|
|
const char *err = remote_closed;
|
|
int length;
|
|
if(conn == NULL)
|
|
return;
|
|
if(IsDead(conn))
|
|
return;
|
|
|
|
if(IsSSLRWantsW(conn))
|
|
{
|
|
ClearSSLRWantsW(conn);
|
|
conn_mod_write_sendq(conn->mod_fd, conn);
|
|
if(IsDead(conn))
|
|
return;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
if(IsDead(conn))
|
|
return;
|
|
|
|
length = rb_read(conn->mod_fd, inbuf, sizeof(inbuf));
|
|
|
|
if(length == 0 || (length < 0 && !rb_ignore_errno(errno)))
|
|
{
|
|
if(length == 0)
|
|
{
|
|
close_conn(conn, WAIT_PLAIN, "%s", remote_closed);
|
|
return;
|
|
}
|
|
|
|
if(IsSSL(conn) && length == RB_RW_SSL_ERROR)
|
|
err = rb_get_ssl_strerror(conn->mod_fd);
|
|
else
|
|
err = strerror(errno);
|
|
close_conn(conn, WAIT_PLAIN, "Read error: %s", err);
|
|
return;
|
|
}
|
|
if(length < 0)
|
|
{
|
|
if(length != RB_RW_SSL_NEED_WRITE)
|
|
rb_setselect(conn->mod_fd, RB_SELECT_READ, conn_mod_read_cb, conn);
|
|
else
|
|
{
|
|
rb_setselect(conn->mod_fd, RB_SELECT_READ, NULL, NULL);
|
|
rb_setselect(conn->mod_fd, RB_SELECT_WRITE, conn_mod_read_cb, conn);
|
|
SetSSLRWantsW(conn);
|
|
}
|
|
conn_plain_write_sendq(conn->plain_fd, conn);
|
|
return;
|
|
}
|
|
conn->mod_in += length;
|
|
#ifdef HAVE_LIBZ
|
|
if(IsZip(conn))
|
|
common_zlib_inflate(conn, inbuf, length);
|
|
else
|
|
#endif
|
|
conn_plain_write(conn, inbuf, length);
|
|
}
|
|
}
|
|
|
|
static void
|
|
conn_plain_write_sendq(rb_fde_t *fd, void *data)
|
|
{
|
|
conn_t *conn = data;
|
|
int retlen;
|
|
|
|
if(IsDead(conn))
|
|
return;
|
|
|
|
while((retlen = rb_rawbuf_flush(conn->plainbuf_out, fd)) > 0)
|
|
{
|
|
conn->plain_out += retlen;
|
|
}
|
|
if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
|
|
{
|
|
close_conn(data, NO_WAIT, NULL);
|
|
return;
|
|
}
|
|
|
|
|
|
if(rb_rawbuf_length(conn->plainbuf_out) > 0)
|
|
rb_setselect(conn->plain_fd, RB_SELECT_WRITE, conn_plain_write_sendq, conn);
|
|
else
|
|
rb_setselect(conn->plain_fd, RB_SELECT_WRITE, NULL, NULL);
|
|
}
|
|
|
|
static int
|
|
maxconn(void)
|
|
{
|
|
#if defined(RLIMIT_NOFILE) && defined(HAVE_SYS_RESOURCE_H)
|
|
struct rlimit limit;
|
|
|
|
if(!getrlimit(RLIMIT_NOFILE, &limit))
|
|
{
|
|
return limit.rlim_cur;
|
|
}
|
|
#endif /* RLIMIT_FD_MAX */
|
|
return MAXCONNECTIONS;
|
|
}
|
|
|
|
static void
|
|
ssl_send_cipher(conn_t *conn)
|
|
{
|
|
size_t len;
|
|
uint8_t buf[512];
|
|
char cstring[256];
|
|
const char *p;
|
|
if(!IsSSL(conn))
|
|
return;
|
|
|
|
p = rb_ssl_get_cipher(conn->mod_fd);
|
|
|
|
if(p == NULL)
|
|
return;
|
|
|
|
rb_strlcpy(cstring, p, sizeof(cstring));
|
|
|
|
buf[0] = 'C';
|
|
uint32_to_buf(&buf[1], conn->id);
|
|
strcpy((char *) &buf[5], cstring);
|
|
len = (strlen(cstring) + 1) + 5;
|
|
mod_cmd_write_queue(conn->ctl, buf, len);
|
|
}
|
|
|
|
static void
|
|
ssl_send_certfp(conn_t *conn)
|
|
{
|
|
uint8_t buf[9 + RB_SSL_CERTFP_LEN];
|
|
|
|
int len = rb_get_ssl_certfp(conn->mod_fd, &buf[9], certfp_method);
|
|
if (!len)
|
|
return;
|
|
|
|
lrb_assert(len <= RB_SSL_CERTFP_LEN);
|
|
buf[0] = 'F';
|
|
uint32_to_buf(&buf[1], conn->id);
|
|
uint32_to_buf(&buf[5], len);
|
|
mod_cmd_write_queue(conn->ctl, buf, 9 + len);
|
|
}
|
|
|
|
static void
|
|
ssl_process_accept_cb(rb_fde_t *F, int status, struct sockaddr *addr, rb_socklen_t len, void *data)
|
|
{
|
|
conn_t *conn = data;
|
|
|
|
if(status == RB_OK)
|
|
{
|
|
conn_mod_read_cb(conn->mod_fd, conn);
|
|
conn_plain_read_cb(conn->plain_fd, conn);
|
|
ssl_send_cipher(conn);
|
|
ssl_send_certfp(conn);
|
|
return;
|
|
}
|
|
/* ircd doesn't care about the reason for this */
|
|
close_conn(conn, NO_WAIT, 0);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ssl_process_connect_cb(rb_fde_t *F, int status, void *data)
|
|
{
|
|
conn_t *conn = data;
|
|
|
|
if(status == RB_OK)
|
|
{
|
|
conn_mod_read_cb(conn->mod_fd, conn);
|
|
conn_plain_read_cb(conn->plain_fd, conn);
|
|
ssl_send_cipher(conn);
|
|
ssl_send_certfp(conn);
|
|
}
|
|
else if(status == RB_ERR_TIMEOUT)
|
|
close_conn(conn, WAIT_PLAIN, "SSL handshake timed out");
|
|
else if(status == RB_ERROR_SSL)
|
|
close_conn(conn, WAIT_PLAIN, "%s", rb_get_ssl_strerror(conn->mod_fd));
|
|
else
|
|
close_conn(conn, WAIT_PLAIN, "SSL handshake failed");
|
|
}
|
|
|
|
|
|
static void
|
|
cleanup_bad_message(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
|
{
|
|
int i;
|
|
|
|
/* XXX should log this somehow */
|
|
for (i = 0; i < ctlb->nfds; i++)
|
|
rb_close(ctlb->F[i]);
|
|
}
|
|
|
|
static void
|
|
ssl_process_accept(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
|
{
|
|
conn_t *conn;
|
|
uint32_t id;
|
|
|
|
conn = make_conn(ctl, ctlb->F[0], ctlb->F[1]);
|
|
|
|
id = buf_to_uint32(&ctlb->buf[1]);
|
|
conn_add_id_hash(conn, id);
|
|
SetSSL(conn);
|
|
|
|
if(rb_get_type(conn->mod_fd) & RB_FD_UNKNOWN)
|
|
rb_set_type(conn->mod_fd, RB_FD_SOCKET);
|
|
|
|
if(rb_get_type(conn->plain_fd) == RB_FD_UNKNOWN)
|
|
rb_set_type(conn->plain_fd, RB_FD_SOCKET);
|
|
|
|
rb_ssl_start_accepted(ctlb->F[0], ssl_process_accept_cb, conn, 10);
|
|
}
|
|
|
|
static void
|
|
ssl_change_certfp_method(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
|
{
|
|
certfp_method = buf_to_uint32(&ctlb->buf[1]);
|
|
}
|
|
|
|
static void
|
|
ssl_process_connect(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
|
{
|
|
conn_t *conn;
|
|
uint32_t id;
|
|
conn = make_conn(ctl, ctlb->F[0], ctlb->F[1]);
|
|
|
|
id = buf_to_uint32(&ctlb->buf[1]);
|
|
conn_add_id_hash(conn, id);
|
|
SetSSL(conn);
|
|
|
|
if(rb_get_type(conn->mod_fd) == RB_FD_UNKNOWN)
|
|
rb_set_type(conn->mod_fd, RB_FD_SOCKET);
|
|
|
|
if(rb_get_type(conn->plain_fd) == RB_FD_UNKNOWN)
|
|
rb_set_type(conn->plain_fd, RB_FD_SOCKET);
|
|
|
|
|
|
rb_ssl_start_connected(ctlb->F[0], ssl_process_connect_cb, conn, 10);
|
|
}
|
|
|
|
static void
|
|
process_stats(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
|
{
|
|
char outstat[512];
|
|
conn_t *conn;
|
|
uint8_t *odata;
|
|
uint32_t id;
|
|
|
|
id = buf_to_uint32(&ctlb->buf[1]);
|
|
|
|
odata = &ctlb->buf[5];
|
|
conn = conn_find_by_id(id);
|
|
|
|
if(conn == NULL)
|
|
return;
|
|
|
|
snprintf(outstat, sizeof(outstat), "S %s %llu %llu %llu %llu", odata,
|
|
(unsigned long long)conn->plain_out,
|
|
(unsigned long long)conn->mod_in,
|
|
(unsigned long long)conn->plain_in,
|
|
(unsigned long long)conn->mod_out);
|
|
conn->plain_out = 0;
|
|
conn->plain_in = 0;
|
|
conn->mod_in = 0;
|
|
conn->mod_out = 0;
|
|
mod_cmd_write_queue(ctl, outstat, strlen(outstat) + 1); /* +1 is so we send the \0 as well */
|
|
}
|
|
|
|
static void
|
|
change_connid(mod_ctl_t *ctl, mod_ctl_buf_t *ctlb)
|
|
{
|
|
uint32_t id = buf_to_uint32(&ctlb->buf[1]);
|
|
uint32_t newid = buf_to_uint32(&ctlb->buf[5]);
|
|
conn_t *conn = conn_find_by_id(id);
|
|
lrb_assert(conn != NULL);
|
|
if(conn == NULL)
|
|
{
|
|
uint8_t buf[256];
|
|
int len;
|
|
|
|
buf[0] = 'D';
|
|
uint32_to_buf(&buf[1], newid);
|
|
sprintf((char *) &buf[5], "connid %d does not exist", id);
|
|
len = (strlen((char *) &buf[5]) + 1) + 5;
|
|
mod_cmd_write_queue(ctl, buf, len);
|
|
|
|
return;
|
|
}
|
|
rb_dlinkDelete(&conn->node, connid_hash(conn->id));
|
|
SetZipSSL(conn);
|
|
conn->id = newid;
|
|
}
|
|
|
|
#ifdef HAVE_LIBZ
|
|
static void
|
|
zlib_process(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
|
{
|
|
uint8_t level;
|
|
size_t recvqlen;
|
|
size_t hdr = (sizeof(uint8_t) * 2) + sizeof(uint32_t);
|
|
void *recvq_start;
|
|
z_stream *instream, *outstream;
|
|
conn_t *conn;
|
|
uint32_t id;
|
|
|
|
conn = make_conn(ctl, ctlb->F[0], ctlb->F[1]);
|
|
if(rb_get_type(conn->mod_fd) == RB_FD_UNKNOWN)
|
|
rb_set_type(conn->mod_fd, RB_FD_SOCKET);
|
|
|
|
if(rb_get_type(conn->plain_fd) == RB_FD_UNKNOWN)
|
|
rb_set_type(conn->plain_fd, RB_FD_SOCKET);
|
|
|
|
id = buf_to_uint32(&ctlb->buf[1]);
|
|
conn_add_id_hash(conn, id);
|
|
|
|
level = (uint8_t)ctlb->buf[5];
|
|
|
|
recvqlen = ctlb->buflen - hdr;
|
|
recvq_start = &ctlb->buf[6];
|
|
|
|
SetZip(conn);
|
|
conn->stream = rb_malloc(sizeof(zlib_stream_t));
|
|
instream = &((zlib_stream_t *) conn->stream)->instream;
|
|
outstream = &((zlib_stream_t *) conn->stream)->outstream;
|
|
|
|
instream->total_in = 0;
|
|
instream->total_out = 0;
|
|
instream->zalloc = (alloc_func) ssld_alloc;
|
|
instream->zfree = (free_func) ssld_free;
|
|
instream->data_type = Z_ASCII;
|
|
inflateInit(&((zlib_stream_t *) conn->stream)->instream);
|
|
|
|
outstream->total_in = 0;
|
|
outstream->total_out = 0;
|
|
outstream->zalloc = (alloc_func) ssld_alloc;
|
|
outstream->zfree = (free_func) ssld_free;
|
|
outstream->data_type = Z_ASCII;
|
|
|
|
if(level > 9)
|
|
level = (uint8_t) Z_DEFAULT_COMPRESSION;
|
|
|
|
deflateInit(&((zlib_stream_t *) conn->stream)->outstream, level);
|
|
if(recvqlen > 0)
|
|
common_zlib_inflate(conn, recvq_start, recvqlen);
|
|
|
|
conn_mod_read_cb(conn->mod_fd, conn);
|
|
conn_plain_read_cb(conn->plain_fd, conn);
|
|
return;
|
|
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
init_prng(mod_ctl_t * ctl, mod_ctl_buf_t * ctl_buf)
|
|
{
|
|
char *path;
|
|
prng_seed_t seed_type;
|
|
|
|
seed_type = (prng_seed_t) ctl_buf->buf[1];
|
|
path = (char *) &ctl_buf->buf[2];
|
|
rb_init_prng(path, seed_type);
|
|
}
|
|
|
|
|
|
static void
|
|
ssl_new_keys(mod_ctl_t * ctl, mod_ctl_buf_t * ctl_buf)
|
|
{
|
|
char *buf;
|
|
char *cert, *key, *dhparam, *cipher_list;
|
|
|
|
buf = (char *) &ctl_buf->buf[2];
|
|
cert = buf;
|
|
buf += strlen(cert) + 1;
|
|
key = buf;
|
|
buf += strlen(key) + 1;
|
|
dhparam = buf;
|
|
if(strlen(dhparam) == 0)
|
|
dhparam = NULL;
|
|
buf += strlen(dhparam) + 1;
|
|
cipher_list = buf;
|
|
if(strlen(cipher_list) == 0)
|
|
cipher_list = NULL;
|
|
|
|
if(!rb_setup_ssl_server(cert, key, dhparam, cipher_list))
|
|
{
|
|
const char *invalid = "I";
|
|
mod_cmd_write_queue(ctl, invalid, strlen(invalid));
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
send_nossl_support(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
|
{
|
|
static const char *nossl_cmd = "N";
|
|
conn_t *conn;
|
|
uint32_t id;
|
|
|
|
if(ctlb != NULL)
|
|
{
|
|
conn = make_conn(ctl, ctlb->F[0], ctlb->F[1]);
|
|
id = buf_to_uint32(&ctlb->buf[1]);
|
|
conn_add_id_hash(conn, id);
|
|
close_conn(conn, WAIT_PLAIN, "libratbox reports no SSL/TLS support");
|
|
}
|
|
mod_cmd_write_queue(ctl, nossl_cmd, strlen(nossl_cmd));
|
|
}
|
|
|
|
static void
|
|
send_i_am_useless(mod_ctl_t * ctl)
|
|
{
|
|
static const char *useless = "U";
|
|
mod_cmd_write_queue(ctl, useless, strlen(useless));
|
|
}
|
|
|
|
static void
|
|
send_version(mod_ctl_t * ctl)
|
|
{
|
|
char version[256] = { 'V', 0 };
|
|
strncpy(&version[1], rb_lib_version(), sizeof(version) - 2);
|
|
mod_cmd_write_queue(ctl, version, strlen(version));
|
|
}
|
|
|
|
static void
|
|
send_nozlib_support(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
|
|
{
|
|
static const char *nozlib_cmd = "z";
|
|
conn_t *conn;
|
|
uint32_t id;
|
|
if(ctlb != NULL)
|
|
{
|
|
conn = make_conn(ctl, ctlb->F[0], ctlb->F[1]);
|
|
id = buf_to_uint32(&ctlb->buf[1]);
|
|
conn_add_id_hash(conn, id);
|
|
close_conn(conn, WAIT_PLAIN, "libratbox reports no zlib support");
|
|
}
|
|
mod_cmd_write_queue(ctl, nozlib_cmd, strlen(nozlib_cmd));
|
|
}
|
|
|
|
static void
|
|
mod_process_cmd_recv(mod_ctl_t * ctl)
|
|
{
|
|
rb_dlink_node *ptr, *next;
|
|
mod_ctl_buf_t *ctl_buf;
|
|
|
|
RB_DLINK_FOREACH_SAFE(ptr, next, ctl->readq.head)
|
|
{
|
|
ctl_buf = ptr->data;
|
|
|
|
switch (*ctl_buf->buf)
|
|
{
|
|
case 'A':
|
|
{
|
|
if (ctl_buf->nfds != 2 || ctl_buf->buflen != 5)
|
|
{
|
|
cleanup_bad_message(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
|
|
if(!ssld_ssl_ok)
|
|
{
|
|
send_nossl_support(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
ssl_process_accept(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
case 'C':
|
|
{
|
|
if (ctl_buf->nfds != 2 || ctl_buf->buflen != 5)
|
|
{
|
|
cleanup_bad_message(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
|
|
if(!ssld_ssl_ok)
|
|
{
|
|
send_nossl_support(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
ssl_process_connect(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
case 'F':
|
|
{
|
|
if (ctl_buf->nfds != 2 || ctl_buf->buflen != 5)
|
|
{
|
|
cleanup_bad_message(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
ssl_change_certfp_method(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
case 'K':
|
|
{
|
|
if(!ssld_ssl_ok)
|
|
{
|
|
send_nossl_support(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
ssl_new_keys(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
case 'I':
|
|
init_prng(ctl, ctl_buf);
|
|
break;
|
|
case 'S':
|
|
{
|
|
process_stats(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
|
|
#ifdef HAVE_LIBZ
|
|
case 'Z':
|
|
{
|
|
if (ctl_buf->nfds != 2 || ctl_buf->buflen < 6)
|
|
{
|
|
cleanup_bad_message(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
|
|
/* just zlib only */
|
|
zlib_process(ctl, ctl_buf);
|
|
break;
|
|
}
|
|
#else
|
|
|
|
case 'Z':
|
|
send_nozlib_support(ctl, ctl_buf);
|
|
break;
|
|
|
|
#endif
|
|
default:
|
|
break;
|
|
/* Log unknown commands */
|
|
}
|
|
rb_dlinkDelete(ptr, &ctl->readq);
|
|
rb_free(ctl_buf->buf);
|
|
rb_free(ctl_buf);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
mod_read_ctl(rb_fde_t *F, void *data)
|
|
{
|
|
mod_ctl_buf_t *ctl_buf;
|
|
mod_ctl_t *ctl = data;
|
|
int retlen;
|
|
int i;
|
|
|
|
do
|
|
{
|
|
ctl_buf = rb_malloc(sizeof(mod_ctl_buf_t));
|
|
ctl_buf->buf = rb_malloc(READBUF_SIZE);
|
|
ctl_buf->buflen = READBUF_SIZE;
|
|
retlen = rb_recv_fd_buf(ctl->F, ctl_buf->buf, ctl_buf->buflen, ctl_buf->F,
|
|
MAXPASSFD);
|
|
if(retlen <= 0)
|
|
{
|
|
rb_free(ctl_buf->buf);
|
|
rb_free(ctl_buf);
|
|
}
|
|
else
|
|
{
|
|
ctl_buf->buflen = retlen;
|
|
rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->readq);
|
|
for (i = 0; i < MAXPASSFD && ctl_buf->F[i] != NULL; i++)
|
|
;
|
|
ctl_buf->nfds = i;
|
|
}
|
|
}
|
|
while(retlen > 0);
|
|
|
|
if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
|
|
exit(0);
|
|
|
|
mod_process_cmd_recv(ctl);
|
|
rb_setselect(ctl->F, RB_SELECT_READ, mod_read_ctl, ctl);
|
|
}
|
|
|
|
static void
|
|
mod_write_ctl(rb_fde_t *F, void *data)
|
|
{
|
|
mod_ctl_t *ctl = data;
|
|
mod_ctl_buf_t *ctl_buf;
|
|
rb_dlink_node *ptr, *next;
|
|
int retlen, x;
|
|
|
|
RB_DLINK_FOREACH_SAFE(ptr, next, ctl->writeq.head)
|
|
{
|
|
ctl_buf = ptr->data;
|
|
retlen = rb_send_fd_buf(ctl->F, ctl_buf->F, ctl_buf->nfds, ctl_buf->buf,
|
|
ctl_buf->buflen, ppid);
|
|
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)))
|
|
exit(0);
|
|
|
|
}
|
|
if(rb_dlink_list_length(&ctl->writeq) > 0)
|
|
rb_setselect(ctl->F, RB_SELECT_WRITE, mod_write_ctl, ctl);
|
|
}
|
|
|
|
|
|
static void
|
|
read_pipe_ctl(rb_fde_t *F, void *data)
|
|
{
|
|
char inbuf[READBUF_SIZE];
|
|
int retlen;
|
|
while((retlen = rb_read(F, inbuf, sizeof(inbuf))) > 0)
|
|
{
|
|
;; /* we don't do anything with the pipe really, just care if the other process dies.. */
|
|
}
|
|
if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
|
|
exit(0);
|
|
rb_setselect(F, RB_SELECT_READ, read_pipe_ctl, NULL);
|
|
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
const char *s_ctlfd, *s_pipe, *s_pid;
|
|
int ctlfd, pipefd, x, maxfd;
|
|
maxfd = maxconn();
|
|
|
|
s_ctlfd = getenv("CTL_FD");
|
|
s_pipe = getenv("CTL_PIPE");
|
|
s_pid = getenv("CTL_PPID");
|
|
|
|
if(s_ctlfd == NULL || s_pipe == NULL || s_pid == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"This is the charybdis ssld for internal ircd use.\n");
|
|
fprintf(stderr,
|
|
"You aren't supposed to run me directly. Exiting.\n");
|
|
exit(1);
|
|
}
|
|
|
|
ctlfd = atoi(s_ctlfd);
|
|
pipefd = atoi(s_pipe);
|
|
ppid = atoi(s_pid);
|
|
x = 0;
|
|
#ifndef _WIN32
|
|
for(x = 0; x < maxfd; x++)
|
|
{
|
|
if(x != ctlfd && x != pipefd && x > 2)
|
|
close(x);
|
|
}
|
|
x = open("/dev/null", O_RDWR);
|
|
|
|
if(x >= 0)
|
|
{
|
|
if(ctlfd != 0 && pipefd != 0)
|
|
dup2(x, 0);
|
|
if(ctlfd != 1 && pipefd != 1)
|
|
dup2(x, 1);
|
|
if(ctlfd != 2 && pipefd != 2)
|
|
dup2(x, 2);
|
|
if(x > 2)
|
|
close(x);
|
|
}
|
|
#endif
|
|
setup_signals();
|
|
rb_lib_init(NULL, NULL, NULL, 0, maxfd, 1024, 4096);
|
|
rb_init_rawbuffers(1024);
|
|
ssld_ssl_ok = rb_supports_ssl();
|
|
mod_ctl = rb_malloc(sizeof(mod_ctl_t));
|
|
mod_ctl->F = rb_open(ctlfd, RB_FD_SOCKET, "ircd control socket");
|
|
mod_ctl->F_pipe = rb_open(pipefd, RB_FD_PIPE, "ircd pipe");
|
|
rb_set_nb(mod_ctl->F);
|
|
rb_set_nb(mod_ctl->F_pipe);
|
|
rb_event_addish("clean_dead_conns", clean_dead_conns, NULL, 10);
|
|
rb_event_add("check_handshake_flood", check_handshake_flood, NULL, 10);
|
|
read_pipe_ctl(mod_ctl->F_pipe, NULL);
|
|
mod_read_ctl(mod_ctl->F, mod_ctl);
|
|
send_version(mod_ctl);
|
|
if(!zlib_ok && !ssld_ssl_ok)
|
|
{
|
|
/* this is really useless... */
|
|
send_i_am_useless(mod_ctl);
|
|
/* sleep until the ircd kills us */
|
|
rb_sleep(2 << 30, 0);
|
|
exit(1);
|
|
}
|
|
|
|
if(!zlib_ok)
|
|
send_nozlib_support(mod_ctl, NULL);
|
|
if(!ssld_ssl_ok)
|
|
send_nossl_support(mod_ctl, NULL);
|
|
rb_lib_loop(0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifndef _WIN32
|
|
static void
|
|
dummy_handler(int sig)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
setup_signals()
|
|
{
|
|
#ifndef _WIN32
|
|
struct sigaction act;
|
|
|
|
act.sa_flags = 0;
|
|
act.sa_handler = SIG_IGN;
|
|
sigemptyset(&act.sa_mask);
|
|
sigaddset(&act.sa_mask, SIGPIPE);
|
|
sigaddset(&act.sa_mask, SIGALRM);
|
|
#ifdef SIGTRAP
|
|
sigaddset(&act.sa_mask, SIGTRAP);
|
|
#endif
|
|
|
|
#ifdef SIGWINCH
|
|
sigaddset(&act.sa_mask, SIGWINCH);
|
|
sigaction(SIGWINCH, &act, 0);
|
|
#endif
|
|
sigaction(SIGPIPE, &act, 0);
|
|
#ifdef SIGTRAP
|
|
sigaction(SIGTRAP, &act, 0);
|
|
#endif
|
|
|
|
act.sa_handler = dummy_handler;
|
|
sigaction(SIGALRM, &act, 0);
|
|
#endif
|
|
}
|