/** process handshake packets from the client */ static int _sx_ack_process(sx_t s, sx_plugin_t p, nad_t nad) { int attr; /* not interested if we're not a server */ if(s->type != type_SERVER) return 1; /* only want ack packets */ if((NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_ACK) || strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_ACK, strlen(uri_ACK)) != 0)) return 1; /* pings */ if(NAD_ENAME_L(nad, 0) == 4 && strncmp(NAD_ENAME(nad, 0), "ping", 4) == 0) { jqueue_push(s->wbufq, _sx_buffer_new("<ack:pong/>", 11, NULL, NULL), 0); s->want_write = 1; /* handled the packet */ nad_free(nad); return 0; } /* enable only when authenticated */ if(s->state == state_OPEN && NAD_ENAME_L(nad, 0) == 6 && strncmp(NAD_ENAME(nad, 0), "enable", 6) == 0) { jqueue_push(s->wbufq, _sx_buffer_new("<ack:enabled/>", 14, NULL, NULL), 254); s->want_write = 1; s->plugin_data[p->index] = (void *) 1; /* handled the packet */ nad_free(nad); return 0; } /* 'r' or 'a' when enabled */ if(s->plugin_data[p->index] != NULL && NAD_ENAME_L(nad, 0) == 1 && (strncmp(NAD_ENAME(nad, 0), "r", 1) == 0 || strncmp(NAD_ENAME(nad, 0), "a", 1) == 0) ) { attr = nad_find_attr(nad, 0, -1, "c", NULL); if(attr >= 0) { char *buf = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, attr) + 13 + 1)); snprintf(buf, NAD_AVAL_L(nad, attr) + 13 + 1, "<ack:a b='%.*s'/>", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); jqueue_push(s->wbufq, _sx_buffer_new(buf, NAD_AVAL_L(nad, attr) + 13, NULL, NULL), 255); free(buf); s->want_write = 1; } /* handled the packet */ nad_free(nad); return 0; } _sx_debug(ZONE, "unhandled ack namespace element '%.*s', dropping packet", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0)); nad_free(nad); return 0; }
static int _router_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) { component_t comp = (component_t) arg; sx_buf_t buf = (sx_buf_t) data; int rlen, len, attr, ns, sns, n; sx_error_t *sxe; nad_t nad; struct jid_st sto, sfrom; jid_static_buf sto_buf, sfrom_buf; jid_t to, from; alias_t alias; /* init static jid */ jid_static(&sto,&sto_buf); jid_static(&sfrom,&sfrom_buf); switch(e) { case event_WANT_READ: log_debug(ZONE, "want read"); mio_read(comp->r->mio, comp->fd); break; case event_WANT_WRITE: log_debug(ZONE, "want write"); mio_write(comp->r->mio, comp->fd); break; case event_READ: log_debug(ZONE, "reading from %d", comp->fd->fd); /* check rate limits */ if(comp->rate != NULL) { if(rate_check(comp->rate) == 0) { /* inform the app if we haven't already */ if(!comp->rate_log) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] is being byte rate limited", comp->ip, comp->port); comp->rate_log = 1; } log_debug(ZONE, "%d is throttled, delaying read", comp->fd->fd); buf->len = 0; return 0; } /* find out how much we can have */ rlen = rate_left(comp->rate); if(rlen > buf->len) rlen = buf->len; } /* no limit, just read as much as we can */ else rlen = buf->len; /* do the read */ len = recv(comp->fd->fd, buf->data, rlen, 0); /* update rate limits */ if(comp->rate != NULL && len > 0) { comp->rate_log = 0; rate_add(comp->rate, len); } if(len < 0) { if(MIO_WOULDBLOCK) { buf->len = 0; return 0; } log_debug(ZONE, "read failed: %s", strerror(errno)); sx_kill(comp->s); return -1; } else if(len == 0) { /* they went away */ sx_kill(comp->s); return -1; } log_debug(ZONE, "read %d bytes", len); buf->len = len; return len; case event_WRITE: log_debug(ZONE, "writing to %d", comp->fd->fd); len = send(comp->fd->fd, buf->data, buf->len, 0); if(len >= 0) { log_debug(ZONE, "%d bytes written", len); return len; } if(MIO_WOULDBLOCK) return 0; log_debug(ZONE, "write failed: %s", strerror(errno)); sx_kill(comp->s); return -1; case event_ERROR: sxe = (sx_error_t *) data; log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] error: %s (%s)", comp->ip, comp->port, sxe->generic, sxe->specific); break; case event_STREAM: /* legacy check */ if(s->ns == NULL || strcmp("jabber:component:accept", s->ns) != 0) return 0; /* component, old skool */ comp->legacy = 1; /* enabled? */ if(comp->r->local_secret == NULL) { sx_error(s, stream_err_INVALID_NAMESPACE, "support for legacy components not available"); /* !!! correct error? */ sx_close(s); return 0; } /* sanity */ if(s->req_to == NULL) { sx_error(s, stream_err_HOST_UNKNOWN, "no 'to' attribute on stream header"); sx_close(s); return 0; } break; case event_OPEN: log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] authenticated as %s", comp->ip, comp->port, comp->s->auth_id); /* make a route for legacy components */ if(comp->legacy) { for(alias = comp->r->aliases; alias != NULL; alias = alias->next) if(strcmp(alias->name, s->req_to) == 0) { sx_error(s, stream_err_HOST_UNKNOWN, "requested name is aliased"); /* !!! correct error? */ sx_close(s); return 0; } n = _route_add(comp->r->routes, s->req_to, comp, route_MULTI_FROM); xhash_put(comp->routes, pstrdup(xhash_pool(comp->routes), s->req_to), (void *) comp); if(n>1) log_write(comp->r->log, LOG_NOTICE, "[%s]:%d online (bound to %s, port %d)", s->req_to, n, comp->ip, comp->port); else log_write(comp->r->log, LOG_NOTICE, "[%s] online (bound to %s, port %d)", s->req_to, comp->ip, comp->port); /* advertise the name */ _router_advertise(comp->r, s->req_to, comp, 0); /* this is a legacy component, so we don't tell it about other routes */ /* bind aliases */ for(alias = comp->r->aliases; alias != NULL; alias = alias->next) { if(strcmp(alias->target, s->req_to) == 0) { _route_add(comp->r->routes, alias->name, comp, route_MULTI_FROM); xhash_put(comp->routes, pstrdup(xhash_pool(comp->routes), alias->name), (void *) comp); log_write(comp->r->log, LOG_NOTICE, "[%s] online (alias of '%s', bound to %s, port %d)", alias->name, s->req_to, comp->ip, comp->port); /* advertise name */ _router_advertise(comp->r, alias->name, comp, 0); } } } break; case event_PACKET: nad = (nad_t) data; /* preauth */ if(comp->s->state == state_STREAM) { /* non-legacy components can't do anything before auth */ if(!comp->legacy) { log_debug(ZONE, "stream is preauth, dropping packet"); nad_free(nad); return 0; } /* watch for handshake requests */ if(NAD_ENAME_L(nad, 0) != 9 || strncmp("handshake", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) != 0) { log_debug(ZONE, "unknown preauth packet %.*s, dropping", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0)); nad_free(nad); return 0; } /* process incoming handshakes */ _router_process_handshake(comp, nad); return 0; } /* legacy processing */ if(comp->legacy) { log_debug(ZONE, "packet from legacy component, munging it"); attr = nad_find_attr(nad, 0, -1, "to", NULL); if(attr < 0 || (to = jid_reset(&sto, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "invalid or missing 'to' address on legacy packet, dropping it"); nad_free(nad); return 0; } attr = nad_find_attr(nad, 0, -1, "from", NULL); if(attr < 0 || (from = jid_reset(&sfrom, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "invalid or missing 'from' address on legacy packet, dropping it"); nad_free(nad); return 0; } /* rewrite component packets into client packets */ ns = nad_find_namespace(nad, 0, "jabber:component:accept", NULL); if(ns >= 0) { if(nad->elems[0].ns == ns) nad->elems[0].ns = nad->nss[nad->elems[0].ns].next; else { for(sns = nad->elems[0].ns; sns >= 0 && nad->nss[sns].next != ns; sns = nad->nss[sns].next); nad->nss[sns].next = nad->nss[nad->nss[sns].next].next; } } ns = nad_find_namespace(nad, 0, uri_CLIENT, NULL); if(ns < 0) { ns = nad_add_namespace(nad, uri_CLIENT, NULL); nad->scope = -1; nad->nss[ns].next = nad->elems[0].ns; nad->elems[0].ns = ns; } nad->elems[0].my_ns = ns; /* wrap up the packet */ ns = nad_add_namespace(nad, uri_COMPONENT, NULL); nad_wrap_elem(nad, 0, ns, "route"); nad_set_attr(nad, 0, -1, "to", to->domain, 0); nad_set_attr(nad, 0, -1, "from", from->domain, 0); } /* top element must be router scoped */ if(NAD_ENS(nad, 0) < 0 || 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, "invalid packet namespace, dropping"); nad_free(nad); return 0; } /* bind a name to this component */ if(NAD_ENAME_L(nad, 0) == 4 && strncmp("bind", NAD_ENAME(nad, 0), 4) == 0) { _router_process_bind(comp, nad); return 0; } /* unbind a name from this component */ if(NAD_ENAME_L(nad, 0) == 6 && strncmp("unbind", NAD_ENAME(nad, 0), 6) == 0) { _router_process_unbind(comp, nad); return 0; } /* route packets */ if(NAD_ENAME_L(nad, 0) == 5 && strncmp("route", NAD_ENAME(nad, 0), 5) == 0) { _router_process_route(comp, nad); return 0; } /* throttle packets */ if(NAD_ENAME_L(nad, 0) == 8 && strncmp("throttle", NAD_ENAME(nad, 0), 8) == 0) { _router_process_throttle(comp, nad); return 0; } log_debug(ZONE, "unknown packet, dropping"); nad_free(nad); return 0; case event_CLOSED: { /* close comp->fd by putting it in closefd ... unless it is already there */ _jqueue_node_t n; for (n = comp->r->closefd->front; n != NULL; n = n->prev) if (n->data == comp->fd) break; if (!n) jqueue_push(comp->r->closefd, (void *) comp->fd, 0 /*priority*/); return 0; } } return 0; }
static void _router_process_route(component_t comp, nad_t nad) { int atype, ato, afrom; unsigned int dest; struct jid_st sto, sfrom; jid_static_buf sto_buf, sfrom_buf; jid_t to = NULL, from = NULL; routes_t targets; component_t target; union xhashv xhv; /* init static jid */ jid_static(&sto,&sto_buf); jid_static(&sfrom,&sfrom_buf); if(nad_find_attr(nad, 0, -1, "error", NULL) >= 0) { log_debug(ZONE, "dropping error packet, trying to avoid loops"); nad_free(nad); return; } atype = nad_find_attr(nad, 0, -1, "type", NULL); ato = nad_find_attr(nad, 0, -1, "to", NULL); afrom = nad_find_attr(nad, 0, -1, "from", NULL); if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato)); if(afrom >= 0) from = jid_reset(&sfrom, NAD_AVAL(nad, afrom), NAD_AVAL_L(nad, afrom)); /* unicast */ if(atype < 0) { if(to == NULL || from == NULL) { log_debug(ZONE, "unicast route with missing or invalid to or from, bouncing"); nad_set_attr(nad, 0, -1, "error", "400", 3); _router_comp_write(comp, nad); return; } log_debug(ZONE, "unicast route from %s to %s", from->domain, to->domain); /* check the from */ if(xhash_get(comp->routes, from->domain) == NULL) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to send a packet from '%s', but that name is not bound to this component", comp->ip, comp->port, from->domain); nad_set_attr(nad, 0, -1, "error", "401", 3); _router_comp_write(comp, nad); return; } /* filter it */ if(comp->r->filter != NULL) { int ret = filter_packet(comp->r, nad); if(ret == stanza_err_REDIRECT) { ato = nad_find_attr(nad, 0, -1, "to", NULL); if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato)); } else if(ret > 0) { log_debug(ZONE, "packet filtered out: %s (%s)", _stanza_errors[ret - stanza_err_BAD_REQUEST].name, _stanza_errors[ret - stanza_err_BAD_REQUEST].code); nad_set_attr(nad, 0, -1, "error", _stanza_errors[ret - stanza_err_BAD_REQUEST].code, 3); _router_comp_write(comp, nad); return; } } /* find a target */ targets = xhash_get(comp->r->routes, to->domain); if(targets == NULL) { if(comp->r->default_route != NULL && strcmp(from->domain, comp->r->default_route) == 0) { log_debug(ZONE, "%s is unbound, bouncing", from->domain); nad_set_attr(nad, 0, -1, "error", "404", 3); _router_comp_write(comp, nad); return; } targets = xhash_get(comp->r->routes, comp->r->default_route); } if(targets == NULL) { log_debug(ZONE, "%s is unbound, and no default route, bouncing", to->domain); nad_set_attr(nad, 0, -1, "error", "404", 3); _router_comp_write(comp, nad); return; } /* copy to any log sinks */ if(xhash_count(comp->r->log_sinks) > 0) xhash_walk(comp->r->log_sinks, _router_route_log_sink, (void *) nad); /* get route candidate */ if(targets->ncomp == 1) { dest = 0; } else { switch(targets->rtype) { case route_MULTI_TO: ato = nad_find_attr(nad, 1, -1, "to", NULL); if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato)); else { ato = nad_find_attr(nad, 1, -1, "target", NULL); if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato)); else { const char *out; int len; nad_print(nad, 0, &out, &len); log_write(comp->r->log, LOG_ERR, "Cannot get destination for multiple route: %.*s", len, out); } } break; case route_MULTI_FROM: ato = nad_find_attr(nad, 1, -1, "from", NULL); if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato)); else { const char *out; int len; nad_print(nad, 0, &out, &len); log_write(comp->r->log, LOG_ERR, "Cannot get source for multiple route: %.*s", len, out); } break; default: log_write(comp->r->log, LOG_ERR, "Multiple components bound to single component route '%s'", targets->name); /* simulate no 'to' info in this case */ } if(to->node == NULL || strlen(to->node) == 0) { /* no node in destination JID - going random */ dest = rand(); log_debug(ZONE, "randomized to %u %% %d = %d", dest, targets->ncomp, dest % targets->ncomp); } else { /* use JID hash */ unsigned char hashval[20]; unsigned int *val; int i; shahash_raw(jid_user(to), hashval); val = (unsigned int *) hashval; dest = *val; for(i=1; i < 20 / (sizeof(unsigned int)/sizeof(unsigned char)); i++, val++) { dest ^= *val; } dest >>= 2; log_debug(ZONE, "JID %s hashed to %u %% %d = %d", jid_user(to), dest, targets->ncomp, dest % targets->ncomp); /* jid_user() calls jid_expand() which may allocate some memory in _user and _full */ if (to->_user != NULL ) free(to->_user); if (to->_full != NULL ) free(to->_full); } dest = dest % targets->ncomp; } target = targets->comp[dest]; /* push it out */ log_debug(ZONE, "writing route for '%s'*%u to %s, port %d", to->domain, dest+1, target->ip, target->port); /* if logging enabled, log messages that match our criteria */ if (comp->r->message_logging_enabled && comp->r->message_logging_file != NULL) { int attr_msg_to; int attr_msg_from; int attr_route_to; int attr_route_from; jid_t jid_msg_from = NULL; jid_t jid_msg_to = NULL; jid_t jid_route_from = NULL; jid_t jid_route_to = NULL; if ((NAD_ENAME_L(nad, 1) == 7 && strncmp("message", NAD_ENAME(nad, 1), 7) == 0) && // has a "message" element ((attr_route_from = nad_find_attr(nad, 0, -1, "from", NULL)) >= 0) && ((attr_route_to = nad_find_attr(nad, 0, -1, "to", NULL)) >= 0) && ((strncmp(NAD_AVAL(nad, attr_route_to), "c2s", 3)) != 0) && // ignore messages to "c2s" or we'd have dups ((jid_route_from = jid_new(NAD_AVAL(nad, attr_route_from), NAD_AVAL_L(nad, attr_route_from))) != NULL) && // has valid JID source in route ((jid_route_to = jid_new(NAD_AVAL(nad, attr_route_to), NAD_AVAL_L(nad, attr_route_to))) != NULL) && // has valid JID destination in route ((attr_msg_from = nad_find_attr(nad, 1, -1, "from", NULL)) >= 0) && ((attr_msg_to = nad_find_attr(nad, 1, -1, "to", NULL)) >= 0) && ((jid_msg_from = jid_new(NAD_AVAL(nad, attr_msg_from), NAD_AVAL_L(nad, attr_msg_from))) != NULL) && // has valid JID source in message ((jid_msg_to = jid_new(NAD_AVAL(nad, attr_msg_to), NAD_AVAL_L(nad, attr_msg_to))) != NULL)) // has valid JID dest in message { message_log(nad, comp->r, jid_full(jid_msg_from), jid_full(jid_msg_to)); } if (jid_msg_from != NULL) jid_free(jid_msg_from); if (jid_msg_to != NULL) jid_free(jid_msg_to); if (jid_route_from != NULL) jid_free(jid_route_from); if (jid_route_to != NULL) jid_free(jid_route_to); } _router_comp_write(target, nad); return; } /* broadcast */ if(NAD_AVAL_L(nad, atype) == 9 && strncmp("broadcast", NAD_AVAL(nad, atype), 9) == 0) { if(from == NULL) { log_debug(ZONE, "broadcast route with missing or invalid from, bouncing"); nad_set_attr(nad, 0, -1, "error", "400", 3); _router_comp_write(comp, nad); return; } log_debug(ZONE, "broadcast route from %s", from->domain); /* check the from */ if(xhash_get(comp->routes, from->domain) == NULL) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to send a packet from '%s', but that name is not bound to this component", comp->ip, comp->port, from->domain); nad_set_attr(nad, 0, -1, "error", "401", 3); _router_comp_write(comp, nad); return; } /* loop the components and distribute */ if(xhash_iter_first(comp->r->components)) do { xhv.comp_val = ⌖ xhash_iter_get(comp->r->components, NULL, NULL, xhv.val); if(target != comp) { log_debug(ZONE, "writing broadcast to %s, port %d", target->ip, target->port); _router_comp_write(target, nad_copy(nad)); } } while(xhash_iter_next(comp->r->components)); nad_free(nad); return; } log_debug(ZONE, "unknown route type '%.*s', dropping", NAD_AVAL_L(nad, atype), NAD_AVAL(nad, atype)); nad_free(nad); }
int message_log(nad_t nad, router_t r, const char *msg_from, const char *msg_to) { time_t t; char *time_pos; int time_sz; struct stat filestat; FILE *message_file; short int new_msg_file = 0; int i; int nad_body_len = 0; const char *nad_body_start = 0; int body_count; const char *nad_body = NULL; char body[MAX_MESSAGE*2]; assert((int) (nad != NULL)); /* timestamp */ t = time(NULL); time_pos = ctime(&t); time_sz = strlen(time_pos); /* chop off the \n */ time_pos[time_sz-1]=' '; // Find the message body for (i = 0; NAD_ENAME_L(nad, i) > 0; i++) { if((NAD_ENAME_L(nad, i) == 4) && (strncmp("body", NAD_ENAME(nad, i), 4) == 0)) { nad_body_len = NAD_CDATA_L(nad, i); if (nad_body_len > 0) { nad_body = NAD_CDATA(nad, i); } else { log_write(r->log, LOG_NOTICE, "message_log received a message with empty body"); return 0; } break; } } // Don't log anything if we found no NAD body if (nad_body == NULL) { return 0; } // Store original pointer address so that we know when to stop iterating through nad_body nad_body_start = nad_body; // replace line endings with "\n" for (body_count = 0; (nad_body < nad_body_start + nad_body_len) && (body_count < (MAX_MESSAGE*2)-3); nad_body++) { if (*nad_body == '\n') { body[body_count++] = '\\'; body[body_count++] = 'n'; } else { body[body_count++] = *nad_body; } } body[body_count] = '\0'; // Log our message umask((mode_t) 0077); if (stat(r->message_logging_file, &filestat)) { new_msg_file = 1; } if ((message_file = fopen(r->message_logging_file, "a")) == NULL) { log_write(r->log, LOG_ERR, "Unable to open message log for writing: %s", strerror(errno)); return 1; } if (new_msg_file) { if (! fprintf(message_file, "# This message log is created by the jabberd router.\n")) { log_write(r->log, LOG_ERR, "Unable to write to message log: %s", strerror(errno)); return 1; } fprintf(message_file, "# See router.xml for logging options.\n"); fprintf(message_file, "# Format: (Date)<tab>(From JID)<tab>(To JID)<tab>(Message Body)<line end>\n"); } if (! fprintf(message_file, "%s\t%s\t%s\t%s\n", time_pos, msg_from, msg_to, body)) { log_write(r->log, LOG_ERR, "Unable to write to message log: %s", strerror(errno)); return 1; } fclose(message_file); return 0; }
static int _in_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) { conn_t in = (conn_t) arg; sx_buf_t buf = (sx_buf_t) data; int len; sx_error_t *sxe; nad_t nad; char ipport[INET6_ADDRSTRLEN + 17]; switch(e) { case event_WANT_READ: log_debug(ZONE, "want read"); mio_read(in->s2s->mio, in->fd); break; case event_WANT_WRITE: log_debug(ZONE, "want write"); mio_write(in->s2s->mio, in->fd); break; case event_READ: log_debug(ZONE, "reading from %d", in->fd->fd); /* do the read */ len = recv(in->fd->fd, buf->data, buf->len, 0); if(len < 0) { if(MIO_WOULDBLOCK) { buf->len = 0; return 0; } log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] read error: %s (%d)", in->fd->fd, in->ip, in->port, MIO_STRERROR(MIO_ERROR), MIO_ERROR); sx_kill(s); return -1; } else if(len == 0) { /* they went away */ 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", in->fd->fd); len = send(in->fd->fd, buf->data, buf->len, 0); if(len >= 0) { log_debug(ZONE, "%d bytes written", len); return len; } if(MIO_WOULDBLOCK) return 0; log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] write error: %s (%d)", in->fd->fd, in->ip, in->port, MIO_STRERROR(MIO_ERROR), MIO_ERROR); sx_kill(s); return -1; case event_ERROR: sxe = (sx_error_t *) data; log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] error: %s (%s)", in->fd->fd, in->ip, in->port, sxe->generic, sxe->specific); break; case event_STREAM: case event_OPEN: log_debug(ZONE, "STREAM or OPEN event from %s port %d (id %s)", in->ip, in->port, s->id); /* first time, bring them online */ if ((!in->online)||(strcmp(in->key,s->id)!=0)) { log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] incoming stream online (id %s)", in->fd->fd, in->ip, in->port, s->id); in->online = 1; /* record the id */ if (in->key != NULL) { log_debug(ZONE,"adding new SSL stream id %s for stream id %s", s->id, in->key); /* remove the initial (non-SSL) stream id from the in connections hash */ xhash_zap(in->s2s->in, in->key); free(in->key); } in->key = strdup(s->id); /* track it - add to open streams hash and remove from new connections hash */ xhash_put(in->s2s->in, in->key, (void *) in); snprintf(ipport, INET6_ADDRSTRLEN + 16, "%s/%d", in->ip, in->port); xhash_zap(in->s2s->in_accept, ipport); } break; case event_PACKET: /* we're counting packets */ in->packet_count++; in->s2s->packet_count++; nad = (nad_t) data; /* update last packet timestamp */ in->last_packet = time(NULL); /* dialback packets */ if(NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_DIALBACK) && strncmp(uri_DIALBACK, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_DIALBACK)) == 0) { /* only result and verify mean anything */ if(NAD_ENAME_L(nad, 0) == 6) { if(strncmp("result", NAD_ENAME(nad, 0), 6) == 0) { _in_result(in, nad); return 0; } if(strncmp("verify", NAD_ENAME(nad, 0), 6) == 0) { _in_verify(in, nad); return 0; } } log_debug(ZONE, "unknown dialback packet, dropping it"); nad_free(nad); return 0; } /* * not dialback, so it has to be a normal-ish jabber packet: * - jabber:client or jabber:server * - message, presence or iq * - has to and from attributes */ if(!( /* must be jabber:client or jabber:server */ NAD_ENS(nad, 0) >= 0 && ((NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_CLIENT) && strncmp(uri_CLIENT, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_CLIENT)) == 0) || (NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_SERVER) && strncmp(uri_SERVER, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_SERVER)) == 0)) && ( /* can be message */ (NAD_ENAME_L(nad, 0) == 7 && strncmp("message", NAD_ENAME(nad, 0), 7) == 0) || /* or presence */ (NAD_ENAME_L(nad, 0) == 8 && strncmp("presence", NAD_ENAME(nad, 0), 8) == 0) || /* or iq */ (NAD_ENAME_L(nad, 0) == 2 && strncmp("iq", NAD_ENAME(nad, 0), 2) == 0) ) && /* to and from required */ nad_find_attr(nad, 0, -1, "to", NULL) >= 0 && nad_find_attr(nad, 0, -1, "from", NULL) >= 0 )) { log_debug(ZONE, "they sent us a non-jabber looking packet, dropping it"); nad_free(nad); return 0; } _in_packet(in, nad); return 0; case event_CLOSED: mio_close(in->s2s->mio, in->fd); return -1; } return 0; }
/** our master callback */ int sm_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) { sm_t sm = (sm_t) arg; sx_buf_t buf = (sx_buf_t) data; sx_error_t *sxe; nad_t nad; pkt_t pkt; int len, ns, elem, attr; char *domain; switch(e) { case event_WANT_READ: log_debug(ZONE, "want read"); mio_read(sm->mio, sm->fd); break; case event_WANT_WRITE: log_debug(ZONE, "want write"); mio_write(sm->mio, sm->fd); break; case event_READ: log_debug(ZONE, "reading from %d", sm->fd->fd); /* do the read */ len = recv(sm->fd->fd, buf->data, buf->len, 0); if (len < 0) { if (MIO_WOULDBLOCK) { buf->len = 0; return 0; } log_write(sm->log, LOG_NOTICE, "[%d] [router] read error: %s (%d)", sm->fd->fd, MIO_STRERROR(MIO_ERROR), MIO_ERROR); sx_kill(s); return -1; } else if (len == 0) { /* they went away */ 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", sm->fd->fd); len = send(sm->fd->fd, buf->data, buf->len, 0); if (len >= 0) { log_debug(ZONE, "%d bytes written", len); return len; } if (MIO_WOULDBLOCK) return 0; log_write(sm->log, LOG_NOTICE, "[%d] [router] write error: %s (%d)", sm->fd->fd, MIO_STRERROR(MIO_ERROR), MIO_ERROR); sx_kill(s); return -1; case event_ERROR: sxe = (sx_error_t *) data; log_write(sm->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(sm->log, LOG_NOTICE, "connection to router established"); /* set connection attempts counter */ sm->retry_left = sm->retry_lost; nad = nad_new(); ns = nad_add_namespace(nad, uri_COMPONENT, NULL); nad_append_elem(nad, ns, "bind", 0); nad_append_attr(nad, -1, "name", sm->id); log_debug(ZONE, "requesting component bind for '%s'", sm->id); sx_nad_write(sm->router, nad); if(xhash_iter_first(sm->hosts)) do { xhash_iter_get(sm->hosts, (void *) &domain, &len, NULL); /* skip already requested SM id */ if (strlen(sm->id) == len && strncmp(sm->id, domain, len) == 0) continue; nad = nad_new(); ns = nad_add_namespace(nad, uri_COMPONENT, NULL); elem = nad_append_elem(nad, ns, "bind", 0); nad_set_attr(nad, elem, -1, "name", domain, len); nad_append_attr(nad, -1, "multi", "to"); log_debug(ZONE, "requesting domain bind for '%.*s'", len, domain); sx_nad_write(sm->router, nad); } while(xhash_iter_next(sm->hosts)); sm_update_host = 1; break; case event_PACKET: nad = (nad_t) data; /* drop unqualified packets */ if (NAD_ENS(nad, 0) < 0) { nad_free(nad); return 0; } /* watch for the features packet */ 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 /* starttls if we can */ if (sm->sx_ssl != 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(sm->sx_ssl, s, NULL, NULL) == 0) { nad_free(nad); return 0; } log_write(sm->log, LOG_NOTICE, "unable to establish encrypted session with router"); } } } #endif /* !!! pull the list of mechanisms, and choose the best one. * if there isn't an appropriate one, error and bail */ /* authenticate */ sx_sasl_auth(sm->sx_sasl, s, "jabberd-router", "DIGEST-MD5", sm->router_user, sm->router_pass); nad_free(nad); return 0; } /* watch for the bind response */ if (s->state == state_OPEN && !sm->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; } /* catch errors */ attr = nad_find_attr(nad, 0, -1, "error", NULL); if(attr >= 0) { log_write(sm->log, LOG_NOTICE, "router refused bind request (%.*s)", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); exit(1); } log_debug(ZONE, "coming online"); /* we're online */ sm->online = sm->started = 1; log_write(sm->log, LOG_NOTICE, "%s ready for sessions", sm->id); nad_free(nad); return 0; } log_debug(ZONE, "got a packet"); pkt = pkt_new(sm, nad); if (pkt == NULL) { log_debug(ZONE, "invalid packet, dropping"); return 0; } /* go */ dispatch(sm, pkt); return 0; case event_CLOSED: mio_close(sm->mio, sm->fd); sm->fd = NULL; return -1; } return 0; }
/** * processor for iq:auth and iq:register packets * return 0 if handled, 1 if not handled */ int authreg_process(c2s_t c2s, sess_t sess, nad_t nad) { int ns, query, type, authreg = -1, getset = -1; /* need iq */ if(NAD_ENAME_L(nad, 0) != 2 || strncmp("iq", NAD_ENAME(nad, 0), 2) != 0) return 1; /* only want auth or register packets */ if((ns = nad_find_scoped_namespace(nad, uri_AUTH, NULL)) >= 0 && (query = nad_find_elem(nad, 0, ns, "query", 1)) >= 0) authreg = 0; else if((ns = nad_find_scoped_namespace(nad, uri_REGISTER, NULL)) >= 0 && (query = nad_find_elem(nad, 0, ns, "query", 1)) >= 0) authreg = 1; else return 1; /* if its to someone else, pass it */ if(nad_find_attr(nad, 0, -1, "to", NULL) >= 0 && nad_find_attr(nad, 0, -1, "to", sess->s->req_to) < 0) return 1; /* need a type */ if((type = nad_find_attr(nad, 0, -1, "type", NULL)) < 0 || NAD_AVAL_L(nad, type) != 3) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return 0; } /* get or set? */ if(strncmp("get", NAD_AVAL(nad, type), NAD_AVAL_L(nad, type)) == 0) getset = 0; else if(strncmp("set", NAD_AVAL(nad, type), NAD_AVAL_L(nad, type)) == 0) getset = 1; else { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return 0; } /* hand to the correct handler */ if(authreg == 0) { /* can't do iq:auth after sasl auth */ if(sess->sasl_authd) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return 0; } if(getset == 0) { log_debug(ZONE, "auth get"); _authreg_auth_get(c2s, sess, nad); } else if(getset == 1) { log_debug(ZONE, "auth set"); _authreg_auth_set(c2s, sess, nad); } } if(authreg == 1) { if(getset == 0) { log_debug(ZONE, "register get"); _authreg_register_get(c2s, sess, nad); } else if(getset == 1) { log_debug(ZONE, "register set"); _authreg_register_set(c2s, sess, nad); } } /* handled */ return 0; }
/** handler for read data */ void _sx_process_read(sx_t s, sx_buf_t buf) { sx_error_t sxe; nad_t nad; char *errstring; int i; int ns, elem; /* Note that buf->len can validly be 0 here, if we got data from the socket but the plugin didn't return anything to us (e.g. a SSL packet was split across a tcp segment boundary) */ /* count bytes parsed */ s->pbytes += buf->len; /* parse it */ if(XML_Parse(s->expat, buf->data, buf->len, 0) == 0) { /* only report error we haven't already */ if(!s->fail) { /* parse error */ errstring = (char *) XML_ErrorString(XML_GetErrorCode(s->expat)); _sx_debug(ZONE, "XML parse error: %s, character %d: %.*s", errstring, XML_GetCurrentByteIndex(s->expat) - s->tbytes, buf->len, buf->data); _sx_gen_error(sxe, SX_ERR_XML_PARSE, "XML parse error", errstring); _sx_event(s, event_ERROR, (void *) &sxe); _sx_error(s, stream_err_XML_NOT_WELL_FORMED, errstring); _sx_close(s); _sx_buffer_free(buf); return; } /* !!! is this the right thing to do? we should probably set * s->fail and let the code further down handle it. */ _sx_buffer_free(buf); return; } /* check if the stanza size limit is exceeded (it wasn't reset by parser) */ if(s->rbytesmax && s->pbytes > s->rbytesmax) { /* parse error */ _sx_debug(ZONE, "maximum stanza size (%d) exceeded by reading %d bytes", s->rbytesmax, s->pbytes); errstring = (char *) XML_ErrorString(XML_GetErrorCode(s->expat)); _sx_gen_error(sxe, SX_ERR_XML_PARSE, "stream read error", "Maximum stanza size exceeded"); _sx_event(s, event_ERROR, (void *) &sxe); _sx_error(s, stream_err_POLICY_VIOLATION, errstring); _sx_close(s); _sx_buffer_free(buf); return; } /* count bytes processed */ s->tbytes += buf->len; /* done with the buffer */ _sx_buffer_free(buf); /* process completed nads */ if(s->state >= state_STREAM) while((nad = jqueue_pull(s->rnadq)) != NULL) { int plugin_error; #ifdef SX_DEBUG const char *out; int len; nad_print(nad, 0, &out, &len); _sx_debug(ZONE, "completed nad: %.*s", len, out); #endif /* check for errors */ if(NAD_ENS(nad, 0) >= 0 && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_STREAMS) && strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_STREAMS, strlen(uri_STREAMS)) == 0 && NAD_ENAME_L(nad, 0) == 5 && strncmp(NAD_ENAME(nad, 0), "error", 5) == 0) { errstring = NULL; /* get text error description if available - XMPP 4.7.2 */ if((ns = nad_find_scoped_namespace(nad, uri_STREAM_ERR, NULL)) >= 0) if((elem = nad_find_elem(nad, 0, ns, "text", 1)) >= 0) if(NAD_CDATA_L(nad, elem) > 0) { errstring = (char *) malloc(sizeof(char) * (NAD_CDATA_L(nad, elem) + 1)); sprintf(errstring, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem)); } /* if not available, look for legacy error text as in <stream:error>description</stream:error> */ if (errstring == NULL && NAD_CDATA_L(nad, 0) > 0) { errstring = (char *) malloc(sizeof(char) * (NAD_CDATA_L(nad, 0) + 1)); sprintf(errstring, "%.*s", NAD_CDATA_L(nad, 0), NAD_CDATA(nad, 0)); } /* if not available, log the whole packet for debugging */ if (errstring == NULL) { const char *xml; int xlen; nad_print(nad, 0, &xml, &xlen); errstring = (char *) malloc(sizeof(char) * (xlen + 1)); sprintf(errstring, "%.*s", xlen, xml); } if(s->state < state_CLOSING) { _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", errstring); _sx_event(s, event_ERROR, (void *) &sxe); _sx_state(s, state_CLOSING); } free(errstring); nad_free(nad); break; } /* check for close */ if ((s->flags & SX_WEBSOCKET_WRAPPER) && NAD_ENS(nad, 0) >= 0 && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_XFRAMING) && strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_XFRAMING, strlen(uri_XFRAMING)) == 0 && NAD_ENAME_L(nad, 0) == 5 && strncmp(NAD_ENAME(nad, 0), "close", 5) == 0) { _sx_debug(ZONE, "<close/> frame @ depth %d", s->depth); s->fail = 1; break; } /* run it by the plugins */ if(_sx_chain_nad_read(s, nad) == 0) return; /* now let the plugins process the completed nad */ plugin_error = 0; if(s->env != NULL) for(i = 0; i < s->env->nplugins; i++) if(s->env->plugins[i]->process != NULL) { int plugin_ret; plugin_ret = (s->env->plugins[i]->process)(s, s->env->plugins[i], nad); if(plugin_ret == 0) { plugin_error ++; break; } } /* hand it to the app */ if ((plugin_error == 0) && (s->state < state_CLOSING)) _sx_event(s, event_PACKET, (void *) nad); } /* something went wrong, bail */ if(s->fail) { _sx_close(s); return; } /* stream was closed */ if(s->depth < 0 && s->state < state_CLOSING) { /* close the stream if necessary */ if(s->state >= state_STREAM_SENT) { if (s->flags & SX_WEBSOCKET_WRAPPER) jqueue_push(s->wbufq, _sx_buffer_new("<close xmlns='" uri_XFRAMING "' />", sizeof(uri_XFRAMING) + 17, NULL, NULL), 0); else jqueue_push(s->wbufq, _sx_buffer_new("</stream:stream>", 16, NULL, NULL), 0); s->want_write = 1; } _sx_state(s, state_CLOSING); return; } }
static int _sx_compress_process(sx_t s, sx_plugin_t p, nad_t nad) { int flags; char *ns = NULL, *to = NULL, *from = NULL, *version = NULL; sx_error_t sxe; /* not interested if we're a server and we never offered it */ if(s->type == type_SERVER && !(s->flags & SX_COMPRESS_OFFER)) return 1; /* only want compress packets */ if(NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != sizeof(uri_COMPRESS)-1 || strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_COMPRESS, sizeof(uri_COMPRESS)-1) != 0) return 1; /* compress from client */ if(s->type == type_SERVER) { if(NAD_ENAME_L(nad, 0) == 8 && strncmp(NAD_ENAME(nad, 0), "compress", 8) == 0) { nad_free(nad); /* can't go on if we've been here before */ if(s->compressed) { _sx_debug(ZONE, "compress requested on already compressed channel, dropping packet"); return 0; } _sx_debug(ZONE, "compress requested, setting up"); /* go ahead */ jqueue_push(s->wbufq, _sx_buffer_new("<compressed xmlns='" uri_COMPRESS "'/>", sizeof(uri_COMPRESS)-1 + 22, _sx_compress_notify_compress, NULL), 0); s->want_write = 1; /* handled the packet */ return 0; } } else if(s->type == type_CLIENT) { /* kick off the handshake */ if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "compressed", 7) == 0) { nad_free(nad); /* save interesting bits */ flags = s->flags; if(s->ns != NULL) ns = strdup(s->ns); if(s->req_to != NULL) to = strdup(s->req_to); if(s->req_from != NULL) from = strdup(s->req_from); if(s->req_version != NULL) version = strdup(s->req_version); /* reset state */ _sx_reset(s); _sx_debug(ZONE, "server ready for compression, starting"); /* second time round */ sx_client_init(s, flags | SX_COMPRESS_WRAPPER, ns, to, from, version); /* free bits */ if(ns != NULL) free(ns); if(to != NULL) free(to); if(from != NULL) free(from); if(version != NULL) free(version); return 0; } /* busted server */ if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "failure", 7) == 0) { nad_free(nad); _sx_debug(ZONE, "server can't handle compression, business as usual"); _sx_gen_error(sxe, SX_ERR_COMPRESS_FAILURE, "compress failure", "Server was unable to establish compression"); _sx_event(s, event_ERROR, (void *) &sxe); return 0; } } _sx_debug(ZONE, "unknown compress namespace element '%.*s', dropping packet", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0)); nad_free(nad); return 0; }