remove debug code
[nsca-ng.git] / perl / client.c
blob1ec797a30cd77b33e509b1d3066905ef99d52fe9
1 /*
2 * Copyright (c) 2014 Alexander Golovko <alexandro@onsec.ru>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
28 #include <openssl/ssl.h>
29 #include <openssl/err.h>
30 #include <string.h>
31 #include <time.h>
33 #include "client.h"
34 #include "uthash.h"
36 static nscang_client_t *nscang_client_instances;
38 unsigned int
39 set_psk(SSL *ssl, const char *hint, char *identity,
40 unsigned int max_identity_length, unsigned char *psk,
41 unsigned int max_psk_length)
43 nscang_client_t *c = NULL;
45 HASH_FIND_PTR(nscang_client_instances, &ssl, c);
46 if (c != NULL) {
47 strncpy(identity, c->identity, max_identity_length);
48 identity[max_identity_length - 1] = 0x00;
50 strncpy((char *)psk, c->psk, max_psk_length);
51 psk[max_psk_length - 1] = 0x00;
53 return strlen((char *)psk);
55 identity[0] = 0x00;
56 psk[0] = 0x00;
57 return 0;
60 int
61 nscang_client_init(nscang_client_t *c, char *host, int port, char *ciphers,
62 char *identity, char *psk)
64 memset(c, 0x00, sizeof(nscang_client_t));
66 c->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
67 if (c->ssl_ctx == NULL) {
68 c->_errno = NSCANG_ERROR_SSL_CTX_CREATE;
69 return 0;
72 if (ciphers != NULL) {
73 if (SSL_CTX_set_cipher_list(c->ssl_ctx, ciphers) != 1) {
74 c->_errno = NSCANG_ERROR_SSL_CIPHERS;
75 return 0;
79 SSL_CTX_set_options(c->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
81 c->bio = BIO_new(BIO_s_connect());
82 if (c->bio == NULL) {
83 c->_errno = NSCANG_ERROR_SSL_BIO_CREATE;
84 return 0;
86 BIO_set_conn_hostname(c->bio, host);
87 BIO_set_conn_int_port(c->bio, &port);
88 BIO_set_nbio(c->bio, 1);
90 c->ssl = SSL_new(c->ssl_ctx);
91 if (c->ssl == NULL) {
92 nscang_client_free(c);
93 c->_errno = NSCANG_ERROR_SSL_CREATE;
94 return 0;
97 SSL_set_bio(c->ssl, c->bio, c->bio);
98 SSL_set_connect_state(c->ssl);
99 SSL_set_psk_client_callback(c->ssl, set_psk);
101 c->identity = malloc(strlen(identity) + 1);
102 c->psk = malloc(strlen(psk) + 1);
104 if ((c->identity == NULL) || (c->psk == NULL)) {
105 c->_errno = NSCANG_ERROR_MALLOC;
106 return 0;
109 strcpy(c->identity, identity);
110 strcpy(c->psk, psk);
112 HASH_ADD_PTR(nscang_client_instances, ssl, c);
114 c->state = NSCANG_STATE_NEW;
116 return 1;
119 void
120 nscang_client_free(nscang_client_t *c)
122 if (c->state != NSCANG_STATE_NONE)
123 nscang_client_disconnect(c);
125 if (c->ssl != NULL) {
126 HASH_DEL(nscang_client_instances, c);
128 if (c->ssl != NULL) {
129 SSL_free(c->ssl);
130 c->ssl = NULL;
131 c->bio = NULL;
133 if (c->ssl_ctx != NULL) {
134 SSL_CTX_free(c->ssl_ctx);
135 c->ssl_ctx = NULL;
137 if (c->identity != NULL) {
138 free(c->identity);
139 c->identity = NULL;
141 if (c->psk != NULL) {
142 free(c->psk);
143 c->psk = NULL;
146 c->state = NSCANG_STATE_NONE;
150 nscang_client_write(nscang_client_t *c, void *buf, int len, int timeout)
152 int rc;
153 time_t endtime;
154 fd_set fdset;
155 struct timeval tv;
157 tv.tv_sec = 0;
158 tv.tv_usec = 100000;
160 endtime = time(NULL) + timeout;
162 while (1) {
163 if ((rc = SSL_write(c->ssl, buf, len)) > 0)
164 return 1;
166 if (!BIO_should_retry(c->bio)) {
167 c->_errno = NSCANG_ERROR_SSL;
168 return 0;
171 if (time(NULL) > endtime) {
172 c->_errno = NSCANG_ERROR_TIMEOUT;
173 return 0;
176 FD_ZERO(&fdset);
177 FD_SET(BIO_get_fd(c->bio, NULL), &fdset);
178 select(1, NULL, &fdset, NULL, &tv);
183 nscang_client_response(nscang_client_t *c, int timeout)
185 int rc;
186 time_t endtime;
187 fd_set fdset;
188 char buf[1024];
189 int was_read = 0;
190 char *ptr = buf;
191 struct timeval tv;
193 tv.tv_sec = 0;
194 tv.tv_usec = 100000;
196 endtime = time(NULL) + timeout;
198 while (1) {
199 rc = SSL_read(c->ssl, ptr, 1024 - was_read);
201 if (rc > 0) {
202 was_read = was_read + rc;
203 ptr = buf + was_read;
205 if (buf[was_read - 1] == '\n') {
206 if (buf[was_read - 2] == '\r')
207 buf[was_read - 2] = 0x00;
208 else
209 buf[was_read - 1] = 0x00;
211 break;
214 if (was_read == 1024) {
215 c->_errno = NSCANG_ERROR_TOO_LONG_RESPONSE;
216 return 0;
218 } else if (!BIO_should_retry(c->bio)) {
219 c->_errno = NSCANG_ERROR_SSL;
220 return 0;
223 if (time(NULL) > endtime) {
224 c->_errno = NSCANG_ERROR_TIMEOUT;
225 return 0;
228 FD_ZERO(&fdset);
229 FD_SET(BIO_get_fd(c->bio, NULL), &fdset);
230 select(1, &fdset, NULL, NULL, &tv);
233 if (strncmp(buf, "MOIN", 4) == 0) {
234 if (strcmp(buf, "MOIN 1") == 0) {
235 return NSCANG_RESP_MOIN;
236 } else {
237 c->_errno = NSCANG_ERROR_BAD_PROTO_VERSION;
238 strncpy(c->errstr, buf + 5, sizeof(c->errstr) - 1);
239 c->errstr[sizeof(c->errstr) - 1] = 0x00;
240 return 0;
242 } else if (strncmp(buf, "OKAY", 4) == 0) {
243 return NSCANG_RESP_OKAY;
244 } else if (strncmp(buf, "FAIL", 4) == 0) {
245 c->_errno = NSCANG_ERROR_FAIL;
246 strncpy(c->errstr, buf + 5, sizeof(c->errstr) - 1);
247 c->errstr[sizeof(c->errstr) - 1] = 0x00;
248 return 0;
249 } else if (strncmp(buf, "BAIL", 4) == 0) {
250 nscang_client_disconnect(c);
251 c->_errno = NSCANG_ERROR_BAIL;
252 strncpy(c->errstr, buf + 5, sizeof(c->errstr) - 1);
253 c->errstr[sizeof(c->errstr) - 1] = 0x00;
254 return 0;
255 } else {
256 char *msg = "BAIL Unknown response!";
258 nscang_client_write(c, msg, sizeof(msg), 0);
259 nscang_client_disconnect(c);
260 c->_errno = NSCANG_ERROR_UNKNOWN_RESPONSE;
261 strncpy(c->errstr, buf, sizeof(c->errstr) - 1);
262 c->errstr[sizeof(c->errstr) - 1] = 0x00;
263 return 0;
267 void
268 nscang_client_disconnect(nscang_client_t *c)
270 fd_set fdset;
271 struct timeval tv;
273 tv.tv_sec = 0;
274 tv.tv_usec = 100000;
276 if (c->state == NSCANG_STATE_MOIN)
277 nscang_client_send_quit(c);
279 if ((SSL_shutdown(c->ssl) == -1) && BIO_should_retry(c->bio)) {
280 FD_ZERO(&fdset);
281 FD_SET(BIO_get_fd(c->bio, NULL), &fdset);
282 select(1, NULL, &fdset, NULL, &tv);
283 SSL_shutdown(c->ssl);
286 SSL_clear(c->ssl);
287 BIO_reset(c->bio);
289 c->state = NSCANG_STATE_NEW;
293 nscang_client_send_moin(nscang_client_t *c, int timeout)
295 char cmd[64];
296 int len, rc;
298 if (c->state == NSCANG_STATE_MOIN)
299 return 1;
301 if (c->state != NSCANG_STATE_NEW) {
302 c->_errno = NSCANG_ERROR_BAD_STATE;
303 return 0;
306 srandom(time(NULL));
307 len = snprintf(cmd, sizeof(cmd), "MOIN 1 %08ld%08ld\r\n", random(),
308 random());
310 if (!nscang_client_write(c, cmd, len, timeout))
311 return 0;
313 rc = nscang_client_response(c, timeout);
315 if (!rc)
316 return 0;
318 if (rc != NSCANG_RESP_MOIN) {
319 c->_errno = NSCANG_ERROR_PROTOCOL_MISMATCH;
320 return 0;
323 c->state = NSCANG_STATE_MOIN;
324 return 1;
327 /* Check whether a string's prefix matches /\[\d{1,9}\]/
328 * Returns 1 for match, 0 for no match, -1 for incomplete/bad match
330 static int
331 has_timestamp_prefix(const char *s)
333 char *end;
335 if(*s++ != '[') return 0;
336 if((*s < '0') || (*s > '9')) return -1; // counter strtol's skip-whitespace and sign permissiveness
337 (void)strtol(s, &end, 10); // discard result
338 if(errno) return -1;
339 if(*end == ']') return 1;
340 return -1;
344 nscang_client_send_command(nscang_client_t *c, const char *command, int timeout)
346 const int command_len = strlen(command);
347 char command_buf[1024];
348 char cmd[32];
349 int rc, len;
351 switch( has_timestamp_prefix(command) ) {
352 case 0 :
353 // There was no timestamp. Add one.
354 snprintf(command_buf, sizeof(command_buf)-1, "[%u] %s", (unsigned int)time(NULL), command);
355 break;
356 case 1 :
357 // Timestamp found, just copy everything
358 strncpy(command_buf, command, sizeof(command_buf)-1);
359 command_buf[sizeof(command_buf)-1] = '\0';
360 break;
361 default :
362 // Malformed timestamp
363 c->_errno = NSCANG_ERROR_FAIL;
364 snprintf(c->errstr, sizeof(c->errstr), "invalid time stamp format in command `%s'", command);
365 return 0;
368 /* make sure command_buf is newline-terminated */
369 len = strlen(command_buf);
370 if(command_buf[len-1] != '\n') {
371 command_buf[len] = '\n';
372 len++;
373 command_buf[len] = '\0';
376 if (!nscang_client_send_moin(c, timeout))
377 return 0;
379 snprintf(cmd, sizeof(cmd), "PUSH %d\r\n", len);
380 if (!nscang_client_write(c, cmd, strlen(cmd), timeout))
381 return 0;
383 rc = nscang_client_response(c, timeout);
385 if (!rc)
386 return 0;
388 if (rc != NSCANG_RESP_OKAY) {
389 c->_errno = NSCANG_ERROR_PROTOCOL_MISMATCH;
390 return 0;
393 if (!nscang_client_write(c, command_buf, len, timeout))
394 return 0;
396 rc = nscang_client_response(c, timeout);
398 if (!rc)
399 return 0;
401 if (rc != NSCANG_RESP_OKAY) {
402 c->_errno = NSCANG_ERROR_PROTOCOL_MISMATCH;
403 return 0;
406 return 1;
410 nscang_client_send_push(nscang_client_t *c, char *host, char *service,
411 int status, char *message, int timeout)
413 char command[1024];
415 if (service == NULL)
416 snprintf(command, sizeof(command) - 1,
417 "PROCESS_HOST_CHECK_RESULT;%s;%d;%s",
418 host, status, message);
419 else
420 snprintf(command, sizeof(command) - 1,
421 "PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s",
422 host, service, status, message);
424 return nscang_client_send_command(c, command, timeout);
428 nscang_client_send_quit(nscang_client_t *c)
430 if (c->state != NSCANG_STATE_MOIN) {
431 c->_errno = NSCANG_ERROR_BAD_STATE;
432 return 0;
435 if (!nscang_client_write(c, "QUIT\n", 5, 0))
436 return 0;
438 return 1;
441 void
442 nscang_client_ssl_error(char *buf, int buf_size, const char *msg) {
443 strncpy(buf, msg, buf_size);
444 buf_size--;
445 strncat(buf, ":", buf_size);
446 strncat(buf, ERR_reason_error_string(ERR_peek_error()), buf_size);
449 char *
450 nscang_client_errstr(nscang_client_t *c, char *buf, int buf_size)
452 switch (c->_errno) {
453 case NSCANG_ERROR_SSL_CTX_CREATE:
454 nscang_client_ssl_error(buf, buf_size, "Can't create SSL context");
455 break;
456 case NSCANG_ERROR_SSL_CIPHERS:
457 nscang_client_ssl_error(buf, buf_size, "Bad ciphers list");
458 break;
459 case NSCANG_ERROR_SSL_BIO_CREATE:
460 nscang_client_ssl_error(buf, buf_size, "Can't create BIO socket");
461 break;
462 case NSCANG_ERROR_SSL_CREATE:
463 nscang_client_ssl_error(buf, buf_size, "Can't create SSL");
464 break;
465 case NSCANG_ERROR_SSL:
466 nscang_client_ssl_error(buf, buf_size, "SSL error");
467 break;
468 case NSCANG_ERROR_MALLOC:
469 strncpy(buf, "Can't allocate memory", buf_size);
470 break;
471 case NSCANG_ERROR_TIMEOUT:
472 strncpy(buf, "Timeout was reached", buf_size);
473 break;
474 case NSCANG_ERROR_TOO_LONG_RESPONSE:
475 strncpy(buf, "Protocol mismatch - too long response", buf_size);
476 break;
477 case NSCANG_ERROR_BAD_PROTO_VERSION:
478 snprintf(buf, buf_size, "Protocol mismatch - bad version '%s'",
479 c->errstr);
480 break;
481 case NSCANG_ERROR_PROTOCOL_MISMATCH:
482 strncpy(buf, "Protocol mismatch - unexpected server response",
483 buf_size);
484 break;
485 case NSCANG_ERROR_UNKNOWN_RESPONSE:
486 strncpy(buf, "Protocol mismatch - unknown server response",
487 buf_size);
488 break;
489 case NSCANG_ERROR_BAIL:
490 snprintf(buf, buf_size, "BAIL: %s", c->errstr);
491 break;
492 case NSCANG_ERROR_FAIL:
493 snprintf(buf, buf_size, "FAIL: %s", c->errstr);
494 break;
495 case NSCANG_ERROR_BAD_STATE:
496 snprintf(buf, buf_size, "Operation not permitted in state %d",
497 c->state);
498 break;
499 default:
500 buf[0] = 0x00;
502 buf[buf_size - 1] = 0x00;
503 return buf;