Logo Search packages:      
Sourcecode: jabberd14 version File versions  Download package

void dialback_out_read ( mio  m,
int  flags,
void *  arg,
xmlnode  x,
char *  unused1,
int  unused2 
)

handle the early connection process

What to do:

  • Send stream header after mio connected a socket for us
  • Process the incoming stream header
  • Check for incoming stream:features
  • Generate db:result queries to start authorizing for a domain
  • Process incoming db:verify queries
  • Process incoming db:result responses (flush/send queue of waiting stanzas)

Definition at line 480 of file dialback_out.cc.

References base64_encode(), dboc::connect_results, connected, dboc::connection_state, could_request, dboc::d, dboc::db, db_failed, dboc::db_state, db_succeeded, dialback_check_settings(), dialback_get_loopcheck_token(), dialback_in_verify(), dialback_merlin(), dialback_miod_hash(), dialback_miod_new(), dialback_out_connect(), dialback_out_connection_cleanup(), dialback_out_qflush(), dialback_out_read_db(), dialback_out_send_verifies(), mio_st::fd, dboc::flags, xmppd::ns_decl_list::get_nsprefix(), got_features, got_streamroot, db_struct::hosts_auth, db_struct::hosts_tls, db_struct::hosts_xmpp, db_struct::i, instance_struct::id, db_struct::in_id, mio_st::in_root, dboc::ip, j_strcasecmp(), j_strcmp(), j_strlen(), dboc::key, log_alert(), log_notice(), log_warn(), dboc::m, mio_close(), mio_reset(), mio_ssl_starttls_possible(), mio_write(), mio_write_root(), mio_xml_reset(), mio_xml_starttls(), not_requested, db_struct::out_ok_db, dboc::p, pmalloco(), pstrdup(), dboc::q, sasl_fail, sasl_started, sasl_success, db_struct::secret, sent_db_request, sent_request, dboc::settings_failed, streamerr_struct::severity, db_struct::std_ns_prefixes, dboc::stream_id, dboc::verifies, waiting_features, want_request, xhash_get(), xhash_get_by_domain(), xmlnode_dup(), xmlnode_free(), xmlnode_get_attrib_ns(), xmlnode_get_data(), xmlnode_get_firstchild(), xmlnode_get_list_item(), xmlnode_get_localname(), xmlnode_get_namespace(), xmlnode_get_nextsibling(), xmlnode_get_tags(), xmlnode_get_type(), xmlnode_hide(), xmlnode_hide_attrib_ns(), xmlnode_insert_cdata(), xmlnode_new_tag_ns(), xmlnode_pool(), xmlnode_put_attrib_ns(), xmlnode_serialize_string(), dboc::xmpp_version, xstream_format_error(), xstream_header(), and xstream_parse_error().

