src/backend/api/src/gmpd.c
/**
* SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileCopyrightText: Copyright 2009-2018 Greenbone Networks GmbH
* SPDX-FileComment: This file defines the API daemon.
* SPDX-FileContributor: Mageni Security LLC
*
*/
#include "gmpd.h"
#include "gmp.h"
#include "scanner.h"
/** @todo For scanner_init_state. */
#include "comm.h"
#include "otp.h"
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include "../../libraries/util/serverutils.h"
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#if FROM_BUFFER_SIZE > SSIZE_MAX
#error FROM_BUFFER_SIZE too big for "read"
#endif
#undef G_LOG_DOMAIN
/**
* @brief GLib log domain.
*/
#define G_LOG_DOMAIN "md main"
/**
* @brief Buffer of input from the client.
*/
char from_client[FROM_BUFFER_SIZE];
/**
* @brief Size of \ref from_client data buffer, in bytes.
*/
buffer_size_t from_buffer_size = FROM_BUFFER_SIZE;
/**
* @brief The start of the data in the \ref from_client buffer.
*/
buffer_size_t from_client_start = 0;
/**
* @brief The end of the data in the \ref from_client buffer.
*/
buffer_size_t from_client_end = 0;
/**
* @brief Flag for running in NVT cache mode.
*/
static int gmpd_nvt_cache_mode = 0;
/**
* @brief Initialise the GMP library for the GMP daemon.
*
* @param[in] log_config Log configuration
* @param[in] nvt_cache_mode 0 operate normally, -1 just update NVT cache.
* @param[in] database Location of manage database.
* @param[in] max_ips_per_target Max number of IPs per target.
* @param[in] max_email_attachment_size Max size of email attachments.
* @param[in] max_email_include_size Max size of email inclusions.
* @param[in] max_email_message_size Max size of email user message text.
* @param[in] fork_connection Function to fork a connection to the GMP
* daemon layer, or NULL.
* @param[in] skip_db_check Skip DB check.
*
* @return 0 success, -1 error, -2 database is wrong version,
* -4 max_ips_per_target out of range.
*/
int
init_gmpd (GSList *log_config,
int nvt_cache_mode,
const gchar *database,
int max_ips_per_target,
int max_email_attachment_size,
int max_email_include_size,
int max_email_message_size,
manage_connection_forker_t fork_connection,
int skip_db_check)
{
return init_gmp (log_config,
nvt_cache_mode,
database,
max_ips_per_target,
max_email_attachment_size,
max_email_include_size,
max_email_message_size,
fork_connection,
skip_db_check);
}
/**
* @brief Initialise a process forked within the GMP daemon.
*
* @param[in] database Location of manage database.
* @param[in] disable Commands to disable.
*/
void
init_gmpd_process (const gchar *database, gchar **disable)
{
openvas_scanner_fork ();
from_client_start = 0;
from_client_end = 0;
init_gmp_process (0, database, NULL, NULL, disable);
}
/**
* @brief Read as much from the client as the \ref from_client buffer will hold.
*
* @param[in] client_socket The socket.
*
* @return 0 on reading everything available, -1 on error, -2 if
* from_client buffer is full or -3 on reaching end of file.
*/
static int
read_from_client_unix (int client_socket)
{
while (from_client_end < from_buffer_size)
{
int count;
count = read (client_socket,
from_client + from_client_end,
from_buffer_size - from_client_end);
if (count < 0)
{
if (errno == EAGAIN)
/* Got everything available, return to `select'. */
return 0;
if (errno == EINTR)
/* Interrupted, try read again. */
continue;
g_warning ("%s: failed to read from client: %s",
__FUNCTION__,
strerror (errno));
return -1;
}
if (count == 0)
{
/* End of file. */
if (from_client_end)
/* There's still client input to process, so pretend we read
* something, to prevent serve_gmp from exiting.
*
* This should instead be dealt with in serve_gmp, but that function
* has got quite complex. */
return 0;
return -3;
}
from_client_end += count;
}
/* Buffer full. */
return -2;
}
/**
* @brief Read as much from the client as the \ref from_client buffer will hold.
*
* @param[in] client_session The TLS session with the client.
*
* @return 0 on reading everything available, -1 on error, -2 if
* from_client buffer is full or -3 on reaching end of file.
*/
static int
read_from_client_tls (gnutls_session_t *client_session)
{
while (from_client_end < from_buffer_size)
{
ssize_t count;
count = gnutls_record_recv (*client_session,
from_client + from_client_end,
from_buffer_size - from_client_end);
if (count < 0)
{
if (count == GNUTLS_E_AGAIN)
/* Got everything available, return to `select'. */
return 0;
if (count == GNUTLS_E_INTERRUPTED)
/* Interrupted, try read again. */
continue;
if (count == GNUTLS_E_REHANDSHAKE)
{
/** @todo Rehandshake. */
g_debug (" should rehandshake");
continue;
}
if (gnutls_error_is_fatal ((int) count) == 0
&& (count == GNUTLS_E_WARNING_ALERT_RECEIVED
|| count == GNUTLS_E_FATAL_ALERT_RECEIVED))
{
int alert = gnutls_alert_get (*client_session);
const char *alert_name = gnutls_alert_get_name (alert);
g_warning (
"%s: TLS Alert %d: %s", __FUNCTION__, alert, alert_name);
}
g_warning ("%s: failed to read from client: %s",
__FUNCTION__,
gnutls_strerror ((int) count));
return -1;
}
if (count == 0)
{
/* End of file. */
if (from_client_end)
/* There's still client input to process, so pretend we read
* something, to prevent serve_gmp from exiting.
*
* This should instead be dealt with in serve_gmp, but that function
* has got quite complex. */
return 0;
return -3;
}
from_client_end += count;
}
/* Buffer full. */
return -2;
}
/**
* @brief Read as much from the client as the \ref from_client buffer will hold.
*
* @param[in] client_connection The connection with the client.
*
* @return 0 on reading everything available, -1 on error, -2 if
* from_client buffer is full or -3 on reaching end of file.
*/
static int
read_from_client (gvm_connection_t *client_connection)
{
if (client_connection->tls)
return read_from_client_tls (&client_connection->session);
return read_from_client_unix (client_connection->socket);
}
/** @todo Move to openvas-libraries? */
/**
* @brief Write as much as possible from \ref to_client to the client.
*
* @param[in] client_session The client session.
*
* @return 0 wrote everything, -1 error, -2 wrote as much as client accepted.
*/
static int
write_to_client_tls (gnutls_session_t *client_session)
{
while (to_client_start < to_client_end)
{
ssize_t count;
count = gnutls_record_send (*client_session,
to_client + to_client_start,
to_client_end - to_client_start);
if (count < 0)
{
if (count == GNUTLS_E_AGAIN)
/* Wrote as much as client would accept. */
return -2;
if (count == GNUTLS_E_INTERRUPTED)
/* Interrupted, try write again. */
continue;
if (count == GNUTLS_E_REHANDSHAKE)
/** @todo Rehandshake. */
continue;
g_warning ("%s: failed to write to client: %s",
__FUNCTION__,
gnutls_strerror ((int) count));
return -1;
}
to_client_start += count;
g_debug ("=> client %u bytes", (unsigned int) count);
}
g_debug ("=> client done");
to_client_start = to_client_end = 0;
/* Wrote everything. */
return 0;
}
/**
* @brief Write as much as possible from \ref to_client to the client.
*
* @param[in] client_socket The client socket.
*
* @return 0 wrote everything, -1 error, -2 wrote as much as client accepted.
*/
static int
write_to_client_unix (int client_socket)
{
while (to_client_start < to_client_end)
{
ssize_t count;
count = write (client_socket,
to_client + to_client_start,
to_client_end - to_client_start);
if (count < 0)
{
if (errno == EAGAIN)
/* Wrote as much as client would accept. */
return -2;
if (errno == EINTR)
/* Interrupted, try write again. */
continue;
g_warning ("%s: failed to write to client: %s",
__FUNCTION__,
strerror (errno));
return -1;
}
to_client_start += count;
g_debug ("=> client %u bytes", (unsigned int) count);
}
g_debug ("=> client done");
to_client_start = to_client_end = 0;
/* Wrote everything. */
return 0;
}
/**
* @brief Write as much as possible from \ref to_client to the client.
*
* @param[in] client_connection The client connection.
*
* @return 0 wrote everything, -1 error, -2 wrote as much as client accepted.
*/
static int
write_to_client (gvm_connection_t *client_connection)
{
if (client_connection->tls)
return write_to_client_tls (&client_connection->session);
return write_to_client_unix (client_connection->socket);
}
/**
* @brief Send a response message to the client.
*
* Queue a message in \ref to_client.
*
* @param[in] msg The message, a string.
* @param[in] write_to_client_data Argument to \p write_to_client.
*
* @return TRUE if write to client failed, else FALSE.
*/
static gboolean
gmpd_send_to_client (const char *msg, void *write_to_client_data)
{
assert (to_client_end <= TO_CLIENT_BUFFER_SIZE);
assert (msg);
while (((buffer_size_t) TO_CLIENT_BUFFER_SIZE) - to_client_end < strlen (msg))
{
buffer_size_t length;
/* Too little space in to_client buffer for message. */
switch (write_to_client (write_to_client_data))
{
case 0: /* Wrote everything in to_client. */
break;
case -1: /* Error. */
g_debug (" %s full (%i < %zu); client write failed",
__FUNCTION__,
((buffer_size_t) TO_CLIENT_BUFFER_SIZE) - to_client_end,
strlen (msg));
return TRUE;
case -2: /* Wrote as much as client was willing to accept. */
break;
default: /* Programming error. */
assert (0);
}
length = ((buffer_size_t) TO_CLIENT_BUFFER_SIZE) - to_client_end;
if (length > strlen (msg))
break;
memmove (to_client + to_client_end, msg, length);
g_debug ("-> client: %.*s", (int) length, msg);
to_client_end += length;
msg += length;
}
if (strlen (msg))
{
assert (strlen (msg)
<= (((buffer_size_t) TO_CLIENT_BUFFER_SIZE) - to_client_end));
memmove (to_client + to_client_end, msg, strlen (msg));
g_debug ("-> client: %s", msg);
to_client_end += strlen (msg);
}
return FALSE;
}
/**
* @brief Clean session.
*
* @param[in] client_connection Connection.
*/
static void
session_clean (gvm_connection_t *client_connection)
{
if (client_connection->session)
{
gnutls_deinit (client_connection->session);
client_connection->session = NULL;
}
if (client_connection->credentials)
{
gnutls_certificate_free_credentials (client_connection->credentials);
client_connection->credentials = NULL;
}
}
/**
* @brief Serve the Greenbone Management Protocol (GMP).
*
* Loop reading input from the sockets, processing
* the input, and writing any results to the appropriate socket.
* Exit the loop on reaching end of file on the client socket.
*
* Read input from the client and scanner.
* Process the input with \ref process_gmp_client_input and
* \ref process_otp_scanner_input. Write the results to the client.
*
* \if STATIC
*
* Read input with \ref read_from_client and \ref openvas_scanner_read.
* Write the results with \ref write_to_client. Write to the server
* with \ref openvas_scanner_write.
*
* \endif
*
* If client socket is 0 or less, then update the NVT cache and exit.
*
* @param[in] client_connection Connection.
* @param[in] database Location of manage database.
* @param[in] disable Commands to disable.
*
* @return 0 success, 1 scanner still loading, -1 error, -2 scanner has no cert.
*/
int
serve_gmp (gvm_connection_t *client_connection,
const gchar *database,
gchar **disable)
{
int nfds, scan_handler = 0, rc = 0;
/* True if processing of the client input is waiting for space in the
* to_scanner or to_client buffer. */
short client_input_stalled;
/* Client status flag. Set to 0 when the client closes the connection
* while the scanner is active. */
short client_active = client_connection->socket > 0;
if (client_connection->socket < 0)
gmpd_nvt_cache_mode = client_connection->socket;
if (gmpd_nvt_cache_mode == 0)
g_debug (" Serving GMP");
/* Initialise the XML parser and the manage library. */
init_gmp_process (gmpd_nvt_cache_mode,
database,
(int (*) (const char *, void *)) gmpd_send_to_client,
(void *) client_connection,
disable);
/* Setup the scanner address and try to connect. */
if (gmpd_nvt_cache_mode && !openvas_scanner_connected ())
{
int ret;
/* Is here because it queries the DB and needs it initialized.
* XXX: Move outside serve_gmp ().
*/
ret = manage_scanner_set_default ();
if (ret)
return ret;
if (openvas_scanner_connect () || openvas_scanner_init (1))
{
openvas_scanner_close ();
return -1;
}
}
client_input_stalled = 0;
/** @todo Confirm and clarify complications, especially last one. */
/* Loop handling input from the sockets.
*
* That is, select on all the socket fds and then, as necessary
* - read from the client into buffer from_client
* - write to the scanner from buffer to_scanner
* - read from the scanner into buffer from_scanner
* - write to the client from buffer to_client.
*
* On reading from an fd, immediately try react to the input. On reading
* from the client call process_gmp_client_input, which parses GMP
* commands and may write to to_scanner and to_client. On reading from
* the scanner call process_otp_scanner_input, which updates information
* kept about the scanner.
*
* There are a few complications here
* - the program must read from or write to an fd returned by select
* before selecting on the fd again,
* - the program need only select on the fds for writing if there is
* something to write,
* - similarly, the program need only select on the fds for reading
* if there is buffer space available,
* - the buffers from_client and from_scanner can become full during
* reading
* - a read from the client can be stalled by the to_scanner buffer
* filling up, or the to_client buffer filling up (in which case
* process_gmp_client_input will try to write the to_client buffer
* itself),
* - a read from the scanner can, theoretically, be stalled by the
* to_scanner buffer filling up (during initialisation).
*/
nfds = openvas_scanner_get_nfds (client_connection->socket);
while (1)
{
int ret;
fd_set readfds, writefds;
int termination_signal = get_termination_signal ();
if (termination_signal)
{
g_debug ("%s: Received %s signal.",
__FUNCTION__,
strsignal (get_termination_signal ()));
if (openvas_scanner_connected ())
{
openvas_scanner_close ();
}
goto client_free;
}
/* Setup for select. */
/** @todo nfds must only include a socket if it's in >= one set. */
FD_ZERO (&readfds);
FD_ZERO (&writefds);
/** @todo Shutdown on failure (for example, if a read fails). */
if (client_active)
{
/* See whether to read from the client. */
if (from_client_end < from_buffer_size)
FD_SET (client_connection->socket, &readfds);
/* See whether to write to the client. */
if (to_client_start < to_client_end)
FD_SET (client_connection->socket, &writefds);
}
/* See whether we need to read from the scannner. */
if (openvas_scanner_connected ()
&& (scanner_init_state == SCANNER_INIT_DONE
|| scanner_init_state == SCANNER_INIT_DONE_CACHE_MODE
|| scanner_init_state == SCANNER_INIT_DONE_CACHE_MODE_UPDATE
|| scanner_init_state == SCANNER_INIT_SENT_COMPLETE_LIST
|| scanner_init_state == SCANNER_INIT_SENT_COMPLETE_LIST_UPDATE
|| scanner_init_state == SCANNER_INIT_SENT_VERSION)
&& !openvas_scanner_full ())
openvas_scanner_fd_set (&readfds);
/* See whether we need to write to the scanner. */
if (openvas_scanner_connected ()
&& (((scanner_init_state == SCANNER_INIT_TOP
|| scanner_init_state == SCANNER_INIT_DONE
|| scanner_init_state == SCANNER_INIT_DONE_CACHE_MODE
|| scanner_init_state == SCANNER_INIT_DONE_CACHE_MODE_UPDATE)
&& to_server_buffer_space () > 0)
|| scanner_init_state == SCANNER_INIT_CONNECTED
|| scanner_init_state == SCANNER_INIT_GOT_FEED_VERSION
|| scanner_init_state == SCANNER_INIT_GOT_PLUGINS))
openvas_scanner_fd_set (&writefds);
/* Select, then handle result. Due to GNUTLS internal buffering
* we test for pending records first and emulate a select call
* in that case. Note, that GNUTLS guarantees that writes are
* not buffered. Note also that GNUTLS versions < 3 did not
* exhibit a problem in Scanner due to a different buffering
* strategy. */
ret = 0;
if (client_connection->socket > 0 && client_connection->tls
&& FD_ISSET (client_connection->socket, &readfds)
&& gnutls_record_check_pending (client_connection->session))
{
FD_ZERO (&readfds);
FD_ZERO (&writefds);
ret++;
FD_SET (client_connection->socket, &readfds);
}
if (openvas_scanner_fd_isset (&readfds))
{
if (openvas_scanner_session_peek ())
{
if (!ret)
{
FD_ZERO (&readfds);
FD_ZERO (&writefds);
}
ret++;
openvas_scanner_fd_set (&readfds);
}
else if (openvas_scanner_peek () == 0)
{
/* Scanner has gone down. Exit. */
rc = -1;
goto client_free;
}
}
if (!ret)
{
/* Timeout periodically, so that process_gmp_change runs
* periodically. */
struct timeval timeout;
timeout.tv_usec = 0;
timeout.tv_sec = 1;
ret = select (nfds, &readfds, &writefds, NULL, &timeout);
}
if ((ret < 0 && errno == EINTR) || ret == 0)
{
if (process_gmp_change () == -1)
{
rc = -1;
goto client_free;
}
if (!scan_handler && !gmpd_nvt_cache_mode)
continue;
}
else if (ret < 0)
{
g_warning (
"%s: child select failed: %s", __FUNCTION__, strerror (errno));
rc = -1;
goto client_free;
}
/* Read any data from the client. */
if (client_connection->socket > 0
&& FD_ISSET (client_connection->socket, &readfds))
{
buffer_size_t initial_start = from_client_end;
switch (read_from_client (client_connection))
{
case 0: /* Read everything. */
break;
case -1: /* Error. */
rc = -1;
goto client_free;
case -2: /* from_client buffer full. */
/* There may be more to read. */
break;
case -3: /* End of file. */
g_debug (" EOF reading from client");
if (client_connection->socket > 0
&& FD_ISSET (client_connection->socket, &writefds))
/* Write rest of to_client to client, so that the client gets
* any buffered output and the response to the error. */
write_to_client (client_connection);
rc = 0;
goto client_free;
default: /* Programming error. */
assert (0);
}
/* This check prevents output in the "asynchronous network
* error" case. */
if (from_client_end > initial_start)
{
if (g_strstr_len (from_client + initial_start,
from_client_end - initial_start,
"<password>"))
g_debug ("<= client Input may contain password, suppressed");
else
g_debug ("<= client \"%.*s\"",
from_client_end - initial_start,
from_client + initial_start);
}
ret = process_gmp_client_input ();
if (ret == 0)
/* Processed all input. */
client_input_stalled = 0;
else if (ret == 3)
{
/* In the parent after a start_task fork. Free the scanner session
* without closing it, for usage by the child process. */
set_scanner_init_state (SCANNER_INIT_TOP);
openvas_scanner_free ();
nfds = openvas_scanner_get_nfds (client_connection->socket);
client_input_stalled = 0;
/* Skip the rest of the loop because the scanner socket is
* a new socket. This is asking for select trouble, really. */
continue;
}
else if (ret == 2)
{
/* Now in a process forked to run a task, which has
* successfully started the task. Close the client
* connection, as the parent process has continued the
* session with the client. */
session_clean (client_connection);
client_active = 0;
client_input_stalled = 0;
scan_handler = 1;
}
else if (ret == 4)
{
/* Now in a process forked for some operation which has
* successfully completed. Close the client connection,
* and exit, as the parent process has continued the
* session with the client. */
session_clean (client_connection);
return 0;
}
else if (ret == -10)
{
/* Now in a process forked to run a task, which has
* failed in starting the task. */
session_clean (client_connection);
return -1;
}
else if (ret == -1 || ret == -4)
{
/* Error. Write rest of to_client to client, so that the
* client gets any buffered output and the response to the
* error. */
write_to_client (client_connection);
rc = -1;
goto client_free;
}
else if (ret == -2)
{
/* to_scanner buffer full. */
g_debug (" client input stalled 1");
client_input_stalled = 1;
/* Carry on to write to_scanner. */
}
else if (ret == -3)
{
/* to_client buffer full. */
g_debug (" client input stalled 2");
client_input_stalled = 2;
/* Carry on to write to_client. */
}
else
{
/* Programming error. */
assert (0);
client_input_stalled = 0;
}
}
/* Read any data from the scanner. */
if (openvas_scanner_connected ()
&& (openvas_scanner_fd_isset (&readfds) || scan_handler))
{
switch (openvas_scanner_read ())
{
case 0: /* Read everything. */
break;
case -1: /* Error. */
/* This may be because the scanner closed the connection
* at the end of a command. */
/** @todo Then should get EOF (-3). */
set_scanner_init_state (SCANNER_INIT_TOP);
rc = -1;
goto client_free;
case -2: /* from_scanner buffer full. */
/* There may be more to read. */
break;
case -3: /* End of file. */
set_scanner_init_state (SCANNER_INIT_TOP);
if (client_active == 0)
/* The client has closed the connection, so exit. */
return 0;
/* Scanner went down, exit. */
rc = -1;
goto client_free;
default: /* Programming error. */
assert (0);
}
}
/* Write any data to the scanner. */
if (openvas_scanner_connected ()
&& (openvas_scanner_fd_isset (&writefds) || scan_handler))
{
/* Write as much as possible to the scanner. */
switch (openvas_scanner_write (gmpd_nvt_cache_mode))
{
case 0: /* Wrote everything in to_scanner. */
break;
case -1: /* Error. */
/** @todo This may be because the scanner closed the connection
* at the end of a command? */
rc = -1;
goto client_free;
case -2: /* Wrote as much as scanner was willing to accept. */
break;
case -3: /* Did an initialisation step. */
break;
default: /* Programming error. */
assert (0);
}
}
/* Write any data to the client. */
if (client_connection->socket > 0
&& FD_ISSET (client_connection->socket, &writefds))
{
/* Write as much as possible to the client. */
switch (write_to_client (client_connection))
{
case 0: /* Wrote everything in to_client. */
break;
case -1: /* Error. */
rc = -1;
goto client_free;
case -2: /* Wrote as much as client was willing to accept. */
break;
default: /* Programming error. */
assert (0);
}
}
if (client_input_stalled)
{
/* Try process the client input, in case writing to the scanner
* or client has freed some space in to_scanner or to_client. */
ret = process_gmp_client_input ();
if (ret == 0)
/* Processed all input. */
client_input_stalled = 0;
else if (ret == 3)
{
/* In the parent after a start_task fork. Free the scanner session
* without closing it, for usage by the child process. */
openvas_scanner_free ();
set_scanner_init_state (SCANNER_INIT_TOP);
nfds = openvas_scanner_get_nfds (client_connection->socket);
/* Skip the rest of the loop because the scanner socket is
* a new socket. This is asking for select trouble, really. */
continue;
}
else if (ret == 2)
{
/* Now in a process forked to run a task, which has
* successfully started the task. Close the client
* connection, as the parent process has continued the
* session with the client. */
session_clean (client_connection);
scan_handler = 1;
client_active = 0;
}
else if (ret == 4)
{
/* Now in a process forked for some operation which has
* successfully completed. Close the client connection,
* and exit, as the parent process has continued the
* session with the client. */
session_clean (client_connection);
return 0;
}
else if (ret == -10)
{
/* Now in a process forked to run a task, which has
* failed in starting the task. */
session_clean (client_connection);
return -1;
}
else if (ret == -1)
{
/* Error. Write rest of to_client to client, so that the
* client gets any buffered output and the response to the
* error. */
write_to_client (client_connection);
rc = -1;
goto client_free;
}
else if (ret == -2)
{
/* to_scanner buffer full. */
g_debug (" client input still stalled (1)");
client_input_stalled = 1;
}
else if (ret == -3)
{
/* to_client buffer full. */
g_debug (" client input still stalled (2)");
client_input_stalled = 2;
}
else
{
/* Programming error. */
assert (0);
client_input_stalled = 0;
}
}
if (openvas_scanner_connected ())
{
/* Try process the scanner input, in case writing to the scanner
* has freed some space in to_scanner. */
ret = process_otp_scanner_input ();
if (ret == 1)
{
/* Received scanner BYE. Write out the rest of to_scanner (the
* BYE ACK).
*/
openvas_scanner_write (gmpd_nvt_cache_mode);
set_scanner_init_state (SCANNER_INIT_TOP);
if (client_active == 0)
return 0;
openvas_scanner_free ();
nfds = openvas_scanner_get_nfds (client_connection->socket);
}
else if (ret == 2)
{
/* Bad login to scanner. */
if (client_active == 0)
return 0;
rc = -1;
goto client_free;
}
else if (ret == 3)
{
/* Calls via serve_client() should continue. */
if (gmpd_nvt_cache_mode)
return 1;
openvas_scanner_close ();
}
else if (ret == 4)
{
/* NVT update requested and NVTS are already at that version. */
assert (gmpd_nvt_cache_mode);
return 0;
}
else if (ret == -1)
{
/* Error. */
rc = -1;
goto client_free;
}
else if (ret == -3)
/* to_scanner buffer still full. */
g_debug (" scanner input stalled");
else
{
/* Programming error. */
assert (ret == 0 || ret == 5);
}
}
if (process_gmp_change () == -1)
{
rc = -1;
goto client_free;
}
} /* while (1) */
client_free:
if (client_active)
gvm_connection_free (client_connection);
return rc;
}