/** find elem using XPath like query * name -- "name" for the child tag of that name * "name/name" for a sub child (recurses) * "?attrib" to match the first tag with that attrib defined * "?attrib=value" to match the first tag with that attrib and value * or any combination: "name/name/?attrib", etc */ int nad_find_elem_path(nad_t nad, int elem, int ns, const char *name) { char *str, *slash, *qmark, *equals; _nad_ptr_check(__func__, nad); /* make sure there are valid args */ if(elem >= nad->ecur || name == NULL) return -1; /* if it's plain name just search children */ if(strstr(name, "/") == NULL && strstr(name,"?") == NULL) return nad_find_elem(nad, elem, ns, name, 1); str = strdup(name); slash = strstr(str, "/"); qmark = strstr(str, "?"); equals = strstr(str, "="); /* no / in element name part */ if(qmark != NULL && (slash == NULL || qmark < slash)) { /* of type ?attrib */ *qmark = '\0'; qmark++; if(equals != NULL) { *equals = '\0'; equals++; } for(elem = nad_find_elem(nad, elem, ns, str, 1); ; elem = nad_find_elem(nad, elem, ns, str, 0)) { if(elem < 0) break; if(strcmp(qmark, "xmlns") == 0) { if(nad_find_namespace(nad, elem, equals, NULL) >= 0) break; } else { if(nad_find_attr(nad, elem, ns, qmark, equals) >= 0) break; } } free(str); return elem; } /* there is a / in element name part - need to recurse */ *slash = '\0'; ++slash; for(elem = nad_find_elem(nad, elem, ns, str, 1); ; elem = nad_find_elem(nad, elem, ns, str, 0)) { if(elem < 0) break; if((elem = nad_find_elem_path(nad, elem, ns, slash)) >= 0) break; } free(str); return elem; }
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; }
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; }
static mod_ret_t _pep_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) { int ns, elem; /* only handle private sets and gets */ if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_PUBSUB) return mod_PASS; /* we're only interested in no to, to our host, or to us */ if(pkt->to != NULL && jid_compare_user(sess->jid, pkt->to) != 0 && strcmp(sess->jid->domain, jid_user(pkt->to)) != 0) return mod_PASS; ns = nad_find_scoped_namespace(pkt->nad, uri_PUBSUB, NULL); elem = nad_find_elem(pkt->nad, 1, ns, "pubsub", 1); log_debug(ZONE, "_pep_in_sess() %d %d", ns, elem); return mod_PASS; }
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; }
int message_log(nad_t nad, router_t r, const char *msg_from, const char *msg_to) { time_t t; struct tm *time_pos; char timestamp[25]; struct stat filestat; FILE *message_file; short int new_msg_file = 0; int i; int nad_body_len = 0; char *nad_body = NULL; int elem; assert((int) (nad != NULL)); // Find the message body elem = nad_find_elem(nad, 0, -1, "message", 1); if (elem >= 0) { elem = nad_find_elem(nad, elem, -1, "body", 1); } // Don't log anything if we found no NAD body if (elem == -1) { return 0; } nad_body_len = NAD_CDATA_L(nad, elem); nad_body = NAD_CDATA(nad, elem); // temporary replace line endings with 0x01, ASCII: <control> SOH <start of heading> for (i = 0; i < nad_body_len; i++) { if (nad_body[i] == '\n') { nad_body[i] = 0x01; } } // Log our message umask((mode_t) 0077); if (stat(r->message_logging_file, &filestat)) { new_msg_file = 1; } if ((message_file = fopen(r->message_logging_file, "a")) == NULL) { log_write(r->log, LOG_ERR, "Unable to open message log for writing: %s", strerror(errno)); return 1; } if (new_msg_file) { if (! fprintf(message_file, "# This message log is created by the jabberd router.\n")) { log_write(r->log, LOG_ERR, "Unable to write to message log: %s", strerror(errno)); return 1; } fprintf(message_file, "# See router.xml for logging options.\n"); fprintf(message_file, "# Format: DateTime FromJID ToJID MessageBody<line end>\n"); } /* ISO8601 timestamp */ t = time(NULL); time_pos = localtime(&t); if (strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S%z", time_pos) == 0) { log_write(r->log, LOG_ERR, "strftime failed: %s", strerror(errno)); } elem = fprintf(message_file, "%s %s %s %.*s\n", timestamp, msg_from, msg_to, nad_body_len, nad_body); fclose(message_file); // revert line endings for (i = 0; i < nad_body_len; i++) { if (nad_body[i] == 0x01) { nad_body[i] = '\n'; } } if (!elem) { log_write(r->log, LOG_ERR, "Unable to write to message log: %s", strerror(errno)); return 1; } return 0; }
/** * processor for iq:auth and iq:register packets * return 0 if handled, 1 if not handled */ int authreg_process(c2s_t c2s, sess_t sess, nad_t nad) { int ns, query, type, authreg = -1, getset = -1; /* need iq */ if(NAD_ENAME_L(nad, 0) != 2 || strncmp("iq", NAD_ENAME(nad, 0), 2) != 0) return 1; /* only want auth or register packets */ if((ns = nad_find_scoped_namespace(nad, uri_AUTH, NULL)) >= 0 && (query = nad_find_elem(nad, 0, ns, "query", 1)) >= 0) authreg = 0; else if((ns = nad_find_scoped_namespace(nad, uri_REGISTER, NULL)) >= 0 && (query = nad_find_elem(nad, 0, ns, "query", 1)) >= 0) authreg = 1; else return 1; /* if its to someone else, pass it */ if(nad_find_attr(nad, 0, -1, "to", NULL) >= 0 && nad_find_attr(nad, 0, -1, "to", sess->s->req_to) < 0) return 1; /* need a type */ if((type = nad_find_attr(nad, 0, -1, "type", NULL)) < 0 || NAD_AVAL_L(nad, type) != 3) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return 0; } /* get or set? */ if(strncmp("get", NAD_AVAL(nad, type), NAD_AVAL_L(nad, type)) == 0) getset = 0; else if(strncmp("set", NAD_AVAL(nad, type), NAD_AVAL_L(nad, type)) == 0) getset = 1; else { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0)); return 0; } /* hand to the correct handler */ if(authreg == 0) { /* can't do iq:auth after sasl auth */ if(sess->sasl_authd) { sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0)); return 0; } if(getset == 0) { log_debug(ZONE, "auth get"); _authreg_auth_get(c2s, sess, nad); } else if(getset == 1) { log_debug(ZONE, "auth set"); _authreg_auth_set(c2s, sess, nad); } } if(authreg == 1) { if(getset == 0) { log_debug(ZONE, "register get"); _authreg_register_get(c2s, sess, nad); } else if(getset == 1) { log_debug(ZONE, "register set"); _authreg_register_set(c2s, sess, nad); } } /* handled */ return 0; }
/** * 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; }
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); }
/** 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; }
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 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 user_table_load(router_t r) { const char *userfile; FILE *f; long size; char *buf; nad_t nad; int nusers, user, name, secret; log_debug(ZONE, "loading user table"); if(r->users != NULL) xhash_free(r->users); r->users = xhash_new(51); userfile = config_get_one(r->config, "local.users", 0); if(userfile == NULL) userfile = CONFIG_DIR "/router-users.xml"; f = fopen(userfile, "rb"); if(f == NULL) { log_write(r->log, LOG_ERR, "couldn't open user table file %s: %s", userfile, strerror(errno)); return 1; } fseek(f, 0, SEEK_END); size = ftell(f); if(size < 0) { log_write(r->log, LOG_ERR, "couldn't seek user table file %s: %s", userfile, strerror(errno)); fclose(f); return 1; } if(size == 0) { log_write(r->log, LOG_ERR, "empty user table file %s", userfile); fclose(f); return 1; } 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 user table 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 user table"); free(buf); return 1; } free(buf); nusers = 0; user = nad_find_elem(nad, 0, -1, "user", 1); while(user >= 0) { name = nad_find_elem(nad, user, -1, "name", 1); secret = nad_find_elem(nad, user, -1, "secret", 1); if(name < 0 || secret < 0 || NAD_CDATA_L(nad, name) <= 0 || NAD_CDATA_L(nad, secret) <= 0) { log_write(r->log, LOG_ERR, "malformed user entry in user table file, skipping"); continue; } log_debug(ZONE, "remembering user '%.*s'", NAD_CDATA_L(nad, name), NAD_CDATA(nad, name)); xhash_put(r->users, pstrdupx(xhash_pool(r->users), NAD_CDATA(nad, name), NAD_CDATA_L(nad, name)), pstrdupx(xhash_pool(r->users), NAD_CDATA(nad, secret), NAD_CDATA_L(nad, secret))); nusers++; user = nad_find_elem(nad, user, -1, "user", 0); } nad_free(nad); log_write(r->log, LOG_NOTICE, "loaded user table (%d users)", nusers); r->users_load = time(NULL); return 0; }
/** 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; }
/** 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 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 _vacation_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) { module_t mod = mi->mod; vacation_t v = sess->user->module_data[mod->index]; int ns, start, end, msg; char dt[30]; pkt_t res; os_t os; os_object_t o; /* we only want to play with vacation iq packets */ if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VACATION) return mod_PASS; /* if it has a to, throw it out */ if(pkt->to != NULL) return -stanza_err_BAD_REQUEST; /* get */ if(pkt->type == pkt_IQ) { if(v->msg == NULL) { res = pkt_create(mod->mm->sm, "iq", "result", NULL, NULL); pkt_id(pkt, res); pkt_free(pkt); pkt_sess(res, sess); return mod_HANDLED; } ns = nad_find_scoped_namespace(pkt->nad, uri_VACATION, NULL); if(v->start != 0) { datetime_out(v->start, dt_DATETIME, dt, 30); nad_insert_elem(pkt->nad, 2, ns, "start", dt); } else nad_insert_elem(pkt->nad, 2, ns, "start", NULL); if(v->end != 0) { datetime_out(v->end, dt_DATETIME, dt, 30); nad_insert_elem(pkt->nad, 2, ns, "end", dt); } else nad_insert_elem(pkt->nad, 2, ns, "end", NULL); nad_insert_elem(pkt->nad, 2, ns, "message", v->msg); pkt_tofrom(pkt); nad_set_attr(pkt->nad, 1, -1, "type", "result", 6); pkt_sess(pkt, sess); return mod_HANDLED; } /* set */ ns = nad_find_scoped_namespace(pkt->nad, uri_VACATION, NULL); start = nad_find_elem(pkt->nad, 2, ns, "start", 1); end = nad_find_elem(pkt->nad, 2, ns, "end", 1); msg = nad_find_elem(pkt->nad, 2, ns, "message", 1); if(start < 0 || end < 0 || msg < 0) { /* forget */ if(v->msg != NULL) { free(v->msg); v->msg = NULL; } v->start = 0; v->end = 0; storage_delete(mi->sm->st, "vacation-settings", jid_user(sess->jid), NULL); res = pkt_create(mod->mm->sm, "iq", "result", NULL, NULL); pkt_id(pkt, res); pkt_free(pkt); pkt_sess(res, sess); return mod_HANDLED; } if(NAD_CDATA_L(pkt->nad, start) > 0) { strncpy(dt, NAD_CDATA(pkt->nad, start), (30 < NAD_CDATA_L(pkt->nad, start) ? 30 : NAD_CDATA_L(pkt->nad, start))); v->start = datetime_in(dt); } else v->start = 0; if(NAD_CDATA_L(pkt->nad, end) > 0) { strncpy(dt, NAD_CDATA(pkt->nad, end), (30 < NAD_CDATA_L(pkt->nad, end) ? 30 : NAD_CDATA_L(pkt->nad, end))); v->end = datetime_in(dt); } else v->end = 0; v->msg = (char *) malloc(sizeof(char) * (NAD_CDATA_L(pkt->nad, msg) + 1)); strncpy(v->msg, NAD_CDATA(pkt->nad, msg), NAD_CDATA_L(pkt->nad, msg)); v->msg[NAD_CDATA_L(pkt->nad, msg)] = '\0'; os = os_new(); o = os_object_new(os); os_object_put(o, "start", &v->start, os_type_INTEGER); os_object_put(o, "end", &v->end, os_type_INTEGER); os_object_put(o, "message", v->msg, os_type_STRING); if(storage_replace(mod->mm->sm->st, "vacation-settings", jid_user(sess->user->jid), NULL, os) != st_SUCCESS) { free(v->msg); v->msg = NULL; v->start = 0; v->end = 0; return -stanza_err_INTERNAL_SERVER_ERROR; } res = pkt_create(mod->mm->sm, "iq", "result", NULL, NULL); pkt_id(pkt, res); pkt_free(pkt); pkt_sess(res, sess); return mod_HANDLED; }
/** 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 mod_ret_t _iq_private_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) { module_t mod = mi->mod; int ns, elem, target, targetns; st_ret_t ret; char filter[4096]; os_t os; os_object_t o; nad_t nad; pkt_t result; sess_t sscan; /* only handle private sets and gets */ if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_PRIVATE) return mod_PASS; /* we're only interested in no to, to our host, or to us */ if(pkt->to != NULL && jid_compare_user(sess->jid, pkt->to) != 0 && strcmp(sess->jid->domain, jid_user(pkt->to)) != 0) return mod_PASS; ns = nad_find_scoped_namespace(pkt->nad, uri_PRIVATE, NULL); elem = nad_find_elem(pkt->nad, 1, ns, "query", 1); /* find the first child */ target = elem + 1; while(target < pkt->nad->ecur) { if(pkt->nad->elems[target].depth > pkt->nad->elems[elem].depth) break; target++; } /* not found, so we're done */ if(target == pkt->nad->ecur) return -stanza_err_BAD_REQUEST; /* find the target namespace */ targetns = NAD_ENS(pkt->nad, target); /* gotta have a namespace */ if(targetns < 0) { log_debug(ZONE, "no namespace specified"); return -stanza_err_BAD_REQUEST; } log_debug(ZONE, "processing private request for %.*s", NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns)); /* get */ if(pkt->type == pkt_IQ) { #ifdef ENABLE_EXPERIMENTAL /* remember that this resource requested the namespace */ if(sess->module_data[mod->index] == NULL) { /* create new hash if necesary */ sess->module_data[mod->index] = xhash_new(101); pool_cleanup(sess->p, (void (*))(void *) xhash_free, sess->module_data[mod->index]); } xhash_put(sess->module_data[mod->index], pstrdupx(sess->p, NAD_NURI(pkt->nad, targetns), NAD_NURI_L(pkt->nad, targetns)), (void *) 1); #endif snprintf(filter, 4096, "(ns=%i:%.*s)", NAD_NURI_L(pkt->nad, targetns), NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns)); ret = storage_get(sess->user->sm->st, "private", jid_user(sess->jid), filter, &os); switch(ret) { case st_SUCCESS: if(os_iter_first(os)) { o = os_iter_object(os); if(os_object_get_nad(os, o, "xml", &nad)) { result = pkt_new(sess->user->sm, nad_copy(nad)); if(result != NULL) { nad_set_attr(result->nad, 1, -1, "type", "result", 6); pkt_id(pkt, result); pkt_sess(result, sess); pkt_free(pkt); os_free(os); return mod_HANDLED; } } } os_free(os); /* drop through */ log_debug(ZONE, "storage_get succeeded, but couldn't make packet, faking st_NOTFOUND"); case st_NOTFOUND: log_debug(ZONE, "namespace not found, returning"); /* * !!! really, we should just return a 404. 1.4 just slaps a * result on the packet and sends it back. hurrah for * legacy namespaces. */ nad_set_attr(pkt->nad, 1, -1, "type", "result", 6); pkt_sess(pkt_tofrom(pkt), sess); return mod_HANDLED; case st_FAILED: return -stanza_err_INTERNAL_SERVER_ERROR; case st_NOTIMPL: return -stanza_err_FEATURE_NOT_IMPLEMENTED; } } os = os_new(); o = os_object_new(os); snprintf(filter, 4096, "%.*s", NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns)); os_object_put(o, "ns", filter, os_type_STRING); os_object_put(o, "xml", pkt->nad, os_type_NAD); snprintf(filter, 4096, "(ns=%i:%.*s)", NAD_NURI_L(pkt->nad, targetns), NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns)); ret = storage_replace(sess->user->sm->st, "private", jid_user(sess->jid), filter, os); os_free(os); switch(ret) { case st_FAILED: return -stanza_err_INTERNAL_SERVER_ERROR; case st_NOTIMPL: return -stanza_err_FEATURE_NOT_IMPLEMENTED; default: /* create result packet */ result = pkt_create(sess->user->sm, "iq", "result", NULL, NULL); pkt_id(pkt, result); /* and flush it to the session */ pkt_sess(result, sess); #ifdef ENABLE_EXPERIMENTAL /* push it to all resources that read this xmlns item */ snprintf(filter, 4096, "%.*s", NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns)); for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) { /* skip our resource and those that didn't read any private-storage */ if(sscan == sess || sscan->module_data[mod->index] == NULL) continue; /* check whether namespace was read */ if(xhash_get(sscan->module_data[mod->index], filter)) { result = pkt_dup(pkt, jid_full(sscan->jid), NULL); if(result->from != NULL) { jid_free(result->from); nad_set_attr(result->nad, 1, -1, "from", NULL, 0); } pkt_id_new(result); pkt_sess(result, sscan); } } #endif /* finally free the packet */ pkt_free(pkt); return mod_HANDLED; } /* we never get here */ return 0; }
/** handler for read data */ void _sx_process_read(sx_t s, sx_buf_t buf) { sx_error_t sxe; nad_t nad; char *errstring; int i; int ns, elem; /* Note that buf->len can validly be 0 here, if we got data from the socket but the plugin didn't return anything to us (e.g. a SSL packet was split across a tcp segment boundary) */ /* count bytes parsed */ s->pbytes += buf->len; /* parse it */ if(XML_Parse(s->expat, buf->data, buf->len, 0) == 0) { /* only report error we haven't already */ if(!s->fail) { /* parse error */ errstring = (char *) XML_ErrorString(XML_GetErrorCode(s->expat)); _sx_debug(ZONE, "XML parse error: %s, character %d: %.*s", errstring, XML_GetCurrentByteIndex(s->expat) - s->tbytes, buf->len, buf->data); _sx_gen_error(sxe, SX_ERR_XML_PARSE, "XML parse error", errstring); _sx_event(s, event_ERROR, (void *) &sxe); _sx_error(s, stream_err_XML_NOT_WELL_FORMED, errstring); _sx_close(s); _sx_buffer_free(buf); return; } /* !!! is this the right thing to do? we should probably set * s->fail and let the code further down handle it. */ _sx_buffer_free(buf); return; } /* check if the stanza size limit is exceeded (it wasn't reset by parser) */ if(s->rbytesmax && s->pbytes > s->rbytesmax) { /* parse error */ _sx_debug(ZONE, "maximum stanza size (%d) exceeded by reading %d bytes", s->rbytesmax, s->pbytes); errstring = (char *) XML_ErrorString(XML_GetErrorCode(s->expat)); _sx_gen_error(sxe, SX_ERR_XML_PARSE, "stream read error", "Maximum stanza size exceeded"); _sx_event(s, event_ERROR, (void *) &sxe); _sx_error(s, stream_err_POLICY_VIOLATION, errstring); _sx_close(s); _sx_buffer_free(buf); return; } /* count bytes processed */ s->tbytes += buf->len; /* done with the buffer */ _sx_buffer_free(buf); /* process completed nads */ if(s->state >= state_STREAM) while((nad = jqueue_pull(s->rnadq)) != NULL) { int plugin_error; #ifdef SX_DEBUG const char *out; int len; nad_print(nad, 0, &out, &len); _sx_debug(ZONE, "completed nad: %.*s", len, out); #endif /* check for errors */ if(NAD_ENS(nad, 0) >= 0 && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_STREAMS) && strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_STREAMS, strlen(uri_STREAMS)) == 0 && NAD_ENAME_L(nad, 0) == 5 && strncmp(NAD_ENAME(nad, 0), "error", 5) == 0) { errstring = NULL; /* get text error description if available - XMPP 4.7.2 */ if((ns = nad_find_scoped_namespace(nad, uri_STREAM_ERR, NULL)) >= 0) if((elem = nad_find_elem(nad, 0, ns, "text", 1)) >= 0) if(NAD_CDATA_L(nad, elem) > 0) { errstring = (char *) malloc(sizeof(char) * (NAD_CDATA_L(nad, elem) + 1)); sprintf(errstring, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem)); } /* if not available, look for legacy error text as in <stream:error>description</stream:error> */ if (errstring == NULL && NAD_CDATA_L(nad, 0) > 0) { errstring = (char *) malloc(sizeof(char) * (NAD_CDATA_L(nad, 0) + 1)); sprintf(errstring, "%.*s", NAD_CDATA_L(nad, 0), NAD_CDATA(nad, 0)); } /* if not available, log the whole packet for debugging */ if (errstring == NULL) { const char *xml; int xlen; nad_print(nad, 0, &xml, &xlen); errstring = (char *) malloc(sizeof(char) * (xlen + 1)); sprintf(errstring, "%.*s", xlen, xml); } if(s->state < state_CLOSING) { _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", errstring); _sx_event(s, event_ERROR, (void *) &sxe); _sx_state(s, state_CLOSING); } free(errstring); nad_free(nad); break; } /* check for close */ if ((s->flags & SX_WEBSOCKET_WRAPPER) && NAD_ENS(nad, 0) >= 0 && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_XFRAMING) && strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_XFRAMING, strlen(uri_XFRAMING)) == 0 && NAD_ENAME_L(nad, 0) == 5 && strncmp(NAD_ENAME(nad, 0), "close", 5) == 0) { _sx_debug(ZONE, "<close/> frame @ depth %d", s->depth); s->fail = 1; break; } /* run it by the plugins */ if(_sx_chain_nad_read(s, nad) == 0) return; /* now let the plugins process the completed nad */ plugin_error = 0; if(s->env != NULL) for(i = 0; i < s->env->nplugins; i++) if(s->env->plugins[i]->process != NULL) { int plugin_ret; plugin_ret = (s->env->plugins[i]->process)(s, s->env->plugins[i], nad); if(plugin_ret == 0) { plugin_error ++; break; } } /* hand it to the app */ if ((plugin_error == 0) && (s->state < state_CLOSING)) _sx_event(s, event_PACKET, (void *) nad); } /* something went wrong, bail */ if(s->fail) { _sx_close(s); return; } /* stream was closed */ if(s->depth < 0 && s->state < state_CLOSING) { /* close the stream if necessary */ if(s->state >= state_STREAM_SENT) { if (s->flags & SX_WEBSOCKET_WRAPPER) jqueue_push(s->wbufq, _sx_buffer_new("<close xmlns='" uri_XFRAMING "' />", sizeof(uri_XFRAMING) + 17, NULL, NULL), 0); else jqueue_push(s->wbufq, _sx_buffer_new("</stream:stream>", 16, NULL, NULL), 0); s->want_write = 1; } _sx_state(s, state_CLOSING); return; } }