Merge remote-tracking branch 'weiss/pr/3'
[nsca-ng.git] / python / client.c
blob86b8ff573cb258fa161bcb052847e9bc3a6806b2
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>
32 #include <pthread.h>
34 #include "client.h"
35 #include "uthash.h"
37 static nscang_client_t *nscang_client_instances;
39 static pthread_rwlock_t nscang_client_instances_lock =
40 PTHREAD_RWLOCK_INITIALIZER;
42 unsigned int
43 set_psk(SSL *ssl, const char *hint, char *identity,
44 unsigned int max_identity_length, unsigned char *psk,
45 unsigned int max_psk_length)
47 int rc;
48 nscang_client_t *c = NULL;
50 while (1) {
51 rc = pthread_rwlock_rdlock(&nscang_client_instances_lock);
52 if ((rc != EBUSY) && (rc != EAGAIN))
53 break;
55 if (rc == 0) {
56 HASH_FIND_PTR(nscang_client_instances, &ssl, c);
57 pthread_rwlock_unlock(&nscang_client_instances_lock);
59 if (c != NULL) {
60 strncpy(identity, c->identity, max_identity_length);
61 identity[max_identity_length - 1] = 0x00;
63 strncpy((char *)psk, c->psk, max_psk_length);
64 psk[max_psk_length - 1] = 0x00;
66 return strlen((char *)psk);
68 identity[0] = 0x00;
69 psk[0] = 0x00;
70 return 0;
73 int
74 nscang_client_init(nscang_client_t *c, char *host, int port, char *ciphers,
75 char *identity, char *psk)
77 int rc;
79 memset(c, 0x00, sizeof(nscang_client_t));
81 c->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
82 if (c->ssl_ctx == NULL) {
83 c->_errno = NSCANG_ERROR_SSL_CTX_CREATE;
84 return 0;
87 if (ciphers != NULL) {
88 if (SSL_CTX_set_cipher_list(c->ssl_ctx, ciphers) != 1) {
89 c->_errno = NSCANG_ERROR_SSL_CIPHERS;
90 return 0;
94 SSL_CTX_set_options(c->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
96 c->bio = BIO_new(BIO_s_connect());
97 if (c->bio == NULL) {
98 c->_errno = NSCANG_ERROR_SSL_BIO_CREATE;
99 return 0;
101 BIO_set_conn_hostname(c->bio, host);
102 BIO_set_conn_int_port(c->bio, &port);
103 BIO_set_nbio(c->bio, 1);
105 c->ssl = SSL_new(c->ssl_ctx);
106 if (c->ssl == NULL) {
107 nscang_client_free(c);
108 c->_errno = NSCANG_ERROR_SSL_CREATE;
109 return 0;
112 SSL_set_bio(c->ssl, c->bio, c->bio);
113 SSL_set_connect_state(c->ssl);
114 SSL_set_psk_client_callback(c->ssl, set_psk);
116 c->identity = malloc(strlen(identity) + 1);
117 c->psk = malloc(strlen(psk) + 1);
119 if ((c->identity == NULL) || (c->psk == NULL)) {
120 c->_errno = NSCANG_ERROR_MALLOC;
121 return 0;
124 strcpy(c->identity, identity);
125 strcpy(c->psk, psk);
127 /* Busyloop until we get an rw lock. */
128 while (1) {
129 rc = pthread_rwlock_wrlock(&nscang_client_instances_lock);
130 if (rc != EBUSY)
131 break;
133 if (rc != 0) {
134 c->_errno = NSCANG_ERROR_LOCKING;
135 return 0;
138 HASH_ADD_PTR(nscang_client_instances, ssl, c);
139 pthread_rwlock_unlock(&nscang_client_instances_lock);
141 c->state = NSCANG_STATE_NEW;
143 return 1;
146 void
147 nscang_client_free(nscang_client_t *c)
149 if (c->state != NSCANG_STATE_NONE)
150 nscang_client_disconnect(c);
152 if (c->ssl != NULL) {
153 int rc;
155 /* Busyloop until we get an rw lock. */
156 while (1) {
157 rc = pthread_rwlock_wrlock
158 (&nscang_client_instances_lock);
159 if (rc != EBUSY)
160 break;
162 if (rc == 0) {
163 HASH_DEL(nscang_client_instances, c);
164 pthread_rwlock_unlock(&nscang_client_instances_lock);
167 if (c->ssl != NULL) {
168 SSL_free(c->ssl);
169 c->ssl = NULL;
170 c->bio = NULL;
172 if (c->ssl_ctx != NULL) {
173 SSL_CTX_free(c->ssl_ctx);
174 c->ssl_ctx = NULL;
176 if (c->identity != NULL) {
177 free(c->identity);
178 c->identity = NULL;
180 if (c->psk != NULL) {
181 free(c->psk);
182 c->psk = NULL;
185 c->state = NSCANG_STATE_NONE;
189 nscang_client_write(nscang_client_t *c, void *buf, int len, int timeout)
191 int rc;
192 time_t endtime;
193 fd_set fdset;
194 struct timeval tv;
196 tv.tv_sec = 0;
197 tv.tv_usec = 100000;
199 endtime = time(NULL) + timeout;
201 while (1) {
202 if ((rc = SSL_write(c->ssl, buf, len)) > 0)
203 return 1;
205 if (!BIO_should_retry(c->bio)) {
206 c->_errno = NSCANG_ERROR_SSL;
207 return 0;
210 if (time(NULL) > endtime) {
211 c->_errno = NSCANG_ERROR_TIMEOUT;
212 return 0;
215 FD_ZERO(&fdset);
216 FD_SET(BIO_get_fd(c->bio, NULL), &fdset);
217 select(1, NULL, &fdset, NULL, &tv);
222 nscang_client_response(nscang_client_t *c, int timeout)
224 int rc;
225 time_t endtime;
226 fd_set fdset;
227 char buf[1024];
228 int was_read = 0;
229 char *ptr = buf;
230 struct timeval tv;
232 tv.tv_sec = 0;
233 tv.tv_usec = 100000;
235 endtime = time(NULL) + timeout;
237 while (1) {
238 rc = SSL_read(c->ssl, ptr, 1024 - was_read);
240 if (rc > 0) {
241 was_read = was_read + rc;
242 ptr = buf + was_read;
244 if (buf[was_read - 1] == '\n') {
245 if (buf[was_read - 2] == '\r')
246 buf[was_read - 2] = 0x00;
247 else
248 buf[was_read - 1] = 0x00;
250 break;
253 if (was_read == 1024) {
254 c->_errno = NSCANG_ERROR_TOO_LONG_RESPONSE;
255 return 0;
257 } else if (!BIO_should_retry(c->bio)) {
258 c->_errno = NSCANG_ERROR_SSL;
259 return 0;
262 if (time(NULL) > endtime) {
263 c->_errno = NSCANG_ERROR_TIMEOUT;
264 return 0;
267 FD_ZERO(&fdset);
268 FD_SET(BIO_get_fd(c->bio, NULL), &fdset);
269 select(1, &fdset, NULL, NULL, &tv);
272 if (strncmp(buf, "MOIN", 4) == 0) {
273 if (strcmp(buf, "MOIN 1") == 0) {
274 return NSCANG_RESP_MOIN;
275 } else {
276 c->_errno = NSCANG_ERROR_BAD_PROTO_VERSION;
277 strncpy(c->errstr, buf + 5, sizeof(c->errstr) - 1);
278 c->errstr[sizeof(c->errstr) - 1] = 0x00;
279 return 0;
281 } else if (strncmp(buf, "OKAY", 4) == 0) {
282 return NSCANG_RESP_OKAY;
283 } else if (strncmp(buf, "FAIL", 4) == 0) {
284 c->_errno = NSCANG_ERROR_FAIL;
285 strncpy(c->errstr, buf + 5, sizeof(c->errstr) - 1);
286 c->errstr[sizeof(c->errstr) - 1] = 0x00;
287 return 0;
288 } else if (strncmp(buf, "BAIL", 4) == 0) {
289 nscang_client_disconnect(c);
290 c->_errno = NSCANG_ERROR_BAIL;
291 strncpy(c->errstr, buf + 5, sizeof(c->errstr) - 1);
292 c->errstr[sizeof(c->errstr) - 1] = 0x00;
293 return 0;
294 } else {
295 char *msg = "BAIL Unknown response!";
297 nscang_client_write(c, msg, sizeof(msg), 0);
298 nscang_client_disconnect(c);
299 c->_errno = NSCANG_ERROR_UNKNOWN_RESPONSE;
300 strncpy(c->errstr, buf, sizeof(c->errstr) - 1);
301 c->errstr[sizeof(c->errstr) - 1] = 0x00;
302 return 0;
306 void
307 nscang_client_disconnect(nscang_client_t *c)
309 fd_set fdset;
310 struct timeval tv;
312 tv.tv_sec = 0;
313 tv.tv_usec = 100000;
315 if (c->state == NSCANG_STATE_MOIN)
316 nscang_client_send_quit(c);
318 if ((SSL_shutdown(c->ssl) == -1) && BIO_should_retry(c->bio)) {
319 FD_ZERO(&fdset);
320 FD_SET(BIO_get_fd(c->bio, NULL), &fdset);
321 select(1, NULL, &fdset, NULL, &tv);
322 SSL_shutdown(c->ssl);
325 SSL_clear(c->ssl);
326 BIO_reset(c->bio);
328 c->state = NSCANG_STATE_NEW;
332 nscang_client_send_moin(nscang_client_t *c, int timeout)
334 char cmd[64];
335 int len, rc;
337 if (c->state == NSCANG_STATE_MOIN)
338 return 1;
340 if (c->state != NSCANG_STATE_NEW) {
341 c->_errno = NSCANG_ERROR_BAD_STATE;
342 return 0;
345 srandom(time(NULL));
346 len = snprintf(cmd, sizeof(cmd), "MOIN 1 %08ld%08ld\r\n", random(),
347 random());
349 if (!nscang_client_write(c, cmd, len, timeout))
350 return 0;
352 rc = nscang_client_response(c, timeout);
354 if (!rc)
355 return 0;
357 if (rc != NSCANG_RESP_MOIN) {
358 c->_errno = NSCANG_ERROR_PROTOCOL_MISMATCH;
359 return 0;
362 c->state = NSCANG_STATE_MOIN;
363 return 1;
367 nscang_client_send_push(nscang_client_t *c, char *host, char *service,
368 int status, char *message, int timeout)
370 char cmd[64], command[1024];
371 int len, rc;
373 if (!nscang_client_send_moin(c, timeout))
374 return 0;
376 if (service == NULL)
377 len = snprintf(command, sizeof(command) - 1,
378 "[%u] PROCESS_HOST_CHECK_RESULT;%s;%d;%s",
379 (unsigned int)time(NULL), host, status, message);
380 else
381 len = snprintf(command, sizeof(command) - 1,
382 "[%u] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s",
383 (unsigned int)time(NULL), host, service, status, message);
385 command[len++] = '\n';
387 snprintf(cmd, sizeof(cmd), "PUSH %d\n", len);
388 if (!nscang_client_write(c, cmd, strlen(cmd), timeout))
389 return 0;
391 rc = nscang_client_response(c, timeout);
393 if (!rc)
394 return 0;
396 if (rc != NSCANG_RESP_OKAY) {
397 c->_errno = NSCANG_ERROR_PROTOCOL_MISMATCH;
398 return 0;
401 if (!nscang_client_write(c, command, len, timeout))
402 return 0;
404 rc = nscang_client_response(c, timeout);
406 if (!rc)
407 return 0;
409 if (rc != NSCANG_RESP_OKAY) {
410 c->_errno = NSCANG_ERROR_PROTOCOL_MISMATCH;
411 return 0;
414 return 1;
418 nscang_client_send_quit(nscang_client_t *c)
420 if (c->state != NSCANG_STATE_MOIN) {
421 c->_errno = NSCANG_ERROR_BAD_STATE;
422 return 0;
425 if (!nscang_client_write(c, "QUIT\n", 5, 0))
426 return 0;
428 return 1;
431 char *
432 nscang_client_errstr(nscang_client_t *c, char *buf, int buf_size)
434 switch (c->_errno) {
435 case NSCANG_ERROR_SSL_CTX_CREATE:
436 strncpy(buf, "Can't create SSL context", buf_size);
437 break;
438 case NSCANG_ERROR_SSL_CIPHERS:
439 strncpy(buf, "Bad ciphers list", buf_size);
440 break;
441 case NSCANG_ERROR_SSL_BIO_CREATE:
442 strncpy(buf, "Can't create BIO socket", buf_size);
443 break;
444 case NSCANG_ERROR_SSL_CREATE:
445 strncpy(buf, "Can't create SSL", buf_size);
446 break;
447 case NSCANG_ERROR_SSL:
448 snprintf(buf, buf_size, "SSL error - %s - %s",
449 ERR_reason_error_string(ERR_peek_last_error()),
450 ERR_reason_error_string(ERR_peek_error()));
451 break;
452 case NSCANG_ERROR_MALLOC:
453 strncpy(buf, "Can't allocate memory", buf_size);
454 break;
455 case NSCANG_ERROR_TIMEOUT:
456 strncpy(buf, "Timeout was reached", buf_size);
457 break;
458 case NSCANG_ERROR_TOO_LONG_RESPONSE:
459 strncpy(buf, "Protocol mismatch - too long response", buf_size);
460 break;
461 case NSCANG_ERROR_BAD_PROTO_VERSION:
462 snprintf(buf, buf_size, "Protocol mismatch - bad version '%s'",
463 c->errstr);
464 break;
465 case NSCANG_ERROR_PROTOCOL_MISMATCH:
466 strncpy(buf, "Protocol mismatch - unexpected server response",
467 buf_size);
468 break;
469 case NSCANG_ERROR_UNKNOWN_RESPONSE:
470 strncpy(buf, "Protocol mismatch - unknown server response",
471 buf_size);
472 break;
473 case NSCANG_ERROR_BAIL:
474 snprintf(buf, buf_size, "BAIL: %s", c->errstr);
475 break;
476 case NSCANG_ERROR_FAIL:
477 snprintf(buf, buf_size, "FAIL: %s", c->errstr);
478 break;
479 case NSCANG_ERROR_BAD_STATE:
480 snprintf(buf, buf_size, "Operation not permitted in state %d",
481 c->state);
482 break;
483 case NSCANG_ERROR_LOCKING:
484 strncpy(buf, "Can't obtain lock for instances list", buf_size);
485 break;
486 default:
487 buf[0] = 0x00;
489 buf[buf_size - 1] = 0x00;
490 return buf;