1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25/*
26 * ldom_xmpp_client.c	Extensible Messaging and Presence Protocol (XMPP)
27 *
28 * Implement an xmpp client to subscribe for domain events from the ldmd.
29 * Notify fmd module clients upon receiving the events.
30 *
31 */
32
33#include "ldom_xmpp_client.h"
34#include "ldom_alloc.h"
35#include "ldom_utils.h"
36
37#include <stdio.h>
38#include <signal.h>
39#include <strings.h>
40#include <unistd.h>
41#include <errno.h>
42
43#include <netdb.h>
44#include <dlfcn.h>
45#include <fcntl.h>
46#include <sys/socket.h>
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <libxml/parser.h>
50#include <openssl/ssl.h>
51
52typedef enum conn_state {
53	CONN_STATE_UNKNOWN,
54	CONN_STATE_TLS,
55	CONN_STATE_FEATURE,
56	CONN_STATE_LDM_INTERFACE,
57	CONN_STATE_LDM_EVENT,
58	CONN_STATE_DONE,
59	CONN_STATE_FAILURE,
60	CONN_STATE_MAX
61} conn_state_t;
62
63typedef struct xmpp_conn {
64	int			fd;
65	int			state;
66	boolean_t		tls_started;
67	SSL			*ssl;
68	xmlParserCtxtPtr	parser;
69} xmpp_conn_t;
70
71/* Forward declaration */
72static int iowrite(xmpp_conn_t *conn, char *buf, int size);
73static void start_element(void *state, const xmlChar *name,
74	const xmlChar **attrs);
75static void end_element(void *state, const xmlChar *name);
76static void error_func(void *state, const char *msg, ...);
77static void xmpp_close(xmpp_conn_t *conn);
78static int start_tls(xmpp_conn_t *conn);
79static void handle_ldm_resp(xmpp_conn_t *conn, char *buf, size_t buf_size);
80static void handle_ldm_event(xmpp_conn_t *conn, char *buf, size_t buf_size);
81
82static int xmpp_enable = 0;
83static int xmpp_notify_pipe[2];
84static pthread_t xmpp_tid = 0;
85static pthread_mutex_t xmpp_tid_lock = PTHREAD_MUTEX_INITIALIZER;
86
87static client_list_t clt_list = { NULL, NULL, PTHREAD_MUTEX_INITIALIZER };
88
89
90#define	FUNCTION_ADD(_function, _pointer, _lib, _func_name, _ret)	\
91	_function = (_pointer)dlsym(_lib, _func_name);			\
92	if (_function == NULL) {					\
93		_ret += -1;						\
94	}
95
96/*
97 * Prototypes and pointers to functions needed from libssl.
98 */
99typedef void (*SSL_load_error_strings_pt)(void);
100typedef int (*SSL_library_init_pt)(void);
101typedef SSL_CTX *(*SSL_CTX_new_pt)(const SSL_METHOD *method);
102typedef SSL_METHOD *(*SSLv23_client_method_pt)(void);
103typedef int (*SSL_write_pt)(SSL *ssl, const void *buf, int num);
104typedef int (*SSL_CTX_use_PrivateKey_file_pt)(SSL_CTX *ctx, const char *file,
105    int type);
106typedef void (*RAND_seed_pt)(const void *buf, int num);
107typedef int (*SSL_get_error_pt)(const SSL *ssl, int ret);
108typedef long (*ERR_get_error_pt)(void);
109typedef char *(*ERR_error_string_pt)(unsigned long e, char *buf);
110typedef int (*SSL_connect_pt)(SSL *ssl);
111typedef int (*SSL_CTX_use_certificate_chain_file_pt)(SSL_CTX *ctx,
112    const char *file);
113typedef int (*SSL_set_fd_pt)(SSL *ssl, int fd);
114typedef void (*SSL_free_pt)(SSL *ssl);
115typedef int (*SSL_read_pt)(SSL *ssl, void *buf, int num);
116typedef SSL *(*SSL_new_pt)(SSL_CTX *ctx);
117typedef SSL_CTX *(*SSL_get_SSL_CTX_pt)(const SSL *ssl);
118typedef void (*SSL_CTX_free_pt)(SSL_CTX *ctx);
119
120static SSL_load_error_strings_pt SSL_load_error_strings_f = NULL;
121static SSL_library_init_pt SSL_library_init_f = NULL;
122static SSL_CTX_new_pt SSL_CTX_new_f = NULL;
123static SSLv23_client_method_pt SSLv23_client_method_f = NULL;
124static SSL_write_pt SSL_write_f = NULL;
125static SSL_CTX_use_PrivateKey_file_pt SSL_CTX_use_PrivateKey_file_f = NULL;
126static RAND_seed_pt RAND_seed_f = NULL;
127static SSL_get_error_pt SSL_get_error_f = NULL;
128static ERR_get_error_pt ERR_get_error_f = NULL;
129static ERR_error_string_pt ERR_error_string_f = NULL;
130static SSL_connect_pt SSL_connect_f = NULL;
131static SSL_CTX_use_certificate_chain_file_pt
132SSL_CTX_use_certificate_chain_file_f = NULL;
133static SSL_set_fd_pt SSL_set_fd_f = NULL;
134static SSL_free_pt SSL_free_f = NULL;
135static SSL_read_pt SSL_read_f = NULL;
136static SSL_new_pt SSL_new_f = NULL;
137static SSL_get_SSL_CTX_pt SSL_get_SSL_CTX_f = NULL;
138static SSL_CTX_free_pt SSL_CTX_free_f = NULL;
139
140static void *xmpp_dl = NULL;
141
142static ldom_event_info_t event_table[] = {
143	{ LDOM_EVENT_UNKNOWN, "unknown" },
144	{ LDOM_EVENT_ADD, "add-domain" },
145	{ LDOM_EVENT_REMOVE, "remove-domain" },
146	{ LDOM_EVENT_BIND, "bind-domain" },
147	{ LDOM_EVENT_UNBIND, "unbind-domain" },
148	{ LDOM_EVENT_START, "start-domain" },
149	{ LDOM_EVENT_STOP, "stop-domain" },
150	{ LDOM_EVENT_RESET, "domain-reset" },
151	{ LDOM_EVENT_PANIC, "panic-domain" },
152	{ LDOM_EVENT_MAX, NULL }
153};
154static int event_table_size = \
155		sizeof (event_table) / sizeof (ldom_event_info_t);
156
157static xmlSAXHandler xml_handler = {
158	NULL,		/* internalSubsetSAXFunc */
159	NULL,		/* isStandaloneSAXFunc */
160	NULL,		/* hasInternalSubsetSAXFunc */
161	NULL,		/* hasExternalSubsetSAXFunc */
162	NULL,		/* resolveEntitySAXFunc */
163	NULL,		/* getEntitySAXFunc */
164	NULL,		/* entityDeclSAXFunc */
165	NULL,		/* notationDeclSAXFunc */
166	NULL,		/* attributeDeclSAXFunc */
167	NULL,		/* elementDeclSAXFunc */
168	NULL,		/* unparsedEntityDeclSAXFunc */
169	NULL,		/* setDocumentLocatorSAXFunc */
170	NULL,		/* startDocumentSAXFunc */
171	NULL,		/* endDocumentSAXFunc */
172	start_element,	/* startElementSAXFunc */
173	end_element,	/* endElementSAXFunc */
174	NULL,		/* referenceSAXFunc */
175	NULL,		/* charactersSAXFunc */
176	NULL,		/* ignorableWhitespaceSAXFunc */
177	NULL,		/* processingInstructionSAXFunc */
178	NULL,		/* commentSAXFunc */
179	NULL,		/* warningSAXFunc */
180	error_func,	/* errorSAXFunc */
181	NULL,		/* fatalErrorSAXFunc */
182	NULL,		/* getParameterEntitySAXFunc */
183	NULL,		/* cdataBlockSAXFunc */
184	NULL,		/* externalSubsetSAXFunc */
185	0,		/* unsigned int */
186	NULL,		/* void * _private */
187	NULL,		/* startElementNsSAX2Func */
188	NULL,		/* endElementNsSAX2Func */
189	NULL		/* xmlStructuredErrorFunc */
190};
191
192static void
193end_element(void *state, const xmlChar *name)
194{
195	xmpp_conn_t	*conn = (xmpp_conn_t *)state;
196
197	if (xmlStrcmp(name, STREAM_NODE) == 0) {
198		conn->state = CONN_STATE_DONE;
199	} else if (xmlStrcmp(name, STARTTLS_NODE) == 0) {
200		(void) iowrite(conn, START_TLS, strlen(START_TLS));
201	} else if (xmlStrcmp(name, PROCEED_NODE) == 0) {
202		if (start_tls(conn)) {
203			conn->state = CONN_STATE_FAILURE;
204		}
205	} else if (xmlStrcmp(name, FEATURE_NODE) == 0) {
206		if (conn->state == CONN_STATE_TLS) {
207			conn->state = CONN_STATE_FEATURE;
208			(void) iowrite(conn, (char *)LDM_REG_DOMAIN_EVENTS,
209			    strlen((char *)LDM_REG_DOMAIN_EVENTS));
210		}
211	} else if (xmlStrcmp(name, XML_LDM_INTERFACE) == 0) {
212		conn->state = CONN_STATE_LDM_INTERFACE;
213	} else if (xmlStrcmp(name, XML_LDM_EVENT) == 0) {
214		conn->state = CONN_STATE_LDM_EVENT;
215	} else if (xmlStrcmp(name, XML_FAILURE) == 0) {
216		conn->state = CONN_STATE_FAILURE;
217	}
218}
219
220/*ARGSUSED*/
221static void
222start_element(void *state, const xmlChar *name, const xmlChar **attrs)
223{
224}
225
226/*ARGSUSED*/
227static void
228error_func(void *state, const char *msg, ...)
229{
230}
231
232static int
233xmpp_connect(xmpp_conn_t *conn)
234{
235	int sock;
236	struct sockaddr_in serveraddr;
237
238	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
239		return (-1);
240	}
241
242	serveraddr.sin_family = AF_INET;
243	serveraddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
244	serveraddr.sin_port = htons(XMPP_DEFAULT_PORT);
245	if (connect(sock, (struct sockaddr *)(&serveraddr),
246	    sizeof (struct sockaddr_in)) < 0) {
247		return (-1);
248	}
249
250	(void) bzero(conn, sizeof (xmpp_conn_t));
251	conn->fd = sock;
252	conn->tls_started = B_FALSE;
253
254	conn->parser = xmlCreatePushParserCtxt(&xml_handler, (void *) conn,
255	    NULL, NULL, NULL);
256	if (conn->parser == NULL) {
257		return (-1);
258	}
259
260	return (0);
261}
262
263static void
264xmpp_close(xmpp_conn_t *conn)
265{
266	(void) close(conn->fd);
267	conn->fd = -1;
268	conn->state = CONN_STATE_UNKNOWN;
269	if (conn->parser != NULL) {
270		xmlFreeParserCtxt(conn->parser);
271		conn->parser = NULL;
272	}
273	if (conn->tls_started) {
274		SSL_free_f(conn->ssl);
275		conn->ssl = NULL;
276	}
277	conn->tls_started = B_FALSE;
278}
279
280static int
281ioread(xmpp_conn_t *conn, char *buf, int size)
282{
283	int count;
284	if (conn->tls_started) {
285		count = SSL_read_f(conn->ssl, buf, size);
286	} else {
287		count = read(conn->fd, buf, size);
288	}
289	if (count <= 0) {
290		conn->state = CONN_STATE_FAILURE;
291	}
292
293	return (count);
294}
295
296static int
297iowrite(xmpp_conn_t *conn, char *buf, int size)
298{
299	int count;
300
301	if (conn->tls_started) {
302		count = SSL_write_f(conn->ssl, buf, size);
303	} else {
304		count = send(conn->fd, buf, size, 0);
305	}
306	if (count <= 0) {
307		conn->state = CONN_STATE_FAILURE;
308	}
309
310	return (count);
311}
312
313/*
314 * notify_event()
315 * Description:
316 *     Notify all clients an event by going through the client list and invoke
317 *     the callback functions.
318 */
319static void
320notify_event(ldom_event_t event, char *ldom_name)
321{
322	client_info_t *p;
323
324	(void) pthread_mutex_lock(&clt_list.lock);
325
326	for (p = clt_list.head; p != NULL; p = p->next) {
327		p->cb(ldom_name, event, p->data);
328	}
329
330	(void) pthread_mutex_unlock(&clt_list.lock);
331}
332
333/*
334 * xmpp_client_thr()
335 * Description:
336 *     The main entry fo the xmpp client thread.
337 */
338/*ARGSUSED*/
339static void *
340xmpp_client_thr(void *data)
341{
342	int rc = 0;
343	int cnt;
344	char buf[XMPP_BUF_SIZE];
345	xmpp_conn_t conn;
346	pollfd_t pollfd[2];
347	struct pollfd *pipe_fd = &pollfd[0];
348	struct pollfd *recv_fd = &pollfd[1];
349
350	while (xmpp_enable) {
351		/* clear the conn struct */
352		bzero(&conn, sizeof (xmpp_conn_t));
353
354		/* keep making a connection until successfully */
355		do {
356			if (rc = xmpp_connect(&conn))
357				(void) sleep(XMPP_SLEEP);
358		} while (rc != 0 && xmpp_enable);
359
360		/* write the stream node */
361		cnt = iowrite(&conn, (char *)STREAM_START,
362		    strlen((char *)STREAM_START));
363		if (cnt != strlen((char *)STREAM_START)) {
364			xmpp_close(&conn);
365			(void) sleep(XMPP_SLEEP);
366			continue;
367		}
368
369		pipe_fd->fd = xmpp_notify_pipe[1];	/* notification pipe */
370		pipe_fd->events = POLLIN;
371		recv_fd->fd = conn.fd;			/* XMPP connection */
372		recv_fd->events = POLLIN;
373
374		/* process input */
375		while ((conn.state != CONN_STATE_FAILURE) &&
376		    (conn.state != CONN_STATE_DONE) && xmpp_enable) {
377
378			/* Wait for xmpp input or the notification */
379			pipe_fd->revents = 0;
380			recv_fd->revents = 0;
381			if (poll(pollfd, 2, -1) <= 0) {
382				break;
383			} else if (pipe_fd->revents & POLLIN) {
384				/* Receive a notification to exit */
385				xmpp_close(&conn);
386				pthread_exit((void *)NULL);
387			}
388
389			/*
390			 * Assume the document size of a ldmd response is
391			 * less than 1KB. This assumption is valid with the
392			 * current ldmd implementation.
393			 * Should the document size exceeds 1KB, the buffer
394			 * size should be revisited accordingly.
395			 */
396			(void) memset(buf, 0, XMPP_BUF_SIZE);
397			cnt = ioread(&conn, buf, XMPP_BUF_SIZE);
398			if (cnt <= 0)
399				break;
400			if (rc = xmlParseChunk(conn.parser, buf, cnt, 0)) {
401				conn.state = CONN_STATE_FAILURE;
402			}
403
404			switch (conn.state) {
405			case CONN_STATE_LDM_INTERFACE:
406				handle_ldm_resp(&conn, buf, cnt);
407				break;
408			case CONN_STATE_LDM_EVENT:
409				handle_ldm_event(&conn, buf, cnt);
410				break;
411			default:
412				break;
413			}
414
415			/*
416			 * For now, the parser is reset after every read.
417			 * It should only be reset once after the ssl is opened
418			 * in the start_tls().
419			 */
420			(void) xmlCtxtResetPush(conn.parser, NULL, NULL, NULL,
421			    NULL);
422		}
423		xmpp_close(&conn);
424		(void) sleep(XMPP_SLEEP);
425	}
426	return (NULL);
427}
428
429/*
430 * find_client()
431 * Description:
432 *     Walk to the list to find a libldom client
433 */
434static client_info_t *
435find_client(ldom_hdl_t *lhp)
436{
437	client_info_t *p;
438
439	for (p = clt_list.head; p != NULL; p = p->next) {
440		if (p->lhp == lhp)
441			return (p);
442	}
443
444	return (NULL);
445}
446
447/*
448 * xmpp_add_client()
449 * Description:
450 *     Add a libldom client from the client list.
451 */
452int
453xmpp_add_client(ldom_hdl_t *lhp, ldom_reg_cb_t cb, ldom_cb_arg_t data)
454{
455	client_info_t *clt;
456
457	(void) pthread_mutex_lock(&clt_list.lock);
458	if (find_client(lhp)) {
459		/* already exists */
460		(void) pthread_mutex_unlock(&clt_list.lock);
461		return (-1);
462	}
463
464	/* new client */
465	clt = (client_info_t *)ldom_alloc(sizeof (client_info_t));
466	clt->lhp = lhp;
467	clt->cb = cb;
468	clt->data = data;
469	clt->next = NULL;
470	clt->prev = NULL;
471
472	if (clt_list.head == NULL && clt_list.tail == NULL) {
473		clt_list.head = clt;
474		clt_list.tail = clt;
475	} else {
476		/* append to the list */
477		clt->prev = clt_list.tail;
478		clt_list.tail->next  = clt;
479		clt_list.tail = clt;
480	}
481
482	(void) pthread_mutex_unlock(&clt_list.lock);
483	return (0);
484}
485
486/*
487 * xmpp_remove_client()
488 * Description:
489 *     Remove a libldom client from the client list.
490 */
491int
492xmpp_remove_client(ldom_hdl_t *lhp)
493{
494	client_info_t *p;
495
496	(void) pthread_mutex_lock(&clt_list.lock);
497	if ((p = find_client(lhp)) == NULL) {
498		/* not present */
499		(void) pthread_mutex_unlock(&clt_list.lock);
500		return (-1);
501	}
502
503	if (clt_list.head == p && clt_list.tail == p) {
504		/* single item list */
505		clt_list.head = NULL;
506		clt_list.tail = NULL;
507	} else if (clt_list.head == p) {
508		/* delete the head */
509		clt_list.head = p->next;
510		clt_list.head->prev = NULL;
511	} else if (clt_list.tail == p) {
512		/* delete the tail */
513		clt_list.tail = p->prev;
514		clt_list.tail->next = NULL;
515	} else {
516		/* delete a middle node */
517		p->next->prev = p->prev;
518		p->prev->next = p->next;
519	}
520	ldom_free(p, sizeof (client_info_t));
521
522	(void) pthread_mutex_unlock(&clt_list.lock);
523	return (0);
524}
525
526/*
527 * xmpp_stop()
528 * Description:
529 *     Stop the xmpp client thread
530 */
531/*ARGSUSED*/
532void
533xmpp_stop(void)
534{
535	(void) pthread_mutex_lock(&xmpp_tid_lock);
536	xmpp_enable = 0;
537	if (xmpp_tid) {
538		/*
539		 * Write a byte to the pipe to notify the xmpp thread to exit.
540		 * Then wait for it to exit.
541		 */
542		(void) write(xmpp_notify_pipe[0], "1", 1);
543		(void) pthread_join(xmpp_tid, NULL);
544		xmpp_tid = 0;
545	}
546	(void) pthread_mutex_unlock(&xmpp_tid_lock);
547}
548
549/*
550 * xmpp_start()
551 * Description:
552 *     Start the xmpp client thread if have not done so.
553 */
554void
555xmpp_start(void)
556{
557	xmpp_conn_t conn;
558
559	/* Check if the xmmp thread has already started */
560	(void) pthread_mutex_lock(&xmpp_tid_lock);
561	if (xmpp_tid != 0) {
562		(void) pthread_mutex_unlock(&xmpp_tid_lock);
563		return;
564	}
565
566	/* Check if the ldmd supports xmpp by opening a connection */
567	if (xmpp_connect(&conn)) {
568		(void) pthread_mutex_unlock(&xmpp_tid_lock);
569		return;
570	}
571	xmpp_close(&conn);
572	xmpp_enable = 1;
573
574	/*
575	 * create xmpp client thread for receiving domain events.
576	 * The notification pipe is for stopping the thread.
577	 */
578	(void) notify_setup(xmpp_notify_pipe);
579	(void) pthread_create(&xmpp_tid, NULL, xmpp_client_thr, NULL);
580
581	(void) pthread_mutex_unlock(&xmpp_tid_lock);
582
583	/*
584	 * Register a function to stop the above thread upon a termination
585	 */
586	(void) atexit(xmpp_stop);
587}
588
589/*
590 * This routine will run through the first time we get a remote XMPP
591 * connection. After that we will not need to do this again. It cannot be run
592 * from main thread at start as we need to alert remote users if the TLS
593 * handshake failed.
594 */
595static int
596load_SSL_lib()
597{
598	int ret = 0;
599
600	/* If we have already opened the library no need to do it again. */
601	if (xmpp_dl != NULL)
602		return (0);
603
604	/*
605	 * If the libssl.so in not in the default path, attempt to open it
606	 * under /usr/sfw/lib.
607	 */
608	xmpp_dl = dlopen("libssl.so", RTLD_NOW);
609	if (xmpp_dl == NULL) {
610		xmpp_dl = dlopen("/usr/sfw/lib/libssl.so", RTLD_NOW);
611		if (xmpp_dl == NULL)
612			return (-1);
613	}
614
615	FUNCTION_ADD(SSL_load_error_strings_f, SSL_load_error_strings_pt,
616	    xmpp_dl, "SSL_load_error_strings", ret);
617	FUNCTION_ADD(SSL_library_init_f, SSL_library_init_pt, xmpp_dl,
618	    "SSL_library_init", ret);
619	FUNCTION_ADD(SSL_CTX_new_f, SSL_CTX_new_pt, xmpp_dl,
620	    "SSL_CTX_new", ret);
621	FUNCTION_ADD(SSLv23_client_method_f, SSLv23_client_method_pt, xmpp_dl,
622	    "SSLv23_client_method", ret);
623	FUNCTION_ADD(SSL_write_f, SSL_write_pt, xmpp_dl, "SSL_write", ret);
624	FUNCTION_ADD(SSL_CTX_use_PrivateKey_file_f,
625	    SSL_CTX_use_PrivateKey_file_pt, xmpp_dl,
626	    "SSL_CTX_use_PrivateKey_file", ret);
627	FUNCTION_ADD(RAND_seed_f, RAND_seed_pt, xmpp_dl, "RAND_seed", ret);
628	FUNCTION_ADD(SSL_get_error_f, SSL_get_error_pt, xmpp_dl,
629	    "SSL_get_error", ret);
630	FUNCTION_ADD(ERR_get_error_f, ERR_get_error_pt, xmpp_dl,
631	    "ERR_get_error", ret);
632	FUNCTION_ADD(ERR_error_string_f, ERR_error_string_pt, xmpp_dl,
633	    "ERR_error_string", ret);
634	FUNCTION_ADD(SSL_connect_f, SSL_connect_pt, xmpp_dl, "SSL_connect",
635	    ret);
636	FUNCTION_ADD(SSL_CTX_use_certificate_chain_file_f,
637	    SSL_CTX_use_certificate_chain_file_pt, xmpp_dl,
638	    "SSL_CTX_use_certificate_chain_file", ret);
639	FUNCTION_ADD(SSL_set_fd_f, SSL_set_fd_pt, xmpp_dl, "SSL_set_fd", ret);
640	FUNCTION_ADD(SSL_free_f, SSL_free_pt, xmpp_dl, "SSL_free", ret);
641	FUNCTION_ADD(SSL_read_f, SSL_read_pt, xmpp_dl, "SSL_read", ret);
642	FUNCTION_ADD(SSL_new_f, SSL_new_pt, xmpp_dl, "SSL_new", ret);
643	FUNCTION_ADD(SSL_get_SSL_CTX_f, SSL_get_SSL_CTX_pt, xmpp_dl,
644	    "SSL_get_SSL_CTX", ret);
645	FUNCTION_ADD(SSL_CTX_free_f, SSL_CTX_free_pt, xmpp_dl,
646	    "SSL_CTX_free", ret);
647
648	if (ret < 0)
649		return (-1);
650	else
651		return (0);
652}
653
654/*
655 * start_tls()
656 * Description:
657 *     Load the libssl.so if has not done so and open a ssl connection.
658 *     It is assumed that there is one xmpp thread to use the ssl connection.
659 *     If multi-thread xmpp clients use the ssl connection, addtional work is
660 *     needed to ensure the usage of the ssl be thread-safe.
661 */
662static int
663start_tls(xmpp_conn_t *conn)
664{
665	int		rv, urand_fd;
666	SSL_CTX		*ssl_ctx;
667	char		rand_buf[RAND_BUF_SIZE];
668
669	rv = load_SSL_lib();
670	if (rv == -1) {
671		return (rv);
672	}
673
674	urand_fd = open("/dev/random", O_RDONLY);
675	if (urand_fd == -1) {
676		return (-1);
677	}
678	(void) read(urand_fd, rand_buf, RAND_BUF_SIZE);
679
680	SSL_library_init_f();
681	RAND_seed_f(rand_buf, RAND_BUF_SIZE);
682
683	ssl_ctx = SSL_CTX_new_f(SSLv23_client_method_f());
684	if (ssl_ctx == NULL) {
685		return (-1);
686	}
687	conn->ssl = SSL_new_f(ssl_ctx);
688	rv = SSL_set_fd_f(conn->ssl, conn->fd);
689	if (rv == 0) {
690		return (-1);
691	}
692	rv = SSL_connect_f(conn->ssl);
693	if (rv != 1) {
694		return (-1);
695	}
696	conn->tls_started = B_TRUE;
697	conn->state = CONN_STATE_TLS;
698
699	(void) iowrite(conn, STREAM_START, strlen(STREAM_START));
700
701	return (0);
702}
703
704/*
705 * Find and return the first-level subnode (if any) of 'node' which has name
706 * 'name'.
707 */
708xmlNodePtr
709xml_find_subnode(xmlNodePtr node, const xmlChar *name)
710{
711	xmlNodePtr subnode;
712
713	if (node == NULL)
714		return (NULL);
715
716	subnode = node->xmlChildrenNode;
717	while (subnode != NULL) {
718		if (((char *)subnode->name != NULL) &&
719		    (xmlStrcmp(subnode->name, name) == 0))
720			break;
721		subnode = subnode->next;
722	}
723
724	return (subnode);
725}
726
727/*
728 * handle_ldm_resp()
729 * Description:
730 *     Parse the ldmd response of the domain event registration for the failure
731 *     status. If found, set the connection to failure so that it will be
732 *     closed and a new xmpp connection is established.
733 */
734void
735handle_ldm_resp(xmpp_conn_t *conn, char *buf, size_t buf_size)
736{
737	xmlDocPtr	xml_output;
738	xmlNodePtr	root, resp, status, cmd, action;
739	char		*status_str, *action_str;
740
741	if ((xml_output = xmlParseMemory((const char *)buf, buf_size)) == NULL)
742		return;
743	if ((root = xmlDocGetRootElement(xml_output)) == NULL)
744		return;
745
746	/* get the cmd node */
747	if ((cmd = xml_find_subnode(root, XML_CMD)) == NULL)
748		return;
749	if (strcmp((char *)cmd->name, (char *)XML_CMD) != 0)
750		return;
751
752	/* get the action node and make sure it is the reg-domain-events */
753	if ((action = xml_find_subnode(cmd, XML_ACTION)) == NULL) {
754		return;
755	}
756	if ((action_str = (char *)xmlNodeGetContent(action)) == NULL)
757		return;
758	if (strcmp(action_str, XML_REGISTER_ACTION) != 0) {
759		xmlFree(action_str);
760		return;
761	}
762	xmlFree(action_str);
763
764	/* check the status of the response */
765	if ((resp = xml_find_subnode(cmd, XML_RESPONSE)) == NULL)
766		return;
767	if ((status = xml_find_subnode(resp, XML_STATUS)) == NULL)
768		return;
769	if ((status_str = (char *)xmlNodeGetContent(status)) == NULL)
770		return;
771	if (strcmp(status_str, (char *)XML_FAILURE) == 0) {
772		conn->state = CONN_STATE_FAILURE;
773	}
774	xmlFree(status_str);
775}
776
777/*
778 * handle_ldm_event()
779 * Description:
780 *     Parse the LDM_event for the ldom name and domain action. Then invokes
781 *     the clients's callback to notify them the event.
782 */
783/*ARGSUSED*/
784void
785handle_ldm_event(xmpp_conn_t *conn, char *buf, size_t buf_size)
786{
787	int		i;
788	xmlDocPtr	xml_output;
789	xmlNodePtr	root, cmd, action, data, envelope, content;
790	char		*action_str, *ldom_name;
791	ldom_event_t	event = LDOM_EVENT_UNKNOWN;
792
793	if ((xml_output = xmlParseMemory((const char *)buf, buf_size)) == NULL)
794		return;
795	if ((root = xmlDocGetRootElement(xml_output)) == NULL)
796		return;
797
798	/* get the action such as bind-domain, unbind-domain, etc. */
799	if ((cmd = xml_find_subnode(root, XML_CMD)) == NULL)
800		return;
801	if ((action = xml_find_subnode(cmd, XML_ACTION)) == NULL) {
802		return;
803	}
804	if ((action_str = (char *)xmlNodeGetContent(action)) == NULL)
805		return;
806	for (i = 0; i < event_table_size; i++) {
807		if (event_table[i].name != NULL &&
808		    strcasecmp(event_table[i].name, action_str) == 0) {
809			event = event_table[i].id;
810			break;
811		}
812	}
813	xmlFree(action_str);
814
815	/* get the ldom name */
816	data = xml_find_subnode(cmd, XML_DATA);
817	envelope = xml_find_subnode(data, XML_ENVELOPE);
818	content = xml_find_subnode(envelope, XML_CONTENT);
819	if ((ldom_name = (char *)xmlGetProp(content, XML_ATTR_ID)) == NULL)
820		return;
821
822	/* Notifies all the clients the event */
823	if (VALID_LDOM_EVENT(event)) {
824		notify_event(event, ldom_name);
825	}
826
827	xmlFree(ldom_name);
828}
829