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 "comms.h"
22 #include "telnet.h"
23 #include "log.h"
24
25 static char prompt_char = '\0';
26
telnet_waitsocket(ZBX_SOCKET socket_fd,int mode)27 static int telnet_waitsocket(ZBX_SOCKET socket_fd, int mode)
28 {
29 const char *__function_name = "telnet_waitsocket";
30 struct timeval tv;
31 int rc;
32 fd_set fd, *readfd = NULL, *writefd = NULL;
33
34 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
35
36 tv.tv_sec = 0;
37 tv.tv_usec = 100000; /* 1/10 sec */
38
39 FD_ZERO(&fd);
40 FD_SET(socket_fd, &fd);
41
42 if (WAIT_READ == mode)
43 readfd = &fd;
44 else
45 writefd = &fd;
46
47 rc = select(ZBX_SOCKET_TO_INT(socket_fd) + 1, readfd, writefd, NULL, &tv);
48
49 if (ZBX_PROTO_ERROR == rc)
50 {
51 zabbix_log(LOG_LEVEL_DEBUG, "%s() rc:%d errno:%d error:[%s]", __function_name, rc,
52 zbx_socket_last_error(), strerror_from_system(zbx_socket_last_error()));
53 }
54
55 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d", __function_name, rc);
56
57 return rc;
58 }
59
telnet_socket_read(ZBX_SOCKET socket_fd,void * buf,size_t count)60 static ssize_t telnet_socket_read(ZBX_SOCKET socket_fd, void *buf, size_t count)
61 {
62 const char *__function_name = "telnet_socket_read";
63 ssize_t rc;
64 int error;
65
66 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
67
68 while (ZBX_PROTO_ERROR == (rc = ZBX_TCP_READ(socket_fd, buf, count)))
69 {
70 error = zbx_socket_last_error(); /* zabbix_log() resets the error code */
71 zabbix_log(LOG_LEVEL_DEBUG, "%s() rc:%ld errno:%d error:[%s]",
72 __function_name, (long int)rc, error, strerror_from_system(error));
73 #ifdef _WINDOWS
74 if (WSAEWOULDBLOCK == error)
75 #else
76 if (EAGAIN == error)
77 #endif
78 {
79 /* wait and if there is still an error or no input available */
80 /* we assume the other side has nothing more to say */
81 if (1 > (rc = telnet_waitsocket(socket_fd, WAIT_READ)))
82 goto ret;
83
84 continue;
85 }
86
87 break;
88 }
89
90 /* when ZBX_TCP_READ returns 0, it means EOF - let's consider it a permanent error */
91 /* note that if telnet_waitsocket() is zero, it is not a permanent condition */
92 if (0 == rc)
93 rc = ZBX_PROTO_ERROR;
94 ret:
95 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%ld", __function_name, (long int)rc);
96
97 return rc;
98 }
99
telnet_socket_write(ZBX_SOCKET socket_fd,const void * buf,size_t count)100 static ssize_t telnet_socket_write(ZBX_SOCKET socket_fd, const void *buf, size_t count)
101 {
102 const char *__function_name = "telnet_socket_write";
103 ssize_t rc;
104 int error;
105
106 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
107
108 while (ZBX_PROTO_ERROR == (rc = ZBX_TCP_WRITE(socket_fd, buf, count)))
109 {
110 error = zbx_socket_last_error(); /* zabbix_log() resets the error code */
111 zabbix_log(LOG_LEVEL_DEBUG, "%s() rc:%ld errno:%d error:[%s]",
112 __function_name, (long int)rc, error, strerror_from_system(error));
113 #ifdef _WINDOWS
114 if (WSAEWOULDBLOCK == error)
115 #else
116 if (EAGAIN == error)
117 #endif
118 {
119 telnet_waitsocket(socket_fd, WAIT_WRITE);
120 continue;
121 }
122
123 break;
124 }
125
126 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%ld", __function_name, (long int)rc);
127
128 return rc;
129 }
130
telnet_read(ZBX_SOCKET socket_fd,char * buf,size_t * buf_left,size_t * buf_offset)131 static ssize_t telnet_read(ZBX_SOCKET socket_fd, char *buf, size_t *buf_left, size_t *buf_offset)
132 {
133 const char *__function_name = "telnet_read";
134 unsigned char c, c1, c2, c3;
135 ssize_t rc = ZBX_PROTO_ERROR;
136
137 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
138
139 for (;;)
140 {
141 if (1 > (rc = telnet_socket_read(socket_fd, &c1, 1)))
142 break;
143
144 zabbix_log(LOG_LEVEL_DEBUG, "%s() c1:[%x=%c]", __function_name, c1, isprint(c1) ? c1 : ' ');
145
146 switch (c1)
147 {
148 case CMD_IAC:
149 while (0 == (rc = telnet_socket_read(socket_fd, &c2, 1)))
150 ;
151
152 if (ZBX_PROTO_ERROR == rc)
153 goto end;
154
155 zabbix_log(LOG_LEVEL_DEBUG, "%s() c2:%x", __function_name, c2);
156
157 switch (c2)
158 {
159 case CMD_IAC: /* only IAC needs to be doubled to be sent as data */
160 if (0 < *buf_left)
161 {
162 buf[(*buf_offset)++] = (char)c2;
163 (*buf_left)--;
164 }
165 break;
166 case CMD_WILL:
167 case CMD_WONT:
168 case CMD_DO:
169 case CMD_DONT:
170 while (0 == (rc = telnet_socket_read(socket_fd, &c3, 1)))
171 ;
172
173 if (ZBX_PROTO_ERROR == rc)
174 goto end;
175
176 zabbix_log(LOG_LEVEL_DEBUG, "%s() c3:%x", __function_name, c3);
177
178 /* reply to all options with "WONT" or "DONT", */
179 /* unless it is Suppress Go Ahead (SGA) */
180
181 c = CMD_IAC;
182 telnet_socket_write(socket_fd, &c, 1);
183
184 if (CMD_WONT == c2)
185 c = CMD_DONT; /* the only valid response */
186 else if (CMD_DONT == c2)
187 c = CMD_WONT; /* the only valid response */
188 else if (OPT_SGA == c3)
189 c = (c2 == CMD_DO ? CMD_WILL : CMD_DO);
190 else
191 c = (c2 == CMD_DO ? CMD_WONT : CMD_DONT);
192
193 telnet_socket_write(socket_fd, &c, 1);
194 telnet_socket_write(socket_fd, &c3, 1);
195 break;
196 default:
197 break;
198 }
199 break;
200 default:
201 if (0 < *buf_left)
202 {
203 buf[(*buf_offset)++] = (char)c1;
204 (*buf_left)--;
205 }
206 break;
207 }
208 }
209 end:
210 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d", __function_name, (int)rc);
211
212 return rc;
213 }
214
215 /******************************************************************************
216 * *
217 * Comments: converts CR+LF to Unix LF and clears CR+NUL *
218 * *
219 ******************************************************************************/
convert_telnet_to_unix_eol(char * buf,size_t * offset)220 static void convert_telnet_to_unix_eol(char *buf, size_t *offset)
221 {
222 size_t i, sz = *offset, new_offset;
223
224 new_offset = 0;
225
226 for (i = 0; i < sz; i++)
227 {
228 if (i + 1 < sz && '\r' == buf[i] && '\n' == buf[i + 1]) /* CR+LF (Windows) */
229 {
230 buf[new_offset++] = '\n';
231 i++;
232 }
233 else if (i + 1 < sz && '\r' == buf[i] && '\0' == buf[i + 1]) /* CR+NUL */
234 {
235 i++;
236 }
237 else if (i + 1 < sz && '\n' == buf[i] && '\r' == buf[i + 1]) /* LF+CR */
238 {
239 buf[new_offset++] = '\n';
240 i++;
241 }
242 else if ('\r' == buf[i]) /* CR */
243 {
244 buf[new_offset++] = '\n';
245 }
246 else
247 buf[new_offset++] = buf[i];
248 }
249
250 *offset = new_offset;
251 }
252
convert_unix_to_telnet_eol(const char * buf,size_t offset,char * out_buf,size_t * out_offset)253 static void convert_unix_to_telnet_eol(const char *buf, size_t offset, char *out_buf, size_t *out_offset)
254 {
255 size_t i;
256
257 *out_offset = 0;
258
259 for (i = 0; i < offset; i++)
260 {
261 if ('\n' != buf[i])
262 {
263 out_buf[(*out_offset)++] = buf[i];
264 }
265 else
266 {
267 out_buf[(*out_offset)++] = '\r';
268 out_buf[(*out_offset)++] = '\n';
269 }
270 }
271 }
272
telnet_lastchar(const char * buf,size_t offset)273 static char telnet_lastchar(const char *buf, size_t offset)
274 {
275 while (0 < offset)
276 {
277 offset--;
278 if (' ' != buf[offset])
279 return buf[offset];
280 }
281
282 return '\0';
283 }
284
telnet_rm_echo(char * buf,size_t * offset,const char * echo,size_t len)285 static int telnet_rm_echo(char *buf, size_t *offset, const char *echo, size_t len)
286 {
287 if (0 == memcmp(buf, echo, len))
288 {
289 *offset -= len;
290 memmove(&buf[0], &buf[len], *offset * sizeof(char));
291
292 return SUCCEED;
293 }
294
295 return FAIL;
296 }
297
telnet_rm_prompt(const char * buf,size_t * offset)298 static void telnet_rm_prompt(const char *buf, size_t *offset)
299 {
300 unsigned char state = 0; /* 0 - init, 1 - prompt */
301
302 while (0 < *offset)
303 {
304 (*offset)--;
305 if (0 == state && buf[*offset] == prompt_char)
306 state = 1;
307 if (1 == state && buf[*offset] == '\n')
308 break;
309 }
310 }
311
telnet_test_login(ZBX_SOCKET socket_fd)312 int telnet_test_login(ZBX_SOCKET socket_fd)
313 {
314 const char *__function_name = "telnet_test_login";
315 char buf[MAX_BUFFER_LEN];
316 size_t sz, offset;
317 int rc, ret = FAIL;
318
319 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
320
321 sz = sizeof(buf);
322 offset = 0;
323 while (ZBX_PROTO_ERROR != (rc = telnet_read(socket_fd, buf, &sz, &offset)))
324 {
325 if (':' == telnet_lastchar(buf, offset))
326 break;
327 }
328
329 convert_telnet_to_unix_eol(buf, &offset);
330 zabbix_log(LOG_LEVEL_DEBUG, "%s() login prompt:'%.*s'", __function_name, (int)offset, buf);
331
332 if (ZBX_PROTO_ERROR != rc)
333 ret = SUCCEED;
334
335 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
336
337 return ret;
338 }
339
telnet_login(ZBX_SOCKET socket_fd,const char * username,const char * password,AGENT_RESULT * result)340 int telnet_login(ZBX_SOCKET socket_fd, const char *username, const char *password, AGENT_RESULT *result)
341 {
342 const char *__function_name = "telnet_login";
343 char buf[MAX_BUFFER_LEN], c;
344 size_t sz, offset;
345 int rc, ret = FAIL;
346
347 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
348
349 sz = sizeof(buf);
350 offset = 0;
351 while (ZBX_PROTO_ERROR != (rc = telnet_read(socket_fd, buf, &sz, &offset)))
352 {
353 if (':' == telnet_lastchar(buf, offset))
354 break;
355 }
356
357 convert_telnet_to_unix_eol(buf, &offset);
358 zabbix_log(LOG_LEVEL_DEBUG, "%s() login prompt:'%.*s'", __function_name, (int)offset, buf);
359
360 if (ZBX_PROTO_ERROR == rc)
361 {
362 SET_MSG_RESULT(result, zbx_strdup(NULL, "No login prompt."));
363 goto fail;
364 }
365
366 telnet_socket_write(socket_fd, username, strlen(username));
367 telnet_socket_write(socket_fd, "\r\n", 2);
368
369 sz = sizeof(buf);
370 offset = 0;
371 while (ZBX_PROTO_ERROR != (rc = telnet_read(socket_fd, buf, &sz, &offset)))
372 {
373 if (':' == telnet_lastchar(buf, offset))
374 break;
375 }
376
377 convert_telnet_to_unix_eol(buf, &offset);
378 zabbix_log(LOG_LEVEL_DEBUG, "%s() password prompt:'%.*s'", __function_name, (int)offset, buf);
379
380 if (ZBX_PROTO_ERROR == rc)
381 {
382 SET_MSG_RESULT(result, zbx_strdup(NULL, "No password prompt."));
383 goto fail;
384 }
385
386 telnet_socket_write(socket_fd, password, strlen(password));
387 telnet_socket_write(socket_fd, "\r\n", 2);
388
389 sz = sizeof(buf);
390 offset = 0;
391 while (ZBX_PROTO_ERROR != (rc = telnet_read(socket_fd, buf, &sz, &offset)))
392 {
393 if ('$' == (c = telnet_lastchar(buf, offset)) || '#' == c || '>' == c || '%' == c)
394 {
395 prompt_char = c;
396 break;
397 }
398 }
399
400 convert_telnet_to_unix_eol(buf, &offset);
401 zabbix_log(LOG_LEVEL_DEBUG, "%s() prompt:'%.*s'", __function_name, (int)offset, buf);
402
403 if (ZBX_PROTO_ERROR == rc)
404 {
405 SET_MSG_RESULT(result, zbx_strdup(NULL, "Login failed."));
406 goto fail;
407 }
408
409 ret = SUCCEED;
410 fail:
411 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
412
413 return ret;
414 }
415
telnet_execute(ZBX_SOCKET socket_fd,const char * command,AGENT_RESULT * result,const char * encoding)416 int telnet_execute(ZBX_SOCKET socket_fd, const char *command, AGENT_RESULT *result, const char *encoding)
417 {
418 const char *__function_name = "telnet_execute";
419 char buf[MAX_BUFFER_LEN];
420 size_t sz, offset;
421 int rc, ret = FAIL;
422 char *command_lf = NULL, *command_crlf = NULL;
423 size_t i, offset_lf, offset_crlf;
424
425 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
426
427 /* `command' with multiple lines may contain CR+LF from the browser; */
428 /* it should be converted to plain LF to remove echo later on properly */
429 offset_lf = strlen(command);
430 command_lf = (char *)zbx_malloc(command_lf, offset_lf + 1);
431 zbx_strlcpy(command_lf, command, offset_lf + 1);
432 convert_telnet_to_unix_eol(command_lf, &offset_lf);
433
434 /* telnet protocol requires that end-of-line is transferred as CR+LF */
435 command_crlf = (char *)zbx_malloc(command_crlf, offset_lf * 2 + 1);
436 convert_unix_to_telnet_eol(command_lf, offset_lf, command_crlf, &offset_crlf);
437
438 telnet_socket_write(socket_fd, command_crlf, offset_crlf);
439 telnet_socket_write(socket_fd, "\r\n", 2);
440
441 sz = sizeof(buf);
442 offset = 0;
443 while (ZBX_PROTO_ERROR != (rc = telnet_read(socket_fd, buf, &sz, &offset)))
444 {
445 if (prompt_char == telnet_lastchar(buf, offset))
446 break;
447 }
448
449 convert_telnet_to_unix_eol(buf, &offset);
450 zabbix_log(LOG_LEVEL_DEBUG, "%s() command output:'%.*s'", __function_name, (int)offset, buf);
451
452 if (ZBX_PROTO_ERROR == rc)
453 {
454 SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot find prompt after command execution: %s",
455 strerror_from_system(zbx_socket_last_error())));
456 goto fail;
457 }
458
459 telnet_rm_echo(buf, &offset, command_lf, offset_lf);
460
461 /* multi-line commands may have returned additional prompts; */
462 /* this is not a perfect solution, because in case of multiple */
463 /* multi-line shell statements these prompts might appear in */
464 /* the middle of the output, but we still try to be helpful by */
465 /* removing additional prompts at least from the beginning */
466 for (i = 0; i < offset_lf; i++)
467 {
468 if ('\n' == command_lf[i])
469 {
470 if (SUCCEED != telnet_rm_echo(buf, &offset, "$ ", 2) &&
471 SUCCEED != telnet_rm_echo(buf, &offset, "# ", 2) &&
472 SUCCEED != telnet_rm_echo(buf, &offset, "> ", 2) &&
473 SUCCEED != telnet_rm_echo(buf, &offset, "% ", 2))
474 {
475 break;
476 }
477 }
478 }
479
480 telnet_rm_echo(buf, &offset, "\n", 1);
481 telnet_rm_prompt(buf, &offset);
482
483 zabbix_log(LOG_LEVEL_DEBUG, "%s() stripped command output:'%.*s'", __function_name, (int)offset, buf);
484
485 if (MAX_BUFFER_LEN == offset)
486 offset--;
487 buf[offset] = '\0';
488
489 SET_STR_RESULT(result, convert_to_utf8(buf, offset, encoding));
490 ret = SUCCEED;
491 fail:
492 zbx_free(command_lf);
493 zbx_free(command_crlf);
494
495 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
496
497 return ret;
498 }
499