Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* conn.c
3 : ** strophe XMPP client library -- connection object functions
4 : **
5 : ** Copyright (C) 2005-2009 Collecta, Inc.
6 : **
7 : ** This software is provided AS-IS with no warranty, either express
8 : ** or implied.
9 : **
10 : ** This program is dual licensed under the MIT or GPLv3 licenses.
11 : */
12 :
13 : /** @file
14 : * Connection management.
15 : */
16 :
17 : /** @defgroup Connections Connection management
18 : * These functions manage a connection object.
19 : *
20 : * A part of those functions is listed under the \ref TLS section.
21 : */
22 :
23 : #include <errno.h>
24 : #include <stdarg.h>
25 : #include <string.h>
26 : #include <limits.h>
27 :
28 : #include "strophe.h"
29 :
30 : #include "common.h"
31 : #include "util.h"
32 : #include "parser.h"
33 :
34 : #ifndef DEFAULT_SEND_QUEUE_MAX
35 : /** @def DEFAULT_SEND_QUEUE_MAX
36 : * The default maximum send queue size. This is currently unused.
37 : */
38 : #define DEFAULT_SEND_QUEUE_MAX 64
39 : #endif
40 : #ifndef DISCONNECT_TIMEOUT
41 : /** @def DISCONNECT_TIMEOUT
42 : * The time to wait (in milliseconds) for graceful disconnection to
43 : * complete before the connection is reset. The default is 2 seconds.
44 : */
45 : #define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
46 : #endif
47 : #ifndef CONNECT_TIMEOUT
48 : /** @def CONNECT_TIMEOUT
49 : * The time to wait (in milliseconds) for a connection attempt to succeed
50 : * or error. The default is 5 seconds.
51 : */
52 : #define CONNECT_TIMEOUT 5000 /* 5 seconds */
53 : #endif
54 :
55 : #ifndef KEEPALIVE_TIMEOUT
56 : /** @def KEEPALIVE_TIMEOUT
57 : * The time (in seconds) the connection needs to remain idle before TCP starts
58 : * sending keepalive probes, if the socket option SO_KEEPALIVE has been set on
59 : * this socket.
60 : * c.f. `TCP_KEEPIDLE` in `man 7 tcp` for linux, FreeBSD and some others or
61 : * `TCP_KEEPALIVE` on MacOS.
62 : */
63 : #define KEEPALIVE_TIMEOUT 60
64 : #endif
65 : #ifndef KEEPALIVE_INTERVAL
66 : /** @def KEEPALIVE_INTERVAL
67 : * The time (in seconds) between individual keepalive probes.
68 : * c.f. `TCP_KEEPINTVL` in `man 7 tcp`
69 : */
70 : #define KEEPALIVE_INTERVAL 30
71 : #endif
72 : #ifndef KEEPALIVE_COUNT
73 : /** @def KEEPALIVE_COUNT
74 : * The maximum number of keepalive probes TCP should send before dropping the
75 : * connection.
76 : * c.f. `TCP_KEEPCNT` in `man 7 tcp`
77 : */
78 : #define KEEPALIVE_COUNT 3
79 : #endif
80 :
81 : static int _is_connected(xmpp_conn_t *conn, xmpp_send_queue_owner_t owner);
82 : static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata);
83 : static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn);
84 : static char *_conn_build_stream_tag(xmpp_conn_t *conn,
85 : char **attributes,
86 : size_t attributes_len);
87 : static int _conn_open_stream_with_attributes(xmpp_conn_t *conn,
88 : char **attributes,
89 : size_t attributes_len);
90 : static void _conn_attributes_new(xmpp_conn_t *conn,
91 : char **attrs,
92 : char ***attributes,
93 : size_t *attributes_len);
94 : static void _conn_attributes_destroy(xmpp_conn_t *conn,
95 : char **attributes,
96 : size_t attributes_len);
97 : static void _handle_stream_start(char *name, char **attrs, void *userdata);
98 : static void _handle_stream_end(char *name, void *userdata);
99 : static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata);
100 : static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
101 : xmpp_stanza_t *stanza);
102 : static unsigned short _conn_default_port(xmpp_conn_t *conn,
103 : xmpp_conn_type_t type);
104 : static void _conn_reset(xmpp_conn_t *conn);
105 : static int _conn_connect(xmpp_conn_t *conn,
106 : const char *domain,
107 : xmpp_conn_type_t type,
108 : xmpp_conn_handler callback,
109 : void *userdata);
110 : static void _send_valist(xmpp_conn_t *conn,
111 : const char *fmt,
112 : va_list ap,
113 : xmpp_send_queue_owner_t owner);
114 : static int _send_raw(xmpp_conn_t *conn,
115 : char *data,
116 : size_t len,
117 : xmpp_send_queue_owner_t owner,
118 : void *userdata);
119 :
120 0 : void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text)
121 : {
122 0 : xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text);
123 :
124 0 : send_stanza(conn, error, XMPP_QUEUE_STROPHE);
125 0 : }
126 :
127 : /** Create a new Strophe connection object.
128 : *
129 : * @param ctx a Strophe context object
130 : *
131 : * @return a Strophe connection object or NULL on an error
132 : *
133 : * @ingroup Connections
134 : */
135 8 : xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
136 : {
137 8 : xmpp_conn_t *conn = NULL;
138 8 : xmpp_connlist_t *tail, *item;
139 :
140 8 : if (ctx == NULL)
141 : return NULL;
142 :
143 8 : conn = strophe_alloc(ctx, sizeof(xmpp_conn_t));
144 8 : if (conn != NULL) {
145 8 : memset(conn, 0, sizeof(xmpp_conn_t));
146 8 : conn->ctx = ctx;
147 :
148 8 : conn->type = XMPP_UNKNOWN;
149 8 : conn->state = XMPP_STATE_DISCONNECTED;
150 :
151 8 : conn->sock = INVALID_SOCKET;
152 8 : conn->ka_timeout = KEEPALIVE_TIMEOUT;
153 8 : conn->ka_interval = KEEPALIVE_INTERVAL;
154 8 : conn->ka_count = KEEPALIVE_COUNT;
155 :
156 : /* default send parameters */
157 8 : conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
158 :
159 : /* default timeouts */
160 8 : conn->connect_timeout = CONNECT_TIMEOUT;
161 :
162 8 : conn->lang = strophe_strdup(conn->ctx, "en");
163 8 : if (!conn->lang) {
164 0 : strophe_free(conn->ctx, conn);
165 0 : return NULL;
166 : }
167 8 : tls_clear_password_cache(conn);
168 8 : conn->password_retries = 1;
169 :
170 16 : conn->parser =
171 8 : parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
172 : _handle_stream_stanza, conn);
173 : /* we own (and will free) the hash values */
174 8 : conn->id_handlers = hash_new(conn->ctx, 32, NULL);
175 :
176 : /* give the caller a reference to connection */
177 8 : conn->ref = 1;
178 :
179 : /* add connection to ctx->connlist */
180 8 : tail = conn->ctx->connlist;
181 8 : while (tail && tail->next)
182 : tail = tail->next;
183 :
184 8 : item = strophe_alloc(conn->ctx, sizeof(xmpp_connlist_t));
185 8 : if (!item) {
186 0 : strophe_error(conn->ctx, "xmpp", "failed to allocate memory");
187 0 : strophe_free(conn->ctx, conn->lang);
188 0 : parser_free(conn->parser);
189 0 : strophe_free(conn->ctx, conn);
190 0 : conn = NULL;
191 : } else {
192 8 : item->conn = conn;
193 8 : item->next = NULL;
194 :
195 8 : if (tail)
196 0 : tail->next = item;
197 : else
198 8 : conn->ctx->connlist = item;
199 : }
200 : }
201 :
202 : return conn;
203 : }
204 :
205 : /** Clone a Strophe connection object.
206 : *
207 : * @param conn a Strophe connection object
208 : *
209 : * @return the same conn object passed in with its reference count
210 : * incremented by 1
211 : *
212 : * @ingroup Connections
213 : */
214 0 : xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn)
215 : {
216 0 : conn->ref++;
217 0 : return conn;
218 : }
219 :
220 : /** Register sockopt callback
221 : * Set function to be called when a new socket is created to allow setting
222 : * socket options before connection is started.
223 : *
224 : * If the connection is already connected, this callback will be called
225 : * immediately.
226 : *
227 : * To set options that can only be applied to disconnected sockets, the
228 : * callback must be registered before connecting.
229 : *
230 : * @param conn The Strophe connection object this callback is being registered
231 : * for
232 : * @param callback a xmpp_sockopt_callback callback function that will receive
233 : * notifications of connection status
234 : *
235 : * @ingroup Connections
236 : */
237 :
238 0 : void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
239 : xmpp_sockopt_callback callback)
240 : {
241 0 : conn->sockopt_cb = callback;
242 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
243 0 : callback(conn, &conn->sock);
244 0 : }
245 :
246 : /** Release a Strophe connection object.
247 : * Decrement the reference count by one for a connection, freeing the
248 : * connection object if the count reaches 0.
249 : *
250 : * @param conn a Strophe connection object
251 : *
252 : * @return TRUE if the connection object was freed and FALSE otherwise
253 : *
254 : * @ingroup Connections
255 : */
256 8 : int xmpp_conn_release(xmpp_conn_t *conn)
257 : {
258 8 : xmpp_ctx_t *ctx;
259 8 : xmpp_connlist_t *item, *prev;
260 8 : xmpp_handlist_t *hlitem, *thli;
261 8 : hash_iterator_t *iter;
262 8 : const char *key;
263 8 : int released = 0;
264 :
265 8 : if (conn->ref > 1)
266 0 : conn->ref--;
267 : else {
268 8 : ctx = conn->ctx;
269 :
270 8 : if (conn->state == XMPP_STATE_CONNECTING ||
271 : conn->state == XMPP_STATE_CONNECTED) {
272 0 : conn_disconnect(conn);
273 : }
274 :
275 : /* remove connection from context's connlist */
276 8 : if (ctx->connlist->conn == conn) {
277 8 : item = ctx->connlist;
278 8 : ctx->connlist = item->next;
279 8 : strophe_free(ctx, item);
280 : } else {
281 : prev = NULL;
282 : item = ctx->connlist;
283 0 : while (item && item->conn != conn) {
284 0 : prev = item;
285 0 : item = item->next;
286 : }
287 :
288 0 : if (!item) {
289 0 : strophe_error(ctx, "xmpp",
290 : "Connection not in context's list\n");
291 : } else {
292 0 : prev->next = item->next;
293 0 : strophe_free(ctx, item);
294 : }
295 : }
296 :
297 8 : _conn_reset(conn);
298 :
299 : /* free handler stuff
300 : * note that userdata is the responsibility of the client
301 : * and the handler pointers don't need to be freed since they
302 : * are pointers to functions */
303 :
304 8 : hlitem = conn->timed_handlers;
305 8 : while (hlitem) {
306 0 : thli = hlitem;
307 0 : hlitem = hlitem->next;
308 :
309 0 : strophe_free(ctx, thli);
310 : }
311 :
312 : /* id handlers
313 : * we have to traverse the hash table freeing list elements
314 : * then release the hash table */
315 8 : iter = hash_iter_new(conn->id_handlers);
316 8 : while ((key = hash_iter_next(iter))) {
317 0 : hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
318 0 : while (hlitem) {
319 0 : thli = hlitem;
320 0 : hlitem = hlitem->next;
321 0 : strophe_free(conn->ctx, thli->u.id);
322 0 : strophe_free(conn->ctx, thli);
323 : }
324 : }
325 8 : hash_iter_release(iter);
326 8 : hash_release(conn->id_handlers);
327 :
328 8 : hlitem = conn->handlers;
329 8 : while (hlitem) {
330 0 : thli = hlitem;
331 0 : hlitem = hlitem->next;
332 :
333 0 : if (thli->u.ns)
334 0 : strophe_free(ctx, thli->u.ns);
335 0 : if (thli->u.name)
336 0 : strophe_free(ctx, thli->u.name);
337 0 : if (thli->u.type)
338 0 : strophe_free(ctx, thli->u.type);
339 0 : strophe_free(ctx, thli);
340 : }
341 :
342 8 : parser_free(conn->parser);
343 :
344 8 : if (conn->jid)
345 0 : strophe_free(ctx, conn->jid);
346 8 : if (conn->pass)
347 0 : strophe_free(ctx, conn->pass);
348 8 : if (conn->lang)
349 8 : strophe_free(ctx, conn->lang);
350 8 : if (conn->tls_client_cert)
351 8 : strophe_free(ctx, conn->tls_client_cert);
352 8 : if (conn->tls_client_key)
353 2 : strophe_free(ctx, conn->tls_client_key);
354 8 : if (conn->tls_cafile)
355 0 : strophe_free(ctx, conn->tls_cafile);
356 8 : if (conn->tls_capath)
357 0 : strophe_free(ctx, conn->tls_capath);
358 8 : if (conn->sm_state)
359 0 : xmpp_free_sm_state(conn->sm_state);
360 8 : tls_clear_password_cache(conn);
361 8 : sock_free(conn->xsock);
362 8 : strophe_free(ctx, conn);
363 8 : released = 1;
364 : }
365 :
366 8 : return released;
367 : }
368 :
369 : /** Get the JID which is or will be bound to the connection.
370 : *
371 : * @param conn a Strophe connection object
372 : *
373 : * @return a string containing the full JID or NULL if it has not been set
374 : *
375 : * @ingroup Connections
376 : */
377 0 : const char *xmpp_conn_get_jid(const xmpp_conn_t *conn)
378 : {
379 0 : return conn->jid;
380 : }
381 :
382 : /**
383 : * Get the JID discovered during binding time.
384 : *
385 : * This JID will contain the resource used by the current connection.
386 : * This is useful in the case where a resource was not specified for
387 : * binding.
388 : *
389 : * @param conn a Strophe connection object.
390 : *
391 : * @return a string containing the full JID or NULL if it's not been discovered
392 : *
393 : * @ingroup Connections
394 : */
395 0 : const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn)
396 : {
397 0 : return conn->bound_jid;
398 : }
399 :
400 : /** Set the JID of the user that will be bound to the connection.
401 : * If any JID was previously set, it will be discarded. This should not be
402 : * be used after a connection is created. The function will make a copy of
403 : * the JID string. If the supplied JID is missing the node, SASL
404 : * ANONYMOUS authentication will be used.
405 : *
406 : * @param conn a Strophe connection object
407 : * @param jid a full or bare JID
408 : *
409 : * @ingroup Connections
410 : */
411 0 : void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
412 : {
413 0 : if (conn->jid)
414 0 : strophe_free(conn->ctx, conn->jid);
415 0 : conn->jid = strophe_strdup(conn->ctx, jid);
416 0 : }
417 :
418 : /** Set the Handler function which will be called when the TLS stack can't
419 : * verify the CA of the server we're trying to connect to.
420 : *
421 : * @param conn a Strophe connection object
422 : * @param hndl certfail Handler function
423 : *
424 : * @ingroup TLS
425 : */
426 0 : void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
427 : xmpp_certfail_handler hndl)
428 : {
429 0 : conn->certfail_handler = hndl;
430 0 : }
431 :
432 : /** Set the CAfile
433 : *
434 : * @param conn a Strophe connection object
435 : * @param path path to a certificate file
436 : *
437 : * @ingroup TLS
438 : */
439 0 : void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
440 : {
441 0 : if (conn->tls_cafile)
442 0 : strophe_free(conn->ctx, conn->tls_cafile);
443 0 : conn->tls_cafile = strophe_strdup(conn->ctx, path);
444 0 : }
445 :
446 : /** Set the CApath
447 : *
448 : * @param conn a Strophe connection object
449 : * @param path path to a folder containing certificates
450 : *
451 : * @ingroup TLS
452 : */
453 0 : void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
454 : {
455 0 : if (conn->tls_capath)
456 0 : strophe_free(conn->ctx, conn->tls_capath);
457 0 : conn->tls_capath = strophe_strdup(conn->ctx, path);
458 0 : }
459 :
460 : /** Retrieve the peer certificate
461 : *
462 : * The returned Certificate object must be free'd by calling
463 : * \ref xmpp_tlscert_free
464 : *
465 : * @param conn a Strophe connection object
466 : *
467 : * @return a Strophe Certificate object
468 : *
469 : * @ingroup TLS
470 : */
471 0 : xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn)
472 : {
473 0 : return tls_peer_cert(conn);
474 : }
475 :
476 : /** Set the Callback function which will be called when the TLS stack can't
477 : * decrypt a password protected key file.
478 : *
479 : * @param conn a Strophe connection object
480 : * @param cb The callback function that shall be called
481 : * @param userdata An opaque data pointer that will be passed to the callback
482 : *
483 : * @ingroup TLS
484 : */
485 3 : void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
486 : xmpp_password_callback cb,
487 : void *userdata)
488 : {
489 3 : conn->password_callback = cb;
490 3 : conn->password_callback_userdata = userdata;
491 3 : }
492 :
493 : /** Set the number of retry attempts to decrypt a private key file.
494 : *
495 : * In case the user enters the password manually it can be useful to
496 : * directly retry if the decryption of the key file failed.
497 : *
498 : * @param conn a Strophe connection object
499 : * @param retries The number of retries that should be tried
500 : *
501 : * @ingroup TLS
502 : */
503 0 : void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
504 : {
505 0 : if (retries == 0)
506 0 : conn->password_retries = 1;
507 : else
508 0 : conn->password_retries = retries;
509 0 : }
510 :
511 : /** Retrieve the path of the key file that shall be unlocked.
512 : *
513 : * This makes usually sense to be called from the
514 : * \ref xmpp_password_callback .
515 : *
516 : * @param conn a Strophe connection object
517 : *
518 : * @return a String of the path to the key file
519 : *
520 : * @ingroup TLS
521 : */
522 0 : const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn)
523 : {
524 0 : return conn->tls_client_key;
525 : }
526 :
527 : /** Set the Client Certificate and Private Key or PKCS#12 encoded file that
528 : * will be bound to the connection. If any of them was previously set, it
529 : * will be discarded. This should not be used after a connection is created.
530 : * The function will make a copy of the strings passed in.
531 : *
532 : * In case the Private Key is encrypted, a callback must be set via
533 : * \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
534 : * password.
535 : *
536 : * In case one wants to use a PKCS#12 encoded file, it should be passed via
537 : * the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
538 : * `key` is deprecated.
539 : *
540 : * @param conn a Strophe connection object
541 : * @param cert path to a certificate file or a P12 file
542 : * @param key path to a private key file or a P12 file
543 : *
544 : * @ingroup TLS
545 : */
546 8 : void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
547 : const char *const cert,
548 : const char *const key)
549 : {
550 8 : strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
551 8 : if (conn->tls_client_cert)
552 0 : strophe_free(conn->ctx, conn->tls_client_cert);
553 8 : conn->tls_client_cert = NULL;
554 8 : if (conn->tls_client_key)
555 0 : strophe_free(conn->ctx, conn->tls_client_key);
556 8 : conn->tls_client_key = NULL;
557 8 : if (cert && key) {
558 2 : conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
559 2 : conn->tls_client_key = strophe_strdup(conn->ctx, key);
560 6 : } else if (cert && !key) {
561 3 : conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
562 3 : } else if (!cert && key) {
563 3 : strophe_warn(conn->ctx, "xmpp",
564 : "xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
565 : "parameter is deprecated. Use 'cert' instead");
566 3 : conn->tls_client_cert = strophe_strdup(conn->ctx, key);
567 : }
568 8 : }
569 :
570 : /** Get the number of xmppAddr entries in the client certificate.
571 : *
572 : * @param conn a Strophe connection object
573 : *
574 : * @return the number of xmppAddr entries in the client certificate
575 : *
576 : * @ingroup TLS
577 : */
578 8 : unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *const conn)
579 : {
580 8 : return tls_id_on_xmppaddr_num(conn);
581 : }
582 :
583 : /** Get a specific xmppAddr entry.
584 : *
585 : * @param conn a Strophe connection object
586 : * @param n the index of the entry, starting at 0
587 : *
588 : * @return a string containing the xmppAddr or NULL if n is out of range
589 : *
590 : * @ingroup TLS
591 : */
592 24 : char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *const conn, unsigned int n)
593 : {
594 24 : return tls_id_on_xmppaddr(conn, n);
595 : }
596 :
597 : /** Get the password used for authentication of a connection.
598 : *
599 : * @param conn a Strophe connection object
600 : *
601 : * @return a string containing the password or NULL if it has not been set
602 : *
603 : * @ingroup Connections
604 : */
605 0 : const char *xmpp_conn_get_pass(const xmpp_conn_t *conn)
606 : {
607 0 : return conn->pass;
608 : }
609 :
610 : /** Set the password used to authenticate the connection.
611 : * If any password was previously set, it will be discarded. The function
612 : * will make a copy of the password string.
613 : *
614 : * @param conn a Strophe connection object
615 : * @param pass the password
616 : *
617 : * @ingroup Connections
618 : */
619 0 : void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
620 : {
621 0 : if (conn->pass)
622 0 : strophe_free(conn->ctx, conn->pass);
623 0 : conn->pass = pass ? strophe_strdup(conn->ctx, pass) : NULL;
624 0 : }
625 :
626 : /** Get the strophe context that the connection is associated with.
627 : * @param conn a Strophe connection object
628 : *
629 : * @return a Strophe context
630 : *
631 : * @ingroup Connections
632 : */
633 0 : xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn)
634 : {
635 0 : return conn->ctx;
636 : }
637 :
638 : /** Initiate a connection to the XMPP server.
639 : * This function returns immediately after starting the connection
640 : * process to the XMPP server, and notifications of connection state changes
641 : * will be sent to the callback function. The domain and port to connect to
642 : * are usually determined by an SRV lookup for the xmpp-client service at
643 : * the domain specified in the JID. If SRV lookup fails, altdomain and
644 : * altport will be used instead if specified.
645 : *
646 : * @param conn a Strophe connection object
647 : * @param altdomain a string with domain to use if SRV lookup fails. If this
648 : * is NULL, the domain from the JID will be used.
649 : * @param altport an integer port number to use if SRV lookup fails. If this
650 : * is 0, the default port will be assumed.
651 : * @param callback a xmpp_conn_handler callback function that will receive
652 : * notifications of connection status
653 : * @param userdata an opaque data pointer that will be passed to the callback
654 : *
655 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
656 : *
657 : * @ingroup Connections
658 : */
659 0 : int xmpp_connect_client(xmpp_conn_t *conn,
660 : const char *altdomain,
661 : unsigned short altport,
662 : xmpp_conn_handler callback,
663 : void *userdata)
664 : {
665 0 : char *domain;
666 0 : int rc;
667 :
668 0 : if (!conn->jid && (conn->tls_client_cert || conn->tls_client_key)) {
669 0 : if (tls_id_on_xmppaddr_num(conn) != 1) {
670 0 : strophe_debug(conn->ctx, "xmpp",
671 : "Client certificate contains multiple or no xmppAddr "
672 : "and no JID was given to be used.");
673 0 : return XMPP_EINVOP;
674 : }
675 0 : conn->jid = tls_id_on_xmppaddr(conn, 0);
676 0 : if (!conn->jid)
677 : return XMPP_EMEM;
678 0 : strophe_debug(conn->ctx, "xmpp", "Use jid %s from id-on-xmppAddr.",
679 : conn->jid);
680 : }
681 :
682 0 : if (!conn->jid) {
683 0 : strophe_error(conn->ctx, "xmpp", "JID is not set.");
684 0 : return XMPP_EINVOP;
685 : }
686 :
687 0 : domain = xmpp_jid_domain(conn->ctx, conn->jid);
688 0 : if (!domain)
689 : return XMPP_EMEM;
690 :
691 0 : if (!conn->sm_state) {
692 0 : conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
693 0 : if (!conn->sm_state)
694 0 : goto err_mem;
695 0 : memset(conn->sm_state, 0, sizeof(*conn->sm_state));
696 0 : conn->sm_state->ctx = conn->ctx;
697 : }
698 :
699 0 : if (altdomain != NULL)
700 0 : strophe_debug(conn->ctx, "conn", "Connecting via altdomain.");
701 :
702 0 : if (conn->tls_legacy_ssl && !altdomain) {
703 : /* SSL tunneled connection on 5223 port is legacy and doesn't
704 : * have an SRV record. */
705 0 : altdomain = domain;
706 : }
707 0 : altport = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
708 :
709 0 : if (conn->xsock)
710 0 : sock_free(conn->xsock);
711 0 : conn->xsock = sock_new(conn, domain, altdomain, altport);
712 0 : if (!conn->xsock)
713 0 : goto err_mem;
714 :
715 0 : rc = _conn_connect(conn, domain, XMPP_CLIENT, callback, userdata);
716 0 : strophe_free(conn->ctx, domain);
717 :
718 0 : return rc;
719 :
720 0 : err_mem:
721 0 : strophe_free(conn->ctx, domain);
722 0 : return XMPP_EMEM;
723 : }
724 :
725 : /** Initiate a component connection to server.
726 : * This function returns immediately after starting the connection
727 : * process to the XMPP server, and notifications of connection state changes
728 : * will be sent to the internal callback function that will set up handler
729 : * for the component handshake as defined in XEP-0114.
730 : * The domain and port to connect to must be provided in this case as the JID
731 : * provided to the call serves as component identifier to the server and is
732 : * not subject to DNS resolution.
733 : *
734 : * @param conn a Strophe connection object
735 : * @param server a string with domain to use directly as the domain can't be
736 : * extracted from the component name/JID. If this is not set, the call
737 : * will fail.
738 : * @param port an integer port number to use to connect to server expecting
739 : * an external component. If this is 0, the port 5347 will be assumed.
740 : * @param callback a xmpp_conn_handler callback function that will receive
741 : * notifications of connection status
742 : * @param userdata an opaque data pointer that will be passed to the callback
743 : *
744 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
745 : *
746 : * @ingroup Connections
747 : */
748 0 : int xmpp_connect_component(xmpp_conn_t *conn,
749 : const char *server,
750 : unsigned short port,
751 : xmpp_conn_handler callback,
752 : void *userdata)
753 : {
754 : /* The server domain, jid and password MUST be specified. */
755 0 : if (!(server && conn->jid && conn->pass))
756 : return XMPP_EINVOP;
757 :
758 : /* XEP-0114 does not support TLS */
759 0 : (void)xmpp_conn_set_flags(conn, xmpp_conn_get_flags(conn) |
760 : XMPP_CONN_FLAG_DISABLE_TLS);
761 0 : if (!conn->tls_disabled) {
762 0 : strophe_error(conn->ctx, "conn",
763 : "Failed to disable TLS. "
764 : "XEP-0114 does not support TLS");
765 0 : return XMPP_EINT;
766 : }
767 :
768 0 : port = port ? port : _conn_default_port(conn, XMPP_COMPONENT);
769 0 : if (conn->xsock)
770 0 : sock_free(conn->xsock);
771 0 : conn->xsock = sock_new(conn, NULL, server, port);
772 0 : if (!conn->xsock)
773 : return XMPP_EMEM;
774 :
775 : /* JID serves as an identifier here and will be used as "to" attribute
776 : of the stream */
777 0 : return _conn_connect(conn, conn->jid, XMPP_COMPONENT, callback, userdata);
778 : }
779 :
780 : /** Initiate a raw connection to the XMPP server.
781 : * Arguments and behaviour of the function are similar to
782 : * xmpp_connect_client(), but it skips authentication process. In opposite to
783 : * xmpp_connect_client() during connection process two events are generated
784 : * instead of one. User's callback is called with event XMPP_CONN_RAW_CONNECT
785 : * when the TCP connection with the server is established. At this point user
786 : * might want to open an XMPP stream with xmpp_conn_open_stream() or establish
787 : * TLS session with xmpp_conn_tls_start(). Event XMPP_CONN_CONNECT is generated
788 : * when the XMPP stream is opened successfully and user may send stanzas over
789 : * the connection.
790 : *
791 : * This function doesn't use password nor node part of a jid. Therefore,
792 : * the only required configuration is a domain (or full jid) passed via
793 : * xmpp_conn_set_jid().
794 : *
795 : * @see xmpp_connect_client()
796 : *
797 : * @return XMPP_EOK (0) on success a number less than 0 on failure
798 : *
799 : * @ingroup Connections
800 : */
801 0 : int xmpp_connect_raw(xmpp_conn_t *conn,
802 : const char *altdomain,
803 : unsigned short altport,
804 : xmpp_conn_handler callback,
805 : void *userdata)
806 : {
807 0 : conn->is_raw = 1;
808 0 : return xmpp_connect_client(conn, altdomain, altport, callback, userdata);
809 : }
810 :
811 : /* Called when tcp connection is established. */
812 0 : void conn_established(xmpp_conn_t *conn)
813 : {
814 0 : if (conn->tls_legacy_ssl && !conn->is_raw) {
815 0 : strophe_debug(conn->ctx, "xmpp", "using legacy SSL connection");
816 0 : if (conn_tls_start(conn) != 0) {
817 0 : conn_disconnect(conn);
818 0 : return;
819 : }
820 : }
821 :
822 0 : if (conn->is_raw) {
823 0 : handler_reset_timed(conn, 0);
824 : /* we skip all the mandatory steps of the stream negotiation for a "raw"
825 : connection, but the event loop ignores user's handlers when
826 : conn->stream_negotiation_completed is not set. */
827 0 : conn->stream_negotiation_completed = 1;
828 0 : conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL,
829 : conn->userdata);
830 : } else {
831 : /* send stream init */
832 0 : conn_open_stream(conn);
833 : }
834 : }
835 :
836 : /** Send the default opening stream tag.
837 : * The default tag is the one sent by xmpp_connect_client().
838 : * User's connection handler is called with event XMPP_CONN_CONNECT when
839 : * server replies with its opening tag.
840 : *
841 : * @return XMPP_EOK (0) on success a number less than 0 on failure
842 : *
843 : * @note The connection must be connected with xmpp_connect_raw().
844 : *
845 : * @ingroup Connections
846 : */
847 0 : int xmpp_conn_open_stream_default(xmpp_conn_t *conn)
848 : {
849 0 : if (!conn->is_raw)
850 : return XMPP_EINVOP;
851 :
852 0 : conn_prepare_reset(conn, auth_handle_open_raw);
853 0 : conn_open_stream(conn);
854 :
855 0 : return XMPP_EOK;
856 : }
857 :
858 : /** Send an opening stream tag.
859 : * User's connection handler is called with event XMPP_CONN_CONNECT when
860 : * server replies with its opening tag.
861 : *
862 : * @param conn a Strophe connection object
863 : * @param attributes Array of strings in format: even index points to
864 : * an attribute name and odd index points to its value
865 : * @param attributes_len Number of elements in the attributes array, it
866 : * should be number of attributes multiplied by 2
867 : *
868 : * @return XMPP_EOK (0) on success a number less than 0 on failure
869 : *
870 : * @note The connection must be connected with xmpp_connect_raw().
871 : *
872 : * @ingroup Connections
873 : */
874 0 : int xmpp_conn_open_stream(xmpp_conn_t *conn,
875 : char **attributes,
876 : size_t attributes_len)
877 : {
878 0 : if (!conn->is_raw)
879 : return XMPP_EINVOP;
880 :
881 0 : conn_prepare_reset(conn, auth_handle_open_raw);
882 :
883 0 : return _conn_open_stream_with_attributes(conn, attributes, attributes_len);
884 : }
885 :
886 : /** Start synchronous TLS handshake with the server.
887 : *
888 : * @return XMPP_EOK (0) on success a number less than 0 on failure
889 : *
890 : * @ingroup Connections
891 : */
892 0 : int xmpp_conn_tls_start(xmpp_conn_t *conn)
893 : {
894 0 : return conn_tls_start(conn);
895 : }
896 :
897 : /** Cleanly disconnect the connection.
898 : * This function is only called by the stream parser when </stream:stream>
899 : * is received, and it not intended to be called by code outside of Strophe.
900 : *
901 : * @param conn a Strophe connection object
902 : */
903 0 : void conn_disconnect_clean(xmpp_conn_t *conn)
904 : {
905 : /* remove the timed handler */
906 0 : xmpp_timed_handler_delete(conn, _disconnect_cleanup);
907 :
908 0 : conn_disconnect(conn);
909 0 : }
910 :
911 : /** Disconnect from the XMPP server.
912 : * This function immediately disconnects from the XMPP server, and should
913 : * not be used outside of the Strophe library.
914 : *
915 : * @param conn a Strophe connection object
916 : */
917 0 : void conn_disconnect(xmpp_conn_t *conn)
918 : {
919 0 : strophe_debug(conn->ctx, "xmpp", "Closing socket.");
920 0 : conn->state = XMPP_STATE_DISCONNECTED;
921 0 : conn->stream_negotiation_completed = 0;
922 0 : if (conn->tls) {
923 0 : tls_stop(conn->tls);
924 0 : tls_free(conn->tls);
925 0 : conn->tls = NULL;
926 : }
927 0 : if (conn->sock != INVALID_SOCKET)
928 0 : sock_close(conn->sock);
929 0 : _reset_sm_state_for_reconnect(conn);
930 :
931 : /* fire off connection handler */
932 0 : conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
933 : conn->stream_error, conn->userdata);
934 0 : }
935 :
936 : /* prepares a parser reset. this is called from handlers. we can't
937 : * reset the parser immediately as it is not re-entrant. */
938 0 : void conn_prepare_reset(xmpp_conn_t *conn, xmpp_open_handler handler)
939 : {
940 0 : conn->reset_parser = 1;
941 0 : conn->open_handler = handler;
942 0 : }
943 :
944 : /* reset the parser */
945 0 : void conn_parser_reset(xmpp_conn_t *conn)
946 : {
947 0 : conn->reset_parser = 0;
948 0 : parser_reset(conn->parser);
949 0 : }
950 :
951 : /** Initiate termination of the connection to the XMPP server.
952 : * This function starts the disconnection sequence by sending
953 : * </stream:stream> to the XMPP server. This function will do nothing
954 : * if the connection state is different from CONNECTING or CONNECTED.
955 : *
956 : * @param conn a Strophe connection object
957 : *
958 : * @ingroup Connections
959 : */
960 0 : void xmpp_disconnect(xmpp_conn_t *conn)
961 : {
962 0 : if (conn->state != XMPP_STATE_CONNECTING &&
963 : conn->state != XMPP_STATE_CONNECTED)
964 : return;
965 :
966 : /* close the stream */
967 0 : send_raw_string(conn, "</stream:stream>");
968 :
969 : /* setup timed handler in case disconnect takes too long */
970 0 : handler_add_timed(conn, _disconnect_cleanup, DISCONNECT_TIMEOUT, NULL);
971 : }
972 :
973 : /** Send a raw string to the XMPP server.
974 : * This function is a convenience function to send raw string data to the
975 : * XMPP server. It is used by Strophe to send short messages instead of
976 : * building up an XML stanza with DOM methods. This should be used with care
977 : * as it does not validate the data; invalid data may result in immediate
978 : * stream termination by the XMPP server.
979 : *
980 : * @param conn a Strophe connection object
981 : * @param fmt a printf-style format string followed by a variable list of
982 : * arguments to format
983 : *
984 : * @ingroup Connections
985 : */
986 0 : void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
987 : {
988 0 : va_list ap;
989 :
990 0 : if (!_is_connected(conn, XMPP_QUEUE_USER))
991 0 : return;
992 :
993 0 : va_start(ap, fmt);
994 0 : _send_valist(conn, fmt, ap, XMPP_QUEUE_USER);
995 0 : va_end(ap);
996 : }
997 :
998 : /** Send raw bytes to the XMPP server.
999 : * This function is a convenience function to send raw bytes to the
1000 : * XMPP server. It is used primarily by xmpp_send_raw_string(). This
1001 : * function should be used with care as it does not validate the bytes and
1002 : * invalid data may result in stream termination by the XMPP server.
1003 : *
1004 : * @param conn a Strophe connection object
1005 : * @param data a buffer of raw bytes
1006 : * @param len the length of the data in the buffer
1007 : *
1008 : * @ingroup Connections
1009 : */
1010 0 : void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len)
1011 : {
1012 0 : send_raw(conn, data, len, XMPP_QUEUE_USER, NULL);
1013 0 : }
1014 :
1015 : /** Send an XML stanza to the XMPP server.
1016 : * This is the main way to send data to the XMPP server. The function will
1017 : * terminate without action if the connection state is not CONNECTED.
1018 : *
1019 : * @param conn a Strophe connection object
1020 : * @param stanza a Strophe stanza object
1021 : *
1022 : * @ingroup Connections
1023 : */
1024 0 : void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
1025 : {
1026 0 : send_stanza(conn, xmpp_stanza_clone(stanza), XMPP_QUEUE_USER);
1027 0 : }
1028 :
1029 : /** Send the opening <stream:stream> tag to the server.
1030 : * This function is used by Strophe to begin an XMPP stream. It should
1031 : * not be used outside of the library.
1032 : *
1033 : * @param conn a Strophe connection object
1034 : */
1035 0 : void conn_open_stream(xmpp_conn_t *conn)
1036 : {
1037 0 : size_t attributes_len;
1038 0 : int rc;
1039 0 : char *from = NULL;
1040 0 : char *ns = conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : XMPP_NS_COMPONENT;
1041 0 : char *attributes[12] = {
1042 0 : "to", conn->domain, "xml:lang", conn->lang,
1043 : "version", "1.0", "xmlns", ns,
1044 : "xmlns:stream", XMPP_NS_STREAMS, "from", NULL};
1045 :
1046 0 : attributes_len = ARRAY_SIZE(attributes);
1047 0 : if (conn->tls && conn->jid && strchr(conn->jid, '@') != NULL)
1048 0 : from = xmpp_jid_bare(conn->ctx, conn->jid);
1049 :
1050 0 : if (from)
1051 0 : attributes[attributes_len - 1] = from;
1052 : else
1053 : attributes_len -= 2;
1054 :
1055 0 : rc = _conn_open_stream_with_attributes(conn, attributes, attributes_len);
1056 0 : if (rc != XMPP_EOK) {
1057 0 : strophe_error(conn->ctx, "conn",
1058 : "Cannot build stream tag: memory error");
1059 0 : conn_disconnect(conn);
1060 : }
1061 0 : if (from)
1062 0 : strophe_free(conn->ctx, from);
1063 0 : }
1064 :
1065 0 : int conn_interface_write(struct conn_interface *intf,
1066 : const void *buff,
1067 : size_t len)
1068 : {
1069 0 : int ret = intf->write(intf, buff, len);
1070 0 : if (ret < 0 && !intf->error_is_recoverable(intf, intf->get_error(intf))) {
1071 0 : intf->conn->error = intf->get_error(intf);
1072 : }
1073 0 : return ret;
1074 : }
1075 :
1076 0 : int conn_int_nop(struct conn_interface *intf)
1077 : {
1078 0 : UNUSED(intf);
1079 0 : return 0;
1080 : }
1081 :
1082 0 : int conn_tls_start(xmpp_conn_t *conn)
1083 : {
1084 0 : int rc;
1085 :
1086 0 : if (conn->tls_disabled) {
1087 0 : conn->tls = NULL;
1088 0 : rc = XMPP_EINVOP;
1089 : } else {
1090 0 : conn->tls = tls_new(conn);
1091 0 : rc = conn->tls == NULL ? XMPP_EMEM : 0;
1092 : }
1093 :
1094 0 : if (conn->tls != NULL) {
1095 0 : conn->intf = tls_intf;
1096 0 : conn->intf.conn = conn;
1097 0 : if (tls_start(conn->tls)) {
1098 0 : conn->secured = 1;
1099 : } else {
1100 0 : rc = XMPP_EINT;
1101 0 : conn->error = tls_error(&conn->intf);
1102 0 : tls_free(conn->tls);
1103 0 : conn->tls = NULL;
1104 0 : conn->tls_failed = 1;
1105 : }
1106 : }
1107 0 : if (rc != 0) {
1108 0 : strophe_debug(conn->ctx, "conn",
1109 : "Couldn't start TLS! "
1110 : "error %d tls_error %d",
1111 : rc, conn->error);
1112 : }
1113 0 : return rc;
1114 : }
1115 :
1116 : /** Return applied flags for the connection.
1117 : *
1118 : * @param conn a Strophe connection object
1119 : *
1120 : * @return ORed connection flags that are applied for the connection.
1121 : *
1122 : * @ingroup Connections
1123 : */
1124 0 : long xmpp_conn_get_flags(const xmpp_conn_t *conn)
1125 : {
1126 0 : long flags;
1127 :
1128 0 : flags =
1129 0 : XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
1130 0 : XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
1131 0 : XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl |
1132 0 : XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust |
1133 0 : XMPP_CONN_FLAG_DISABLE_SM * conn->sm_disable |
1134 0 : XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression.allowed |
1135 0 : XMPP_CONN_FLAG_COMPRESSION_DONT_RESET * conn->compression.dont_reset |
1136 0 : XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
1137 :
1138 0 : return flags;
1139 : }
1140 :
1141 : /** Set flags for the connection.
1142 : * This function applies set flags and resets unset ones. Default connection
1143 : * configuration is all flags unset. Flags can be applied only for a connection
1144 : * in disconnected state.
1145 : * All unsupported flags are ignored. If a flag is unset after successful set
1146 : * operation then the flag is not supported by current version.
1147 : *
1148 : * Supported flags are:
1149 : *
1150 : * - XMPP_CONN_FLAG_DISABLE_TLS
1151 : * - XMPP_CONN_FLAG_MANDATORY_TLS
1152 : * - XMPP_CONN_FLAG_LEGACY_SSL
1153 : * - XMPP_CONN_FLAG_TRUST_TLS
1154 : * - XMPP_CONN_FLAG_LEGACY_AUTH
1155 : * - XMPP_CONN_FLAG_DISABLE_SM
1156 : * - XMPP_CONN_FLAG_ENABLE_COMPRESSION
1157 : * - XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
1158 : *
1159 : * @param conn a Strophe connection object
1160 : * @param flags ORed connection flags
1161 : *
1162 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
1163 : *
1164 : * @ingroup Connections
1165 : */
1166 0 : int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags)
1167 : {
1168 0 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1169 0 : strophe_error(conn->ctx, "conn",
1170 : "Flags can be set only "
1171 : "for disconnected connection");
1172 0 : return XMPP_EINVOP;
1173 : }
1174 0 : if ((flags & XMPP_CONN_FLAG_DISABLE_TLS) &&
1175 : (flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL |
1176 : XMPP_CONN_FLAG_TRUST_TLS))) {
1177 0 : strophe_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags);
1178 0 : return XMPP_EINVOP;
1179 : }
1180 :
1181 0 : conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0;
1182 0 : conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0;
1183 0 : conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0;
1184 0 : conn->tls_trust = (flags & XMPP_CONN_FLAG_TRUST_TLS) ? 1 : 0;
1185 0 : conn->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 1 : 0;
1186 0 : conn->sm_disable = (flags & XMPP_CONN_FLAG_DISABLE_SM) ? 1 : 0;
1187 0 : conn->compression.allowed =
1188 0 : (flags & XMPP_CONN_FLAG_ENABLE_COMPRESSION) ? 1 : 0;
1189 0 : conn->compression.dont_reset =
1190 0 : (flags & XMPP_CONN_FLAG_COMPRESSION_DONT_RESET) ? 1 : 0;
1191 0 : flags &= ~(XMPP_CONN_FLAG_DISABLE_TLS | XMPP_CONN_FLAG_MANDATORY_TLS |
1192 : XMPP_CONN_FLAG_LEGACY_SSL | XMPP_CONN_FLAG_TRUST_TLS |
1193 : XMPP_CONN_FLAG_LEGACY_AUTH | XMPP_CONN_FLAG_DISABLE_SM |
1194 : XMPP_CONN_FLAG_ENABLE_COMPRESSION |
1195 : XMPP_CONN_FLAG_COMPRESSION_DONT_RESET);
1196 0 : if (flags) {
1197 0 : strophe_error(conn->ctx, "conn", "Flags 0x%04lx unknown", flags);
1198 0 : return XMPP_EINVOP;
1199 : }
1200 :
1201 : return 0;
1202 : }
1203 :
1204 : /** Return whether TLS session is established or not.
1205 : *
1206 : * @return TRUE if TLS session is established and FALSE otherwise
1207 : *
1208 : * @ingroup Connections
1209 : */
1210 0 : int xmpp_conn_is_secured(xmpp_conn_t *conn)
1211 : {
1212 0 : return conn->secured && !conn->tls_failed && conn->tls != NULL;
1213 : }
1214 :
1215 : /**
1216 : * @return TRUE if connection is in connecting state and FALSE otherwise
1217 : *
1218 : * @ingroup Connections
1219 : */
1220 0 : int xmpp_conn_is_connecting(xmpp_conn_t *conn)
1221 : {
1222 0 : return conn->state == XMPP_STATE_CONNECTING ||
1223 0 : (conn->state == XMPP_STATE_CONNECTED &&
1224 0 : conn->stream_negotiation_completed == 0);
1225 : }
1226 :
1227 0 : static int _is_connected(xmpp_conn_t *conn, xmpp_send_queue_owner_t owner)
1228 : {
1229 0 : return conn->state == XMPP_STATE_CONNECTED &&
1230 0 : (owner != XMPP_QUEUE_USER ||
1231 0 : conn->stream_negotiation_completed == 1);
1232 : }
1233 :
1234 : /**
1235 : * @return TRUE if connection is established and FALSE otherwise
1236 : *
1237 : * @ingroup Connections
1238 : */
1239 0 : int xmpp_conn_is_connected(xmpp_conn_t *conn)
1240 : {
1241 0 : return _is_connected(conn, XMPP_QUEUE_USER);
1242 : }
1243 :
1244 : /**
1245 : * @return TRUE if connection is in disconnected state and FALSE otherwise
1246 : *
1247 : * @ingroup Connections
1248 : */
1249 0 : int xmpp_conn_is_disconnected(xmpp_conn_t *conn)
1250 : {
1251 0 : return conn->state == XMPP_STATE_DISCONNECTED;
1252 : }
1253 :
1254 : /**
1255 : * This returns the Stream Management state of a connection object after
1256 : * it has been disconnected.
1257 : * One can then initialise a fresh connection object and set this Stream
1258 : * Management state by calling \ref xmpp_conn_set_sm_state
1259 : *
1260 : * In case one wants to dispose of the state w/o setting it into a fresh
1261 : * connection object, one can call \ref xmpp_free_sm_state
1262 : *
1263 : * After calling this function to retrieve the state, only call one of the
1264 : * other two.
1265 : *
1266 : * @param conn a Strophe connection object
1267 : * @return The Stream Management state of the connection or NULL on error
1268 : *
1269 : * @ingroup Connections
1270 : */
1271 0 : xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn)
1272 : {
1273 0 : xmpp_sm_state_t *ret;
1274 :
1275 : /* We can only return the SM state when we're disconnected */
1276 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
1277 : return NULL;
1278 :
1279 0 : ret = conn->sm_state;
1280 0 : conn->sm_state = NULL;
1281 :
1282 0 : return ret;
1283 : }
1284 :
1285 0 : static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
1286 : {
1287 0 : xmpp_sm_state_t *s = conn->sm_state;
1288 :
1289 0 : if (s->previd) {
1290 0 : strophe_free(conn->ctx, s->previd);
1291 0 : s->previd = NULL;
1292 : }
1293 :
1294 0 : if (s->can_resume) {
1295 0 : s->previd = s->id;
1296 0 : s->id = NULL;
1297 :
1298 0 : s->bound_jid = conn->bound_jid;
1299 0 : conn->bound_jid = NULL;
1300 0 : } else if (s->id) {
1301 0 : strophe_free(conn->ctx, s->id);
1302 0 : s->id = NULL;
1303 : }
1304 :
1305 0 : s->r_sent = s->sm_enabled = s->sm_support = s->resume = 0;
1306 :
1307 0 : if (s->bind) {
1308 0 : xmpp_stanza_release(s->bind);
1309 0 : s->bind = NULL;
1310 : }
1311 0 : }
1312 :
1313 : /**
1314 : * @param conn a Strophe connection object
1315 : * @param sm_state A Stream Management state returned from a call to
1316 : * `xmpp_conn_get_sm_state()`
1317 : *
1318 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
1319 : *
1320 : * @ingroup Connections
1321 : */
1322 0 : int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state)
1323 : {
1324 : /* We can only set the SM state when we're disconnected */
1325 0 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1326 0 : strophe_error(conn->ctx, "conn",
1327 : "SM state can only be set the when we're disconnected");
1328 0 : return XMPP_EINVOP;
1329 : }
1330 :
1331 0 : if (conn->sm_state) {
1332 0 : strophe_error(conn->ctx, "conn", "SM state is already set!");
1333 0 : return XMPP_EINVOP;
1334 : }
1335 :
1336 0 : if (conn->ctx != sm_state->ctx) {
1337 0 : strophe_error(
1338 : conn->ctx, "conn",
1339 : "SM state has to be assigned to connection that stems from "
1340 : "the same context!");
1341 0 : return XMPP_EINVOP;
1342 : }
1343 :
1344 0 : conn->sm_state = sm_state;
1345 0 : return XMPP_EOK;
1346 : }
1347 :
1348 0 : void reset_sm_state(xmpp_sm_state_t *sm_state)
1349 : {
1350 0 : xmpp_ctx_t *ctx = sm_state->ctx;
1351 :
1352 0 : strophe_free_and_null(ctx, sm_state->id);
1353 0 : strophe_free_and_null(ctx, sm_state->previd);
1354 0 : strophe_free_and_null(ctx, sm_state->bound_jid);
1355 0 : if (sm_state->bind)
1356 0 : xmpp_stanza_release(sm_state->bind);
1357 0 : sm_state->bind = NULL;
1358 0 : sm_state->sm_handled_nr = 0;
1359 0 : sm_state->sm_sent_nr = 0;
1360 0 : sm_state->r_sent = 0;
1361 0 : }
1362 :
1363 : /** c.f. \ref xmpp_conn_get_sm_state for usage documentation
1364 : *
1365 : * @param sm_state A Stream Management state returned from a call to
1366 : * `xmpp_conn_get_sm_state()`
1367 : *
1368 : * @ingroup Connections
1369 : */
1370 0 : void xmpp_free_sm_state(xmpp_sm_state_t *sm_state)
1371 : {
1372 0 : xmpp_send_queue_t *smq;
1373 0 : xmpp_ctx_t *ctx;
1374 :
1375 0 : if (!sm_state || !sm_state->ctx)
1376 : return;
1377 :
1378 : ctx = sm_state->ctx;
1379 :
1380 0 : while ((smq = pop_queue_front(&sm_state->sm_queue))) {
1381 0 : strophe_free(ctx, queue_element_free(ctx, smq));
1382 : }
1383 :
1384 0 : reset_sm_state(sm_state);
1385 0 : strophe_free(ctx, sm_state);
1386 : }
1387 :
1388 : /**
1389 : * @return The number of entries in the send queue
1390 : *
1391 : * @ingroup Connections
1392 : */
1393 0 : int xmpp_conn_send_queue_len(const xmpp_conn_t *conn)
1394 : {
1395 0 : if (conn->send_queue_head && conn->send_queue_head->wip &&
1396 0 : conn->send_queue_head->owner == XMPP_QUEUE_USER)
1397 0 : return conn->send_queue_user_len - 1;
1398 : else
1399 0 : return conn->send_queue_user_len;
1400 : }
1401 :
1402 0 : static char *_drop_send_queue_element(xmpp_conn_t *conn, xmpp_send_queue_t *e)
1403 : {
1404 0 : if (e == conn->send_queue_head)
1405 0 : conn->send_queue_head = e->next;
1406 0 : if (e == conn->send_queue_tail)
1407 0 : conn->send_queue_tail = e->prev;
1408 0 : if (!conn->send_queue_head)
1409 0 : conn->send_queue_tail = NULL;
1410 0 : if (e->prev)
1411 0 : e->prev->next = e->next;
1412 0 : if (e->next)
1413 0 : e->next->prev = e->prev;
1414 0 : conn->send_queue_len--;
1415 0 : if (e->owner == XMPP_QUEUE_USER)
1416 0 : conn->send_queue_user_len--;
1417 0 : return queue_element_free(conn->ctx, e);
1418 : }
1419 :
1420 : /** Drop an element of the send queue.
1421 : * This can be used to manage the send queue in case a server
1422 : * isn't fast enough in processing the elements you're trying
1423 : * to send or your outgoing bandwidth isn't fast enough to transfer
1424 : * everything you want to send out.
1425 : *
1426 : * @param conn a Strophe connection object
1427 : * @param which the element that shall be removed
1428 : *
1429 : * @return The rendered stanza. The pointer returned has to be free'd by the
1430 : * caller of this function.
1431 : *
1432 : * @ingroup Connections
1433 : */
1434 0 : char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
1435 : xmpp_queue_element_t which)
1436 : {
1437 0 : xmpp_send_queue_t *t;
1438 :
1439 : /* Fast return paths */
1440 : /* empty queue */
1441 0 : if (!conn->send_queue_head)
1442 : return NULL;
1443 : /* one element in queue */
1444 0 : if (conn->send_queue_head == conn->send_queue_tail) {
1445 : /* head is already sent out partially */
1446 0 : if (conn->send_queue_head->wip)
1447 : return NULL;
1448 : /* the element is no USER element */
1449 0 : if (conn->send_queue_head->owner != XMPP_QUEUE_USER)
1450 : return NULL;
1451 : }
1452 :
1453 : /* Regular flow */
1454 0 : if (which == XMPP_QUEUE_OLDEST) {
1455 : t = conn->send_queue_head;
1456 0 : } else if (which == XMPP_QUEUE_YOUNGEST) {
1457 : t = conn->send_queue_tail;
1458 : /* search backwards to find last USER element */
1459 0 : while (t && t->owner != XMPP_QUEUE_USER)
1460 0 : t = t->prev;
1461 : } else {
1462 0 : strophe_error(conn->ctx, "conn", "Unknown queue element %d", which);
1463 0 : return NULL;
1464 : }
1465 : /* there was no USER element in the queue */
1466 0 : if (!t)
1467 : return NULL;
1468 :
1469 : /* head is already sent out partially */
1470 0 : if (t == conn->send_queue_head && t->wip)
1471 0 : t = t->next;
1472 :
1473 : /* search forward to find the first USER element */
1474 0 : while (t && t->owner != XMPP_QUEUE_USER)
1475 0 : t = t->next;
1476 :
1477 : /* there was no USER element in the queue we could drop */
1478 0 : if (!t)
1479 : return NULL;
1480 :
1481 : /* In case there exists a SM stanza that is linked to the
1482 : * one we're currently dropping, also delete that one.
1483 : */
1484 0 : if (t->next && t->next->userdata == t)
1485 0 : strophe_free(conn->ctx, _drop_send_queue_element(conn, t->next));
1486 : /* Finally drop the element */
1487 0 : return _drop_send_queue_element(conn, t);
1488 : }
1489 :
1490 : /* timed handler for cleanup if normal disconnect procedure takes too long */
1491 0 : static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata)
1492 : {
1493 0 : UNUSED(userdata);
1494 :
1495 0 : strophe_debug(conn->ctx, "xmpp", "disconnection forced by cleanup timeout");
1496 :
1497 0 : conn_disconnect(conn);
1498 :
1499 0 : return 0;
1500 : }
1501 :
1502 0 : static char *_conn_build_stream_tag(xmpp_conn_t *conn,
1503 : char **attributes,
1504 : size_t attributes_len)
1505 : {
1506 0 : char *tag;
1507 0 : size_t len;
1508 0 : size_t i;
1509 :
1510 0 : static const char *tag_head = "<stream:stream";
1511 0 : static const char *tag_tail = ">";
1512 :
1513 : /* ignore the last element unless number is even */
1514 0 : attributes_len &= ~(size_t)1;
1515 :
1516 0 : len = strlen(tag_head) + strlen(tag_tail);
1517 0 : for (i = 0; i < attributes_len; ++i)
1518 0 : len += strlen(attributes[i]) + 2;
1519 0 : tag = strophe_alloc(conn->ctx, len + 1);
1520 0 : if (!tag)
1521 : return NULL;
1522 :
1523 0 : strcpy(tag, tag_head);
1524 0 : for (i = 0; i < attributes_len; ++i) {
1525 0 : if ((i & 1) == 0) {
1526 0 : strcat(tag, " ");
1527 0 : strcat(tag, attributes[i]);
1528 0 : strcat(tag, "=\"");
1529 : } else {
1530 0 : strcat(tag, attributes[i]);
1531 0 : strcat(tag, "\"");
1532 : }
1533 : }
1534 0 : strcat(tag, tag_tail);
1535 :
1536 0 : if (strlen(tag) != len) {
1537 0 : strophe_error(conn->ctx, "xmpp",
1538 : "Internal error in "
1539 : "_conn_build_stream_tag().");
1540 0 : strophe_free(conn->ctx, tag);
1541 0 : tag = NULL;
1542 : }
1543 :
1544 : return tag;
1545 : }
1546 :
1547 0 : static int _conn_open_stream_with_attributes(xmpp_conn_t *conn,
1548 : char **attributes,
1549 : size_t attributes_len)
1550 : {
1551 0 : char *tag;
1552 :
1553 0 : tag = _conn_build_stream_tag(conn, attributes, attributes_len);
1554 0 : if (!tag)
1555 : return XMPP_EMEM;
1556 :
1557 0 : send_raw_string(conn, "<?xml version=\"1.0\"?>%s", tag);
1558 0 : strophe_free(conn->ctx, tag);
1559 :
1560 0 : return XMPP_EOK;
1561 : }
1562 :
1563 0 : static void _conn_attributes_new(xmpp_conn_t *conn,
1564 : char **attrs,
1565 : char ***attributes,
1566 : size_t *attributes_len)
1567 : {
1568 0 : char **array = NULL;
1569 0 : size_t nr = 0;
1570 0 : size_t i;
1571 :
1572 0 : if (attrs) {
1573 0 : for (; attrs[nr]; ++nr)
1574 : ;
1575 0 : array = strophe_alloc(conn->ctx, sizeof(*array) * nr);
1576 0 : for (i = 0; array && i < nr; ++i) {
1577 0 : array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i])
1578 0 : : strophe_strdup(conn->ctx, attrs[i]);
1579 0 : if (array[i] == NULL)
1580 : break;
1581 : }
1582 0 : if (!array || i < nr) {
1583 0 : strophe_error(conn->ctx, "xmpp", "Memory allocation error.");
1584 0 : _conn_attributes_destroy(conn, array, i);
1585 0 : array = NULL;
1586 0 : nr = 0;
1587 : }
1588 : }
1589 0 : *attributes = array;
1590 0 : *attributes_len = nr;
1591 0 : }
1592 :
1593 0 : static void _conn_attributes_destroy(xmpp_conn_t *conn,
1594 : char **attributes,
1595 : size_t attributes_len)
1596 : {
1597 0 : size_t i;
1598 :
1599 0 : if (attributes) {
1600 0 : for (i = 0; i < attributes_len; ++i)
1601 0 : strophe_free(conn->ctx, attributes[i]);
1602 0 : strophe_free(conn->ctx, attributes);
1603 : }
1604 0 : }
1605 :
1606 0 : static void _log_open_tag(xmpp_conn_t *conn, char **attrs)
1607 : {
1608 0 : char **attributes;
1609 0 : char *tag;
1610 0 : size_t nr;
1611 :
1612 0 : _conn_attributes_new(conn, attrs, &attributes, &nr);
1613 0 : tag = _conn_build_stream_tag(conn, attributes, nr);
1614 0 : if (tag) {
1615 0 : strophe_debug(conn->ctx, "xmpp", "RECV: %s", tag);
1616 0 : strophe_free(conn->ctx, tag);
1617 : }
1618 0 : _conn_attributes_destroy(conn, attributes, nr);
1619 0 : }
1620 :
1621 0 : static char *_get_stream_attribute(char **attrs, char *name)
1622 : {
1623 0 : int i;
1624 :
1625 0 : if (!attrs)
1626 : return NULL;
1627 :
1628 0 : for (i = 0; attrs[i]; i += 2)
1629 0 : if (strcmp(name, attrs[i]) == 0)
1630 0 : return attrs[i + 1];
1631 :
1632 : return NULL;
1633 : }
1634 :
1635 0 : static void _handle_stream_start(char *name, char **attrs, void *userdata)
1636 : {
1637 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1638 0 : char *id;
1639 0 : int failed = 0;
1640 :
1641 0 : if (conn->stream_id)
1642 0 : strophe_free(conn->ctx, conn->stream_id);
1643 0 : conn->stream_id = NULL;
1644 :
1645 0 : if (strcmp(name, "stream") == 0) {
1646 0 : _log_open_tag(conn, attrs);
1647 0 : id = _get_stream_attribute(attrs, "id");
1648 0 : if (id)
1649 0 : conn->stream_id = strophe_strdup(conn->ctx, id);
1650 :
1651 0 : if (id && !conn->stream_id) {
1652 0 : strophe_error(conn->ctx, "conn", "Memory allocation failed.");
1653 0 : failed = 1;
1654 : }
1655 : } else {
1656 0 : strophe_error(conn->ctx, "conn",
1657 : "Server did not open valid stream."
1658 : " name = %s.",
1659 : name);
1660 0 : failed = 1;
1661 : }
1662 :
1663 0 : if (!failed) {
1664 : /* call stream open handler */
1665 0 : conn->open_handler(conn);
1666 : } else {
1667 0 : conn_disconnect(conn);
1668 : }
1669 0 : }
1670 :
1671 0 : static void _handle_stream_end(char *name, void *userdata)
1672 : {
1673 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1674 :
1675 0 : UNUSED(name);
1676 :
1677 : /* stream is over */
1678 0 : strophe_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
1679 : /* the session has been terminated properly, i.e. it can't be resumed */
1680 0 : conn->sm_state->can_resume = 0;
1681 0 : conn_disconnect_clean(conn);
1682 0 : }
1683 :
1684 0 : static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata)
1685 : {
1686 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1687 0 : char *buf;
1688 0 : size_t len;
1689 :
1690 0 : if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
1691 0 : strophe_debug(conn->ctx, "xmpp", "RECV: %s", buf);
1692 0 : strophe_free(conn->ctx, buf);
1693 : }
1694 :
1695 0 : handler_fire_stanza(conn, stanza);
1696 0 : if (conn->sm_state->sm_enabled)
1697 0 : _conn_sm_handle_stanza(conn, stanza);
1698 0 : }
1699 :
1700 : /* XEP-0198 stream management */
1701 0 : static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
1702 : xmpp_stanza_t *stanza)
1703 : {
1704 0 : xmpp_stanza_t *a;
1705 0 : xmpp_send_queue_t *e;
1706 0 : char *c;
1707 0 : const char *name, *ns, *attr_h;
1708 0 : char h[11];
1709 0 : unsigned long ul_h;
1710 :
1711 0 : ns = xmpp_stanza_get_ns(stanza);
1712 0 : if (ns && strcmp(ns, XMPP_NS_SM) != 0)
1713 0 : ++conn->sm_state->sm_handled_nr;
1714 : else {
1715 0 : name = xmpp_stanza_get_name(stanza);
1716 0 : if (!name)
1717 0 : return;
1718 0 : if (strcmp(name, "r") == 0) {
1719 0 : a = xmpp_stanza_new(conn->ctx);
1720 0 : if (!a) {
1721 0 : strophe_debug(conn->ctx, "conn", "Couldn't create <a> stanza.");
1722 0 : return;
1723 : }
1724 0 : xmpp_stanza_set_name(a, "a");
1725 0 : xmpp_stanza_set_ns(a, XMPP_NS_SM);
1726 0 : strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
1727 0 : xmpp_stanza_set_attribute(a, "h", h);
1728 0 : send_stanza(conn, a, XMPP_QUEUE_SM_STROPHE);
1729 0 : } else if (strcmp(name, "a") == 0) {
1730 0 : attr_h = xmpp_stanza_get_attribute(stanza, "h");
1731 0 : if (!attr_h) {
1732 0 : strophe_debug(conn->ctx, "conn", "Didn't find 'h' attribute.");
1733 0 : return;
1734 : }
1735 0 : if (string_to_ul(attr_h, &ul_h)) {
1736 0 : strophe_error(
1737 0 : conn->ctx, "conn",
1738 : "Error on strtoul() of '%s', returned value is %llu.",
1739 : attr_h, ul_h);
1740 : /* We continue here and drop the complete SM queue instead of
1741 : * returning and letting the queue fill up.
1742 : */
1743 0 : ul_h = ULONG_MAX;
1744 : }
1745 0 : while (conn->sm_state->sm_queue.head &&
1746 0 : conn->sm_state->sm_queue.head->sm_h < ul_h) {
1747 0 : e = pop_queue_front(&conn->sm_state->sm_queue);
1748 0 : strophe_debug_verbose(2, conn->ctx, "conn",
1749 : "SM_Q_DROP: %p, h=%lu", e, e->sm_h);
1750 0 : c = queue_element_free(conn->ctx, e);
1751 0 : strophe_free(conn->ctx, c);
1752 : }
1753 0 : conn->sm_state->r_sent = 0;
1754 : }
1755 : }
1756 : }
1757 :
1758 0 : static unsigned short _conn_default_port(xmpp_conn_t *conn,
1759 : xmpp_conn_type_t type)
1760 : {
1761 0 : switch (type) {
1762 0 : case XMPP_CLIENT:
1763 0 : return conn->tls_legacy_ssl ? XMPP_PORT_CLIENT_LEGACY_SSL
1764 0 : : XMPP_PORT_CLIENT;
1765 : case XMPP_COMPONENT:
1766 : return XMPP_PORT_COMPONENT;
1767 : default:
1768 : return 0;
1769 : };
1770 : }
1771 :
1772 0 : char *queue_element_free(xmpp_ctx_t *ctx, xmpp_send_queue_t *e)
1773 : {
1774 0 : char *ret = e->data;
1775 0 : strophe_debug_verbose(2, ctx, "conn", "Q_FREE: %p", e);
1776 0 : memset(e, 0, sizeof(*e));
1777 0 : strophe_free(ctx, e);
1778 0 : strophe_debug_verbose(3, ctx, "conn", "Q_CONTENT: %s", ret);
1779 0 : return ret;
1780 : }
1781 :
1782 8 : static void _conn_reset(xmpp_conn_t *conn)
1783 : {
1784 8 : xmpp_ctx_t *ctx = conn->ctx;
1785 8 : xmpp_send_queue_t *sq, *tsq;
1786 :
1787 8 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1788 0 : strophe_debug(ctx, "conn", "Can't reset connected object.");
1789 0 : return;
1790 : }
1791 :
1792 8 : compression_free(conn);
1793 :
1794 8 : conn->intf = sock_intf;
1795 8 : conn->intf.conn = conn;
1796 :
1797 : /* free queued */
1798 8 : sq = conn->send_queue_head;
1799 8 : while (sq) {
1800 0 : tsq = sq;
1801 0 : sq = sq->next;
1802 0 : strophe_free(ctx, queue_element_free(ctx, tsq));
1803 : }
1804 8 : conn->send_queue_head = NULL;
1805 8 : conn->send_queue_tail = NULL;
1806 8 : conn->send_queue_len = 0;
1807 8 : conn->send_queue_user_len = 0;
1808 :
1809 8 : if (conn->stream_error) {
1810 0 : xmpp_stanza_release(conn->stream_error->stanza);
1811 0 : strophe_free_and_null(ctx, conn->stream_error->text);
1812 0 : strophe_free_and_null(ctx, conn->stream_error);
1813 : }
1814 :
1815 8 : strophe_free_and_null(ctx, conn->domain);
1816 8 : strophe_free_and_null(ctx, conn->bound_jid);
1817 8 : strophe_free_and_null(ctx, conn->stream_id);
1818 8 : conn->stream_negotiation_completed = 0;
1819 8 : conn->secured = 0;
1820 8 : conn->tls_failed = 0;
1821 8 : conn->error = 0;
1822 :
1823 8 : conn->tls_support = 0;
1824 :
1825 8 : conn->bind_required = 0;
1826 8 : conn->session_required = 0;
1827 :
1828 8 : handler_system_delete_all(conn);
1829 : }
1830 :
1831 0 : static int _conn_connect(xmpp_conn_t *conn,
1832 : const char *domain,
1833 : xmpp_conn_type_t type,
1834 : xmpp_conn_handler callback,
1835 : void *userdata)
1836 : {
1837 0 : xmpp_open_handler open_handler;
1838 :
1839 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
1840 : return XMPP_EINVOP;
1841 0 : if (type != XMPP_CLIENT && type != XMPP_COMPONENT)
1842 : return XMPP_EINVOP;
1843 :
1844 0 : _conn_reset(conn);
1845 :
1846 0 : conn->type = type;
1847 0 : conn->domain = strophe_strdup(conn->ctx, domain);
1848 0 : if (!conn->domain)
1849 : return XMPP_EMEM;
1850 :
1851 0 : conn->sock = sock_connect(conn->xsock);
1852 0 : if (conn->sock == INVALID_SOCKET)
1853 : return XMPP_EINT;
1854 :
1855 : /* setup handler */
1856 0 : conn->conn_handler = callback;
1857 0 : conn->userdata = userdata;
1858 :
1859 0 : open_handler = conn->is_raw ? auth_handle_open_stub
1860 0 : : type == XMPP_CLIENT ? auth_handle_open
1861 0 : : auth_handle_component_open;
1862 0 : conn_prepare_reset(conn, open_handler);
1863 :
1864 : /* FIXME: it could happen that the connect returns immediately as
1865 : * successful, though this is pretty unlikely. This would be a little
1866 : * hard to fix, since we'd have to detect and fire off the callback
1867 : * from within the event loop */
1868 :
1869 0 : conn->state = XMPP_STATE_CONNECTING;
1870 0 : conn->timeout_stamp = time_stamp();
1871 :
1872 0 : return 0;
1873 : }
1874 :
1875 0 : void send_raw(xmpp_conn_t *conn,
1876 : const char *data,
1877 : size_t len,
1878 : xmpp_send_queue_owner_t owner,
1879 : void *userdata)
1880 : {
1881 0 : char *d;
1882 :
1883 0 : if (conn->state != XMPP_STATE_CONNECTED)
1884 : return;
1885 :
1886 0 : d = strophe_strndup(conn->ctx, data, len);
1887 0 : if (!d) {
1888 0 : strophe_error(conn->ctx, "conn", "Failed to strndup");
1889 0 : return;
1890 : }
1891 :
1892 0 : _send_raw(conn, d, len, owner, userdata);
1893 : }
1894 :
1895 0 : static void _send_valist(xmpp_conn_t *conn,
1896 : const char *fmt,
1897 : va_list ap,
1898 : xmpp_send_queue_owner_t owner)
1899 : {
1900 0 : va_list apdup;
1901 0 : size_t len;
1902 0 : char buf[1024]; /* small buffer for common case */
1903 0 : char *bigbuf;
1904 :
1905 0 : if (!_is_connected(conn, owner))
1906 0 : return;
1907 :
1908 0 : va_copy(apdup, ap);
1909 0 : len = strophe_vsnprintf(buf, sizeof(buf), fmt, apdup);
1910 0 : va_end(apdup);
1911 :
1912 0 : if (len >= sizeof(buf)) {
1913 : /* we need more space for this data, so we allocate a big
1914 : * enough buffer and print to that */
1915 0 : len++; /* account for trailing \0 */
1916 0 : bigbuf = strophe_alloc(conn->ctx, len);
1917 0 : if (!bigbuf) {
1918 0 : strophe_debug(conn->ctx, "xmpp",
1919 : "Could not allocate memory for send_raw_string");
1920 0 : return;
1921 : }
1922 0 : va_copy(apdup, ap);
1923 0 : strophe_vsnprintf(bigbuf, len, fmt, apdup);
1924 0 : va_end(apdup);
1925 :
1926 : /* len - 1 so we don't send trailing \0 */
1927 0 : _send_raw(conn, bigbuf, len - 1, owner, NULL);
1928 : } else {
1929 : /* go through send_raw() which does the strdup() for us */
1930 0 : send_raw(conn, buf, len, owner, NULL);
1931 : }
1932 : }
1933 :
1934 0 : void send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
1935 : {
1936 0 : va_list ap;
1937 :
1938 0 : if (conn->state != XMPP_STATE_CONNECTED)
1939 0 : return;
1940 :
1941 0 : va_start(ap, fmt);
1942 0 : _send_valist(conn, fmt, ap, XMPP_QUEUE_SM_STROPHE);
1943 0 : va_end(ap);
1944 : }
1945 :
1946 0 : void send_stanza(xmpp_conn_t *conn,
1947 : xmpp_stanza_t *stanza,
1948 : xmpp_send_queue_owner_t owner)
1949 : {
1950 0 : char *buf = NULL;
1951 0 : size_t len;
1952 :
1953 0 : if (!_is_connected(conn, owner))
1954 0 : goto out;
1955 :
1956 0 : if (xmpp_stanza_to_text(stanza, &buf, &len) != 0) {
1957 0 : strophe_error(conn->ctx, "conn", "Failed to stanza_to_text");
1958 0 : goto out;
1959 : }
1960 :
1961 0 : _send_raw(conn, buf, len, owner, NULL);
1962 0 : out:
1963 0 : xmpp_stanza_release(stanza);
1964 0 : }
1965 :
1966 0 : void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item)
1967 : {
1968 0 : item->next = NULL;
1969 0 : if (!queue->tail) {
1970 0 : item->prev = NULL;
1971 0 : queue->head = item;
1972 0 : queue->tail = item;
1973 : } else {
1974 0 : item->prev = queue->tail;
1975 0 : queue->tail->next = item;
1976 0 : queue->tail = item;
1977 : }
1978 0 : }
1979 :
1980 0 : xmpp_send_queue_t *peek_queue_front(xmpp_queue_t *queue)
1981 : {
1982 0 : return queue->head;
1983 : }
1984 :
1985 0 : xmpp_send_queue_t *pop_queue_front(xmpp_queue_t *queue)
1986 : {
1987 0 : xmpp_send_queue_t *ret = queue->head;
1988 0 : if (queue->head) {
1989 0 : queue->head = queue->head->next;
1990 0 : if (!queue->head) {
1991 0 : queue->tail = NULL;
1992 : } else {
1993 0 : queue->head->prev = NULL;
1994 : }
1995 0 : ret->prev = ret->next = NULL;
1996 : }
1997 0 : return ret;
1998 : }
1999 :
2000 0 : static int _send_raw(xmpp_conn_t *conn,
2001 : char *data,
2002 : size_t len,
2003 : xmpp_send_queue_owner_t owner,
2004 : void *userdata)
2005 : {
2006 0 : xmpp_send_queue_t *item;
2007 0 : const char *req_ack = "<r xmlns='urn:xmpp:sm:3'/>";
2008 :
2009 : /* create send queue item for queue */
2010 0 : item = strophe_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
2011 0 : if (!item) {
2012 0 : strophe_error(conn->ctx, "conn", "DROPPED: %s", data);
2013 0 : strophe_free(conn->ctx, data);
2014 0 : return XMPP_EMEM;
2015 : }
2016 :
2017 0 : item->data = data;
2018 0 : item->len = len;
2019 0 : item->next = NULL;
2020 0 : item->prev = conn->send_queue_tail;
2021 0 : item->written = 0;
2022 0 : item->wip = 0;
2023 0 : item->userdata = userdata;
2024 0 : item->owner = owner;
2025 :
2026 0 : if (!conn->send_queue_tail) {
2027 : /* first item, set head and tail */
2028 0 : conn->send_queue_head = item;
2029 0 : conn->send_queue_tail = item;
2030 : } else {
2031 : /* add to the tail */
2032 0 : conn->send_queue_tail->next = item;
2033 0 : conn->send_queue_tail = item;
2034 : }
2035 0 : conn->send_queue_len++;
2036 0 : if (owner == XMPP_QUEUE_USER)
2037 0 : conn->send_queue_user_len++;
2038 0 : strophe_debug_verbose(3, conn->ctx, "conn", "QUEUED: %s", data);
2039 0 : strophe_debug_verbose(1, conn->ctx, "conn", "Q_ADD: %p", item);
2040 0 : if (!(owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled &&
2041 0 : !conn->sm_state->r_sent) {
2042 0 : send_raw(conn, req_ack, strlen(req_ack), XMPP_QUEUE_SM_STROPHE, item);
2043 0 : conn->sm_state->r_sent = 1;
2044 : }
2045 : return XMPP_EOK;
2046 : }
|