📄 client.c
字号:
/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact * information for Jabber.com, Inc. is available at http://www.jabber.com/. * * Portions Copyright (c) 1998-1999 Jeremie Miller. * * Acknowledgements * * Special thanks to the Jabber Open Source Contributors for their * suggestions and support of Jabber. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * * --------------------------------------------------------------------------*//* <service id="pthsock client"> <host>pth-csock.127.0.0.1</host> <!-- Can be anything --> <load> <pthsock_client>../load/pthsock_client.so</pthsock_client> </load> <pthcsock xmlns='jabber:config:pth-csock'> <alias to="main.host.com">alias.host.com</alias> <alias to="default.host.com"/> <listen>5222</listen> <!-- Port to listen on --> <!-- allow 25 connects per 5 seconts --> <rate time="5" points="25"/> </pthcsock> </service>*/#include <jabberd.h>#define DEFAULT_AUTH_TIMEOUT 120#define DEFAULT_HEARTBEAT 60/* socket manager instance */typedef struct smi_st{ instance i; int auth_timeout; int heartbeat; xht aliases; xht users; xmlnode cfg; char *host; int register_feature; /**< should we advertise the register stream feature? */} *smi, _smi;typedef enum { state_UNKNOWN, state_AUTHD } user_state;typedef struct edata_st{ smi si; int aliased; jid session_id; jid sending_id; user_state state; char *client_id, *sid, *res, *auth_id; time_t connect_time; time_t last_activity; mio m; pth_msgport_t pre_auth_mp;} _cdata,*cdata;/* makes a route packet, intelligently */xmlnode pthsock_make_route(xmlnode x, char *to, char *from, char *type){ xmlnode new; new = x ? xmlnode_wrap(x, "route") : xmlnode_new_tag("route"); if(type != NULL) xmlnode_put_attrib(new, "type", type); if(to != NULL) xmlnode_put_attrib(new, "to", to); if(from != NULL) xmlnode_put_attrib(new, "from", from); return new;}/* incoming jabberd deliver()ed packets */result pthsock_client_packets(instance id, dpacket p, void *arg){ smi s__i = (smi)arg; cdata cdcur; mio m; int fd = 0; if(p->id->user != NULL) fd = atoi(p->id->user); if(p->type != p_ROUTE || fd == 0) { /* we only want <route/> packets or ones with a valid connection */ log_warn(p->host, "pthsock_client bouncing invalid %s packet from %s", xmlnode_get_name(p->x), xmlnode_get_attrib(p->x,"from")); deliver_fail(p, "invalid client packet"); return r_DONE; } if ((cdcur = xhash_get(s__i->users, xmlnode_get_attrib(p->x,"to"))) == NULL) { if (!j_strcmp(xmlnode_get_attrib(p->x, "type"), "session")) { jutil_tofrom(p->x); xmlnode_put_attrib(p->x, "type", "error"); deliver(dpacket_new(p->x), s__i->i); } else { xmlnode_free(p->x); } return r_DONE; } if (fd != cdcur->m->fd || cdcur->m->state != state_ACTIVE) m = NULL; else if (j_strcmp(p->id->resource,cdcur->res) != 0) m = NULL; else m = cdcur->m; if(m == NULL) { if (j_strcmp(xmlnode_get_attrib(p->x, "type"), "error") == 0) { /* we got a 510, but no session to end */ log_debug2(ZONE, LOGT_SESSION, "[%s] received Session close for non-existant session: %s", ZONE, xmlnode_get_attrib(p->x, "from")); xmlnode_free(p->x); return r_DONE; } log_debug2(ZONE, LOGT_SESSION, "[%s] connection not found for %s, closing session", ZONE, xmlnode_get_attrib(p->x, "from")); jutil_tofrom(p->x); xmlnode_put_attrib(p->x, "type", "error"); deliver(dpacket_new(p->x), s__i->i); return r_DONE; } log_debug2(ZONE, LOGT_DELIVER, "[%s] %s has an active session, delivering packet", ZONE, xmlnode_get_attrib(p->x, "from")); if (j_strcmp(xmlnode_get_attrib(p->x, "type"), "error") == 0) { /* <route type="error" means we were disconnected */ log_debug2(ZONE, LOGT_SESSION, "[%s] closing down session %s at request of session manager", ZONE, xmlnode_get_attrib(p->x, "from")); mio_write(m, NULL, "<stream:error><conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xmlns='urn:ietf:params:xml:ns:xmpp-streams' xml:lang='en'>Disconnected</text></stream:error></stream:stream>", -1); mio_close(m); xmlnode_free(p->x); return r_DONE; } else if(cdcur->state == state_UNKNOWN && j_strcmp(xmlnode_get_attrib(p->x, "type"), "auth") == 0) { /* look for our auth packet back */ char *type = xmlnode_get_attrib(xmlnode_get_firstchild(p->x), "type"); char *id = xmlnode_get_attrib(xmlnode_get_tag(p->x, "iq"), "id"); if((j_strcmp(type, "result") == 0) && j_strcmp(cdcur->auth_id, id) == 0) { /* update the cdata status if it's a successfull auth */ xmlnode x; log_debug2(ZONE, LOGT_AUTH|LOGT_SESSION, "[%s], auth for user successful", ZONE); /* notify SM to start a session */ x = pthsock_make_route(NULL, jid_full(cdcur->session_id), cdcur->client_id, "session"); log_debug2(ZONE, LOGT_SESSION, "[%s] requesting Session Start for %s", ZONE, xmlnode_get_attrib(p->x, "from")); deliver(dpacket_new(x), s__i->i); } else if(j_strcmp(type,"error") == 0) { log_record(jid_full(jid_user(cdcur->session_id)), "login", "fail", "%s %s %s", mio_ip(cdcur->m), xmlnode_get_attrib(xmlnode_get_tag(p->x, "iq/error"),"code"), cdcur->session_id->resource); } } else if(cdcur->state == state_UNKNOWN && j_strcmp(xmlnode_get_attrib(p->x, "type"), "session") == 0) { /* got a session reply from the server */ mio_wbq q; cdcur->state = state_AUTHD; log_record(jid_full(jid_user(cdcur->session_id)), "login", "ok", "%s %s", mio_ip(cdcur->m), cdcur->session_id->resource); /* change the host id */ cdcur->session_id = jid_new(m->p, xmlnode_get_attrib(p->x, "from")); xmlnode_free(p->x); /* if we have packets in the queue, write them */ while((q = (mio_wbq)pth_msgport_get(cdcur->pre_auth_mp)) != NULL) { q->x = pthsock_make_route(q->x, jid_full(cdcur->session_id), cdcur->client_id, NULL); deliver(dpacket_new(q->x), s__i->i); } pth_msgport_destroy(cdcur->pre_auth_mp); cdcur->pre_auth_mp = NULL; return r_DONE; } if(xmlnode_get_firstchild(p->x) == NULL || xhash_get(s__i->users, xmlnode_get_attrib(p->x, "to")) == NULL) { xmlnode_free(p->x); } else { /* change the to name to match what the client wants to hear * jer: I have no sympathy for clients that can't handle this right, they should check the stream from="" anyway if(cdcur->aliased) { jid j = jid_new(p->p, xmlnode_get_attrib(xmlnode_get_firstchild(p->x), "to")); if(j != NULL && j_strcmp(j->server, cdcur->sending_id->server) != 0) { jid_set(j, cdcur->sending_id->server, JID_SERVER); xmlnode_put_attrib(xmlnode_get_firstchild(p->x), "to", jid_full(j)); } j = jid_new(p->p, xmlnode_get_attrib(xmlnode_get_firstchild(p->x), "from")); if(j != NULL && j->user == NULL && j_strcmp(j->server, cdcur->session_id->server) == 0) { jid_set(j, cdcur->sending_id->server, JID_SERVER); xmlnode_put_attrib(xmlnode_get_firstchild(p->x), "from", jid_full(j)); } }*/ log_debug2(ZONE, LOGT_IO, "[%s] Writing packet to MIO: %s", ZONE, xmlnode2str(xmlnode_get_firstchild(p->x))); mio_write(m, xmlnode_get_firstchild(p->x), NULL, 0); cdcur->last_activity = time(NULL); } return r_DONE;}cdata pthsock_client_cdata(mio m, smi s__i){ cdata cd; char *buf; cd = pmalloco(m->p, sizeof(_cdata)); cd->pre_auth_mp = pth_msgport_create("pre_auth_mp"); cd->state = state_UNKNOWN; cd->connect_time = time(NULL); cd->last_activity = cd->connect_time; cd->m = m; cd->si = s__i; buf = pmalloco(m->p, 100); /* HACK to fix race conditon */ snprintf(buf, 99, "%X", m); cd->res = pstrdup(m->p, buf); /* we use <fd>@host to identify connetions */ snprintf(buf, 99, "%d@%s/%s", m->fd, s__i->host, cd->res); cd->client_id = pstrdup(m->p, buf); return cd;}void pthsock_client_read(mio m, int flag, void *arg, xmlnode x){ cdata cd = (cdata)arg; xmlnode h; char *alias, *to; int version = 0; if(cd == NULL) return; log_debug2(ZONE, LOGT_IO, "[%s] pthsock_client_read called with: m:%X flag:%d arg:%X", ZONE, m, flag, arg); switch(flag) { case MIO_CLOSED: log_debug2(ZONE, LOGT_IO, "[%s] io_select Socket %d close notification", ZONE, m->fd); xhash_zap(cd->si->users, cd->client_id); if(cd->state == state_AUTHD) { h = pthsock_make_route(NULL, jid_full(cd->session_id), cd->client_id, "error"); deliver(dpacket_new(h), cd->si->i); } if(cd->pre_auth_mp != NULL) { /* if there is a pre_auth queue still */ mio_wbq q; while((q = (mio_wbq)pth_msgport_get(cd->pre_auth_mp)) != NULL) { log_debug2(ZONE, LOGT_IO, "[%s] freeing unsent packet due to disconnect with no auth: %s", ZONE, xmlnode2str(q->x)); xmlnode_free(q->x); } pth_msgport_destroy(cd->pre_auth_mp); cd->pre_auth_mp = NULL; } break; case MIO_ERROR: while((h = mio_cleanup(m)) != NULL) deliver_fail(dpacket_new(h), "Socket Error to Client"); break; case MIO_XML_ROOT: log_debug2(ZONE, LOGT_IO, "[%s] root received for %d", ZONE, m->fd); to = xmlnode_get_attrib(x, "to"); cd->sending_id = jid_new(cd->m->p, to); /* check for XMPP version attribute */ version = j_atoi(xmlnode_get_attrib(x, "version"), 0); /* check for a matching alias or use default alias */ log_debug2(ZONE, LOGT_IO, "[%s] Recieved connection to: %s", ZONE, jid_full(cd->sending_id)); alias = xhash_get(cd->si->aliases, to); alias = alias ? alias : xhash_get(cd->si->aliases, "default"); /* set host to that alias, or to the given host */ cd->session_id = alias ? jid_new(m->p, alias) : cd->sending_id; /* if we are using an alias, set the alias flag */ if(j_strcmp(jid_full(cd->session_id), jid_full(cd->sending_id)) != 0) cd->aliased = 1; if(cd->aliased) log_debug2(ZONE, LOGT_SESSION, "[%s] using alias %s --> %s", ZONE, jid_full(cd->sending_id), jid_full(cd->session_id)); /* write header */ h = xstream_header("jabber:client", NULL, jid_full(cd->session_id)); cd->sid = pstrdup(m->p, xmlnode_get_attrib(h, "id")); /* XXX hack in the style that jabber.com uses for flash mode support */ if(j_strncasecmp(xmlnode_get_attrib(x, "xmlns:flash"), "http://www.jabber.com/streams/flash",35) == 0) { h = xmlnode_new_tag_pool(xmlnode_pool(h),"flash:stream"); xmlnode_put_attrib(h, "xmlns:flash", "http://www.jabber.com/streams/flash"); xmlnode_put_attrib(h, "xmlns:stream", "http://etherx.jabber.org/streams"); xmlnode_put_attrib(h, "xmlns", "jabber:client"); xmlnode_put_attrib(h, "id", cd->sid); xmlnode_put_attrib(h, "from", jid_full(cd->session_id)); /* put real stream declaration on incoming root */ xmlnode_put_attrib(x, "xmlns:stream", "http://etherx.jabber.org/streams"); } if (version>=1) { xmlnode_put_attrib(h, "version", "1.0"); } mio_write(m, NULL, xstream_header_char(h), -1); xmlnode_free(h); if(j_strcmp(xmlnode_get_attrib(x, "xmlns"), "jabber:client") != 0) { /* if they sent something other than jabber:client */ mio_write(m, NULL, "<stream:error><invalid-namespace xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xmlns='urn:ietf:params:xml:ns:xmpp-streams' xml:lang='en'>Invalid Namespace</text></stream:error></stream:stream>", -1); mio_close(m); } else if(cd->session_id == NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -