#include "s2s.h"
int s2s_router_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) {
s2s_t s2s = (s2s_t) arg;
sx_buf_t buf = (sx_buf_t) data;
sx_error_t *sxe;
nad_t nad;
int len, ns, elem, attr;
pkt_t pkt;
switch(e) {
case event_WANT_READ:
log_debug(ZONE, "want read");
mio_read(s2s->mio, s2s->fd);
break;
case event_WANT_WRITE:
log_debug(ZONE, "want write");
mio_write(s2s->mio, s2s->fd);
break;
case event_READ:
log_debug(ZONE, "reading from %d", s2s->fd);
len = recv(s2s->fd, buf->data, buf->len, 0);
if(len < 0) {
if(errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN) {
buf->len = 0;
return 0;
}
log_write(s2s->log, LOG_NOTICE, "[%d] [router] read error: %s (%d)", s2s->fd, strerror(errno), errno);
sx_kill(s);
return -1;
}
else if(len == 0) {
sx_kill(s);
return -1;
}
log_debug(ZONE, "read %d bytes", len);
buf->len = len;
return len;
case event_WRITE:
log_debug(ZONE, "writing to %d", s2s->fd);
len = send(s2s->fd, buf->data, buf->len, 0);
if(len >= 0) {
log_debug(ZONE, "%d bytes written", len);
return len;
}
if(errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN)
return 0;
log_write(s2s->log, LOG_NOTICE, "[%d] [router] write error: %s (%d)", s2s->fd, strerror(errno), errno);
sx_kill(s);
return -1;
case event_ERROR:
sxe = (sx_error_t *) data;
log_write(s2s->log, LOG_NOTICE, "error from router: %s (%s)", sxe->generic, sxe->specific);
if(sxe->code == SX_ERR_AUTH)
sx_close(s);
break;
case event_STREAM:
break;
case event_OPEN:
log_write(s2s->log, LOG_NOTICE, "connection to router established");
s2s->retry_left = s2s->retry_init;
nad = nad_new(s2s->router->nad_cache);
ns = nad_add_namespace(nad, uri_COMPONENT, NULL);
nad_append_elem(nad, ns, "bind", 0);
nad_append_attr(nad, -1, "name", s2s->id);
if(s2s->router_default)
nad_append_elem(nad, ns, "default", 1);
log_debug(ZONE, "requesting component bind for '%s'", s2s->id);
sx_nad_write(s2s->router, nad);
break;
case event_PACKET:
nad = (nad_t) data;
if(NAD_ENS(nad, 0) < 0) {
nad_free(nad);
return 0;
}
if(s->state == state_STREAM) {
if(NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_STREAMS) || strncmp(uri_STREAMS, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_STREAMS)) != 0 || NAD_ENAME_L(nad, 0) != 8 || strncmp("features", NAD_ENAME(nad, 0), 8) != 0) {
log_debug(ZONE, "got a non-features packet on an unauth'd stream, dropping");
nad_free(nad);
return 0;
}
#ifdef HAVE_SSL
if(s2s->sx_ssl != NULL && s2s->router_pemfile != NULL && s->ssf == 0) {
ns = nad_find_scoped_namespace(nad, uri_TLS, NULL);
if(ns >= 0) {
elem = nad_find_elem(nad, 0, ns, "starttls", 1);
if(elem >= 0) {
if(sx_ssl_client_starttls(s2s->sx_ssl, s, s2s->router_pemfile) == 0) {
nad_free(nad);
return 0;
}
log_write(s2s->log, LOG_NOTICE, "unable to establish encrypted session with router");
}
}
}
#endif
sx_sasl_auth(s2s->sx_sasl, s, "jabberd-router", "DIGEST-MD5", s2s->router_user, s2s->router_pass);
nad_free(nad);
return 0;
}
if(s->state == state_OPEN && !s2s->online) {
if(NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_COMPONENT) || strncmp(uri_COMPONENT, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_COMPONENT)) != 0 || NAD_ENAME_L(nad, 0) != 4 || strncmp("bind", NAD_ENAME(nad, 0), 4)) {
log_debug(ZONE, "got a packet from router, but we're not online, dropping");
nad_free(nad);
return 0;
}
attr = nad_find_attr(nad, 0, -1, "error", NULL);
if(attr >= 0) {
log_write(s2s->log, LOG_NOTICE, "router refused bind request (%.*s)", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
exit(1);
}
log_debug(ZONE, "coming online");
if(s2s->server_fd == 0) {
if(s2s->local_port != 0) {
s2s->server_fd = mio_listen(s2s->mio, s2s->local_port, s2s->local_ip, in_mio_callback, (void *) s2s);
if(s2s->server_fd < 0) {
log_write(s2s->log, LOG_ERR, "[%s, port=%d] failed to listen", s2s->local_ip, s2s->local_port);
exit(1);
} else
log_write(s2s->log, LOG_NOTICE, "[%s, port=%d] listening for connections", s2s->local_ip, s2s->local_port);
}
}
s2s->online = s2s->started = 1;
log_write(s2s->log, LOG_NOTICE, "ready for connections", s2s->id);
nad_free(nad);
return 0;
}
log_debug(ZONE, "got a packet");
if(NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_COMPONENT) || strncmp(uri_COMPONENT, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_COMPONENT)) != 0) {
log_debug(ZONE, "unknown namespace, dropping packet");
nad_free(nad);
return 0;
}
if(NAD_ENAME_L(nad, 0) != 5 || strncmp("route", NAD_ENAME(nad, 0), 5) != 0) {
log_debug(ZONE, "dropping non-route packet");
nad_free(nad);
return 0;
}
if(nad_find_attr(nad, 0, -1, "error", NULL) >= 0) {
log_debug(ZONE, "dropping route error packet");
nad_free(nad);
return 0;
}
if(nad_find_attr(nad, 0, -1, "type", NULL) >= 0) {
log_debug(ZONE, "dropping non-unicast packet");
nad_free(nad);
return 0;
}
attr = nad_find_attr(nad, 0, -1, "to", NULL);
if(NAD_AVAL_L(nad, attr) == strlen(s2s->id) && strncmp(s2s->id, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr)) == 0) {
if(nad->ecur >= 2 &&
NAD_NURI_L(nad, NAD_ENS(nad, 1)) == strlen(uri_RESOLVER) && strncmp(uri_RESOLVER, NAD_NURI(nad, NAD_ENS(nad, 1)), strlen(uri_RESOLVER)) == 0 &&
NAD_ENAME_L(nad, 1) == 7 && strncmp("resolve", NAD_ENAME(nad, 1), 7) == 0) {
out_resolve(s2s, nad);
return 0;
}
log_debug(ZONE, "dropping unknown or invalid packet for s2s component proper");
nad_free(nad);
return 0;
}
pkt = (pkt_t) malloc(sizeof(struct pkt_st));
memset(pkt, 0, sizeof(struct pkt_st));
pkt->nad = nad;
attr = nad_find_attr(nad, 0, -1, "from", NULL);
pkt->from = jid_new(s2s->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
attr = nad_find_attr(nad, 0, -1, "to", NULL);
pkt->to = jid_new(s2s->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
nad_set_attr(nad, 0, -1, "to", s2s->id, 0);
if(NAD_NURI_L(pkt->nad, 0) == 22 && strncmp("jabber:server:dialback", NAD_NURI(pkt->nad, 0), 22) == 0)
pkt->db = 1;
out_packet(s2s, pkt);
return 0;
case event_CLOSED:
mio_close(s2s->mio, s2s->fd);
break;
}
return 0;
}
int s2s_router_mio_callback(mio_t m, mio_action_t a, int fd, void *data, void *arg) {
s2s_t s2s = (s2s_t) arg;
int nbytes;
switch(a) {
case action_READ:
log_debug(ZONE, "read action on fd %d", fd);
ioctl(fd, FIONREAD, &nbytes);
if(nbytes == 0) {
sx_kill(s2s->router);
return 0;
}
return sx_can_read(s2s->router);
case action_WRITE:
log_debug(ZONE, "write action on fd %d", fd);
return sx_can_write(s2s->router);
case action_CLOSE:
log_debug(ZONE, "close action on fd %d", fd);
log_write(s2s->log, LOG_NOTICE, "connection to router closed");
s2s_lost_router = 1;
s2s->online = 0;
break;
case action_ACCEPT:
break;
}
return 0;
}