#include "s2s.h"
#ifdef HAVE_IDN
#include <stringprep.h>
#endif
static sig_atomic_t s2s_shutdown = 0;
sig_atomic_t s2s_lost_router = 0;
static sig_atomic_t s2s_logrotate = 0;
static void _s2s_signal(int signum) {
s2s_shutdown = 1;
s2s_lost_router = 0;
}
static void _s2s_signal_hup(int signum) {
s2s_logrotate = 1;
}
static void _s2s_pidfile(s2s_t s2s) {
char *pidfile;
FILE *f;
pid_t pid;
pidfile = config_get_one(s2s->config, "pidfile", 0);
if(pidfile == NULL)
return;
pid = getpid();
if((f = fopen(pidfile, "w+")) == NULL) {
log_write(s2s->log, LOG_ERR, "couldn't open %s for writing: %s", pidfile, strerror(errno));
return;
}
if(fprintf(f, "%d", pid) < 0) {
log_write(s2s->log, LOG_ERR, "couldn't write to %s: %s", pidfile, strerror(errno));
return;
}
fclose(f);
log_write(s2s->log, LOG_INFO, "process id is %d, written to %s", pid, pidfile);
}
static void _s2s_config_expand(s2s_t s2s) {
char *str, secret[41];
int i, r;
config_elem_t elem;
s2s->id = config_get_one(s2s->config, "id", 0);
if(s2s->id == NULL)
s2s->id = "s2s";
s2s->router_ip = config_get_one(s2s->config, "router.ip", 0);
if(s2s->router_ip == NULL)
s2s->router_ip = "127.0.0.1";
s2s->router_port = j_atoi(config_get_one(s2s->config, "router.port", 0), 5347);
s2s->router_user = config_get_one(s2s->config, "router.user", 0);
if(s2s->router_user == NULL)
s2s->router_user = "jabberd";
s2s->router_pass = config_get_one(s2s->config, "router.pass", 0);
if(s2s->router_pass == NULL)
s2s->router_pass = "secret";
s2s->router_pemfile = config_get_one(s2s->config, "router.pemfile", 0);
s2s->retry_init = j_atoi(config_get_one(s2s->config, "router.retry.init", 0), 3);
s2s->retry_lost = j_atoi(config_get_one(s2s->config, "router.retry.lost", 0), 3);
if((s2s->retry_sleep = j_atoi(config_get_one(s2s->config, "router.retry.sleep", 0), 2)) < 1)
s2s->retry_sleep = 1;
s2s->router_default = config_count(s2s->config, "router.non-default") ? 0 : 1;
s2s->log_type = log_STDOUT;
if(config_get(s2s->config, "log") != NULL) {
if((str = config_get_attr(s2s->config, "log", 0, "type")) != NULL) {
if(strcmp(str, "file") == 0)
s2s->log_type = log_FILE;
else if(strcmp(str, "syslog") == 0)
s2s->log_type = log_SYSLOG;
}
}
if(s2s->log_type == log_SYSLOG) {
s2s->log_facility = config_get_one(s2s->config, "log.facility", 0);
s2s->log_ident = config_get_one(s2s->config, "log.ident", 0);
if(s2s->log_ident == NULL)
s2s->log_ident = "jabberd/s2s";
} else if(s2s->log_type == log_FILE)
s2s->log_ident = config_get_one(s2s->config, "log.file", 0);
s2s->local_ip = config_get_one(s2s->config, "local.ip", 0);
if(s2s->local_ip == NULL)
s2s->local_ip = "0.0.0.0";
s2s->local_port = j_atoi(config_get_one(s2s->config, "local.port", 0), 0);
s2s->local_resolver = config_get_one(s2s->config, "local.resolver", 0);
if(s2s->local_resolver == NULL)
s2s->local_resolver = "resolver";
if(config_get(s2s->config, "local.secret") != NULL)
s2s->local_secret = strdup(config_get_one(s2s->config, "local.secret", 0));
else {
for(i = 0; i < 40; i++) {
r = (int) (36.0 * rand() / RAND_MAX);
secret[i] = (r >= 0 && r <= 9) ? (r + 48) : (r + 87);
}
secret[40] = '\0';
s2s->local_secret = strdup(secret);
}
if(s2s->local_secret == NULL)
s2s->local_secret = "secret";
s2s->local_pemfile = config_get_one(s2s->config, "local.pemfile", 0);
if (s2s->local_pemfile != NULL)
log_debug(ZONE,"loaded local pemfile for peer s2s connections");
s2s->require_tls = j_atoi(config_get_one(s2s->config, "security.require_tls", 0), 0);
s2s->enable_whitelist = j_atoi(config_get_one(s2s->config, "security.enable_whitelist", 0), 0);
if((elem = config_get(s2s->config, "security.whitelist_domain")) != NULL) {
_s2s_populate_whitelist_domains(s2s, elem->values, elem->nvalues);
}
s2s->check_interval = j_atoi(config_get_one(s2s->config, "check.interval", 0), 60);
s2s->check_queue = j_atoi(config_get_one(s2s->config, "check.queue", 0), 60);
s2s->check_keepalive = j_atoi(config_get_one(s2s->config, "check.keepalive", 0), 0);
s2s->check_idle = j_atoi(config_get_one(s2s->config, "check.idle", 0), 86400);
}
static int _s2s_router_connect(s2s_t s2s) {
log_write(s2s->log, LOG_NOTICE, "attempting connection to router at %s, port=%d", s2s->router_ip, s2s->router_port);
s2s->fd = mio_connect(s2s->mio, s2s->router_port, s2s->router_ip, s2s_router_mio_callback, (void *) s2s);
if(s2s->fd < 0) {
if(errno == ECONNREFUSED)
s2s_lost_router = 1;
log_write(s2s->log, LOG_NOTICE, "connection attempt to router failed: %s (%d)", strerror(errno), errno);
return 1;
}
s2s->router = sx_new(s2s->sx_env, s2s->fd, s2s_router_sx_callback, (void *) s2s);
sx_client_init(s2s->router, 0, NULL, NULL, NULL, "1.0");
return 0;
}
int _s2s_check_conn_routes(s2s_t s2s, conn_t conn, const char *direction)
{
char *rkey;
conn_state_t state;
time_t now, dialback_time;
union xhashv xhv;
now = time(NULL);
if(xhash_iter_first(conn->states))
do {
xhv.state_val = &state;
xhash_iter_get(conn->states, (const char **) &rkey, xhv.val);
if (state == conn_INPROGRESS) {
dialback_time = (time_t) xhash_get(conn->states_time, rkey);
if(now > dialback_time + s2s->check_queue) {
log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] dialback for %s route '%s' timed out", conn->fd, conn->ip, conn->port, direction, rkey);
xhash_zap(conn->states, rkey);
xhash_zap(conn->states_time, rkey);
sx_error(conn->s, stream_err_CONNECTION_TIMEOUT, "dialback timed out");
sx_close(conn->s);
return 0;
}
}
} while(xhash_iter_next(conn->states));
return 1;
}
static void _s2s_time_checks(s2s_t s2s) {
conn_t conn;
time_t now;
char *domain, ipport[INET6_ADDRSTRLEN + 17], *key;
jqueue_t q;
dnscache_t dns;
union xhashv xhv;
now = time(NULL);
if(s2s->check_queue > 0) {
if(xhash_iter_first(s2s->outq))
do {
xhv.jq_val = &q;
xhash_iter_get(s2s->outq, (const char **) &domain, xhv.val);
log_debug(ZONE, "running time checks for %s", domain);
dns = xhash_get(s2s->dnscache, domain);
if(dns == NULL)
continue;
if(dns->pending) {
log_debug(ZONE, "dns lookup pending for %s", domain);
if(now > dns->init_time + s2s->check_queue) {
log_write(s2s->log, LOG_NOTICE, "dns lookup for %s timed out", domain);
out_bounce_queue(s2s, domain, stanza_err_REMOTE_SERVER_NOT_FOUND);
xhash_zap(s2s->dnscache, dns->name);
free(dns);
}
continue;
}
snprintf(ipport, INET6_ADDRSTRLEN + 16, "%s/%d", dns->ip, dns->port);
conn = xhash_get(s2s->out, ipport);
if(conn == NULL) {
if(jqueue_size(q) > 0) {
log_debug(ZONE, "no pending connection for %s, bouncing %i packets in queue", domain, jqueue_size(q));
out_bounce_queue(s2s, domain, stanza_err_REMOTE_SERVER_TIMEOUT);
}
continue;
}
if(!conn->online && now > conn->init_time + s2s->check_queue) {
log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] connection to %s timed out", conn->fd, conn->ip, conn->port, domain);
out_bounce_queue(s2s, domain, stanza_err_REMOTE_SERVER_TIMEOUT);
sx_close(conn->s);
}
} while(xhash_iter_next(s2s->outq));
}
if(s2s->check_queue > 0) {
if(xhash_iter_first(s2s->out))
do {
xhv.conn_val = &conn;
xhash_iter_get(s2s->out, (const char **) &key, xhv.val);
log_debug(ZONE, "checking dialback state for outgoing conn %s", key);
if (_s2s_check_conn_routes(s2s, conn, "outgoing")) {
log_debug(ZONE, "checking pending verify requests for outgoing conn %s", key);
if (conn->verify > 0 && now > conn->last_verify + s2s->check_queue) {
log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] dialback verify request timed out", conn->fd, conn->ip, conn->port);
sx_error(conn->s, stream_err_CONNECTION_TIMEOUT, "dialback verify request timed out");
sx_close(conn->s);
}
}
} while(xhash_iter_next(s2s->out));
if(xhash_iter_first(s2s->in))
do {
xhv.conn_val = &conn;
xhash_iter_get(s2s->in, (const char **) &key, xhv.val);
log_debug(ZONE, "checking dialback state for incoming conn %s", key);
if (_s2s_check_conn_routes(s2s, conn, "incoming"))
if(!xhash_count(conn->states) && now > conn->init_time + s2s->check_queue) {
log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] no dialback started", conn->fd, conn->ip, conn->port);
sx_error(conn->s, stream_err_CONNECTION_TIMEOUT, "no dialback initiated");
sx_close(conn->s);
}
} while(xhash_iter_next(s2s->in));
if(xhash_iter_first(s2s->in_accept))
do {
xhv.conn_val = &conn;
xhash_iter_get(s2s->in_accept, (const char **) &key, xhv.val);
log_debug(ZONE, "checking stream connection state for incoming conn %i", conn->fd);
if(!conn->online && now > conn->init_time + s2s->check_queue) {
log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] stream initiation timed out", conn->fd, conn->ip, conn->port);
sx_close(conn->s);
}
} while(xhash_iter_next(s2s->in_accept));
}
if(xhash_iter_first(s2s->out))
do {
xhv.conn_val = &conn;
xhash_iter_get(s2s->out, NULL, xhv.val);
if(s2s->check_keepalive > 0 && conn->last_activity > 0 && now > conn->last_activity + s2s->check_keepalive && conn->s->state >= state_STREAM) {
log_debug(ZONE, "sending keepalive for %d", conn->fd);
sx_raw_write(conn->s, " ", 1);
mio_write(s2s->mio, conn->fd);
}
} while(xhash_iter_next(s2s->out));
if(s2s->check_idle > 0) {
if(xhash_iter_first(s2s->out))
do {
xhv.conn_val = &conn;
xhash_iter_get(s2s->out, (const char **) &key, xhv.val);
log_debug(ZONE, "checking idle state for %s", key);
if (conn->last_packet > 0 && now > conn->last_packet + s2s->check_idle && conn->s->state >= state_STREAM) {
log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] idle timeout", conn->fd, conn->ip, conn->port);
sx_close(conn->s);
}
} while(xhash_iter_next(s2s->out));
if(xhash_iter_first(s2s->in))
do {
xhv.conn_val = &conn;
xhash_iter_get(s2s->in, (const char **) &key, xhv.val);
log_debug(ZONE, "checking idle state for %s", key);
if (conn->last_packet > 0 && now > conn->last_packet + s2s->check_idle && conn->s->state >= state_STREAM) {
log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] idle timeout", conn->fd, conn->ip, conn->port);
sx_close(conn->s);
}
} while(xhash_iter_next(s2s->in));
}
return;
}
int _s2s_populate_whitelist_domains(s2s_t s2s, char **values, int nvalues) {
int i, j;
int elem_len;
s2s->whitelist_domains = (char **)malloc(sizeof(char*) * (nvalues));
memset(s2s->whitelist_domains, 0, (sizeof(char *) * (nvalues)));
for (i = 0, j = 0; i < nvalues; i++) {
elem_len = strlen(values[i]);
if (elem_len > MAX_DOMAIN_LEN) {
log_write(s2s->log, LOG_NOTICE, "whitelist domain element is too large, skipping");
continue;
}
if (elem_len == 0) {
log_write(s2s->log, LOG_NOTICE, "whitelist domain element is blank, skipping");
continue;
}
s2s->whitelist_domains[j] = (char *) malloc(sizeof(char) * (elem_len+1));
memset(s2s->whitelist_domains[j], 0, (sizeof(char) * (elem_len+1)));
strncpy(s2s->whitelist_domains[j], values[i], elem_len);
s2s->whitelist_domains[j][elem_len+1] = '\0';
log_debug(ZONE, "s2s whitelist domain read from file: %s\n", s2s->whitelist_domains[j]);
j++;
}
s2s->n_whitelist_domains = j;
log_write(s2s->log, LOG_NOTICE, "n_whitelist_domains = %i", s2s->n_whitelist_domains);
return 0;
}
int _s2s_domain_in_whitelist(s2s_t s2s, char *in_domain) {
char *wl_domain = NULL;
int segcount = 0;
int dotcount;
char **segments = NULL;
char **dst = NULL;
char *seg_tmp = NULL;
int seg_tmp_len;
char matchstr[MAX_DOMAIN_LEN + 1];
int domain_index;
int x, i;
int wl_index;
int wl_len;
int matchstr_len;
char domain[strlen(in_domain)+1];
char *domain_ptr = &domain[0];
int domain_len;
strlcpy((char *)&domain, in_domain, sizeof(domain));
domain_len = strlen((const char *)&domain);
if (domain_len <= 0) {
log_write(s2s->log, LOG_NOTICE, "_s2s_domain_in_whitelist: in_domain is empty");
return 0;
}
if (domain_len > MAX_DOMAIN_LEN) {
log_write(s2s->log, LOG_NOTICE, "_s2s_domain_in_whitelist: in_domain is longer than %s chars", MAX_DOMAIN_LEN);
return 0;
}
if (s2s->n_whitelist_domains <= 0)
return 0;
for (wl_index =0; wl_index < s2s->n_whitelist_domains; wl_index++) {
wl_len = strlen(s2s->whitelist_domains[wl_index]);
if (!strncmp((const char *)&domain, s2s->whitelist_domains[wl_index], (domain_len > wl_len) ? domain_len : wl_len)) {
log_debug(ZONE, "domain \"%s\" matches whitelist entry", &domain);
return 1;
}
else {
}
}
for (dotcount = 0, x = 0; domain[x] != '\0'; x++) {
if (domain[x] == '.')
dotcount++;
}
segments = (char **)malloc(sizeof(char*) * (dotcount + 1));
if (segments == NULL) {
log_write(s2s->log, LOG_ERR, "_s2s_domain_in_whitelist: malloc() error");
return 0;
}
memset((char **)segments, 0, (sizeof(char*) * (dotcount + 1)));
do {
if (segcount > (dotcount+1)) {
log_write(s2s->log, LOG_ERR, "_s2s_domain_in_whitelist: did not malloc enough room for domain segments; should never get here");
if (seg_tmp != NULL) {
free(seg_tmp);
seg_tmp = NULL;
}
for (x = 0; x < segcount; x++) {
free(segments[x]);
segments[x] = NULL;
}
free(segments);
segments = NULL;
return 0;
}
seg_tmp = strsep(&domain_ptr, ".");
if (seg_tmp == NULL) {
break;
}
seg_tmp_len = strlen(seg_tmp);
if (seg_tmp_len > MAX_DOMAIN_LEN) {
log_write(s2s->log, LOG_NOTICE, "_s2s_domain_in_whitelist: domain contains a segment greater than %s chars", MAX_DOMAIN_LEN);
if (seg_tmp != NULL) {
free(seg_tmp);
seg_tmp = NULL;
}
for (x = 0; x < segcount; x++) {
free(segments[x]);
segments[x] = NULL;
}
free(segments);
segments = NULL;
return 0;
}
dst = &segments[segcount];
*dst = (char *)malloc(seg_tmp_len + 1);
if (*dst != NULL) {
strlcpy(*dst, seg_tmp, seg_tmp_len + 1);
} else {
if (seg_tmp != NULL) {
free(seg_tmp);
seg_tmp = NULL;
}
for (x = 0; x < segcount; x++) {
free(segments[x]);
segments[x] = NULL;
}
free(segments);
segments = NULL;
log_write(s2s->log, LOG_ERR, "_s2s_domain_in_whitelist: malloc() error");
return 0;
}
segcount++;
} while (seg_tmp != NULL);
if (segcount > 1) {
for (domain_index = segcount-2; domain_index > 0; domain_index--) {
matchstr[0] = '\0';
for (i = domain_index; i < segcount; i++) {
if (i > domain_index)
strlcat((char *)&matchstr, ".", sizeof(matchstr));
strlcat((char *)&matchstr, (char *)segments[i], sizeof(matchstr));
}
for (wl_index = 0; wl_index < s2s->n_whitelist_domains; wl_index++) {
wl_len = strlen(s2s->whitelist_domains[wl_index]);
matchstr_len = strlen((const char *)&matchstr);
if (!strncmp((const char *)&matchstr, s2s->whitelist_domains[wl_index], (wl_len > matchstr_len ? wl_len : matchstr_len))) {
log_debug(ZONE, "matchstr \"%s\" matches whitelist entry", &matchstr);
for (x = 0; x < segcount; x++) {
free(segments[x]);
segments[x] = NULL;
}
free(segments);
segments = NULL;
return 1;
}
else {
}
}
}
}
for (x = 0; x < segcount; x++) {
free(segments[x]);
segments[x] = NULL;
}
free(segments);
segments = NULL;
return 0;
}
int main(int argc, char **argv) {
s2s_t s2s;
char *config_file;
int optchar;
conn_t conn;
jqueue_t q;
dnscache_t dns;
union xhashv xhv;
int newuid, newgid;
struct passwd *p;
#ifdef POOL_DEBUG
time_t pool_time = 0;
#endif
#ifdef JABBER_USER
p = getpwnam(JABBER_USER);
if (p == NULL) {
printf("Error: could not find user %s\n", JABBER_USER);
return 1;
}
newuid = p->pw_uid;
newgid = p->pw_gid;
memset(p, 0, sizeof(struct passwd));
if (initgroups(JABBER_USER, newgid)) {
printf("cannot initialize groups for user %s: %s\n", JABBER_USER, strerror(errno));
return 1;
}
if (setgid(newgid)) {
printf("cannot setgid: %s\n", strerror(errno));
return 1;
}
if (seteuid(newuid)) {
printf("cannot seteuid: %s\n", strerror(errno));
return 1;
}
#else
printf("No user is defined for setuid/setgid, continuing\n");
#endif
#ifdef HAVE_UMASK
umask((mode_t) 0027);
#endif
srand(time(NULL));
#ifdef HAVE_WINSOCK2_H
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return 0;
}
}
#endif
jabber_signal(SIGINT, _s2s_signal);
jabber_signal(SIGTERM, _s2s_signal);
#ifdef SIGHUP
jabber_signal(SIGHUP, _s2s_signal_hup);
#endif
#ifdef SIGPIPE
jabber_signal(SIGPIPE, SIG_IGN);
#endif
s2s = (s2s_t) malloc(sizeof(struct s2s_st));
memset(s2s, 0, sizeof(struct s2s_st));
s2s->config = config_new();
config_file = CONFIG_DIR "/s2s.xml";
while((optchar = getopt(argc, argv, "Dc:h?")) >= 0)
{
switch(optchar)
{
case 'c':
config_file = optarg;
break;
case 'D':
#ifdef DEBUG
set_debug_flag(1);
#else
printf("WARN: Debugging not enabled. Ignoring -D.\n");
#endif
break;
case 'h': case '?': default:
fputs(
"s2s - jabberd server-to-server connector (" VERSION ")\n"
"Usage: s2s <options>\n"
"Options are:\n"
" -c <config> config file to use [default: " CONFIG_DIR "/s2s.xml]\n"
#ifdef DEBUG
" -D Show debug output\n"
#endif
,
stdout);
config_free(s2s->config);
free(s2s);
return 1;
}
}
if(config_load(s2s->config, config_file) != 0) {
fputs("s2s: couldn't load config, aborting\n", stderr);
config_free(s2s->config);
free(s2s);
return 2;
}
s2s->log = log_new(s2s->log_type, s2s->log_ident, s2s->log_facility);
_s2s_config_expand(s2s);
log_write(s2s->log, LOG_NOTICE, "starting up (interval=%i, queue=%i, keepalive=%i, idle=%i)", s2s->check_interval, s2s->check_queue, s2s->check_keepalive, s2s->check_idle);
_s2s_pidfile(s2s);
s2s->outq = xhash_new(401);
s2s->out = xhash_new(401);
s2s->in = xhash_new(401);
s2s->in_accept = xhash_new(401);
s2s->dnscache = xhash_new(401);
s2s->pc = prep_cache_new();
s2s->dead = jqueue_new();
s2s->sx_env = sx_env_new();
#ifdef HAVE_SSL
#ifdef JABBER_USER
if (seteuid(0)) {
log_write(s2s->log, LOG_ERR, "cannot seteuid to root: %s", strerror(errno));
return 1;
}
#else
log_write(s2s->log, LOG_NOTICE, "No user is defined for setuid/setgid, continuing");
#endif // JABBER_USER
if(s2s->local_pemfile != NULL) {
s2s->sx_ssl = sx_env_plugin(s2s->sx_env, sx_ssl_init, s2s->local_pemfile, NULL);
if(s2s->sx_ssl == NULL) {
log_write(s2s->log, LOG_ERR, "failed to load local SSL pemfile, SSL will not be available to peers");
s2s->local_pemfile = NULL;
} else
log_debug(ZONE, "loaded pemfile for SSL connections to peers");
}
if(s2s->sx_ssl == NULL && s2s->router_pemfile != NULL) {
s2s->sx_ssl = sx_env_plugin(s2s->sx_env, sx_ssl_init, s2s->router_pemfile, NULL);
if(s2s->sx_ssl == NULL) {
log_write(s2s->log, LOG_ERR, "failed to load router SSL pemfile, channel to router will not be SSL encrypted");
s2s->router_pemfile = NULL;
}
}
#endif // HAVE_SSL
#ifdef JABBER_USER
if (setuid(newuid)) {
log_write(s2s->log, LOG_ERR, "cannot setuid(%d): %s", newuid, strerror(errno));
return 1;
}
#else
log_write(s2s->log, LOG_NOTICE, "No user is defined for setuid/setgid, continuing");
#endif // JABBER_USER
s2s->sx_sasl = sx_env_plugin(s2s->sx_env, sx_sasl_init, "xmpp", SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT, NULL, NULL, 0);
if(s2s->sx_sasl == NULL) {
log_write(s2s->log, LOG_ERR, "failed to initialise SASL context, aborting");
exit(1);
}
s2s->sx_db = sx_env_plugin(s2s->sx_env, s2s_db_init);
s2s->mio = mio_new(1024);
s2s->retry_left = s2s->retry_init;
_s2s_router_connect(s2s);
while(!s2s_shutdown) {
mio_run(s2s->mio, 5);
if(s2s_logrotate) {
log_write(s2s->log, LOG_NOTICE, "reopening log ...");
log_free(s2s->log);
s2s->log = log_new(s2s->log_type, s2s->log_ident, s2s->log_facility);
log_write(s2s->log, LOG_NOTICE, "log started");
s2s_logrotate = 0;
}
if(s2s_lost_router) {
if(s2s->retry_left < 0) {
log_write(s2s->log, LOG_NOTICE, "attempting reconnect");
sleep(s2s->retry_sleep);
s2s_lost_router = 0;
_s2s_router_connect(s2s);
}
else if(s2s->retry_left == 0) {
s2s_shutdown = 1;
}
else {
log_write(s2s->log, LOG_NOTICE, "attempting reconnect (%d left)", s2s->retry_left);
s2s->retry_left--;
sleep(s2s->retry_sleep);
s2s_lost_router = 0;
_s2s_router_connect(s2s);
}
}
while(jqueue_size(s2s->dead) > 0)
sx_free((sx_t) jqueue_pull(s2s->dead));
if(s2s->check_interval > 0 && time(NULL) >= s2s->next_check) {
log_debug(ZONE, "running time checks");
_s2s_time_checks(s2s);
s2s->next_check = time(NULL) + s2s->check_interval;
log_debug(ZONE, "next time check at %d", s2s->next_check);
}
#ifdef POOL_DEBUG
if(time(NULL) > pool_time + 60) {
pool_stat(1);
pool_time = time(NULL);
}
#endif
}
log_write(s2s->log, LOG_NOTICE, "shutting down");
xhv.conn_val = &conn;
if(xhash_iter_first(s2s->out))
do {
xhash_iter_get(s2s->out, NULL, xhv.val);
sx_error(conn->s, stream_err_SYSTEM_SHUTDOWN, "s2s shutdown");
sx_close(conn->s);
} while(xhash_count(s2s->out));
if(xhash_iter_first(s2s->in))
do {
xhash_iter_get(s2s->in, NULL, xhv.val);
sx_error(conn->s, stream_err_SYSTEM_SHUTDOWN, "s2s shutdown");
sx_close(conn->s);
} while(xhash_count(s2s->in));
if(xhash_iter_first(s2s->in_accept))
do {
xhash_iter_get(s2s->in_accept, NULL, xhv.val);
sx_close(conn->s);
} while(xhash_count(s2s->in_accept));
while(jqueue_size(s2s->dead) > 0)
sx_free((sx_t) jqueue_pull(s2s->dead));
xhv.jq_val = &q;
if(xhash_iter_first(s2s->outq))
do {
xhash_iter_get(s2s->outq, NULL, xhv.val);
jqueue_free(q);
} while(xhash_iter_next(s2s->outq));
xhv.dns_val = &dns;
if(xhash_iter_first(s2s->dnscache))
do {
xhash_iter_get(s2s->dnscache, NULL, xhv.val);
free(dns);
} while(xhash_iter_next(s2s->dnscache));
xhash_free(s2s->outq);
xhash_free(s2s->out);
xhash_free(s2s->in);
xhash_free(s2s->in_accept);
xhash_free(s2s->dnscache);
prep_cache_free(s2s->pc);
jqueue_free(s2s->dead);
sx_free(s2s->router);
sx_env_free(s2s->sx_env);
mio_free(s2s->mio);
log_free(s2s->log);
config_free(s2s->config);
free(s2s->local_secret);
free(s2s);
#ifdef POOL_DEBUG
pool_stat(1);
#endif
return 0;
}