/** 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; }
xht aci_load(router_t r) { xht aci; int aelem, uelem, attr; char type[33]; aci_user_t list_head, list_tail, user; log_debug(ZONE, "loading aci"); aci = xhash_new(51); if((aelem = nad_find_elem(r->config->nad, 0, -1, "aci", 1)) < 0) return aci; aelem = nad_find_elem(r->config->nad, aelem, -1, "acl", 1); while(aelem >= 0) { if((attr = nad_find_attr(r->config->nad, aelem, -1, "type", NULL)) < 0) { aelem = nad_find_elem(r->config->nad, aelem, -1, "acl", 0); continue; } list_head = NULL; list_tail = NULL; snprintf(type, 33, "%.*s", NAD_AVAL_L(r->config->nad, attr), NAD_AVAL(r->config->nad, attr)); log_debug(ZONE, "building list for '%s'", type); uelem = nad_find_elem(r->config->nad, aelem, -1, "user", 1); while(uelem >= 0) { if(NAD_CDATA_L(r->config->nad, uelem) > 0) { user = (aci_user_t) calloc(1, sizeof(struct aci_user_st)); user->name = (char *) malloc(sizeof(char) * (NAD_CDATA_L(r->config->nad, uelem) + 1)); sprintf(user->name, "%.*s", NAD_CDATA_L(r->config->nad, uelem), NAD_CDATA(r->config->nad, uelem)); if(list_tail != NULL) { list_tail->next = user; list_tail = user; } /* record the head of the list */ if(list_head == NULL) { list_head = user; list_tail = user; } log_debug(ZONE, "added '%s'", user->name); } uelem = nad_find_elem(r->config->nad, uelem, -1, "user", 0); } if(list_head != NULL) xhash_put(aci, pstrdup(xhash_pool(aci), type), (void *) list_head); aelem = nad_find_elem(r->config->nad, aelem, -1, "acl", 0); } return aci; }
static void _router_comp_write(component_t comp, nad_t nad) { int attr; if(comp->tq != NULL) { log_debug(ZONE, "%s port %d is throttled, jqueueing packet", comp->ip, comp->port); jqueue_push(comp->tq, nad, 0); return; } /* packets go raw to normal components */ if(!comp->legacy) { sx_nad_write(comp->s, nad); return; } log_debug(ZONE, "packet for legacy component, munging"); attr = nad_find_attr(nad, 0, -1, "error", NULL); if(attr >= 0) { if(NAD_AVAL_L(nad, attr) == 3 && strncmp("400", NAD_AVAL(nad, attr), 3) == 0) stanza_error(nad, 1, stanza_err_BAD_REQUEST); else stanza_error(nad, 1, stanza_err_SERVICE_UNAVAILABLE); } sx_nad_write_elem(comp->s, nad, 1); }
xht aci_load(sm_t sm) { xht acls; int aelem, jelem, attr; char type[33]; jid_t list, jid; log_debug(ZONE, "loading aci"); acls = xhash_new(51); if((aelem = nad_find_elem(sm->config->nad, 0, -1, "aci", 1)) < 0) return acls; aelem = nad_find_elem(sm->config->nad, aelem, -1, "acl", 1); while(aelem >= 0) { list = NULL; if((attr = nad_find_attr(sm->config->nad, aelem, -1, "type", NULL)) < 0) { aelem = nad_find_elem(sm->config->nad, aelem, -1, "acl", 0); continue; } snprintf(type, 33, "%.*s", NAD_AVAL_L(sm->config->nad, attr), NAD_AVAL(sm->config->nad, attr)); log_debug(ZONE, "building list for '%s'", type); jelem = nad_find_elem(sm->config->nad, aelem, -1, "jid", 1); while(jelem >= 0) { if(NAD_CDATA_L(sm->config->nad, jelem) > 0) { jid = jid_new(NAD_CDATA(sm->config->nad, jelem), NAD_CDATA_L(sm->config->nad, jelem)); list = jid_append(list, jid); log_debug(ZONE, "added '%s'", jid_user(jid)); jid_free(jid); } jelem = nad_find_elem(sm->config->nad, jelem, -1, "jid", 0); } if(list != NULL) { xhash_put(acls, pstrdup(xhash_pool(acls), type), (void *) list); } aelem = nad_find_elem(sm->config->nad, aelem, -1, "acl", 0); } return acls; }
int nad_get_attrval(nad_t nad, int elem, int ns, const char *name, char* buff, int len) { int xmlindex; if((xmlindex = nad_find_attr(nad, elem, ns, name, NULL)) < 0) { buff[0] = 0; return -1; } if(NAD_AVAL_L(nad, xmlindex) == 0 || NAD_AVAL_L(nad, xmlindex) >= len) { strncpy(buff, NAD_AVAL(nad, xmlindex), len); buff[len -1] = 0; return -1; } strncpy(buff, NAD_AVAL(nad, xmlindex), NAD_AVAL_L(nad, xmlindex)); buff[NAD_AVAL_L(nad, xmlindex)] = 0; return 0; }
/** flip the to and from attributes on this elem */ nad_t stanza_tofrom(nad_t nad, int elem) { int attr; char to[3072], from[3072]; assert((int) (nad != NULL)); to[0] = '\0'; from[0] = '\0'; attr = nad_find_attr(nad, elem, -1, "to", NULL); if(attr >= 0) snprintf(to, 3072, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); attr = nad_find_attr(nad, elem, -1, "from", NULL); if(attr >= 0) snprintf(from, 3072, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); nad_set_attr(nad, elem, -1, "to", from[0] != '\0' ? from : NULL, 0); nad_set_attr(nad, elem, -1, "from", to[0] != '\0' ? to : NULL, 0); return 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); }
int filter_packet(router_t r, nad_t nad) { acl_t acl; int ato, afrom, error = 0; unsigned char *cur, *to = NULL, *from = NULL; ato = nad_find_attr(nad, 1, -1, "to", NULL); afrom = nad_find_attr(nad, 1, -1, "from", NULL); if(ato >= 0 && NAD_AVAL_L(nad,ato) > 0) { to = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, ato) + 1)); sprintf(to, "%.*s", NAD_AVAL_L(nad, ato), NAD_AVAL(nad, ato)); cur = strstr(to, "@"); /* skip node part */ if(cur != NULL) cur = strstr(cur, "/"); else cur = strstr(to, "/"); if(cur != NULL) *cur = '\0'; /* remove the resource part */ } if(afrom >= 0 && NAD_AVAL_L(nad,afrom) > 0) { from = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, afrom) + 1)); sprintf(from, "%.*s", NAD_AVAL_L(nad, afrom), NAD_AVAL(nad, afrom)); cur = strstr(from, "@"); if(cur != NULL) cur = strstr(cur, "/"); else cur = strstr(from, "/"); if(cur != NULL) *cur = '\0'; } for(acl = r->filter; acl != NULL; acl = acl->next) { if( from == NULL && acl->from != NULL) continue; /* no match if NULL matched vs not-NULL */ if( to == NULL && acl->to != NULL ) continue; if( from != NULL && acl->from == NULL) continue; /* no match if not-NULL matched vs NULL */ if( to != NULL && acl->to == NULL ) continue; if( from != NULL && acl->from != NULL && fnmatch(acl->from, from, 0) != 0 ) continue; /* do filename-like match */ if( to != NULL && acl->to != NULL && fnmatch(acl->to, to, 0) != 0 ) continue; if( acl->what != NULL && nad_find_elem_path(nad, 0, -1, acl->what) < 0 ) continue; /* match packet type */ log_debug(ZONE, "matched packet %s->%s vs rule (%s %s->%s)", from, to, acl->what, acl->from, acl->to); if (acl->log) { if (acl->redirect) log_write(r->log, LOG_NOTICE, "filter: redirect packet from=%s to=%s - rule (from=%s to=%s what=%s), new to=%s", from, to, acl->from, acl->to, acl->what, acl->redirect); else log_write(r->log, LOG_NOTICE, "filter: %s packet from=%s to=%s - rule (from=%s to=%s what=%s)",(acl->error?"deny":"allow"), from, to, acl->from, acl->to, acl->what); } if (acl->redirect) nad_set_attr(nad, 0, -1, "to", acl->redirect, acl->redirect_len); error = acl->error; break; } if(to != NULL) free(to); if(from != NULL) free(from); return error; }
static void _router_process_unbind(component_t comp, nad_t nad) { int attr; jid_t name; attr = nad_find_attr(nad, 0, -1, "name", NULL); if(attr < 0 || (name = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "no or invalid 'name' on unbind packet, bouncing"); nad_set_attr(nad, 0, -1, "error", "400", 3); sx_nad_write(comp->s, nad); return; } if(xhash_get(comp->routes, name->domain) == NULL) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to unbind '%s', but it's not bound to this component", comp->ip, comp->port, name->domain); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "404", 3); sx_nad_write(comp->s, nad); jid_free(name); return; } xhash_zap(comp->r->log_sinks, name->domain); _route_remove(comp->r->routes, name->domain, comp); xhash_zap(comp->routes, name->domain); if(comp->r->default_route != NULL && strcmp(comp->r->default_route, name->domain) == 0) { log_write(comp->r->log, LOG_NOTICE, "[%s] default route offline", name->domain); free((void*)(comp->r->default_route)); comp->r->default_route = NULL; } log_write(comp->r->log, LOG_NOTICE, "[%s] offline", name->domain); nad_set_attr(nad, 0, -1, "name", NULL, 0); sx_nad_write(comp->s, nad); /* deadvertise name */ if(xhash_get(comp->r->routes, name->domain) == NULL) _router_advertise(comp->r, name->domain, comp, 1); jid_free(name); }
static void _router_process_bind(component_t comp, nad_t nad) { int attr, multi, n; jid_t name; alias_t alias; char *user, *c; attr = nad_find_attr(nad, 0, -1, "name", NULL); if(attr < 0 || (name = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "no or invalid 'name' on bind packet, bouncing"); nad_set_attr(nad, 0, -1, "error", "400", 3); sx_nad_write(comp->s, nad); return; } user = strdup(comp->s->auth_id); c = strchr(user, '@'); if(c != NULL) *c = '\0'; if(strcmp(user, name->domain) != 0 && !aci_check(comp->r->aci, "bind", user)) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s', but their username (%s) is not permitted to bind other names", comp->ip, comp->port, name->domain, user); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "403", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } multi = nad_find_attr(nad, 0, -1, "multi", NULL); if(xhash_get(comp->r->routes, name->domain) != NULL && multi < 0) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s', but it's already bound", comp->ip, comp->port, name->domain); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "409", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } for(alias = comp->r->aliases; alias != NULL; alias = alias->next) if(strcmp(alias->name, name->domain) == 0) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s', but that name is aliased", comp->ip, comp->port); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "409", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } /* default route */ if(nad_find_elem(nad, 0, NAD_ENS(nad, 0), "default", 1) >= 0) { if(!aci_check(comp->r->aci, "default-route", user)) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s' as the default route, but their username (%s) is not permitted to set a default route", comp->ip, comp->port, name->domain, user); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "403", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } if(comp->r->default_route != NULL) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s' as the default route, but one already exists", comp->ip, comp->port, name->domain); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "409", 3); sx_nad_write(comp->s, nad); jid_free(name); return; } log_write(comp->r->log, LOG_NOTICE, "[%s] set as default route", name->domain); comp->r->default_route = strdup(name->domain); } /* log sinks */ if(nad_find_elem(nad, 0, NAD_ENS(nad, 0), "log", 1) >= 0) { if(!aci_check(comp->r->aci, "log", user)) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s' as a log sink, but their username (%s) is not permitted to do this", comp->ip, comp->port, name->domain, user); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "403", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } log_write(comp->r->log, LOG_NOTICE, "[%s] set as log sink", name->domain); xhash_put(comp->r->log_sinks, pstrdup(xhash_pool(comp->r->log_sinks), name->domain), (void *) comp); } free(user); n = _route_add(comp->r->routes, name->domain, comp, multi<0?route_SINGLE:route_MULTI_TO); xhash_put(comp->routes, pstrdup(xhash_pool(comp->routes), name->domain), (void *) comp); if(n>1) log_write(comp->r->log, LOG_NOTICE, "[%s]:%d online (bound to %s, port %d)", name->domain, n, comp->ip, comp->port); else log_write(comp->r->log, LOG_NOTICE, "[%s] online (bound to %s, port %d)", name->domain, comp->ip, comp->port); nad_set_attr(nad, 0, -1, "name", NULL, 0); sx_nad_write(comp->s, nad); /* advertise name */ _router_advertise(comp->r, name->domain, comp, 0); /* tell the new component about everyone else */ xhash_walk(comp->r->routes, _router_advertise_reverse, (void *) comp); /* bind aliases */ for(alias = comp->r->aliases; alias != NULL; alias = alias->next) { if(strcmp(alias->target, name->domain) == 0) { _route_add(comp->r->routes, name->domain, comp, route_MULTI_TO); 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, name->domain, comp->ip, comp->port); /* advertise name */ _router_advertise(comp->r, alias->name, comp, 0); } } /* done with this */ jid_free(name); }
/** main packet dispatcher */ void dispatch(sm_t sm, pkt_t pkt) { user_t user; mod_ret_t ret; /* handle broadcasts */ if(pkt->rtype == route_BROADCAST) { log_debug(ZONE, "can't handle broadcast routes (yet), dropping"); pkt_free(pkt); return; } /* routing errors, add a im error */ if(pkt->rtype & route_ERROR) { int i, aerror, stanza_err; aerror = nad_find_attr(pkt->nad, 0, -1, "error", NULL); stanza_err = stanza_err_REMOTE_SERVER_NOT_FOUND; if(aerror >= 0) { for(i=0; _stanza_errors[i].code != NULL; i++) if(strncmp(_stanza_errors[i].code, NAD_AVAL(pkt->nad, aerror), NAD_AVAL_L(pkt->nad, aerror)) == 0) { stanza_err = stanza_err_BAD_REQUEST + i; break; } } if(pkt_error(pkt, stanza_err) == NULL) return; } /* * - if its from the router (non-route) it goes straight to pkt_router * - hand it to in_router chain * - if its for the sm itself (no user), hand it to the pkt_sm chain * - find the user * - hand to pkt_user */ /* non route packets are special-purpose things from the router */ if(!(pkt->rtype & route_UNICAST)) { ret = mm_pkt_router(pkt->sm->mm, pkt); switch(ret) { case mod_HANDLED: break; case mod_PASS: default: /* don't ever bounce these */ pkt_free(pkt); break; } return; } /* preprocessing */ if (pkt != NULL && pkt->sm != NULL) { ret = mm_in_router(pkt->sm->mm, pkt); switch(ret) { case mod_HANDLED: return; case mod_PASS: break; default: pkt_router(pkt_error(pkt, -ret)); return; } } /* has to come from someone and be directed to someone */ if(pkt->from == NULL || pkt->to == NULL) { pkt_router(pkt_error(pkt, stanza_err_BAD_REQUEST)); return; } /* packet is for the sm itself */ if(*pkt->to->node == '\0') { ret = mm_pkt_sm(pkt->sm->mm, pkt); switch(ret) { case mod_HANDLED: break; case mod_PASS: /* ignore IQ result packets that haven't been handled - XMPP 9.2.3.4 */ if(pkt->type == pkt_IQ_RESULT) { pkt_free(pkt); break; } else ret = -stanza_err_FEATURE_NOT_IMPLEMENTED; default: pkt_router(pkt_error(pkt, -ret)); break; } return; } /* get the user */ user = user_load(sm, pkt->to); if(user == NULL) { if(pkt->type & pkt_PRESENCE && pkt->type != pkt_PRESENCE_PROBE) { pkt_free(pkt); return; } if(pkt->type == pkt_PRESENCE_PROBE) { pkt_router(pkt_create(pkt->sm, "presence", "unsubscribed", jid_full(pkt->from), jid_full(pkt->to))); pkt_free(pkt); return; } pkt_router(pkt_error(pkt, stanza_err_SERVICE_UNAVAILABLE)); return; } if (pkt->sm != NULL) { ret = mm_pkt_user(pkt->sm->mm, user, pkt); switch(ret) { case mod_HANDLED: break; case mod_PASS: /* ignore IQ result packets that haven't been handled - XMPP 9.2.3.4 */ if(pkt->type == pkt_IQ_RESULT) { pkt_free(pkt); break; } else ret = -stanza_err_FEATURE_NOT_IMPLEMENTED; default: pkt_router(pkt_error(pkt, -ret)); break; } } /* if they have no sessions, they were only loaded to do delivery, so free them */ if(user->sessions == NULL) user_free(user); }
/** 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); }
/** validate their key */ static void _in_verify(conn_t in, nad_t nad) { int attr; jid_t from, to; char *id, *dbkey, *type; 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 verify 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 verify packet"); jid_free(from); nad_free(nad); return; } attr = nad_find_attr(nad, 0, -1, "id", NULL); if(attr < 0) { log_debug(ZONE, "missing id on db verify packet"); jid_free(from); jid_free(to); nad_free(nad); return; } if(NAD_CDATA_L(nad, 0) <= 0) { log_debug(ZONE, "no cdata on db verify packet"); jid_free(from); jid_free(to); nad_free(nad); return; } /* extract the id */ id = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, attr) + 1)); snprintf(id, NAD_AVAL_L(nad, attr) + 1, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); /* generate a dialback key */ dbkey = s2s_db_key(NULL, in->s2s->local_secret, from->domain, id); /* valid */ if(NAD_CDATA_L(nad, 0) == strlen(dbkey) && strncmp(dbkey, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0)) == 0) { log_debug(ZONE, "valid dialback key %s, verify succeeded", dbkey); type = "valid"; } else { log_debug(ZONE, "invalid dialback key %s, verify failed", dbkey); type = "invalid"; } log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] checking dialback verification from %s: sending %s", in->fd->fd, in->ip, in->port, from->domain, type); log_debug(ZONE, "letting them know"); /* now munge the packet and send it back to them */ stanza_tofrom(nad, 0); nad_set_attr(nad, 0, -1, "type", type, 0); 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(dbkey); free(id); jid_free(from); jid_free(to); return; }
/** 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; }
/** 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; }
/** turn an xml file into a config hash */ int config_load_with_id(config_t c, const char *file, const char *id) { struct build_data bd; FILE *f; XML_Parser p; int done, len, end, i, j, attr; char buf[1024], *next; struct nad_elem_st **path; config_elem_t elem; int rv = 0; /* open the file */ f = fopen(file, "r"); if(f == NULL) { fprintf(stderr, "config_load: couldn't open %s for reading: %s\n", file, strerror(errno)); return 1; } /* new parser */ p = XML_ParserCreate(NULL); if(p == NULL) { fprintf(stderr, "config_load: couldn't allocate XML parser\n"); fclose(f); return 1; } /* nice new nad to parse it into */ bd.nad = nad_new(); bd.depth = 0; /* setup the parser */ XML_SetUserData(p, (void *) &bd); XML_SetElementHandler(p, _config_startElement, _config_endElement); XML_SetCharacterDataHandler(p, _config_charData); for(;;) { /* read that file */ len = fread(buf, 1, 1024, f); if(ferror(f)) { fprintf(stderr, "config_load: read error: %s\n", strerror(errno)); XML_ParserFree(p); fclose(f); nad_free(bd.nad); return 1; } done = feof(f); /* parse it */ if(!XML_Parse(p, buf, len, done)) { fprintf(stderr, "config_load: parse error at line %llu: %s\n", (unsigned long long) XML_GetCurrentLineNumber(p), XML_ErrorString(XML_GetErrorCode(p))); XML_ParserFree(p); fclose(f); nad_free(bd.nad); return 1; } if(done) break; } /* done reading */ XML_ParserFree(p); fclose(f); // Put id if specified if (id) { elem = (config_elem_t)pmalloco(xhash_pool(c->hash), sizeof(struct config_elem_st)); xhash_put(c->hash, pstrdup(xhash_pool(c->hash), "id"), elem); elem->values = calloc(1, sizeof(char *)); elem->values[0] = pstrdup(xhash_pool(c->hash), id); elem->nvalues = 1; } /* now, turn the nad into a config hash */ path = NULL; len = 0, end = 0; /* start at 1, so we skip the root element */ for(i = 1; i < bd.nad->ecur && rv == 0; i++) { /* make sure we have enough room to add this element to our path */ if(end <= bd.nad->elems[i].depth) { end = bd.nad->elems[i].depth + 1; path = (struct nad_elem_st **) realloc((void *) path, sizeof(struct nad_elem_st *) * end); } /* save this path element */ path[bd.nad->elems[i].depth] = &bd.nad->elems[i]; len = bd.nad->elems[i].depth + 1; /* construct the key from the current path */ next = buf; for(j = 1; j < len; j++) { strncpy(next, bd.nad->cdata + path[j]->iname, path[j]->lname); next = next + path[j]->lname; *next = '.'; next++; } next--; *next = '\0'; /* find the config element for this key */ elem = (config_elem_t)xhash_get(c->hash, buf); if(elem == NULL) { /* haven't seen it before, so create it */ elem = (config_elem_t)pmalloco(xhash_pool(c->hash), sizeof(struct config_elem_st)); xhash_put(c->hash, pstrdup(xhash_pool(c->hash), buf), elem); } /* make room for this value .. can't easily realloc off a pool, so * we do it this way and let _config_reaper clean up */ elem->values = realloc((void *) elem->values, sizeof(char *) * (elem->nvalues + 1)); /* and copy it in */ if(NAD_CDATA_L(bd.nad, i) > 0) { // Expand values const char *val = _config_expandx(c, NAD_CDATA(bd.nad, i), NAD_CDATA_L(bd.nad, i)); if (!val) { rv = 1; break; } // Make a copy elem->values[elem->nvalues] = val; } else { elem->values[elem->nvalues] = "1"; } /* make room for the attribute lists */ elem->attrs = realloc((void *) elem->attrs, sizeof(char **) * (elem->nvalues + 1)); elem->attrs[elem->nvalues] = NULL; /* count the attributes */ for(attr = bd.nad->elems[i].attr, j = 0; attr >= 0; attr = bd.nad->attrs[attr].next, j++); /* make space */ elem->attrs[elem->nvalues] = pmalloc(xhash_pool(c->hash), sizeof(char *) * (j * 2 + 2)); /* if we have some */ if(j > 0) { /* copy them in */ j = 0; attr = bd.nad->elems[i].attr; while(attr >= 0) { elem->attrs[elem->nvalues][j] = pstrdupx(xhash_pool(c->hash), NAD_ANAME(bd.nad, attr), NAD_ANAME_L(bd.nad, attr)); elem->attrs[elem->nvalues][j + 1] = pstrdupx(xhash_pool(c->hash), NAD_AVAL(bd.nad, attr), NAD_AVAL_L(bd.nad, attr)); /* * pstrdupx(blob, 0) returns NULL - which means that later * there's no way of telling whether an attribute is defined * as empty, or just not defined. This fixes that by creating * an empty string for attributes which are defined empty */ if (NAD_AVAL_L(bd.nad, attr)==0) { elem->attrs[elem->nvalues][j + 1] = pstrdup(xhash_pool(c->hash), ""); } else { elem->attrs[elem->nvalues][j + 1] = pstrdupx(xhash_pool(c->hash), NAD_AVAL(bd.nad, attr), NAD_AVAL_L(bd.nad, attr)); } j += 2; attr = bd.nad->attrs[attr].next; } } /* do this and we can use j_attr */ elem->attrs[elem->nvalues][j] = NULL; elem->attrs[elem->nvalues][j + 1] = NULL; elem->nvalues++; } if(path != NULL) free(path); if(c->nad != NULL) nad_free(c->nad); c->nad = bd.nad; return rv; }
/** Handles iq messages */ mod_ret_t archive_iq_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) { int prefs, type; int section, ptr; os_t os; os_object_t o; char* owner; char buff[2048]; log_debug(ZONE, "In session"); // we only want to play IQs if((!((pkt->type & pkt_IQ) || (pkt->type & pkt_IQ_SET))) || (pkt->ns != ns_ARCHIVE)) return mod_PASS; log_debug(ZONE, "Passed through packet checks"); // if it has a to, throw it out if(pkt->to != NULL) return -stanza_err_BAD_REQUEST; // Who are we? owner=jid_user(sess->jid); // Check for no type or get to send current settings if((type = (nad_find_attr(pkt->nad, 2, -1, "type", NULL)<0)) || ((NAD_AVAL_L(pkt->nad, type ) == 3) && strncmp("get", NAD_AVAL(pkt->nad, type), 3))) { log_debug(ZONE, "Somebody is asking about settings"); send_arch_prefs(mi, sess, pkt); return mod_HANDLED; } // We are setting stuff if(pkt->type == pkt_IQ_SET) { // Prepare to store them log_debug(ZONE, "Setting archiving preferences..."); os = os_new(); if(os == NULL) return mod_PASS; o = os_object_new(os); if(o == NULL) return mod_PASS; // Get rid of old settings storage_delete(pkt->sm->st, tbl_name "_settings", owner, NULL); log_debug(ZONE, "Got rid of old preferences..."); // Find if there is an default option if(!((prefs=nad_find_elem(pkt->nad, 1, -1, "prefs", 1))>0)) return mod_PASS; ptr=nad_find_attr(pkt->nad, prefs, -1, "default", NULL); if(ptr>0) { // Defaults log_debug(ZONE, "Saving defaults..."); os_object_put(o, "jid", owner, os_type_STRING); if (strncmp("roster",NAD_AVAL(pkt->nad, ptr), NAD_AVAL_L(pkt->nad, ptr))==0) prefs=A_ROSTER; else if(strncmp("always",NAD_AVAL(pkt->nad, ptr), NAD_AVAL_L(pkt->nad, ptr))==0) prefs=A_ALWAYS; else prefs=A_NEVER; os_object_put(o, "setting", &prefs, os_type_INTEGER); log_debug(ZONE, "Setting default to %d...", prefs); // What to save always log_debug(ZONE, "Saving what to save always..."); if(((section = nad_find_elem(pkt->nad, prefs, -1, "always", 1))>0)) { log_debug(ZONE, "Found always section %d...", section); prefs=A_ALWAYS; for(ptr = nad_find_elem(pkt->nad, section, -1, "jid", 1); ptr > 0; ptr = nad_find_elem(pkt->nad, ptr, -1, "jid", 0)) { o = os_object_new(os); strncpy(buff, NAD_CDATA(pkt->nad, ptr), min(2047,NAD_CDATA_L(pkt->nad, ptr))); buff[min(2047,NAD_CDATA_L(pkt->nad, ptr))]=0; os_object_put(o, "jid", buff, os_type_STRING); os_object_put(o, "setting", &prefs, os_type_INTEGER); log_debug(ZONE, "Always archiving %s...", buff); } } // What to never save log_debug(ZONE, "Saving what never save..."); if(((section = nad_find_elem(pkt->nad, 2, -1, "never", 1))>0)) { log_debug(ZONE, "Found never section %d...", section); prefs=A_NEVER; for(ptr = nad_find_elem(pkt->nad, section, -1, "jid", 1); ptr > 0; ptr = nad_find_elem(pkt->nad, ptr, -1, "jid", 0)) { o = os_object_new(os); strncpy(buff, NAD_CDATA(pkt->nad, ptr), min(2047,NAD_CDATA_L(pkt->nad, ptr))); buff[min(2047,NAD_CDATA_L(pkt->nad, ptr))]=0; os_object_put(o, "jid", buff, os_type_STRING); os_object_put(o, "setting", &prefs, os_type_INTEGER); log_debug(ZONE, "Never archiving %s...", buff); } } // Save everything storage_put(pkt->sm->st, tbl_name "_settings", owner, os); } // Send current settings send_arch_prefs(mi, sess, pkt); return mod_HANDLED; } return mod_PASS; }
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); }
/** * 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; }
/** 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; }
/** 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; }
static mod_ret_t _session_in_router(mod_instance_t mi, pkt_t pkt) { sm_t sm = mi->mod->mm->sm; int ns, iq, elem, attr; jid_t jid; sess_t sess = (sess_t) NULL; mod_ret_t ret; /* if we've got this namespace, its from a c2s */ if(pkt->nad->ecur <= 1 || (ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL)) < 0) return mod_PASS; /* don't bother if its a failure */ if(pkt->type & pkt_SESS_FAILED) { /* !!! check failed=1, handle */ pkt_free(pkt); return mod_HANDLED; } /* session commands */ if(pkt->type & pkt_SESS) { ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL); /* only end can get away without a target */ attr = nad_find_attr(pkt->nad, 1, -1, "target", NULL); if(attr < 0 && pkt->type != pkt_SESS_END) { nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); return mod_HANDLED; } /* session start */ if(pkt->type == pkt_SESS) { jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); if(jid != NULL) sess = sess_start(sm, jid); if(jid == NULL || sess == NULL) { nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); if(jid != NULL) jid_free(jid); return mod_HANDLED; } /* c2s component that is handling this session */ strcpy(sess->c2s, pkt->rfrom->domain); /* remember what c2s calls us */ attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL); snprintf(sess->c2s_id, sizeof(sess->c2s_id), "%.*s", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr)); /* mark PBX session as fake */ if(!strncmp("PBX", sess->c2s_id, 3)) { sess->fake = 1; } /* add our id */ nad_set_attr(pkt->nad, 1, ns, "sm", sess->sm_id, 0); /* mark that it started OK */ nad_set_attr(pkt->nad, 1, -1, "action", "started", 7); /* set this SM name */ nad_set_attr(pkt->nad, 0, -1, "to", sm->id, 0); /* inform c2s */ sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); jid_free(jid); return mod_HANDLED; } /* user create */ if(pkt->type == pkt_SESS_CREATE) { jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); if(jid == NULL || user_create(sm, jid) != 0) { nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); if(jid != NULL) jid_free(jid); return mod_HANDLED; } /* inform c2s */ nad_set_attr(pkt->nad, 1, -1, "action", "created", 7); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); jid_free(jid); return mod_HANDLED; } /* user delete */ if(pkt->type == pkt_SESS_DELETE) { jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); if(jid == NULL) { pkt_free(pkt); return mod_HANDLED; } user_delete(sm, jid); /* inform c2s */ nad_set_attr(pkt->nad, 1, -1, "action", "deleted", 7); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); jid_free(jid); return mod_HANDLED; } /* get the session id */ attr = nad_find_attr(pkt->nad, 1, ns, "sm", NULL); if(attr < 0) { log_debug(ZONE, "no session id, bouncing"); nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); return mod_HANDLED; } /* find the corresponding session */ sess = xhash_getx(sm->sessions, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); /* active check */ if(sess == NULL) { log_debug(ZONE, "session %.*s doesn't exist, bouncing", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr)); nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); return mod_HANDLED; } /* make sure its from them */ attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL); if(attr >= 0 && (strlen(sess->c2s_id) != NAD_AVAL_L(pkt->nad, attr) || strncmp(sess->c2s_id, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)) != 0)) { log_debug(ZONE, "invalid sender on route from %s for session %s (should be %s)", pkt->rfrom->domain, sess->sm_id, sess->c2s_id); pkt_free(pkt); return mod_HANDLED; } /* session end */ if(pkt->type == pkt_SESS_END) { sm_c2s_action(sess, "ended", NULL); sess_end(sess); pkt_free(pkt); return mod_HANDLED; } log_debug(ZONE, "unknown session packet, dropping"); pkt_free(pkt); return mod_HANDLED; } /* otherwise, its a normal packet for the session */ /* #ifdef ENABLE_SUPERSEDED // FIXME XXX TODO clients are not yet ready for this */ /* check for RFC3920 session request * * with RFC3920bis it is unneeded * * session is activated by bind, so we just return back result */ if((ns = nad_find_scoped_namespace(pkt->nad, uri_XSESSION, NULL)) >= 0 && (iq = nad_find_elem(pkt->nad, 0, -1, "iq", 1)) >= 0 && (elem = nad_find_elem(pkt->nad, iq, ns, "session", 1)) >= 0) { log_debug(ZONE, "session create request"); /* build a result packet */ nad_drop_elem(pkt->nad, elem); nad_set_attr(pkt->nad, iq, -1, "type", "result", 6); /* return the result */ sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); return mod_HANDLED; } /* #endif */ /* get the session id */ attr = nad_find_attr(pkt->nad, 1, ns, "sm", NULL); if(attr < 0) { log_debug(ZONE, "no session id, bouncing"); nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); return mod_HANDLED; } /* find the corresponding session */ sess = xhash_getx(sm->sessions, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); /* active check */ if(sess == NULL) { log_debug(ZONE, "session %.*s doesn't exist, bouncing", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr)); nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1); sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0)); pkt->nad = NULL; pkt_free(pkt); return mod_HANDLED; } /* make sure its from them */ attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL); if(attr >= 0 && (strlen(sess->c2s_id) != NAD_AVAL_L(pkt->nad, attr) || strncmp(sess->c2s_id, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)) != 0)) { log_debug(ZONE, "invalid sender on route from %s for session %s (should be %s)", jid_full(pkt->rfrom), sess->sm_id, sess->c2s_id); pkt_free(pkt); return mod_HANDLED; } /* where it came from */ pkt->source = sess; /* hand it to the modules */ ret = mm_in_sess(pkt->sm->mm, sess, pkt); switch(ret) { case mod_HANDLED: break; case mod_PASS: /* ignore IQ result packets that haven't been handled - XMPP 9.2.3.4 */ if(pkt->type == pkt_IQ_RESULT) break; else ret = -stanza_err_FEATURE_NOT_IMPLEMENTED; default: pkt_sess(pkt_error(pkt, -ret), sess); break; } return mod_HANDLED; }
static mod_ret_t _offline_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) { st_ret_t ret; os_t os; os_object_t o; nad_t nad; pkt_t queued; int ns, elem, attr; char cttl[15], cstamp[18]; time_t ttl, stamp; /* if they're becoming available for the first time */ if(pkt->type == pkt_PRESENCE && pkt->to == NULL && sess->user->top == NULL) { ret = storage_get(pkt->sm->st, "queue", jid_user(sess->jid), NULL, &os); if(ret != st_SUCCESS) { log_debug(ZONE, "storage_get returned %d", ret); return mod_PASS; } if(os_iter_first(os)) do { o = os_iter_object(os); if(os_object_get_nad(os, o, "xml", &nad)) { queued = pkt_new(pkt->sm, nad_copy(nad)); if(queued == NULL) { log_debug(ZONE, "invalid queued packet, not delivering"); } else { /* check expiry as necessary */ if((ns = nad_find_scoped_namespace(queued->nad, uri_EXPIRE, NULL)) >= 0 && (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 && (attr = nad_find_attr(queued->nad, elem, -1, "seconds", NULL)) >= 0) { snprintf(cttl, 15, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr)); ttl = atoi(cttl); /* it should have a x:delay stamp, because we stamp everything we store */ if((ns = nad_find_scoped_namespace(queued->nad, uri_DELAY, NULL)) >= 0 && (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 && (attr = nad_find_attr(queued->nad, elem, -1, "stamp", NULL)) >= 0) { snprintf(cstamp, 18, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr)); stamp = datetime_in(cstamp); if(stamp + ttl <= time(NULL)) { log_debug(ZONE, "queued packet has expired, dropping"); pkt_free(queued); continue; } } } log_debug(ZONE, "delivering queued packet to %s", jid_full(sess->jid)); pkt_sess(queued, sess); } } } while(os_iter_next(os)); os_free(os); /* drop the spool */ storage_delete(pkt->sm->st, "queue", jid_user(sess->jid), NULL); } /* pass it so that other modules and mod_presence can get it */ return mod_PASS; }
int filter_load(router_t r) { char *filterfile; FILE *f; long size; char *buf; nad_t nad; int i, nfilters, filter, from, to, what, redirect, error, log; acl_t list_tail, acl; log_debug(ZONE, "loading filter"); if(r->filter != NULL) filter_unload(r); filterfile = config_get_one(r->config, "aci.filter", 0); if(filterfile == NULL) filterfile = CONFIG_DIR "/router-filter.xml"; f = fopen(filterfile, "rb"); if(f == NULL) { log_write(r->log, LOG_NOTICE, "couldn't open filter file %s: %s", filterfile, strerror(errno)); r->filter_load = time(NULL); return 0; } fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); buf = (char *) malloc(sizeof(char) * size); if (fread(buf, 1, size, f) != size || ferror(f)) { log_write(r->log, LOG_ERR, "couldn't read from filter file: %s", strerror(errno)); free(buf); fclose(f); return 1; } fclose(f); nad = nad_parse(buf, size); if(nad == NULL) { log_write(r->log, LOG_ERR, "couldn't parse filter file"); free(buf); return 1; } free(buf); list_tail = NULL; log_debug(ZONE, "building filter list"); nfilters = 0; filter = nad_find_elem(nad, 0, -1, "rule", 1); while(filter >= 0) { from = nad_find_attr(nad, filter, -1, "from", NULL); to = nad_find_attr(nad, filter, -1, "to", NULL); what = nad_find_attr(nad, filter, -1, "what", NULL); redirect = nad_find_attr(nad, filter, -1, "redirect", NULL); error = nad_find_attr(nad, filter, -1, "error", NULL); log = nad_find_attr(nad, filter, -1, "log", NULL); acl = (acl_t) calloc(1, sizeof(struct acl_s)); if(from >= 0) { if (NAD_AVAL_L(nad, from) == 0 ) acl->from = NULL; else { acl->from = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, from) + 1)); sprintf(acl->from, "%.*s", NAD_AVAL_L(nad, from), NAD_AVAL(nad, from)); } } if(to >= 0) { if (NAD_AVAL_L(nad, to) == 0 ) acl->to = NULL; else { acl->to = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, to) + 1)); sprintf(acl->to, "%.*s", NAD_AVAL_L(nad, to), NAD_AVAL(nad, to)); } } if(what >= 0) { if (NAD_AVAL_L(nad, what) == 0 || strncmp(NAD_AVAL(nad, what), "*", NAD_AVAL_L(nad, what)) == 0) acl->what = NULL; else { acl->what = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, what) + 1)); sprintf(acl->what, "%.*s", NAD_AVAL_L(nad, what), NAD_AVAL(nad, what)); } } if(redirect >= 0) { if (NAD_AVAL_L(nad, redirect) == 0) acl->redirect = NULL; else { acl->redirect_len = NAD_AVAL_L(nad, redirect); acl->redirect = (char *) malloc(sizeof(char) * (acl->redirect_len + 1)); sprintf(acl->redirect, "%.*s", acl->redirect_len, NAD_AVAL(nad, redirect)); acl->error = stanza_err_REDIRECT; } } if(error >= 0) { acl->error = stanza_err_NOT_ALLOWED; for(i=0; _stanza_errors[i].code != NULL; i++) { if(_stanza_errors[i].name != NULL && strncmp(_stanza_errors[i].name, NAD_AVAL(nad, error), NAD_AVAL_L(nad, error)) == 0) { acl->error = stanza_err_BAD_REQUEST + i; break; } } } if(log >= 0) { acl->log = ! strncasecmp(NAD_AVAL(nad, log), "YES", NAD_AVAL_L(nad, log)); acl->log |= ! strncasecmp(NAD_AVAL(nad, log), "ON", NAD_AVAL_L(nad, log)); } if(list_tail != NULL) { list_tail->next = acl; list_tail = acl; } /* record the head of the list */ if(r->filter == NULL) { r->filter = acl; list_tail = acl; } log_debug(ZONE, "added %s rule: from=%s, to=%s, what=%s, redirect=%s, error=%d, log=%s", (acl->error?"deny":"allow"), acl->from, acl->to, acl->what, acl->redirect, acl->error, (acl->log?"yes":"no")); nfilters++; filter = nad_find_elem(nad, filter, -1, "rule", 0); } nad_free(nad); log_write(r->log, LOG_NOTICE, "loaded filters (%d rules)", nfilters); r->filter_load = time(NULL); 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; }
/** * Saves packet into database. * @param direct Direction of the message - 2 = incomming, 1 = sent */ mod_ret_t savepkt(pkt_t pkt, int direct) { int body=-1, sub=-1, type=-1, arch=-1; char *mem=NULL; const *owner = NULL, *other = NULL; int sz = 0; int archive=default_mode; char filter[2060]; // 2048 is jid_user maximum length time_t t=0; jid_t own_jid, other_jid; os_t os, set_os=NULL; os_object_t o, set_o=NULL; // Is it a message? log_debug(ZONE, "Testing message..."); if ( (pkt->type & pkt_MESSAGE) == 0) return mod_PASS; log_debug(ZONE, "It is a message..."); // 0 element is route, 1st is message, we need subelement of message body = nad_find_elem(pkt->nad, 1, -1, "body", 1); log_debug(ZONE, "Body is at %d", body); sub = nad_find_elem(pkt->nad, 1, -1, "subject", 1); log_debug(ZONE, "Subject is at %d", sub); type = nad_find_attr(pkt->nad, 1, -1, "type", NULL); log_debug(ZONE, "Type %d", type); // Are these parts really interesting? if( ( (body < 0) || (NAD_CDATA_L(pkt->nad, body) < 1) ) && ( (sub < 0) || (NAD_CDATA_L(pkt->nad, sub ) < 1) ) ) return mod_PASS; log_debug(ZONE, "It's meaningful message!", pkt->from); // What direction are we talking about? if (direct == IN_D) { own_jid = pkt->to; other_jid = pkt->from; } else { own_jid = pkt->from; other_jid = pkt->to; } // Get JIDs if(own_jid != NULL) { owner=jid_user(own_jid); } else { return mod_PASS; } if(other_jid != NULL) { other=jid_user(other_jid); } else { return mod_PASS; } // Check settings // Load defaults snprintf(filter, 2060, "(jid=%s)", owner); if((storage_get(pkt->sm->st, tbl_name "_settings", owner, filter, &set_os) == st_SUCCESS) && (os_iter_first(set_os)) && ((set_o=os_iter_object(set_os))!=NULL)) os_object_get_int(set_os, set_o, "setting", &archive); // Cleanup if(set_o != NULL) { os_object_free(set_o); set_o=NULL; } if(set_os != NULL) { os_free(set_os); set_os=NULL; } // Load contact specific snprintf(filter, 2060, "(jid=%s)", other); if((storage_get(pkt->sm->st, tbl_name "_settings", owner, filter, &set_os) == st_SUCCESS) && (os_iter_first(set_os)) && ((set_o=os_iter_object(set_os))!=NULL)) os_object_get_int(set_os, set_o, "setting", &archive); // Cleanup if(set_o != NULL) { os_object_free(set_o); set_o=NULL; } if(set_os != NULL) { os_free(set_os); set_os=NULL; } // Do we need to check roster? if(archive==A_ROSTER) { snprintf(filter, 2060, "(jid=%s)", other); if(storage_get(pkt->sm->st, "roster-items", owner, filter, &set_os) == st_SUCCESS) archive=A_ALWAYS; else archive=A_NEVER; if(set_os != NULL) { os_free(set_os); set_os=NULL; } } // Decide if(archive==A_NEVER) return mod_PASS; // Prepare to store them os = os_new(); if(os == NULL) return mod_PASS; o = os_object_new(os); if(o == NULL) return mod_PASS; // Real storing log_debug(ZONE, "Saving message for %s (other party is %s)", owner, other); // Message // Buffer allocation if(body > 0) { sz = NAD_CDATA_L(pkt->nad, body) / 1024; log_debug(ZONE, "Body size %d", NAD_CDATA_L(pkt->nad, body)); } if(sub > 0) { sz = max(sz, NAD_CDATA_L(pkt->nad, sub) / 1024); log_debug(ZONE, "Subj size %d", NAD_CDATA_L(pkt->nad, sub)); } log_debug(ZONE, "Creating buffer of size %d", sz); mem = (char*)malloc(1024 * (sz+1)); if(mem == NULL) return mod_PASS; log_debug(ZONE, "We got past the buffer allocation."); // JID os_object_put(o, "other_jid", other, os_type_STRING); // Body mem[0]=0; if ( (body > 0) && (NAD_CDATA_L(pkt->nad, body) > 0) ) { strncpy(mem, NAD_CDATA(pkt->nad, body), NAD_CDATA_L(pkt->nad, body)); mem[NAD_CDATA_L(pkt->nad, body)] = 0; } os_object_put(o, "message", mem, os_type_STRING); // Subject mem[0]=0; if ( (sub > 0) && (NAD_CDATA_L(pkt->nad, sub ) > 0) ) { strncpy(mem, NAD_CDATA(pkt->nad, sub), NAD_CDATA_L(pkt->nad, sub)); mem[NAD_CDATA_L(pkt->nad, sub)] = 0; } os_object_put(o, "subject", mem, os_type_STRING); // Type mem[0]=0; if ( (type > 0) && (NAD_AVAL_L(pkt->nad, type ) > 0) ) { strncpy(mem, NAD_AVAL(pkt->nad, type), NAD_AVAL_L(pkt->nad, type)); mem[NAD_AVAL_L(pkt->nad, type)] = 0; } os_object_put(o, "type", mem, os_type_STRING); // To and from resources os_object_put(o, "my_resource", own_jid->resource, os_type_STRING); os_object_put(o, "other_resource", other_jid->resource, os_type_STRING); // Time and direction t=time(NULL); os_object_put(o, "direct", &direct, os_type_INTEGER); os_object_put_time(o, "time", &t); // Message ID if (direct == IN_D) { arch = nad_insert_elem(pkt->nad, 1, -1, "archived", ""); nad_set_attr(pkt->nad,arch,-1,"by",jid_user(own_jid), strlen(jid_user(own_jid))); } set_mid(&(pkt->sm->st), &o, pkt->nad, arch); // Save itself storage_put(pkt->sm->st, tbl_name, owner, os); // Cleanup os_object_free(o); os_free(os); free(mem); log_debug(ZONE, "Saved."); return mod_PASS; }