1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "sysinfo.h"
22 #include "log.h"
23
24 #include "zbxmedia.h"
25
26 #ifdef HAVE_JABBER
27
28 #include <iksemel.h>
29
zbx_io_close(void * socket)30 static void zbx_io_close(void *socket)
31 {
32 int *sock = (int *)socket;
33
34 if (NULL == sock)
35 return;
36
37 close(*sock);
38 }
39
40 static int zbx_j_sock = -1;
41 static const char *__module_name = "JABBER";
42
zbx_io_connect(iksparser * prs,void ** socketptr,const char * server,int port)43 static int zbx_io_connect(iksparser *prs, void **socketptr, const char *server, int port)
44 {
45 int tmp;
46 #ifdef HAVE_GETADDRINFO
47 struct addrinfo hints, *addr_res, *addr_ptr;
48 char port_str[6];
49
50 *socketptr = NULL;
51
52 hints.ai_flags = AI_CANONNAME;
53 hints.ai_family = PF_UNSPEC;
54 hints.ai_socktype = SOCK_STREAM;
55 hints.ai_protocol = 0;
56 hints.ai_addrlen = 0;
57 hints.ai_canonname = NULL;
58 hints.ai_addr = NULL;
59 hints.ai_next = NULL;
60
61 zbx_snprintf(port_str, sizeof(port_str), "%d", port);
62
63 if (0 != getaddrinfo(server, port_str, &hints, &addr_res))
64 return IKS_NET_NODNS;
65
66 addr_ptr = addr_res;
67
68 while (NULL != addr_ptr)
69 {
70 if (-1 != (zbx_j_sock = socket(addr_ptr->ai_family, addr_ptr->ai_socktype, addr_ptr->ai_protocol)))
71 break;
72
73 addr_ptr = addr_ptr->ai_next;
74 }
75
76 if (-1 == zbx_j_sock)
77 {
78 freeaddrinfo(addr_res);
79 return IKS_NET_NOSOCK;
80 }
81
82 tmp = connect(zbx_j_sock, addr_ptr->ai_addr, addr_ptr->ai_addrlen);
83
84 freeaddrinfo(addr_res);
85 #else
86 struct hostent *host;
87 struct sockaddr_in sin;
88
89 if (NULL == (host = gethostbyname(server)))
90 return IKS_NET_NODNS;
91
92 memcpy(&sin.sin_addr, host->h_addr, host->h_length);
93 sin.sin_family = host->h_addrtype;
94 sin.sin_port = htons(port);
95
96 if (-1 == (zbx_j_sock = socket(host->h_addrtype, SOCK_STREAM, 0)))
97 return IKS_NET_NOSOCK;
98
99 tmp = connect(zbx_j_sock, (struct sockaddr *)&sin, sizeof(sin));
100 #endif
101 if (0 != tmp)
102 {
103 zbx_io_close((void *)&zbx_j_sock);
104 return IKS_NET_NOCONN;
105 }
106
107 *socketptr = (void *)&zbx_j_sock;
108
109 return IKS_OK;
110 }
111
zbx_io_send(void * socket,const char * data,size_t len)112 static int zbx_io_send(void *socket, const char *data, size_t len)
113 {
114 int *sock = (int *)socket;
115
116 if (NULL == sock)
117 return IKS_NET_RWERR;
118
119 if (write(*sock, data, len) < (ssize_t)len)
120 return IKS_NET_RWERR;
121
122 return IKS_OK;
123 }
124
zbx_io_recv(void * socket,char * buffer,size_t buf_len,int timeout)125 static int zbx_io_recv(void *socket, char *buffer, size_t buf_len, int timeout)
126 {
127 int *sock = (int *)socket, len;
128 struct timeval tv;
129 fd_set fds;
130
131 if (NULL == sock)
132 return -1;
133
134 tv.tv_sec = timeout;
135 tv.tv_usec = 0;
136
137 FD_ZERO(&fds);
138 FD_SET(*sock, &fds);
139
140 if (0 < select(*sock + 1, &fds, NULL, NULL, -1 != timeout ? &tv : NULL))
141 {
142 len = recv(*sock, buffer, buf_len, 0);
143
144 if (0 < len)
145 return len;
146 else if (0 >= len)
147 return -1;
148 }
149
150 return 0;
151 }
152
153 static ikstransport zbx_iks_transport =
154 {
155 IKS_TRANSPORT_V1,
156 zbx_io_connect,
157 zbx_io_send,
158 zbx_io_recv,
159 zbx_io_close,
160 NULL
161 };
162
163 #define JABBER_DISCONNECTED 0
164 #define JABBER_ERROR 1
165
166 #define JABBER_CONNECTING 2
167 #define JABBER_CONNECTED 3
168 #define JABBER_AUTHORIZED 4
169 #define JABBER_WORKING 5
170 #define JABBER_READY 10
171
172 typedef struct
173 {
174 iksparser *prs;
175 iksid *acc;
176 char *pass;
177 int features;
178 iksfilter *my_filter;
179 int opt_use_tls;
180 int opt_use_sasl;
181 int status;
182 }
183 jabber_session_t, *jabber_session_p;
184
185 static jabber_session_p jsess = NULL;
186 static char *jabber_error = NULL;
187 static int jabber_error_len = 0;
188
on_result(jabber_session_p sess,ikspak * pak)189 static int on_result(jabber_session_p sess, ikspak *pak)
190 {
191 const char *__function_name = "on_result";
192
193 zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s()", __module_name, __function_name);
194
195 sess->status = JABBER_READY;
196
197 zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s()", __module_name, __function_name);
198
199 return IKS_FILTER_EAT;
200 }
201
202 /******************************************************************************
203 * *
204 * Function: lookup_jabber *
205 * *
206 * Purpose: lookup Jabber SRV record *
207 * *
208 * Author: Aleksandrs Saveljevs, based on code by Edward Rudd *
209 * *
210 ******************************************************************************/
lookup_jabber(const char * server,int port,char * real_server,size_t real_server_len,int * real_port)211 static void lookup_jabber(const char *server, int port, char *real_server, size_t real_server_len, int *real_port)
212 {
213 const char *__function_name = "lookup_jabber";
214 char buffer[MAX_STRING_LEN], command[MAX_STRING_LEN];
215 AGENT_RESULT result;
216 int ret = SYSINFO_RET_FAIL;
217
218 zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s() server:'%s' port:%d", __module_name, __function_name, server, port);
219
220 init_result(&result);
221
222 zbx_snprintf(command, sizeof(command), "net.dns.record[,_xmpp-client._tcp.%s,SRV]", server);
223
224 if (SUCCEED == process(command, 0, &result))
225 {
226 int max_priority = 65536, max_weight = -1;
227 int cur_priority, cur_weight, cur_port;
228 const char *p = result.text;
229
230 zabbix_log(LOG_LEVEL_DEBUG, "response to DNS query: [%s]", result.text);
231
232 /* let us now choose the server with the highest priority and maximum weight */
233
234 zbx_snprintf(command, sizeof(command), "_xmpp-client._tcp.%s SRV %%d %%d %%d %%" ZBX_FS_SIZE_T "s",
235 server, (zbx_fs_size_t)sizeof(buffer));
236
237 while (NULL != p)
238 {
239 if (4 == sscanf(p, command, &cur_priority, &cur_weight, &cur_port, buffer))
240 {
241 if (cur_priority < max_priority || (cur_priority == max_priority && cur_weight > max_weight))
242 {
243 ret = SYSINFO_RET_OK;
244
245 max_priority = cur_priority;
246 max_weight = cur_weight;
247
248 zbx_strlcpy(real_server, buffer, real_server_len);
249 *real_port = cur_port;
250 }
251 }
252
253 if (NULL != (p = strchr(p, '\n')))
254 p++;
255 }
256 }
257
258 free_result(&result);
259
260 if (SYSINFO_RET_OK != ret)
261 {
262 zbx_strlcpy(real_server, server, real_server_len);
263 *real_port = port;
264 }
265
266 zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s() real_server:'%s' real_port:%d",
267 __module_name, __function_name, real_server, *real_port);
268 }
269
270 /******************************************************************************
271 * *
272 * Function: disconnect_jabber *
273 * *
274 * Purpose: disconnect from Jabber server *
275 * *
276 * Return value: always return SUCCEED *
277 * *
278 * Author: Eugene Grigorjev *
279 * *
280 ******************************************************************************/
disconnect_jabber()281 static int disconnect_jabber()
282 {
283 const char *__function_name = "disconnect_jabber";
284
285 zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s()", __module_name, __function_name);
286
287 if (JABBER_DISCONNECTED != jsess->status)
288 iks_disconnect(jsess->prs);
289
290 if (NULL != jsess->my_filter)
291 {
292 iks_filter_delete(jsess->my_filter);
293 jsess->my_filter = NULL;
294 }
295
296 if (NULL != jsess->prs)
297 {
298 iks_parser_delete(jsess->prs);
299 jsess->prs = NULL;
300 }
301
302 zbx_free(jsess->pass);
303
304 jsess->acc = NULL;
305
306 jsess->status = JABBER_DISCONNECTED;
307
308 zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s()", __module_name, __function_name);
309
310 return SUCCEED;
311 }
312
on_stream(jabber_session_p sess,int type,iks * node)313 static int on_stream(jabber_session_p sess, int type, iks *node)
314 {
315 const char *__function_name = "on_stream";
316 iks *x = NULL;
317 ikspak *pak = NULL;
318 int ret = IKS_OK;
319
320 zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s()", __module_name, __function_name);
321
322 switch (type)
323 {
324 case IKS_NODE_START:
325 break;
326 case IKS_NODE_NORMAL:
327 if (0 == strcmp("stream:features", iks_name(node)))
328 {
329 sess->features = iks_stream_features(node);
330
331 if (IKS_STREAM_STARTTLS == (sess->features & IKS_STREAM_STARTTLS))
332 {
333 iks_start_tls(sess->prs);
334 }
335 else
336 {
337 if (JABBER_AUTHORIZED == sess->status)
338 {
339 if (IKS_STREAM_BIND == (sess->features & IKS_STREAM_BIND))
340 {
341 x = iks_make_resource_bind(sess->acc);
342 iks_send(sess->prs, x);
343 iks_delete(x);
344 }
345 if (IKS_STREAM_SESSION == (sess->features & IKS_STREAM_SESSION))
346 {
347 x = iks_make_session();
348 iks_insert_attrib(x, "id", "auth");
349 iks_send(sess->prs, x);
350 iks_delete(x);
351 }
352 }
353 else
354 {
355 if (IKS_STREAM_SASL_MD5 == (sess->features & IKS_STREAM_SASL_MD5))
356 iks_start_sasl(sess->prs, IKS_SASL_DIGEST_MD5, sess->acc->user, sess->pass);
357 else if (IKS_STREAM_SASL_PLAIN == (sess->features & IKS_STREAM_SASL_PLAIN))
358 iks_start_sasl(sess->prs, IKS_SASL_PLAIN, sess->acc->user, sess->pass);
359 }
360 }
361 }
362 else if (0 == strcmp("failure", iks_name(node)))
363 {
364 zbx_snprintf(jabber_error, jabber_error_len, "sasl authentication failed");
365 jsess->status = JABBER_ERROR;
366 ret = IKS_HOOK;
367 }
368 else if (0 == strcmp("success", iks_name(node)))
369 {
370 zabbix_log(LOG_LEVEL_DEBUG, "%s: authorized", __module_name);
371 sess->status = JABBER_AUTHORIZED;
372 iks_send_header(sess->prs, sess->acc->server);
373 }
374 else
375 {
376 pak = iks_packet(node);
377 iks_filter_packet(sess->my_filter, pak);
378 if (JABBER_READY == jsess->status)
379 ret = IKS_HOOK;
380 }
381 break;
382 case IKS_NODE_STOP:
383 zbx_snprintf(jabber_error, jabber_error_len, "server disconnected");
384 jsess->status = JABBER_ERROR;
385 ret = IKS_HOOK;
386 break;
387 case IKS_NODE_ERROR:
388 zbx_snprintf(jabber_error, jabber_error_len, "stream error");
389 jsess->status = JABBER_ERROR;
390 ret = IKS_HOOK;
391 }
392
393 if (NULL != node)
394 iks_delete(node);
395
396 zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s()", __module_name, __function_name);
397
398 return ret;
399 }
400
on_error(void * user_data,ikspak * pak)401 static int on_error(void *user_data, ikspak *pak)
402 {
403 zbx_snprintf(jabber_error, jabber_error_len, "authorization failed");
404
405 jsess->status = JABBER_ERROR;
406
407 return IKS_FILTER_EAT;
408 }
409
410 #ifdef DEBUG
on_log(jabber_session_p sess,const char * data,size_t size,int is_incoming)411 static void on_log(jabber_session_p sess, const char *data, size_t size, int is_incoming)
412 {
413 zabbix_log(LOG_LEVEL_DEBUG, "%s: %s%s: %s",
414 __module_name, iks_is_secure(sess->prs) ? "Sec" : "", is_incoming ? "RECV" : "SEND", data);
415 }
416 #endif
417
418 /******************************************************************************
419 * *
420 * Function: connect_jabber *
421 * *
422 * Purpose: connect to Jabber server *
423 * *
424 * Return value: SUCCEED on successful connection *
425 * FAIL - otherwise *
426 * *
427 * Author: Eugene Grigorjev *
428 * *
429 ******************************************************************************/
connect_jabber(const char * jabber_id,const char * password,int use_sasl,int port)430 static int connect_jabber(const char *jabber_id, const char *password, int use_sasl, int port)
431 {
432 const char *__function_name = "connect_jabber";
433 char *buf = NULL;
434 char real_server[MAX_STRING_LEN];
435 int real_port = 0, iks_error, timeout, ret = FAIL;
436
437 zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s() jabber_id:'%s'", __module_name, __function_name, jabber_id);
438
439 if (NULL == jsess)
440 {
441 jsess = zbx_malloc(jsess, sizeof(jabber_session_t));
442 memset(jsess, 0, sizeof(jabber_session_t));
443 }
444 else if (JABBER_DISCONNECTED != jsess->status)
445 {
446 disconnect_jabber();
447 }
448
449 if (NULL == (jsess->prs = iks_stream_new(IKS_NS_CLIENT, jsess, (iksStreamHook *)on_stream)))
450 {
451 zbx_snprintf(jabber_error, jabber_error_len, "cannot create iksemel parser: %s", zbx_strerror(errno));
452 goto lbl_fail;
453 }
454
455 #ifdef DEBUG
456 iks_set_log_hook(jsess->prs, (iksLogHook *)on_log);
457 #endif
458
459 jsess->acc = iks_id_new(iks_parser_stack(jsess->prs), jabber_id);
460
461 if (NULL == jsess->acc->resource)
462 {
463 /* user gave no resource name, use the default */
464 buf = zbx_dsprintf(buf, "%s@%s/%s", jsess->acc->user, jsess->acc->server, "ZABBIX");
465 jsess->acc = iks_id_new(iks_parser_stack(jsess->prs), buf);
466 zbx_free(buf);
467 }
468
469 jsess->pass = zbx_strdup(jsess->pass, password);
470 jsess->opt_use_sasl = use_sasl;
471
472 if (NULL == (jsess->my_filter = iks_filter_new()))
473 {
474 zbx_snprintf(jabber_error, jabber_error_len, "cannot create filter: %s", zbx_strerror(errno));
475 goto lbl_fail;
476 }
477
478 iks_filter_add_rule(jsess->my_filter, (iksFilterHook *)on_result, jsess,
479 IKS_RULE_TYPE, IKS_PAK_IQ,
480 IKS_RULE_SUBTYPE, IKS_TYPE_RESULT,
481 IKS_RULE_ID, "auth",
482 IKS_RULE_DONE);
483
484 iks_filter_add_rule(jsess->my_filter, on_error, jsess,
485 IKS_RULE_TYPE, IKS_PAK_IQ,
486 IKS_RULE_SUBTYPE, IKS_TYPE_ERROR,
487 IKS_RULE_ID, "auth",
488 IKS_RULE_DONE);
489
490 lookup_jabber(jsess->acc->server, port, real_server, sizeof(real_server), &real_port);
491
492 switch (iks_connect_with(jsess->prs, real_server, real_port, jsess->acc->server, &zbx_iks_transport))
493 {
494 case IKS_OK:
495 break;
496 case IKS_NET_NODNS:
497 zbx_snprintf(jabber_error, jabber_error_len, "hostname lookup failed");
498 goto lbl_fail;
499 case IKS_NET_NOCONN:
500 zbx_snprintf(jabber_error, jabber_error_len, "connection failed: %s",
501 strerror_from_system(errno));
502 goto lbl_fail;
503 default:
504 zbx_snprintf(jabber_error, jabber_error_len, "connection error: %s",
505 strerror_from_system(errno));
506 goto lbl_fail;
507 }
508
509 timeout = 30;
510
511 while (JABBER_READY != jsess->status && JABBER_ERROR != jsess->status)
512 {
513 iks_error = iks_recv(jsess->prs, 1);
514
515 if (IKS_HOOK == iks_error)
516 break;
517
518 if (IKS_NET_TLSFAIL == iks_error)
519 {
520 zbx_snprintf(jabber_error, jabber_error_len, "tls handshake failed");
521 break;
522 }
523
524 if (IKS_OK != iks_error)
525 {
526 zbx_snprintf(jabber_error, jabber_error_len, "received error [%d]: %s",
527 iks_error, zbx_strerror(errno));
528 break;
529 }
530
531 if (0 == --timeout)
532 break;
533 }
534
535 if (JABBER_READY == jsess->status)
536 ret = SUCCEED;
537 lbl_fail:
538 zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s():%s", __module_name, __function_name, zbx_result_string(ret));
539
540 return ret;
541 }
542
543 /******************************************************************************
544 * *
545 * Function: send_jabber *
546 * *
547 * Purpose: send Jabber message *
548 * *
549 * Return value: SUCCEED if message sent *
550 * FAIL - otherwise *
551 * *
552 * Author: Eugene Grigorjev *
553 * *
554 ******************************************************************************/
send_jabber(const char * username,const char * password,const char * sendto,const char * subject,const char * message,char * error,int max_error_len)555 int send_jabber(const char *username, const char *password, const char *sendto,
556 const char *subject, const char *message, char *error, int max_error_len)
557 {
558 const char *__function_name = "send_jabber";
559 iks *x;
560 int ret = FAIL, iks_error = IKS_OK;
561
562 assert(error);
563
564 zabbix_log(LOG_LEVEL_DEBUG, "%s: In %s()", __module_name, __function_name);
565
566 *error = '\0';
567
568 jabber_error = error;
569 jabber_error_len = max_error_len;
570
571 if (SUCCEED != connect_jabber(username, password, 1, IKS_JABBER_PORT))
572 goto lbl_fail;
573
574 zabbix_log(LOG_LEVEL_DEBUG, "%s: sending", __module_name);
575
576 if (NULL != (x = iks_make_msg(IKS_TYPE_NONE, sendto, message)))
577 {
578 iks_insert_cdata(iks_insert(x, "subject"), subject, 0);
579 iks_insert_attrib(x, "from", username);
580
581 if (IKS_OK == (iks_error = iks_send(jsess->prs, x)))
582 {
583 zabbix_log(LOG_LEVEL_DEBUG, "%s: message sent", __module_name);
584 ret = SUCCEED;
585 }
586 else
587 {
588 zbx_snprintf(error, max_error_len, "cannot send message: %s", strerror_from_system(errno));
589 jsess->status = JABBER_ERROR;
590 }
591
592 iks_delete(x);
593 }
594 else
595 zbx_snprintf(error, max_error_len, "cannot create message");
596 lbl_fail:
597 if (NULL != jsess && JABBER_DISCONNECTED != jsess->status)
598 disconnect_jabber();
599
600 jabber_error = NULL;
601 jabber_error_len = 0;
602
603 if ('\0' != *error)
604 zabbix_log(LOG_LEVEL_WARNING, "%s: [%s] %s", __module_name, username, error);
605
606 zabbix_log(LOG_LEVEL_DEBUG, "%s: End of %s():%s", __module_name, __function_name, zbx_result_string(ret));
607
608 return ret;
609 }
610
611 #endif /* HAVE_JABBER */
612