Referenced by dialback_out_connect().

                                                                                           {
    dboc c = (dboc)arg;
    xmlnode cur;
    miod md;

    log_debug2(ZONE, LOGT_IO, "dbout read: fd %d flag %d key %s", m->fd, flags, jid_full(c->key));

    switch (flags) {
      case MIO_NEW:
          log_debug2(ZONE, LOGT_IO, "NEW outgoing server socket connected at %d", m->fd);

          /* add to the connect result messages */
          if (c->connection_state != sasl_success) {
            if (c->connect_results != NULL && c->connection_state != connected) {
                spool_add(c->connect_results, "Connected");
            }
            c->connection_state = connected;
          }

          /* outgoing conneciton, write the header */
          cur = xstream_header(c->key->server, c->key->resource);
          xmlnode_hide_attrib_ns(cur, "id", NULL);                            /* no, we don't need the id on this stream */
          if (j_strcmp(static_cast<char*>(xhash_get_by_domain(c->d->hosts_auth, c->key->server)), "sasl") != 0)
            xmlnode_put_attrib_ns(cur, "db", "xmlns", NS_XMLNS, NS_DIALBACK); /* flag ourselves as dialback capable */
          if (j_strcmp(static_cast<char*>(xhash_get_by_domain(c->d->hosts_xmpp, c->key->server)), "no") != 0) {
            /* we flag support for XMPP 1.0 */
            xmlnode_put_attrib_ns(cur, "version", NULL, NULL, "1.0");
          }
          xmlnode_put_attrib_ns(cur, "check", "loop", NS_JABBERD_LOOPCHECK, dialback_get_loopcheck_token(c->d));
          mio_write_root(m, cur, 0);
          return;

      case MIO_XML_ROOT:
          log_debug2(ZONE, LOGT_IO, "Incoming root %s", xmlnode_serialize_string(x, xmppd::ns_decl_list(), 0));
          if (c->connection_state != sasl_success)
            c->connection_state = got_streamroot;
          else {
            if (dialback_check_settings(c->d, m, c->key->server, 1, 1, c->xmpp_version) == 0) {
                c->settings_failed = 1;
                break;
            }
          }

          /* remember the stream id the connected entity assigned ... required to do dialback */
          c->stream_id = pstrdup(c->p, xmlnode_get_attrib_ns(x, "id", NULL));
          if (c->stream_id == NULL) {
            mio_write(m, NULL, "<stream:error><invalid-id xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xmlns='urn:ietf:params:xml:ns:xmpp-streams' xml:lang='en'>You are missing the id attribute in your stream header!</text></stream:error>", -1);
            mio_close(m);
            break;
          }

          /* make sure we're not connecting to ourselves */
          if (xhash_get(c->d->in_id,c->stream_id) != NULL) {
            log_alert(c->key->server,"hostname maps back to ourselves!- No service defined for this hostname, can not handle request. Check jabberd configuration.");
            mio_write(m, NULL, "<stream:error><internal-server-error xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xmlns='urn:ietf:params:xml:ns:xmpp-streams' xml:lang='en'>Mirror Mirror on the wall (we connected to ourself)</text></stream:error>", -1);
            mio_close(m);
            break;
          }

          /* check version */
          c->xmpp_version = j_atoi(xmlnode_get_attrib_ns(x, "version", NULL), 0);
          try {
            m->in_root->get_nsprefix(NS_DIALBACK);
            c->flags.db = 1;
          } catch (std::invalid_argument) {
            c->flags.db = 0;
          }

          /* deprecated non-dialback protocol, reject connection */
          if (c->xmpp_version < 1 && !c->flags.db) {
            /* Muahahaha!  you suck! *click* */
            log_notice(c->key->server,"Legacy server access denied");
            mio_write(m, NULL, "<stream:error><not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xmlns='urn:ietf:params:xml:ns:xmpp-streams' xml:lang='en'>Legacy Access Denied!</text></stream:error>", -1);
            mio_close(m);
            break;
          }

          /* create and send our result request to initiate dialback for non XMPP-sessions (XMPP has to wait for stream features) */
          if (c->xmpp_version < 1) {
            /* check the require-tls setting */
            if (dialback_check_settings(c->d, m, c->key->server, 1, 0, c->xmpp_version) == 0) {
                c->settings_failed = 1;
                break;
            }

            if (j_strcmp(static_cast<char*>(xhash_get_by_domain(c->d->hosts_auth, "sasl")), "sasl") == 0) {
                log_warn(c->d->i->id, "pre-XMPP 1.0 peer %s cannot support SASL, but we are configured to require this.", c->key->server);
                mio_write(m, NULL, "<stream:error><not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>Sorry, but we require SASL auth, but you seem to only support dialback.</text></stream:error>", -1);
                mio_close(m);
                break;
            }

            log_debug2(ZONE, LOGT_IO, "pre-XMPP 1.0 stream could now send <db:result/>");
            if (c->db_state == want_request) {
                /* send db request */
                cur = xmlnode_new_tag_ns("result", "db", NS_DIALBACK);
                xmlnode_put_attrib_ns(cur, "to", NULL, NULL, c->key->server);
                xmlnode_put_attrib_ns(cur, "from", NULL, NULL, c->key->resource);
                xmlnode_insert_cdata(cur,  dialback_merlin(xmlnode_pool(cur), c->d->secret, c->key->server, c->key->resource, c->stream_id), -1);
                mio_write(m,cur, NULL, 0);
                c->db_state = sent_request;
                c->connection_state = sent_db_request;
                log_debug2(ZONE, LOGT_IO, "... and we wanted ... and we sent <db:result/>");
            } else if (c->db_state == not_requested) {
                c->db_state = could_request;
                log_debug2(ZONE, LOGT_IO, "... but we didn't want yet");
            }
          } else if (c->connection_state != sasl_success) {
            c->connection_state = waiting_features;
          }

          /* well, we're connected to a dialback server, we can at least send verify requests now */
          c->m = m;
          if (c->xmpp_version < 1) {
            for(cur = xmlnode_get_firstchild(c->verifies); cur != NULL; cur = xmlnode_get_nextsibling(cur)) {
                mio_write(m, xmlnode_dup(cur), NULL, -1);
                xmlnode_hide(cur);
            }
          }

          break;
      case MIO_XML_NODE:
          /* watch for stream errors */
          if (j_strcmp(xmlnode_get_localname(x), "error") == 0 && j_strcmp(xmlnode_get_namespace(x), NS_STREAM) == 0) {
            spool s = spool_new(x->p);
            streamerr errstruct = static_cast<streamerr>(pmalloco(x->p, sizeof(_streamerr)));
            char *errmsg = NULL;

            /* generate error message */
            xstream_parse_error(x->p, x, errstruct);
            xstream_format_error(s, errstruct);
            errmsg = spool_print(s);

            /* append error message to connect_results */
            if (c->connect_results != NULL && errmsg != NULL) {
                spool_add(c->connect_results, " (");
                spool_add(c->connect_results, pstrdup(c->connect_results->p, errmsg));
                spool_add(c->connect_results, ")");
            }

            /* logging */
            switch (errstruct->severity) {
                case normal:
                  log_debug2(ZONE, LOGT_IO, "stream error on outgoing%s conn to %s (%s): %s", c->xmpp_version < 0 ? "" : c->xmpp_version == 0 ? " preXMPP" : " XMPP1.0", mio_ip(m), jid_full(c->key), errmsg);
                  break;
                case configuration:
                case feature_lack:
                case unknown:
                  log_warn(c->d->i->id, "received stream error on outgoing%s conn to %s (%s): %s", c->xmpp_version < 0 ? "" : c->xmpp_version == 0 ? " preXMPP" : " XMPP1.0", mio_ip(m), jid_full(c->key), errmsg);
                  break;
                case error:
                default:
                  log_error(c->d->i->id, "received stream error on outgoing%s conn to %s (%s): %s", c->xmpp_version < 0 ? "" : c->xmpp_version == 0 ? " preXMPP" : " XMPP1.0", mio_ip(m), jid_full(c->key), errmsg);
            }
            mio_close(m);
            break;
          }
          /* watch for stream:features */
          if (j_strcmp(xmlnode_get_localname(x), "features") == 0 && j_strcmp(xmlnode_get_namespace(x), NS_STREAM) == 0) {
            xmlnode mechanisms = NULL;

            /* the stream has just restarted after SASL? */
            if (c->connection_state == sasl_success) {
                mio_reset(m, dialback_out_read_db, (void *)(c->d)); /* different handler now */
                md = dialback_miod_new(c->d, m); /* set up the mio wrapper */
                dialback_miod_hash(md, c->d->out_ok_db, c->key); /* this registers us to get stuff directly now */

                /* send the db:verify packets */
                dialback_out_send_verifies(m, c);

                /* flush the queue of packets */
                dialback_out_qflush(md, c->q);
                c->q = NULL;

                /* we are connected, and can trash this now */
                dialback_out_connection_cleanup(c);

                break;
            }

            c->connection_state = got_features;
            /* is starttls supported? */
            if (xmlnode_get_list_item(xmlnode_get_tags(x, "tls:starttls", c->d->std_ns_prefixes), 0) != NULL) {
                /* don't start if forbidden by caller (configuration) */
                if (j_strcmp(static_cast<char*>(xhash_get_by_domain(c->d->hosts_tls, c->key->server)), "no") == 0) {
                  log_notice(c->d->i->id, "Server %s advertized starttls, but disabled by our configuration.", c->key->server);
                } else if (mio_ssl_starttls_possible(m, c->key->resource)) {
                  /* our side is prepared for starttls */
                  xmlnode starttls = NULL;

                  /* request to start tls on this connection */
                  log_debug2(ZONE, LOGT_IO, "requesting starttls for an outgoing connection to %s", c->key->server);

                  starttls = xmlnode_new_tag_ns("starttls", NULL, NS_XMPP_TLS);
                  mio_write(m, starttls, NULL, 0);
                  break;
                }
            }

            /* is sasl-external supported? */
            mechanisms = xmlnode_get_list_item(xmlnode_get_tags(x, "sasl:mechanisms", c->d->std_ns_prefixes), 0);
            if (mechanisms != NULL) {
                xmlnode mechanism = NULL;
                xmlnode auth = NULL;
                char *base64_source_domain = NULL;
                size_t base64_source_domain_len = 0;
                
                /* check for mechanism EXTERNAL */
                for (mechanism = xmlnode_get_firstchild(mechanisms); mechanism!=NULL; mechanism = xmlnode_get_nextsibling(mechanism)) {
                  if (xmlnode_get_type(mechanism) != NTYPE_TAG)
                      continue;
                  if (j_strcasecmp(xmlnode_get_data(mechanism), "EXTERNAL") != 0)
                      continue;

                  /* SASL EXTERNAL is supported: use it */
                  log_debug2(ZONE, LOGT_IO, "SASL EXTERNAL seems to be supported: %s", xmlnode_serialize_string(mechanisms, xmppd::ns_decl_list(), 0));
                  auth = xmlnode_new_tag_ns("auth", NULL, NS_XMPP_SASL);
                  xmlnode_put_attrib_ns(auth, "mechanism", NULL, NULL, xmlnode_get_data(mechanism));

                  /* add our id as base64 encoded CDATA */
                  base64_source_domain_len = (j_strlen(c->key->resource)+2)/3*4+1;
                  base64_source_domain = static_cast<char*>(pmalloco(xmlnode_pool(x), base64_source_domain_len));
                  base64_encode((unsigned char *)c->key->resource, j_strlen(c->key->resource), base64_source_domain, base64_source_domain_len);
                  xmlnode_insert_cdata(auth, base64_source_domain, -1);

                  /* send the initial exchange */
                  log_debug2(ZONE, LOGT_IO, "trying authentication: %s", xmlnode_serialize_string(auth, xmppd::ns_decl_list(), 0));
                  mio_write(m, auth, NULL, 0);

                  c->db_state = sent_request;
                  c->connection_state = sasl_started;
                  break;
                }

                /* SASL EXTERNAL found and used */
                if (mechanism != NULL)
                  break;
            }

            /* no stream:feature we'd like to use, now check the settings */
            if (dialback_check_settings(c->d, m, c->key->server, 1, 0, c->xmpp_version) == 0) {
                c->settings_failed = 1;
                break;
            }

            /* new connection established, we can now send the outstanding db:result - or we could, if we did not want */
            log_debug2(ZONE, LOGT_IO, "XMPP-stream: we could now send <db:result/>s");
            if (c->db_state == want_request) {
                /* send the dialback query */
                cur = xmlnode_new_tag_ns("result", "db", NS_DIALBACK);
                xmlnode_put_attrib_ns(cur, "to", NULL, NULL, c->key->server);
                xmlnode_put_attrib_ns(cur, "from", NULL, NULL, c->key->resource);
                xmlnode_insert_cdata(cur,  dialback_merlin(xmlnode_pool(cur), c->d->secret, c->key->server, c->key->resource, c->stream_id), -1);
                mio_write(m,cur, NULL, 0);
                c->db_state = sent_request;
                c->connection_state = sent_db_request;
                log_debug2(ZONE, LOGT_IO, "... and we wanted ... and we did sent a <db:result/>");
            } else if (c->db_state == not_requested) {
                c->db_state = could_request;
                log_debug2(ZONE, LOGT_IO, "... but we did not want to");
            }

            /* and we can send the verify requests */
            dialback_out_send_verifies(m, c);

            /* finished processing stream:features */
            break;
          }

          /* watch for positive starttls result */
          if (j_strcmp(xmlnode_get_localname(x), "proceed") == 0 && j_strcmp(xmlnode_get_namespace(x), NS_XMPP_TLS) == 0) {
            /* start tls on our side */
            if (mio_xml_starttls(m, 1, c->key->resource)) {
                /* starting tls failed */
                log_warn(c->d->i->id, "Starting TLS on an outgoing s2s to %s failed on our side (%s).", c->key->server, c->key->resource);
                mio_close(m);
                break;
            }

            /* we forget about the headers we got now again */
            c->connection_state = connected;

            /* send stream header again */
            dialback_out_read(m, MIO_NEW, c, NULL, NULL, 0);

            break;
          }

          /* watch for negative starttls result */
          if (j_strcmp(xmlnode_get_localname(x), "failure") == 0 && j_strcmp(xmlnode_get_namespace(x), NS_XMPP_TLS) == 0) {
            log_warn(c->d->i->id, "Starting TLS on an outgoing s2s to %s failed on the other side.", c->key->server);
            mio_close(m);
            break;
          }

          /* watch for SASL success */
          if (j_strcmp(xmlnode_get_localname(x), "success") == 0 && j_strcmp(xmlnode_get_namespace(x), NS_XMPP_SASL) == 0) {
            log_debug2(ZONE, LOGT_IO, "SASL success response: %s", xmlnode_serialize_string(x, xmppd::ns_decl_list(), 0));

            c->connection_state = sasl_success;

            /* reset the stream */
            mio_xml_reset(m);

            /* send stream head again */
            dialback_out_read(m, MIO_NEW, c, NULL, NULL, 0);

            break;
          }

          /* watch for SASL failure */
          if (j_strcmp(xmlnode_get_localname(x), "failure") == 0 && j_strcmp(xmlnode_get_namespace(x), NS_XMPP_SASL) == 0) {
            log_debug2(ZONE, LOGT_IO, "SASL failure response: %s", xmlnode_serialize_string(x, xmppd::ns_decl_list(), 0));
            
            /* something went wrong, we were invalid? */
            c->connection_state = sasl_fail;
            if (c->connect_results != NULL) {
                spool_add(c->connect_results, " (SASL EXTERNAL auth failed: ");
                spool_add(c->connect_results, xmlnode_serialize_string(x, xmppd::ns_decl_list(), 0));
                spool_add(c->connect_results, ")");
            }
            log_alert(c->d->i->id, "SASL EXTERNAL authentication failed on authenticating ourselfs to %s (sending name: %s)", c->key->server, c->key->resource);
            /* close the stream (in former times we sent a stream error, but I think we shouldn't. There is stream fault by the other entity!) */ 
            mio_write(m, NULL, "</stream:stream>", -1);
            mio_close(m);
            break;
          }

          /* watch for a valid result, then we're set to rock! */
          if(j_strcmp(xmlnode_get_localname(x),"result") == 0 && j_strcmp(xmlnode_get_namespace(x), NS_DIALBACK) == 0) {
            if(j_strcmp(xmlnode_get_attrib_ns(x, "from", NULL), c->key->server) != 0 || j_strcmp(xmlnode_get_attrib_ns(x, "to", NULL),c->key->resource) != 0) {
                /* naughty... *click* */
                log_warn(c->d->i->id,"Received illegal dialback validation remote %s != %s or to %s != %s", c->key->server, xmlnode_get_attrib_ns(x, "from", NULL),c->key->resource, xmlnode_get_attrib_ns(x, "to", NULL));
                mio_write(m, NULL, "<stream:error><not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xmlns='urn:ietf:params:xml:ns:xmpp-streams' xml:lang='en'>Invalid Dialback Result</text></stream:error>", -1);
                mio_close(m);
                break;
            }

            /* process the returned result */
            if(j_strcmp(xmlnode_get_attrib_ns(x, "type", NULL),"valid") == 0) {
                c->connection_state = db_succeeded;

                mio_reset(m, dialback_out_read_db, (void *)(c->d)); /* different handler now */
                md = dialback_miod_new(c->d, m); /* set up the mio wrapper */
                dialback_miod_hash(md, c->d->out_ok_db, c->key); /* this registers us to get stuff directly now */

                /* flush the queue of packets */
                dialback_out_qflush(md, c->q);
                c->q = NULL;

                /* we are connected, and can trash this now */
                dialback_out_connection_cleanup(c);
                break;
            }
            /* something went wrong, we were invalid? */
            c->connection_state = db_failed;
            if (c->connect_results != NULL) {
                char *type_attribute = pstrdup(c->connect_results->p, xmlnode_get_attrib_ns(x, "type", NULL));
                spool_add(c->connect_results, " (dialback result: ");
                spool_add(c->connect_results, type_attribute ? type_attribute : "no type attribute");
                spool_add(c->connect_results, ")");
            }
            log_alert(c->d->i->id,"We were told by %s that our sending name %s is invalid, either something went wrong on their end, we tried using that name improperly, or dns does not resolve to us",c->key->server,c->key->resource);
            /* close the stream (in former times we sent a stream error, but I think we shouldn't. There is stream fault by the other entity!) */ 
            mio_write(m, NULL, "</stream:stream>", -1);
            mio_close(m);
            break;
          }

          /* otherwise it's either a verify response, or bust! */
          if (j_strcmp(xmlnode_get_localname(x), "verify") == 0 && j_strcmp(xmlnode_get_namespace(x), NS_DIALBACK) == 0) {
            dialback_in_verify(c->d, x);
            return;
          }

          log_warn(c->d->i->id,"Dropping connection due to illegal incoming packet on an unverified socket from %s to %s (%s): %s",c->key->resource,c->key->server, mio_ip(m), xmlnode_serialize_string(x, xmppd::ns_decl_list(), 0));
          mio_write(m, NULL, "<stream:error><not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xmlns='urn:ietf:params:xml:ns:xmpp-streams' xml:lang='en'>Not Allowed to send data on this socket!</text></stream:error>", -1);
          mio_close(m);
          break;

      case MIO_CLOSED:
          /* add the connect error message to the list of messages for the tried hosts */
          if (c->connect_results != NULL) {
            spool_add(c->connect_results, mio_connect_errmsg(m));
          }
          if(c->ip == NULL) {
            dialback_out_connection_cleanup(c); /* buh bye! */
          } else {
            if (c->connect_results != NULL) {
                spool_add(c->connect_results, " / ");
            }
            dialback_out_connect(c); /* this one failed, try another */
          }
          return;

      default:
          return;
    }
    xmlnode_free(x);
}


Generated by  Doxygen 1.6.0   Back to index