/** make a new action route */ static nad_t _sm_build_route(sess_t sess, bres_t res, const char *action, const char *target, const char *id) { nad_t nad; int ns, ans; nad = nad_new(); ns = nad_add_namespace(nad, uri_COMPONENT, NULL); nad_append_elem(nad, ns, "route", 0); nad_append_attr(nad, -1, "to", sess->smcomp?sess->smcomp:((char *) res->jid->domain)); nad_append_attr(nad, -1, "from", sess->c2s->id); ans = nad_add_namespace(nad, uri_SESSION, "sc"); nad_append_elem(nad, ans, "session", 1); if(res->c2s_id[0] != '\0') nad_append_attr(nad, ans, "c2s", res->c2s_id); if(res->sm_id[0] != '\0') nad_append_attr(nad, ans, "sm", res->sm_id); nad_append_attr(nad, -1, "action", action); if(target != NULL) { nad_set_attr(nad, 0, -1, "target", target, NULL); nad_append_attr(nad, -1, "target", target); } if(id != NULL) nad_append_attr(nad, -1, "id", id); log_debug(ZONE, "built new route nad for %s action %s target %s id %s", jid_full(res->jid), action, target, id); return nad; }
/** send a new action route */ void sm_c2s_action(sess_t dest, const char *action, const char *target) { nad_t nad; int rns, sns; nad = nad_new(); rns = nad_add_namespace(nad, uri_COMPONENT, NULL); nad_append_elem(nad, rns, "route", 0); nad_append_attr(nad, -1, "to", dest->c2s); nad_append_attr(nad, -1, "from", dest->user->sm->id); sns = nad_add_namespace(nad, uri_SESSION, "sc"); nad_append_elem(nad, sns, "session", 1); if (dest->c2s_id[0] != '\0') nad_append_attr(nad, sns, "c2s", dest->c2s_id); if (dest->sm_id[0] != '\0') nad_append_attr(nad, sns, "sm", dest->sm_id); nad_append_attr(nad, -1, "action", action); if (target != NULL) nad_append_attr(nad, -1, "target", target); log_debug(ZONE, "routing nad to %s from %s c2s %s s2s %s action %s target %s", dest->c2s, dest->user->sm->id, dest->c2s_id, dest->sm_id, action, target); sx_nad_write(dest->user->sm->router, nad); }
/** register get handler */ static void _authreg_register_get(c2s_t c2s, sess_t sess, nad_t nad) { int attr, ns; char id[128]; /* registrations can happen if reg is enabled and we can create users and set passwords */ if(sess->active || !(sess->host->ar->set_password != NULL && sess->host->ar->create_user != NULL && (sess->host->ar_register_enable || sess->host->ar_register_oob))) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return; } /* extract the id */ attr = nad_find_attr(nad, 0, -1, "id", NULL); if(attr >= 0) snprintf(id, 128, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); nad_free(nad); /* build a result packet */ nad = nad_new(); ns = nad_add_namespace(nad, uri_CLIENT, NULL); nad_append_elem(nad, ns, "iq", 0); nad_append_attr(nad, -1, "type", "result"); if(attr >= 0) nad_append_attr(nad, -1, "id", id); ns = nad_add_namespace(nad, uri_REGISTER, NULL); nad_append_elem(nad, ns, "query", 1); nad_append_elem(nad, ns, "instructions", 2); nad_append_cdata(nad, sess->host->ar_register_instructions, strlen(sess->host->ar_register_instructions), 3); if(sess->host->ar_register_enable) { nad_append_elem(nad, ns, "username", 2); nad_append_elem(nad, ns, "password", 2); } if(sess->host->ar_register_oob) { int ns = nad_add_namespace(nad, uri_OOB, NULL); nad_append_elem(nad, ns, "x", 2); nad_append_elem(nad, ns, "url", 3); nad_append_cdata(nad, sess->host->ar_register_oob, strlen(sess->host->ar_register_oob), 4); } /* give it back to the client */ sx_nad_write(sess->s, nad); }
/** insert a roster item into this pkt, starting at elem */ static void _roster_insert_item(pkt_t pkt, item_t item, int elem) { int ns, i; char *sub; ns = nad_add_namespace(pkt->nad, uri_CLIENT, NULL); elem = nad_insert_elem(pkt->nad, elem, ns, "item", NULL); nad_set_attr(pkt->nad, elem, -1, "jid", jid_full(item->jid), 0); if(item->to && item->from) sub = "both"; else if(item->to) sub = "to"; else if(item->from) sub = "from"; else sub = "none"; nad_set_attr(pkt->nad, elem, -1, "subscription", sub, 0); if(item->ask == 1) nad_set_attr(pkt->nad, elem, -1, "ask", "subscribe", 9); else if(item->ask == 2) nad_set_attr(pkt->nad, elem, -1, "ask", "unsubscribe", 11); if(item->name != NULL) nad_set_attr(pkt->nad, elem, -1, "name", item->name, 0); for(i = 0; i < item->ngroups; i++) nad_insert_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "group", item->groups[i]); }
/** tell a component about all the others */ static void _router_advertise_reverse(const char *key, int keylen, void *val, void *arg) { component_t dest = (component_t) arg; routes_t routes = (routes_t) val; int el, ns, i; nad_t nad; assert((int) (routes->name != NULL)); assert((int) (routes->comp != NULL)); assert(routes->ncomp); /* don't tell me about myself */ for(i = 0; i < routes->ncomp; i++) if(routes->comp[i] == dest) return; log_debug(ZONE, "informing component about %.*s", keylen, key); /* create a new packet */ nad = nad_new(); ns = nad_add_namespace(nad, uri_COMPONENT, NULL); el = nad_append_elem(nad, ns, "presence", 0); nad_set_attr(nad, el, -1, "from", key, keylen); sx_nad_write(dest->s, nad); }
static void _nad_parse_namespace_start(void *arg, const char *prefix, const char *uri) { struct build_data *bd = (struct build_data *) arg; int ns; ns = nad_add_namespace(bd->nad, (char *) uri, (char *) prefix); /* Always set the namespace (to catch cases where nad_add_namespace doesn't add it) */ bd->nad->scope = ns; }
/** utility: generate an abort nad */ static nad_t _sx_sasl_abort(sx_t s) { nad_t nad; int ns; nad = nad_new(); ns = nad_add_namespace(nad, uri_SASL, NULL); nad_append_elem(nad, ns, "abort", 0); return nad; }
/** utility: generate a failure nad */ static nad_t _sx_sasl_failure(sx_t s, const char *err) { nad_t nad; int ns; nad = nad_new(); ns = nad_add_namespace(nad, uri_SASL, NULL); nad_append_elem(nad, ns, "failure", 0); if(err != NULL) nad_append_elem(nad, ns, err, 1); return nad; }
/** utility: generate a response nad */ static nad_t _sx_sasl_response(sx_t s, char *data, int dlen) { nad_t nad; int ns; nad = nad_new(); ns = nad_add_namespace(nad, uri_SASL, NULL); nad_append_elem(nad, ns, "response", 0); if(data != NULL) nad_append_cdata(nad, data, dlen, 1); return nad; }
/** sx features callback */ static void _address_features(sx_t s, sx_plugin_t p, nad_t nad) { int ns; /* offer feature only when not authenticated yet */ if(s->state >= state_OPEN) return; _sx_debug(ZONE, "adding address feature"); ns = nad_add_namespace(nad, uri_ADDRESS_FEATURE, NULL); nad_append_elem(nad, ns, "address", 1); nad_append_cdata(nad, s->ip, strlen(s->ip), 2); }
static void _sx_compress_features(sx_t s, sx_plugin_t p, nad_t nad) { int ns; /* if the session is already compressed, or the app told us not to, * or STARTTLS is required and stream is not encrypted yet, then we don't offer anything */ if(s->compressed || !(s->flags & SX_COMPRESS_OFFER) || ((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0)) return; _sx_debug(ZONE, "offering compression"); ns = nad_add_namespace(nad, uri_COMPRESS_FEATURE, NULL); nad_append_elem(nad, ns, "compression", 1); nad_append_elem(nad, ns, "method", 2); nad_append_cdata(nad, "zlib", 4, 3); }
void sm_packet(sess_t sess, bres_t res, nad_t nad) { int ns; ns = nad_add_namespace(nad, uri_COMPONENT, NULL); nad_wrap_elem(nad, 0, ns, "route"); nad_set_attr(nad, 0, -1, "to", sess->smcomp?sess->smcomp:((char *) res->jid->domain), 0); nad_set_attr(nad, 0, -1, "from", sess->c2s->id, 0); ns = nad_append_namespace(nad, 1, uri_SESSION, "sc"); nad_set_attr(nad, 1, ns, "c2s", res->c2s_id, 0); if(res->c2s_id[0] != '\0') nad_set_attr(nad, 1, ns, "sm", res->sm_id, 0); sx_nad_write(sess->c2s->router, nad); }
/** domain advertisement */ static void _router_advertise(router_t r, const char *domain, component_t src, int unavail) { struct broadcast_st bc; int ns; log_debug(ZONE, "advertising %s to all routes (unavail=%d)", domain, unavail); bc.r = r; bc.src = src; /* create a new packet */ bc.nad = nad_new(); ns = nad_add_namespace(bc.nad, uri_COMPONENT, NULL); nad_append_elem(bc.nad, ns, "presence", 0); nad_append_attr(bc.nad, -1, "from", domain); if(unavail) nad_append_attr(bc.nad, -1, "type", "unavailable"); xhash_walk(r->routes, _router_broadcast, (void *) &bc); nad_free(bc.nad); }
pkt_t amp_build_response_pkt(pkt_t pkt, amp_rule_t rule) { if (!pkt || !rule) return NULL; if (rule->result == AMP_TRIGGERED) { int ns; pkt_t res = pkt_create(pkt->sm, "message", NULL, jid_full(pkt->from), jid_full(pkt->to)); pkt_id(pkt, res); ns = nad_add_namespace(res->nad, uri_AMP, NULL); nad_append_elem(res->nad, ns, "amp", 2); nad_append_attr(res->nad, -1, "status", rule->action); nad_append_attr(res->nad, -1, "from", jid_full(pkt->from)); nad_append_attr(res->nad, -1, "to", jid_full(pkt->to)); nad_append_elem(res->nad, ns, "rule", 3); nad_append_attr(res->nad, -1, "condition", rule->condition); nad_append_attr(res->nad, -1, "value", rule->value); nad_append_attr(res->nad, -1, "action", rule->action); return res; } return NULL; }
/** error the packet */ nad_t stanza_error(nad_t nad, int elem, int err) { int ns; assert((int) (nad != NULL)); assert((int) (elem >= 0)); assert((int) (err >= stanza_err_BAD_REQUEST && err < stanza_err_LAST)); err = err - stanza_err_BAD_REQUEST; nad_set_attr(nad, elem, -1, "type", "error", 5); elem = nad_insert_elem(nad, elem, NAD_ENS(nad, elem), "error", NULL); if(_stanza_errors[err].code != NULL) nad_set_attr(nad, elem, -1, "code", _stanza_errors[err].code, 0); if(_stanza_errors[err].type != NULL) nad_set_attr(nad, elem, -1, "type", _stanza_errors[err].type, 0); if(_stanza_errors[err].name != NULL) { ns = nad_add_namespace(nad, uri_STANZA_ERR, NULL); nad_insert_elem(nad, elem, ns, _stanza_errors[err].name, NULL); } return nad; }
/** they're trying to send us something */ static void _in_packet(conn_t in, nad_t nad) { int attr, ns, sns; jid_t from, to; char *rkey; attr = nad_find_attr(nad, 0, -1, "from", NULL); if(attr < 0 || (from = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "missing or invalid from on incoming packet"); nad_free(nad); return; } attr = nad_find_attr(nad, 0, -1, "to", NULL); if(attr < 0 || (to = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "missing or invalid to on incoming packet"); jid_free(from); nad_free(nad); return; } rkey = s2s_route_key(NULL, to->domain, from->domain); log_debug(ZONE, "received packet from %s for %s", in->key, rkey); /* drop packets received on routes not valid on that connection as per XMPP 8.3.10 */ if((conn_state_t) xhash_get(in->states, rkey) != conn_VALID) { log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] dropping packet on unvalidated route: '%s'", in->fd->fd, in->ip, in->port, rkey); free(rkey); nad_free(nad); jid_free(from); jid_free(to); return; } free(rkey); /* its good, off to the router with it */ log_debug(ZONE, "incoming packet on valid route, preparing it for the router"); /* rewrite server packets into client packets */ ns = nad_find_namespace(nad, 0, uri_SERVER, 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; } } /* * If stanza is not in any namespace (either because we removed the * jabber:server namespace above or because it's in the default * namespace for this stream) then this packet is intended to be * handled by sm (and not just routed through the server), so set the * jabber:client namespace. */ if(ns >= 0 || nad->elems[0].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 = nad->elems[0].ns; /* wrap up the packet */ ns = nad_add_namespace(nad, uri_COMPONENT, "comp"); nad_wrap_elem(nad, 0, ns, "route"); nad_set_attr(nad, 0, -1, "to", to->domain, 0); nad_set_attr(nad, 0, -1, "from", in->s2s->id, 0); /* route is from s2s, not packet source */ log_debug(ZONE, "sending packet to %s", to->domain); /* go */ sx_nad_write(in->s2s->router, nad); jid_free(from); jid_free(to); }
static nad_t _pbx_presence_nad(int available, char *cmd) { nad_t nad; int ns; char *show = NULL; nad = nad_new(); ns = nad_add_namespace(nad, uri_CLIENT, NULL); nad_append_elem(nad, ns, "presence", 0); if(!available) { nad_append_attr(nad, -1, "type", "unavailable"); } else { char *cont; long int priority; char prioritystr[5]; // -128 to +127 + \0 priority = strtol(cmd, &cont, 10); log_debug(ZONE, "Read %ld priority", priority); if(cmd == cont) priority = -1; // use -1 priority if not given if(priority < -128) priority = -128; if(priority > 127) priority = 127; nad_append_elem(nad, -1, "priority", 1); snprintf(prioritystr, 5, "%ld", priority); nad_append_cdata(nad, prioritystr, strlen(prioritystr), 2); if(cmd != cont) { cmd = cont; while(*cmd == ' ') { cmd++; } } if(!strncmp("CHAT", cmd, 4)) { cmd += 4; show = "chat"; } if(!strncmp("ONLINE", cmd, 6)) { cmd += 6; } if(!strncmp("DND", cmd, 3)) { cmd += 3; show = "dnd"; } if(!strncmp("AWAY", cmd, 4)) { cmd += 4; show = "away"; } if(!strncmp("XA", cmd, 2)) { cmd += 2; show = "xa"; } if(show) { nad_append_elem(nad, -1, "show", 1); nad_append_cdata(nad, show, strlen(show), 2); } } while(*cmd == ' ') { cmd++; } if(*cmd != '\0' && *cmd != '\n') { int len = strlen(cmd); nad_append_elem(nad, -1, "status", 1); nad_append_cdata(nad, cmd, len - (cmd[len-1] == '\n' ? 1 : 0), 2); } return nad; }
/** auth get handler */ static void _authreg_auth_get(c2s_t c2s, sess_t sess, nad_t nad) { int ns, elem, attr; char username[1024], id[128]; int ar_mechs; /* can't auth if they're active */ if(sess->active) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return; } /* sort out the username */ ns = nad_find_scoped_namespace(nad, uri_AUTH, NULL); elem = nad_find_elem(nad, 1, ns, "username", 1); if(elem < 0) { log_debug(ZONE, "auth get with no username, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return; } snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem)); if(stringprep_xmpp_nodeprep(username, 1024) != 0) { log_debug(ZONE, "auth get username failed nodeprep, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_JID_MALFORMED), 0)); return; } ar_mechs = c2s->ar_mechanisms; if (sess->s->ssf>0) ar_mechs = ar_mechs | c2s->ar_ssl_mechanisms; /* no point going on if we have no mechanisms */ if(!(ar_mechs & (AR_MECH_TRAD_PLAIN | AR_MECH_TRAD_DIGEST))) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_FORBIDDEN), 0)); return; } /* do we have the user? */ if((c2s->ar->user_exists)(c2s->ar, username, sess->host->realm) == 0) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_OLD_UNAUTH), 0)); return; } /* extract the id */ attr = nad_find_attr(nad, 0, -1, "id", NULL); if(attr >= 0) snprintf(id, 128, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); nad_free(nad); /* build a result packet */ nad = nad_new(); ns = nad_add_namespace(nad, uri_CLIENT, NULL); nad_append_elem(nad, ns, "iq", 0); nad_append_attr(nad, -1, "type", "result"); if(attr >= 0) nad_append_attr(nad, -1, "id", id); ns = nad_add_namespace(nad, uri_AUTH, NULL); nad_append_elem(nad, ns, "query", 1); nad_append_elem(nad, ns, "username", 2); nad_append_cdata(nad, username, strlen(username), 3); nad_append_elem(nad, ns, "resource", 2); /* fill out the packet with available auth mechanisms */ if(ar_mechs & AR_MECH_TRAD_PLAIN && (c2s->ar->get_password != NULL || c2s->ar->check_password != NULL)) nad_append_elem(nad, ns, "password", 2); if(ar_mechs & AR_MECH_TRAD_DIGEST && c2s->ar->get_password != NULL) nad_append_elem(nad, ns, "digest", 2); /* give it back to the client */ sx_nad_write(sess->s, nad); return; }
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; }
/** auth set handler */ static void _authreg_auth_set(c2s_t c2s, sess_t sess, nad_t nad) { int ns, elem, attr, authd = 0; char username[1024], resource[1024], str[1024], hash[280]; int ar_mechs; /* can't auth if they're active */ if(sess->active) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return; } ns = nad_find_scoped_namespace(nad, uri_AUTH, NULL); /* sort out the username */ elem = nad_find_elem(nad, 1, ns, "username", 1); if(elem < 0) { log_debug(ZONE, "auth set with no username, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return; } snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem)); if(stringprep_xmpp_nodeprep(username, 1024) != 0) { log_debug(ZONE, "auth set username failed nodeprep, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_JID_MALFORMED), 0)); return; } /* make sure we have the resource */ elem = nad_find_elem(nad, 1, ns, "resource", 1); if(elem < 0) { log_debug(ZONE, "auth set with no resource, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return; } snprintf(resource, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem)); if(stringprep_xmpp_resourceprep(resource, 1024) != 0) { log_debug(ZONE, "auth set resource failed resourceprep, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_JID_MALFORMED), 0)); return; } ar_mechs = c2s->ar_mechanisms; if (sess->s->ssf > 0) ar_mechs = ar_mechs | c2s->ar_ssl_mechanisms; /* no point going on if we have no mechanisms */ if(!(ar_mechs & (AR_MECH_TRAD_PLAIN | AR_MECH_TRAD_DIGEST))) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_FORBIDDEN), 0)); return; } /* do we have the user? */ if((c2s->ar->user_exists)(c2s->ar, username, sess->host->realm) == 0) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_OLD_UNAUTH), 0)); return; } /* digest auth */ if(!authd && ar_mechs & AR_MECH_TRAD_DIGEST && c2s->ar->get_password != NULL) { elem = nad_find_elem(nad, 1, ns, "digest", 1); if(elem >= 0) { if((c2s->ar->get_password)(c2s->ar, username, sess->host->realm, str) == 0) { snprintf(hash, 280, "%s%s", sess->s->id, str); shahash_r(hash, hash); if(strlen(hash) == NAD_CDATA_L(nad, elem) && strncmp(hash, NAD_CDATA(nad, elem), NAD_CDATA_L(nad, elem)) == 0) { log_debug(ZONE, "digest auth succeeded"); authd = 1; _authreg_auth_log(c2s, sess, "traditional.digest", username, resource, TRUE); } } } } /* plaintext auth (compare) */ if(!authd && ar_mechs & AR_MECH_TRAD_PLAIN && c2s->ar->get_password != NULL) { elem = nad_find_elem(nad, 1, ns, "password", 1); if(elem >= 0) { if((c2s->ar->get_password)(c2s->ar, username, sess->host->realm, str) == 0 && strlen(str) == NAD_CDATA_L(nad, elem) && strncmp(str, NAD_CDATA(nad, elem), NAD_CDATA_L(nad, elem)) == 0) { log_debug(ZONE, "plaintext auth (compare) succeeded"); authd = 1; _authreg_auth_log(c2s, sess, "traditional.plain(compare)", username, resource, TRUE); } } } /* plaintext auth (check) */ if(!authd && ar_mechs & AR_MECH_TRAD_PLAIN && c2s->ar->check_password != NULL) { elem = nad_find_elem(nad, 1, ns, "password", 1); if(elem >= 0) { snprintf(str, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem)); if((c2s->ar->check_password)(c2s->ar, username, sess->host->realm, str) == 0) { log_debug(ZONE, "plaintext auth (check) succeded"); authd = 1; _authreg_auth_log(c2s, sess, "traditional.plain", username, resource, TRUE); } } } /* now, are they authenticated? */ if(authd) { /* create new bound jid holder */ if(sess->resources == NULL) { sess->resources = (bres_t) calloc(1, sizeof(struct bres_st)); } /* our local id */ sprintf(sess->resources->c2s_id, "%d", sess->s->tag); /* the full user jid for this session */ sess->resources->jid = jid_new(sess->s->req_to, -1); jid_reset_components(sess->resources->jid, username, sess->resources->jid->domain, resource); log_write(sess->c2s->log, LOG_NOTICE, "[%d] requesting session: jid=%s", sess->s->tag, jid_full(sess->resources->jid)); /* build a result packet, we'll send this back to the client after we have a session for them */ sess->result = nad_new(); ns = nad_add_namespace(sess->result, uri_CLIENT, NULL); nad_append_elem(sess->result, ns, "iq", 0); nad_set_attr(sess->result, 0, -1, "type", "result", 6); attr = nad_find_attr(nad, 0, -1, "id", NULL); if(attr >= 0) nad_set_attr(sess->result, 0, -1, "id", NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr)); /* start a session with the sm */ sm_start(sess, sess->resources); /* finished with the nad */ nad_free(nad); return; } _authreg_auth_log(c2s, sess, "traditional", username, resource, FALSE); /* auth failed, so error */ sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_OLD_UNAUTH), 0)); return; }
/** register set handler */ static void _authreg_register_set(c2s_t c2s, sess_t sess, nad_t nad) { int ns = 0, elem, attr; char username[1024], password[1024]; /* if we're not configured for registration (or pw changes), or we can't set passwords, fail outright */ if(!(sess->host->ar_register_enable || sess->host->ar_register_password) || c2s->ar->set_password == NULL) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return; } ns = nad_find_scoped_namespace(nad, uri_REGISTER, NULL); /* removals */ if(sess->active && nad_find_elem(nad, 1, ns, "remove", 1) >= 0) { /* only if full reg is enabled */ if(!sess->host->ar_register_enable) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return; } log_debug(ZONE, "user remove requested"); /* make sure we can delete them */ if(c2s->ar->delete_user == NULL) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return; } /* otherwise, delete them */ if((c2s->ar->delete_user)(c2s->ar, sess->resources->jid->node, sess->host->realm) != 0) { log_debug(ZONE, "user delete failed"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_INTERNAL_SERVER_ERROR), 0)); return; } log_write(c2s->log, LOG_NOTICE, "[%d] deleted user: user=%s; realm=%s", sess->s->tag, sess->resources->jid->node, sess->host->realm); log_write(c2s->log, LOG_NOTICE, "[%d] registration remove succeeded, requesting user deletion: jid=%s", sess->s->tag, jid_user(sess->resources->jid)); /* make a result nad */ sess->result = nad_new(); ns = nad_add_namespace(sess->result, uri_CLIENT, NULL); nad_append_elem(sess->result, ns, "iq", 0); nad_set_attr(sess->result, 0, -1, "type", "result", 6); /* extract the id */ attr = nad_find_attr(nad, 0, -1, "id", NULL); if(attr >= 0) nad_set_attr(sess->result, 0, -1, "id", NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr)); nad_free(nad); sx_nad_write(sess->s, sess->result); sess->result = NULL; /* get the sm to delete them (it will force their sessions to end) */ sm_delete(sess, sess->resources); return; } /* username is required */ elem = nad_find_elem(nad, 1, ns, "username", 1); if(elem < 0) { log_debug(ZONE, "register set with no username, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return; } snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem)); if(stringprep_xmpp_nodeprep(username, 1024) != 0) { log_debug(ZONE, "register set username failed nodeprep, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_JID_MALFORMED), 0)); return; } elem = nad_find_elem(nad, 1, ns, "password", 1); if(elem < 0) { log_debug(ZONE, "register set with no password, bouncing it"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return; } /* if they're already auth'd, its a password change */ if(sess->active) { /* confirm that the username matches their auth id */ if(strcmp(username, sess->resources->jid->node) != 0) { log_debug(ZONE, "%s is trying to change password for %s, bouncing it", jid_full(sess->resources->jid), username); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_OLD_UNAUTH), 0)); return; } } /* can't go on if we're not doing full reg */ else if(!sess->host->ar_register_enable) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return; } /* if they exist, bounce */ else if((c2s->ar->user_exists)(c2s->ar, username, sess->host->realm)) { log_debug(ZONE, "attempt to register %s, but they already exist", username); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_CONFLICT), 0)); return; } /* make sure we can create them */ else if(c2s->ar->create_user == NULL) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return; } /* otherwise, create them */ else if((c2s->ar->create_user)(c2s->ar, username, sess->host->realm) != 0) { log_debug(ZONE, "user create failed"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_INTERNAL_SERVER_ERROR), 0)); return; } else log_write(c2s->log, LOG_NOTICE, "[%d] created user: user=%s; realm=%s", sess->s->tag, username, sess->host->realm); /* extract the password */ snprintf(password, 257, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem)); /* change it */ if((c2s->ar->set_password)(c2s->ar, username, sess->host->realm, password) != 0) { log_debug(ZONE, "password store failed"); sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_INTERNAL_SERVER_ERROR), 0)); return; } log_debug(ZONE, "updated auth creds for %s", username); /* make a result nad */ sess->result = nad_new(); ns = nad_add_namespace(sess->result, uri_CLIENT, NULL); nad_append_elem(sess->result, ns, "iq", 0); nad_set_attr(sess->result, 0, -1, "type", "result", 6); /* extract the id */ attr = nad_find_attr(nad, 0, -1, "id", NULL); if(attr >= 0) nad_set_attr(sess->result, 0, -1, "id", NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr)); /* if they're active, then this was just a password change, and we're done */ if(sess->active) { log_write(c2s->log, LOG_NOTICE, "[%d] password changed: jid=%s", sess->s->tag, jid_user(sess->resources->jid)); sx_nad_write(sess->s, sess->result); sess->result = NULL; return; } /* create new bound jid holder */ if(sess->resources == NULL) { sess->resources = (bres_t) calloc(1, sizeof(struct bres_st)); } /* our local id */ sprintf(sess->resources->c2s_id, "%d", sess->s->tag); /* the user jid for this transaction */ sess->resources->jid = jid_new(sess->s->req_to, -1); jid_reset_components(sess->resources->jid, username, sess->resources->jid->domain, sess->resources->jid->resource); log_write(c2s->log, LOG_NOTICE, "[%d] registration succeeded, requesting user creation: jid=%s", sess->s->tag, jid_user(sess->resources->jid)); /* get the sm to create them */ sm_create(sess, sess->resources); nad_free(nad); return; }
static void _nad_parse_element_start(void *arg, const char *name, const char **atts) { struct build_data *bd = (struct build_data *) arg; char buf[1024]; char *uri, *elem, *prefix; const char **attr; int el, ns; /* make a copy */ strncpy(buf, name, 1024); buf[1023] = '\0'; /* expat gives us: prefixed namespaced elem: uri|elem|prefix default namespaced elem: uri|elem un-namespaced elem: elem */ /* extract all the bits */ uri = buf; elem = strchr(uri, '|'); if(elem != NULL) { *elem = '\0'; elem++; prefix = strchr(elem, '|'); if(prefix != NULL) { *prefix = '\0'; prefix++; } ns = nad_add_namespace(bd->nad, uri, prefix); } else { /* un-namespaced, just take it as-is */ uri = NULL; elem = buf; prefix = NULL; ns = -1; } /* add it */ el = nad_append_elem(bd->nad, ns, elem, bd->depth); /* now the attributes, one at a time */ attr = atts; while(attr[0] != NULL) { /* make a copy */ strncpy(buf, attr[0], 1024); buf[1023] = '\0'; /* extract all the bits */ uri = buf; elem = strchr(uri, '|'); if(elem != NULL) { *elem = '\0'; elem++; prefix = strchr(elem, '|'); if(prefix != NULL) { *prefix = '\0'; prefix++; } ns = nad_append_namespace(bd->nad, el, uri, prefix); } else { /* un-namespaced, just take it as-is */ uri = NULL; elem = buf; prefix = NULL; ns = -1; } /* add it */ nad_append_attr(bd->nad, ns, elem, (char *) attr[1]); attr += 2; } bd->depth++; }
/** insert part of a nad into another nad */ int nad_insert_nad(nad_t dest, int delem, nad_t src, int selem) { int nelem, first, i, j, ns, nattr, attr; char buri[256], *uri = buri, bprefix[256], *prefix = bprefix; _nad_ptr_check(__func__, dest); _nad_ptr_check(__func__, src); /* can't do anything if these aren't real elems */ if(src->ecur <= selem || dest->ecur <= delem) return -1; /* figure out how many elements to copy */ nelem = 1; while(selem + nelem < src->ecur && src->elems[selem + nelem].depth > src->elems[selem].depth) nelem++; /* make room */ NAD_SAFE(dest->elems, (dest->ecur + nelem) * sizeof(struct nad_elem_st), dest->elen); /* relocate all the elems after us */ memmove(&dest->elems[delem + nelem + 1], &dest->elems[delem + 1], (dest->ecur - delem - 1) * sizeof(struct nad_elem_st)); dest->ecur += nelem; /* relink parents on moved elements */ for(i = delem + nelem; i < dest->ecur; i++) if(dest->elems[i].parent > delem) dest->elems[i].parent += nelem; first = delem + 1; /* copy them in, one at a time */ for(i = 0; i < nelem; i++) { /* link the parent */ dest->elems[first + i].parent = delem + (src->elems[selem + i].parent - src->elems[selem].parent); /* depth */ dest->elems[first + i].depth = dest->elems[delem].depth + (src->elems[selem + i].depth - src->elems[selem].depth) + 1; /* name */ dest->elems[first + i].lname = src->elems[selem + i].lname; dest->elems[first + i].iname = _nad_cdata(dest, src->cdata + src->elems[selem + i].iname, src->elems[selem + i].lname); /* cdata */ dest->elems[first + i].lcdata = src->elems[selem + i].lcdata; dest->elems[first + i].icdata = _nad_cdata(dest, src->cdata + src->elems[selem + i].icdata, src->elems[selem + i].lcdata); dest->elems[first + i].ltail = src->elems[selem + i].ltail; dest->elems[first + i].itail = _nad_cdata(dest, src->cdata + src->elems[selem + i].itail, src->elems[selem + i].ltail); /* namespaces */ dest->elems[first + i].my_ns = dest->elems[first + i].ns = dest->scope = -1; /* first, the element namespace */ ns = src->elems[selem + i].my_ns; if(ns >= 0) { for(j = 0; j < dest->ncur; j++) if(NAD_NURI_L(src, ns) == NAD_NURI_L(dest, j) && strncmp(NAD_NURI(src, ns), NAD_NURI(dest, j), NAD_NURI_L(src, ns)) == 0) { dest->elems[first + i].my_ns = j; break; } /* not found, gotta add it */ if(j == dest->ncur) { /* make room */ /* !!! this can go once we have _ex() functions */ if(NAD_NURI_L(src, ns) > 255) uri = (char *) malloc(sizeof(char) * (NAD_NURI_L(src, ns) + 1)); if(NAD_NPREFIX_L(src, ns) > 255) prefix = (char *) malloc(sizeof(char) * (NAD_NURI_L(src, ns) + 1)); sprintf(uri, "%.*s", NAD_NURI_L(src, ns), NAD_NURI(src, ns)); if(NAD_NPREFIX_L(src, ns) > 0) { sprintf(prefix, "%.*s", NAD_NPREFIX_L(src, ns), NAD_NPREFIX(src, ns)); dest->elems[first + i].my_ns = nad_add_namespace(dest, uri, prefix); } else dest->elems[first + i].my_ns = nad_add_namespace(dest, uri, NULL); if(uri != buri) free(uri); if(prefix != bprefix) free(prefix); } } /* then, any declared namespaces */ for(ns = src->elems[selem + i].ns; ns >= 0; ns = src->nss[ns].next) { for(j = 0; j < dest->ncur; j++) if(NAD_NURI_L(src, ns) == NAD_NURI_L(dest, j) && strncmp(NAD_NURI(src, ns), NAD_NURI(dest, j), NAD_NURI_L(src, ns)) == 0) break; /* not found, gotta add it */ if(j == dest->ncur) { /* make room */ /* !!! this can go once we have _ex() functions */ if(NAD_NURI_L(src, ns) > 255) uri = (char *) malloc(sizeof(char) * (NAD_NURI_L(src, ns) + 1)); if(NAD_NPREFIX_L(src, ns) > 255) prefix = (char *) malloc(sizeof(char) * (NAD_NURI_L(src, ns) + 1)); sprintf(uri, "%.*s", NAD_NURI_L(src, ns), NAD_NURI(src, ns)); if(NAD_NPREFIX_L(src, ns) > 0) { sprintf(prefix, "%.*s", NAD_NPREFIX_L(src, ns), NAD_NPREFIX(src, ns)); nad_add_namespace(dest, uri, prefix); } else nad_add_namespace(dest, uri, NULL); if(uri != buri) free(uri); if(prefix != bprefix) free(prefix); } } /* scope any new namespaces onto this element */ dest->elems[first + i].ns = dest->scope; dest->scope = -1; /* attributes */ dest->elems[first + i].attr = -1; if(src->acur > 0) { nattr = 0; for(attr = src->elems[selem + i].attr; attr >= 0; attr = src->attrs[attr].next) nattr++; /* make room */ NAD_SAFE(dest->attrs, (dest->acur + nattr) * sizeof(struct nad_attr_st), dest->alen); /* kopy ker-azy! */ for(attr = src->elems[selem + i].attr; attr >= 0; attr = src->attrs[attr].next) { /* name */ dest->attrs[dest->acur].lname = src->attrs[attr].lname; dest->attrs[dest->acur].iname = _nad_cdata(dest, src->cdata + src->attrs[attr].iname, src->attrs[attr].lname); /* val */ dest->attrs[dest->acur].lval = src->attrs[attr].lval; dest->attrs[dest->acur].ival = _nad_cdata(dest, src->cdata + src->attrs[attr].ival, src->attrs[attr].lval); /* namespace */ dest->attrs[dest->acur].my_ns = -1; ns = src->attrs[attr].my_ns; if(ns >= 0) for(j = 0; j < dest->ncur; j++) if(NAD_NURI_L(src, ns) == NAD_NURI_L(dest, j) && strncmp(NAD_NURI(src, ns), NAD_NURI(dest, j), NAD_NURI_L(src, ns)) == 0) { dest->attrs[dest->acur].my_ns = j; break; } /* link it up */ dest->attrs[dest->acur].next = dest->elems[first + i].attr; dest->elems[first + i].attr = dest->acur; dest->acur++; } } } return first; }
/** Send archiving preferences */ void send_arch_prefs(mod_instance_t mi, sess_t sess, pkt_t pkt) { module_t mod = mi->mod; int prefs; int archive=default_mode; int section; int check; char buff[2060]; // 2048 is jid_user maximum length char* ret_str=buff; os_t os = NULL; os_object_t o = NULL; pkt_t reply; // Create a new packet log_debug(ZONE, "Construction of reply with current settings"); reply = pkt_create(sess->user->sm, "iq", "result", NULL, NULL); // Defaults log_debug(ZONE, "Getting defaults"); prefs = nad_append_elem(reply->nad, nad_add_namespace(reply->nad, archive_uri, NULL), "prefs", 2); // Load defaults snprintf(buff, 2060, "(jid=%s)", jid_user(sess->jid)); if((storage_get(sess->user->sm->st, tbl_name "_settings", jid_user(sess->jid), buff, &os) == st_SUCCESS) && (os_iter_first(os)) && ((o=os_iter_object(os))!=NULL)) os_object_get_int(os, o, "setting", &archive); // Set defaults switch(archive) { case A_ALWAYS: nad_set_attr(reply->nad, prefs, -1,"default", "always", 6); break; case A_ROSTER: nad_set_attr(reply->nad, prefs, -1,"default", "roster", 6); break; default: nad_set_attr(reply->nad, prefs, -1,"default", "never", 5); break; } // Cleanup if(o != NULL) { os_object_free(o); o=NULL; } if(os != NULL) { os_free(os); os=NULL; } // Always archiving log_debug(ZONE, "Getting what to archive always"); section = nad_append_elem(reply->nad, -1, "always", 3); if(storage_get(sess->user->sm->st, tbl_name "_settings", jid_user(sess->jid), "(setting=1)", &os) == st_SUCCESS) if(os_iter_first(os)) do { o = os_iter_object(os); if((o != NULL) && (os_object_get_str(os, o, "jid", &ret_str)) && (ret_str != NULL)) { nad_append_elem(reply->nad, -1, "jid", 4); nad_append_cdata(reply->nad, ret_str, strlen(ret_str), 5); } } while(os_iter_next(os)); // Cleanup if(o != NULL) { os_object_free(o); o=NULL; } if(os != NULL) { os_free(os); os=NULL; } // Never archiving log_debug(ZONE, "Getting what to never archive"); section = nad_append_elem(reply->nad, -1, "never", 3); if(storage_get(sess->user->sm->st, tbl_name "_settings", jid_user(sess->jid), "(setting=0)", &os) == st_SUCCESS) if(os_iter_first(os)) do { o = os_iter_object(os); if((o != NULL) && (os_object_get_str(os, o, "jid", &ret_str)) && (ret_str != NULL)) { nad_append_elem(reply->nad, -1, "jid", 4); nad_append_cdata(reply->nad, ret_str, strlen(ret_str), 5); } } while(os_iter_next(os)); // Cleanup if(o != NULL) { os_object_free(o); o=NULL; } if(os != NULL) { os_free(os); os=NULL; } // Send packet pkt_id(pkt, reply); pkt_sess(reply, sess); pkt_free(pkt); }
/** auth requests */ static void _in_result(conn_t in, nad_t nad) { int attr, ns; jid_t from, to; char *rkey; nad_t verify; pkt_t pkt; time_t now; attr = nad_find_attr(nad, 0, -1, "from", NULL); if(attr < 0 || (from = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "missing or invalid from on db result packet"); nad_free(nad); return; } attr = nad_find_attr(nad, 0, -1, "to", NULL); if(attr < 0 || (to = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "missing or invalid to on db result packet"); jid_free(from); nad_free(nad); return; } rkey = s2s_route_key(NULL, to->domain, from->domain); log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] received dialback auth request for route '%s'", in->fd->fd, in->ip, in->port, rkey); /* get current state */ if((conn_state_t) xhash_get(in->states, rkey) == conn_VALID) { log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] route '%s' is already valid: sending valid", in->fd->fd, in->ip, in->port, rkey); /* its already valid, just reply right now */ stanza_tofrom(nad, 0); nad_set_attr(nad, 0, -1, "type", "valid", 5); nad->elems[0].icdata = nad->elems[0].itail = -1; nad->elems[0].lcdata = nad->elems[0].ltail = 0; sx_nad_write(in->s, nad); free(rkey); jid_free(from); jid_free(to); return; } /* not valid, so we need to verify */ /* need the key */ if(NAD_CDATA_L(nad, 0) <= 0) { log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] no dialback key given with db result packet", in->fd->fd, in->ip, in->port, rkey); free(rkey); nad_free(nad); jid_free(from); jid_free(to); return; } log_debug(ZONE, "requesting verification for route %s", rkey); /* set the route status to INPROGRESS and set timestamp */ xhash_put(in->states, pstrdup(xhash_pool(in->states), rkey), (void *) conn_INPROGRESS); /* record the time that we set conn_INPROGRESS state */ now = time(NULL); xhash_put(in->states_time, pstrdup(xhash_pool(in->states_time), rkey), (void *) now); free(rkey); /* new packet */ verify = nad_new(); ns = nad_add_namespace(verify, uri_DIALBACK, "db"); nad_append_elem(verify, ns, "verify", 0); nad_append_attr(verify, -1, "to", from->domain); nad_append_attr(verify, -1, "from", to->domain); nad_append_attr(verify, -1, "id", in->s->id); nad_append_cdata(verify, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0), 1); /* new packet */ pkt = (pkt_t) calloc(1, sizeof(struct pkt_st)); pkt->nad = verify; pkt->to = from; pkt->from = to; pkt->db = 1; /* its away */ out_packet(in->s2s, pkt); nad_free(nad); }
/** 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; }