jabberd2  2.3.3
mod_session.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002-2003 Jeremie Miller, Thomas Muldowney,
4  * Ryan Eatmon, Robert Norris
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
19  */
20 
21 #include "sm.h"
22 
45 /* union for xhash_iter_get to comply with strict-alias rules for gcc3 */
46 union xhashv
47 {
48  void **val;
50 };
51 
53  sm_t sm = mi->mod->mm->sm;
54  int ns, iq, elem, attr;
55  jid_t jid;
56  sess_t sess = (sess_t) NULL;
57  mod_ret_t ret;
58 
59  /* if we've got this namespace, its from a c2s */
60  if(pkt->nad->ecur <= 1 || (ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL)) < 0)
61  return mod_PASS;
62 
63  /* don't bother if its a failure */
64  if(pkt->type & pkt_SESS_FAILED) {
65  /* !!! check failed=1, handle */
66  pkt_free(pkt);
67  return mod_HANDLED;
68  }
69 
70  /* session commands */
71  if(pkt->type & pkt_SESS) {
72 
73  ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL);
74 
75  /* only end can get away without a target */
76  attr = nad_find_attr(pkt->nad, 1, -1, "target", NULL);
77  if(attr < 0 && pkt->type != pkt_SESS_END) {
78  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
79  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
80 
81  pkt->nad = NULL;
82  pkt_free(pkt);
83 
84  return mod_HANDLED;
85  }
86 
87  /* session start */
88  if(pkt->type == pkt_SESS) {
89  jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
90 
91  if(jid != NULL)
92  sess = sess_start(sm, jid);
93 
94  if(jid == NULL || sess == NULL) {
95  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
96  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
97 
98  pkt->nad = NULL;
99  pkt_free(pkt);
100  if(jid != NULL)
101  jid_free(jid);
102 
103  return mod_HANDLED;
104  }
105 
106  /* c2s component that is handling this session */
107  strcpy(sess->c2s, pkt->rfrom->domain);
108 
109  /* remember what c2s calls us */
110  attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
111  snprintf(sess->c2s_id, sizeof(sess->c2s_id), "%.*s", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
112 
113  /* mark PBX session as fake */
114  if(!strncmp("PBX", sess->c2s_id, 3)) {
115  sess->fake = 1;
116  }
117 
118  /* add our id */
119  nad_set_attr(pkt->nad, 1, ns, "sm", sess->sm_id, 0);
120 
121  /* mark that it started OK */
122  nad_set_attr(pkt->nad, 1, -1, "action", "started", 7);
123 
124  /* set this SM name */
125  nad_set_attr(pkt->nad, 0, -1, "to", sm->id, 0);
126 
127  /* inform c2s */
128  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
129 
130  pkt->nad = NULL;
131  pkt_free(pkt);
132  jid_free(jid);
133 
134  return mod_HANDLED;
135  }
136 
137  /* user create */
138  if(pkt->type == pkt_SESS_CREATE) {
139  jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
140 
141  if(jid == NULL || user_create(sm, jid) != 0) {
142  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
143  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
144 
145  pkt->nad = NULL;
146  pkt_free(pkt);
147  if(jid != NULL)
148  jid_free(jid);
149 
150  return mod_HANDLED;
151  }
152 
153  /* inform c2s */
154  nad_set_attr(pkt->nad, 1, -1, "action", "created", 7);
155  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
156 
157  pkt->nad = NULL;
158  pkt_free(pkt);
159  jid_free(jid);
160 
161  return mod_HANDLED;
162  }
163 
164  /* user delete */
165  if(pkt->type == pkt_SESS_DELETE) {
166  jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
167  if(jid == NULL) {
168  pkt_free(pkt);
169  return mod_HANDLED;
170  }
171 
172  user_delete(sm, jid);
173 
174  /* inform c2s */
175  nad_set_attr(pkt->nad, 1, -1, "action", "deleted", 7);
176  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
177 
178  pkt->nad = NULL;
179  pkt_free(pkt);
180  jid_free(jid);
181 
182  return mod_HANDLED;
183  }
184 
185  /* get the session id */
186  attr = nad_find_attr(pkt->nad, 1, ns, "sm", NULL);
187  if(attr < 0) {
188  log_debug(ZONE, "no session id, bouncing");
189  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
190  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
191 
192  pkt->nad = NULL;
193  pkt_free(pkt);
194 
195  return mod_HANDLED;
196  }
197 
198  /* find the corresponding session */
199  sess = xhash_getx(sm->sessions, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
200 
201  /* active check */
202  if(sess == NULL) {
203  log_debug(ZONE, "session %.*s doesn't exist, bouncing", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
204  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
205  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
206 
207  pkt->nad = NULL;
208  pkt_free(pkt);
209 
210  return mod_HANDLED;
211  }
212 
213  /* make sure its from them */
214  attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
215  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)) {
216  log_debug(ZONE, "invalid sender on route from %s for session %s (should be %s)", pkt->rfrom->domain, sess->sm_id, sess->c2s_id);
217  pkt_free(pkt);
218  return mod_HANDLED;
219  }
220 
221  /* session end */
222  if(pkt->type == pkt_SESS_END) {
223  sm_c2s_action(sess, "ended", NULL);
224  sess_end(sess);
225 
226  pkt_free(pkt);
227  return mod_HANDLED;
228  }
229 
230  log_debug(ZONE, "unknown session packet, dropping");
231  pkt_free(pkt);
232 
233  return mod_HANDLED;
234  }
235 
236  /* otherwise, its a normal packet for the session */
237 
238 /* #ifdef ENABLE_SUPERSEDED // FIXME XXX TODO clients are not yet ready for this */
239  /* check for RFC3920 session request *
240  * with RFC3920bis it is unneeded *
241  * session is activated by bind, so we just return back result */
242  if((ns = nad_find_scoped_namespace(pkt->nad, uri_XSESSION, NULL)) >= 0 &&
243  (iq = nad_find_elem(pkt->nad, 0, -1, "iq", 1)) >= 0 &&
244  (elem = nad_find_elem(pkt->nad, iq, ns, "session", 1)) >= 0) {
245  log_debug(ZONE, "session create request");
246 
247  /* build a result packet */
248  nad_drop_elem(pkt->nad, elem);
249  nad_set_attr(pkt->nad, iq, -1, "type", "result", 6);
250 
251  /* return the result */
252  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
253 
254  pkt->nad = NULL;
255  pkt_free(pkt);
256 
257  return mod_HANDLED;
258  }
259 /* #endif */
260  /* get the session id */
261  attr = nad_find_attr(pkt->nad, 1, ns, "sm", NULL);
262  if(attr < 0) {
263  log_debug(ZONE, "no session id, bouncing");
264  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
265  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
266 
267  pkt->nad = NULL;
268  pkt_free(pkt);
269 
270  return mod_HANDLED;
271  }
272 
273  /* find the corresponding session */
274  sess = xhash_getx(sm->sessions, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
275 
276  /* active check */
277  if(sess == NULL) {
278  log_debug(ZONE, "session %.*s doesn't exist, bouncing", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
279  nad_set_attr(pkt->nad, 1, ns, "failed", "1", 1);
280  sx_nad_write(sm->router, stanza_tofrom(pkt->nad, 0));
281 
282  pkt->nad = NULL;
283  pkt_free(pkt);
284 
285  return mod_HANDLED;
286  }
287 
288  /* make sure its from them */
289  attr = nad_find_attr(pkt->nad, 1, ns, "c2s", NULL);
290  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)) {
291  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);
292  pkt_free(pkt);
293  return mod_HANDLED;
294  }
295 
296  /* where it came from */
297  pkt->source = sess;
298 
299  /* hand it to the modules */
300  ret = mm_in_sess(pkt->sm->mm, sess, pkt);
301  switch(ret) {
302  case mod_HANDLED:
303  break;
304 
305  case mod_PASS:
306  /* ignore IQ result packets that haven't been handled - XMPP 9.2.3.4 */
307  if(pkt->type == pkt_IQ_RESULT)
308  break;
309  else
311 
312  default:
313  pkt_sess(pkt_error(pkt, -ret), sess);
314 
315  break;
316  }
317 
318  return mod_HANDLED;
319 }
320 
322  sess_t sess;
323  union xhashv xhv;
324 
325  /* we want unadvertisments */
326  if(pkt->from == NULL || !(pkt->rtype & route_ADV) || pkt->rtype != route_ADV_UN)
327  return mod_PASS;
328 
329  log_debug(ZONE, "component '%s' went offline, checking for sessions held there", jid_full(pkt->from));
330 
331  /* this is fairly inefficient, especially if we have a lot of sessions
332  * online, but it shouldn't be called that often (components are usually
333  * long-running) */
334 
335  xhv.sess_val = &sess;
336  if(xhash_iter_first(mi->mod->mm->sm->sessions))
337  do {
338  xhash_iter_get(mi->mod->mm->sm->sessions, NULL, NULL, xhv.val);
339  if(sess && strcmp(sess->c2s, pkt->from->domain) == 0)
340  sess_end(sess);
341  } while (xhash_iter_next(mi->mod->mm->sm->sessions));
342 
343  return mod_PASS;
344 }
345 
346 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
347  if(mi->mod->init) return 0;
348 
351 
352  return 0;
353 }
pkt_t pkt_error(pkt_t pkt, int err)
Definition: pkt.c:30
pkt_type_t type
packet type
Definition: sm.h:138
static sm_t sm
Definition: main.c:33
struct sess_st * sess_t
Definition: c2s.h:55
data structures and prototypes for the session manager
session end request
Definition: sm.h:110
SM_API void user_delete(sm_t sm, jid_t jid)
trash a user
Definition: user.c:121
const char * id
component id
Definition: sm.h:168
#define sx_nad_write(s, nad)
Definition: sx.h:166
session start request
Definition: sm.h:109
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:347
jid_t rfrom
addressing of enclosing route
Definition: sm.h:134
jid_t jid_new(const char *id, int len)
make a new jid
Definition: jid.c:81
single instance of a module in a chain
Definition: sm.h:445
int nad_find_elem(nad_t nad, int elem, int ns, const char *name, int depth)
locate the next elem at a given depth with an optional matching name
Definition: nad.c:204
void * xhash_getx(xht h, const char *key, int len)
Definition: xhash.c:170
int init
number of times the module intialiser has been called
Definition: sm.h:415
mm_t mm
module subsystem
Definition: sm.h:212
void sm_c2s_action(sess_t dest, const char *action, const char *target)
send a new action route
Definition: sm.c:280
#define stanza_err_FEATURE_NOT_IMPLEMENTED
Definition: util.h:369
info/query (result)
Definition: sm.h:108
mod_ret_t(* in_router)(mod_instance_t mi, pkt_t pkt)
in-router handler
Definition: sm.h:423
sm_t sm
sm context
Definition: sm.h:130
int xhash_iter_next(xht h)
Definition: xhash.c:320
xht sessions
pointers to all connected sessions (key is random sm id)
Definition: sm.h:190
char c2s_id[44]
remote id (for session control)
Definition: sm.h:262
mm_t mm
module manager
Definition: sm.h:403
#define DLLEXPORT
Definition: c2s.h:47
void nad_set_attr(nad_t nad, int elem, int ns, const char *name, const char *val, int vallen)
create, update, or zap any matching attr on this elem
Definition: nad.c:375
sm_t sm
sm context
Definition: sm.h:365
void sess_end(sess_t sess)
Definition: sess.c:85
#define uri_XSESSION
Definition: uri.h:43
mod_ret_t(* pkt_router)(mod_instance_t mi, pkt_t pkt)
pkt-router handler
Definition: sm.h:431
module_t mod
module that this is an instance of
Definition: sm.h:448
jid_t from
packet addressing (not used for routing)
Definition: sm.h:140
sx_t router
SX of router connection.
Definition: sm.h:185
void ** val
Definition: c2s.h:384
packet summary data wrapper
Definition: sm.h:129
nad_t nad
nad of the entire packet
Definition: sm.h:146
session manager global context
Definition: sm.h:167
void jid_free(jid_t jid)
free a jid
Definition: jid.c:286
advertisement (available)
Definition: sm.h:123
session delete request
Definition: sm.h:112
sess_t * sess_val
Definition: c2s.h:386
static mod_ret_t _session_in_router(mod_instance_t mi, pkt_t pkt)
Definition: mod_session.c:52
DLLEXPORT int module_init(mod_instance_t mi, const char *arg)
Definition: mod_session.c:346
char * domain
Definition: jid.h:45
char sm_id[41]
local id (for session control)
Definition: sm.h:261
nad_t stanza_tofrom(nad_t nad, int elem)
flip the to and from attributes on this elem
Definition: stanza.c:78
Definition: jid.h:42
int ecur
Definition: nad.h:105
sess_t sess_start(sm_t sm, jid_t jid)
Definition: sess.c:103
int xhash_iter_get(xht h, const char **key, int *keylen, void **val)
Definition: xhash.c:374
#define NAD_AVAL_L(N, A)
Definition: nad.h:190
void pkt_free(pkt_t pkt)
Definition: pkt.c:315
#define log_debug(...)
Definition: log.h:65
#define NAD_AVAL(N, A)
Definition: nad.h:189
packet was unhandled, should be passed to the next module
Definition: sm.h:339
mod_ret_t mm_in_sess(mm_t mm, sess_t sess, pkt_t pkt)
packets from active session
Definition: mm.c:441
packet was handled (and freed)
Definition: sm.h:338
There is one instance of this struct per user who is logged in to this c2s instance.
Definition: c2s.h:74
int xhash_iter_first(xht h)
iteration
Definition: xhash.c:311
int fake
true if session is fake (ie.
Definition: sm.h:268
static mod_ret_t _session_pkt_router(mod_instance_t mi, pkt_t pkt)
Definition: mod_session.c:321
int nad_find_namespace(nad_t nad, int elem, const char *uri, const char *prefix)
get a matching ns on this elem, both uri and optional prefix
Definition: nad.c:262
void nad_drop_elem(nad_t nad, int elem)
remove an element (and its subelements)
Definition: nad.c:452
#define ZONE
Definition: mio_impl.h:76
session packet handling
Definition: c2s.h:382
advertisement (unavailable)
Definition: sm.h:124
int nad_find_attr(nad_t nad, int elem, int ns, const char *name, const char *val)
get a matching attr on this elem, both name and optional val
Definition: nad.c:235
c2s_t c2s
Definition: c2s.h:75
session create request
Definition: sm.h:111
#define uri_SESSION
Definition: uri.h:52
mod_ret_t
module return values
Definition: sm.h:337
sess_t source
session this packet came from
Definition: sm.h:132
void pkt_sess(pkt_t pkt, sess_t sess)
Definition: pkt.c:459
SM_API int user_create(sm_t sm, jid_t jid)
initialise a user
Definition: user.c:88
int nad_find_scoped_namespace(nad_t nad, const char *uri, const char *prefix)
find a namespace in scope
Definition: nad.c:290
session request failed (mask)
Definition: sm.h:113
route_type_t rtype
type of enclosing route
Definition: sm.h:136