struct pkt *pkt_word_list_new(char **words) { int len = 0; int i; for (i = 0; words[i]; i++) { len += strlen(words[i]) + 1; } if (!len) { pkt_error("pkt_wordlist_new(): empty packet\n"); return NULL; } char *data = malloc(len); if (!data) { pkt_error("pkt_wordlist_new(): unable to allocate %d bytes\n", len); return NULL; } int offset = 0; for (i = 0; words[i]; i++) { strcpy(data + offset, words[i]); offset += strlen(words[i]) + 1; } struct pkt *pkt = pkt_new(PKT_TYPE_WORD_LIST, data, len); return pkt; }
struct pkt *pkt_word_list_new_fixed_len(char *words, int num_words, int max_len) { int len = 0; int i; for (i = 0; i < num_words; i++) { len += strnlen(words + i*max_len, max_len) + 1; } if (!len) { pkt_error("pkt_word_list_new_fixed_len(): empty packet\n"); return NULL; } char *data = malloc(len); if (!data) { pkt_error("pkt_word_list_new_fixed_len(): unable to allocate %d bytes\n", len); return NULL; } int offset = 0; for (i = 0; i < num_words; i++) { int word_len = strnlen(words + i*max_len, max_len); memcpy(data + offset, words + i*max_len, word_len); offset += word_len; *(data + offset++) = 0; } struct pkt *pkt = pkt_new(PKT_TYPE_WORD_LIST, data, len); return pkt; }
/** 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); }
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; }
/* Parse one logical MySQL packet This function has 6 parts: 1. Parse error pkts 2. Handle no last origin 3. Handle client re-sending pkt (set state back to STATE_SLEEP) 4. Set state and number of possible events for state 5. Try to handle pkt given state and possible events 6. Check if pkt was handled/parsed ok If the pkt fails to be handled, something is broken or missing because when 6 fails, it goes to 2 which acts as a kind of catch-all. But if 2 then fails and we get back to 6 a second time, a warning is printed ("Client pkt has no valid handler") and the function returns a failure. */ int parse_pkt(u_char *pkt, u_int len) { u_short state; u_short event; u_short num_events; u_short have_failed_before = 0; PKT_HANDLER ha = 0; if(op.dump) { dump_pkt(pkt, len, 1); return PKT_PARSED_OK; } if(*pkt == 0xFF) { // Error pkt_error(pkt, len); tag->state = STATE_SLEEP; return PKT_PARSED_OK; } /* If there is no last origin, this usually means this is the first pkt we're seeing for this connection. However, this pkt could then be anything, so we have to wait for a reliable starting point which is any client > server command because a client will only send a command from a state of sleep (i.e, after the server is done responding). */ if(!tag->last_origin) { DETERMINE_PKT: if(tag->current_origin == ORIGIN_SERVER) { ha = state_map[STATE_NOT_CONNECTED].ha[0]; // server handshake pkt? if((*ha)(pkt, len) == PKT_HANDLED) { tag->state = state_map[STATE_NOT_CONNECTED].next_state[0]; return PKT_PARSED_OK; } else { /* We're in the middle of a pre-established connection or something weird happened like the server responding for no reason (perhaps the client sent a TCP re-xmit?) */ printf("Waiting for server to finish response... "); dump_pkt(pkt, len, 0); tag->state = STATE_SLEEP; // Will be asleep when done tag->last_origin = 0; return PKT_UNKNOWN_STATE; // Keeps multi_pkts() from setting tag->last_origin } } else { // pkt from client /* Since the MySQL protocol is purely request-response, if a pkt is from the client it must mean MySQL has finished responding and is asleep waiting for further commands. */ tag->state = STATE_SLEEP; // Special cases if(len == 1) { if(*pkt == COM_STATISTICS) tag->state = STATE_ONE_STRING; } } } // Client re-sent pkt (MySQL probably didn't respond to the first pkt) if(tag->last_origin == ORIGIN_CLIENT && tag->current_origin == ORIGIN_CLIENT && tag->state != STATE_SLEEP) { printf("::RETRANSMIT:: "); tag->state = STATE_SLEEP; } // Safeguard if(tag->current_origin == ORIGIN_CLIENT && tag->current_pkt_id == 0 && tag->state != STATE_SLEEP) { tag->state = STATE_SLEEP; } state = tag->state; // Current state num_events = state_map[state].num_events; // Number of possible events for current state if(op.state) printf("state '%s' ", state_name[state]); // Try to handle the pkt... if(num_events == 1) { tag->event = state_map[state].event[0]; ha = state_map[state].ha[0]; if((*ha)(pkt, len)) tag->state = state_map[state].next_state[0]; // pkt was handled else ha = 0; } else { for(event = 0; event < num_events; event++) { tag->event = state_map[state].event[event]; ha = state_map[state].ha[event]; if((*ha)(pkt, len)) { // pkt was handled tag->state = state_map[state].next_state[event]; break; } else ha = 0; } } // ...Check if pkt was handled if(!ha) { printf("::Unhandled Event:: "); if(!have_failed_before) { have_failed_before = 1; // Prevent infinite loop goto DETERMINE_PKT; } else { printf("Client pkt has no valid handler "); dump_pkt(pkt, len, 1); return PKT_UNKNOWN_STATE; } } return PKT_PARSED_OK; }