"Fossies" - the Fresh Open Source Software Archive

Member "gvm-libs-11.0.1/osp/osp.c" (12 May 2020, 39902 Bytes) of package /linux/misc/openvas/gvm-libs-11.0.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "osp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 11.0.0_vs_11.0.1.

    1 /* Copyright (C) 2014-2019 Greenbone Networks GmbH
    2  *
    3  * SPDX-License-Identifier: GPL-2.0-or-later
    4  *
    5  * This program is free software; you can redistribute it and/or
    6  * modify it under the terms of the GNU General Public License
    7  * as published by the Free Software Foundation; either version 2
    8  * of the License, or (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 St, Fifth Floor, Boston, MA 02110-1301 USA.
   18  */
   19 
   20 /**
   21  * @file
   22  * @brief API for Open Scanner Protocol communication.
   23  */
   24 
   25 #include "osp.h"
   26 
   27 #include "../base/hosts.h"       /* for gvm_get_host_type */
   28 #include "../util/serverutils.h" /* for gvm_server_close, gvm_server_open_w... */
   29 
   30 #include <assert.h>        /* for assert */
   31 #include <gnutls/gnutls.h> /* for gnutls_session_int, gnutls_session_t */
   32 #include <stdarg.h>        /* for va_list */
   33 #include <stdlib.h>        /* for NULL, atoi */
   34 #include <stdio.h>         /* for FILE, fprintf and related functions */
   35 #include <string.h>        /* for strcmp, strlen, strncpy */
   36 #include <sys/socket.h>    /* for AF_UNIX, connect, socket, SOCK_STREAM */
   37 #include <sys/un.h>        /* for sockaddr_un, sa_family_t */
   38 #include <unistd.h>        /* for close */
   39 
   40 #undef G_LOG_DOMAIN
   41 /**
   42  * @brief GLib log domain.
   43  */
   44 #define G_LOG_DOMAIN "lib  osp"
   45 
   46 /**
   47  * @brief Struct holding options for OSP connection.
   48  */
   49 struct osp_connection
   50 {
   51   gnutls_session_t session; /**< Pointer to GNUTLS Session. */
   52   int socket;               /**< Socket. */
   53   char *host;               /**< Host. */
   54   int port;                 /**< Port. */
   55 };
   56 
   57 /**
   58  * @brief Struct holding options for OSP parameters.
   59  */
   60 struct osp_param
   61 {
   62   char *id;              /**< Parameter id. */
   63   char *name;            /**< Parameter name. */
   64   char *desc;            /**< Parameter description. */
   65   char *def;             /**< Default value. */
   66   osp_param_type_t type; /**< Parameter type. */
   67   int mandatory;         /**< If mandatory or not. */
   68 };
   69 
   70 /**
   71  * @brief Struct credential information for OSP.
   72  */
   73 struct osp_credential
   74 {
   75   gchar *type;            /**< Credential type */
   76   gchar *service;         /**< Service the credential is for */
   77   gchar *port;            /**< Port the credential is for */
   78   GHashTable *auth_data;  /**< Authentication data (username, password, etc.)*/
   79 };
   80 
   81 /**
   82  * @brief Struct holding target information.
   83  */
   84 struct osp_target
   85 {
   86   GSList *credentials;    /** Credentials to use in the scan */
   87   gchar *exclude_hosts;   /** String defining one or many hosts to exclude */
   88   gchar *hosts;           /** String defining one or many hosts to scan */
   89   gchar *ports;           /** String defining the ports to scan */
   90   gchar *finished_hosts;  /** String defining hosts to exclude as finished */
   91 };
   92 
   93 /**
   94  * @brief Struct holding vt_group information
   95  */
   96 struct osp_vt_group
   97 {
   98   gchar *filter;
   99 };
  100 
  101 /**
  102  * @brief Struct holding vt_group information
  103  */
  104 struct osp_vt_single
  105 {
  106   gchar *vt_id;
  107   GHashTable *vt_values;
  108 };
  109 
  110 static int
  111 osp_send_command (osp_connection_t *, entity_t *, const char *, ...)
  112   __attribute__ ((__format__ (__printf__, 3, 4)));
  113 
  114 /**
  115  * @brief Open a new connection to an OSP server.
  116  *
  117  * @param[in]   host    Host of OSP server.
  118  * @param[in]   port    Port of OSP server.
  119  * @param[in]   cacert  CA public key.
  120  * @param[in]   cert    Client public key.
  121  * @param[in]   key     Client private key.
  122  *
  123  * @return New osp connection, NULL if error.
  124  */
  125 osp_connection_t *
  126 osp_connection_new (const char *host, int port, const char *cacert,
  127                     const char *cert, const char *key)
  128 {
  129   osp_connection_t *connection;
  130 
  131   if (host && *host == '/')
  132     {
  133       struct sockaddr_un addr;
  134       int len;
  135 
  136       connection = g_malloc0 (sizeof (*connection));
  137       connection->socket = socket (AF_UNIX, SOCK_STREAM, 0);
  138       if (connection->socket == -1)
  139         return NULL;
  140 
  141       addr.sun_family = AF_UNIX;
  142       strncpy (addr.sun_path, host, sizeof (addr.sun_path) - 1);
  143       len = strlen (addr.sun_path) + sizeof (addr.sun_family);
  144       if (connect (connection->socket, (struct sockaddr *) &addr, len) == -1)
  145         {
  146           close (connection->socket);
  147           return NULL;
  148         }
  149     }
  150   else
  151     {
  152       if (port <= 0 || port > 65535)
  153         return NULL;
  154       if (!host || gvm_get_host_type (host) == -1)
  155         return NULL;
  156       if (!cert || !key || !cacert)
  157         return NULL;
  158 
  159       connection = g_malloc0 (sizeof (*connection));
  160       connection->socket = gvm_server_open_with_cert (
  161         &connection->session, host, port, cacert, cert, key);
  162     }
  163   if (connection->socket == -1)
  164     {
  165       g_free (connection);
  166       return NULL;
  167     }
  168 
  169   connection->host = g_strdup (host);
  170   connection->port = port;
  171   return connection;
  172 }
  173 
  174 /**
  175  * @brief Send a command to an OSP server.
  176  *
  177  * @param[in]   connection  Connection to OSP server.
  178  * @param[out]  response    Response from OSP server.
  179  * @param[in]   fmt         OSP Command to send.
  180  *
  181  * @return 0 and response, 1 if error.
  182  */
  183 int
  184 osp_send_command (osp_connection_t *connection, entity_t *response,
  185                   const char *fmt, ...)
  186 {
  187   va_list ap;
  188   int rc = 1;
  189 
  190   va_start (ap, fmt);
  191 
  192   if (!connection || !fmt || !response)
  193     goto out;
  194 
  195   if (*connection->host == '/')
  196     {
  197       if (gvm_socket_vsendf (connection->socket, fmt, ap) == -1)
  198         goto out;
  199       if (read_entity_s (connection->socket, response))
  200         goto out;
  201     }
  202   else
  203     {
  204       if (gvm_server_vsendf (&connection->session, fmt, ap) == -1)
  205         goto out;
  206       if (read_entity (&connection->session, response))
  207         goto out;
  208     }
  209 
  210   rc = 0;
  211 
  212 out:
  213   va_end (ap);
  214 
  215   return rc;
  216 }
  217 
  218 /**
  219  * @brief Close a connection to an OSP server.
  220  *
  221  * @param[in]   connection  Connection to OSP server to close.
  222  */
  223 void
  224 osp_connection_close (osp_connection_t *connection)
  225 {
  226   if (!connection)
  227     return;
  228 
  229   if (*connection->host == '/')
  230     close (connection->socket);
  231   else
  232     gvm_server_close (connection->socket, connection->session);
  233   g_free (connection->host);
  234   g_free (connection);
  235 }
  236 
  237 /**
  238  * @brief Get the scanner version from an OSP server.
  239  *
  240  * @param[in]   connection  Connection to an OSP server.
  241  * @param[out]  s_name      Parsed scanner name.
  242  * @param[out]  s_version   Parsed scanner version.
  243  * @param[out]  d_name      Parsed scanner name.
  244  * @param[out]  d_version   Parsed scanner version.
  245  * @param[out]  p_name      Parsed scanner name.
  246  * @param[out]  p_version   Parsed scanner version.
  247  *
  248  * @return 0 if success, 1 if error.
  249  */
  250 int
  251 osp_get_version (osp_connection_t *connection, char **s_name, char **s_version,
  252                  char **d_name, char **d_version, char **p_name,
  253                  char **p_version)
  254 {
  255   entity_t entity, child, gchild;
  256 
  257   if (!connection)
  258     return 1;
  259 
  260   if (osp_send_command (connection, &entity, "<get_version/>"))
  261     return 1;
  262 
  263   child = entity_child (entity, "scanner");
  264   if (!child)
  265     goto err_get_version;
  266   gchild = entity_child (child, "name");
  267   if (!gchild)
  268     goto err_get_version;
  269   if (s_name)
  270     *s_name = g_strdup (entity_text (gchild));
  271   gchild = entity_child (child, "version");
  272   if (!gchild)
  273     goto err_get_version;
  274   if (s_version)
  275     *s_version = g_strdup (entity_text (gchild));
  276 
  277   child = entity_child (entity, "daemon");
  278   if (!child)
  279     goto err_get_version;
  280   gchild = entity_child (child, "name");
  281   if (!gchild)
  282     goto err_get_version;
  283   if (d_name)
  284     *d_name = g_strdup (entity_text (gchild));
  285   gchild = entity_child (child, "version");
  286   if (!gchild)
  287     goto err_get_version;
  288   if (d_version)
  289     *d_version = g_strdup (entity_text (gchild));
  290 
  291   child = entity_child (entity, "protocol");
  292   if (!child)
  293     goto err_get_version;
  294   gchild = entity_child (child, "name");
  295   if (!gchild)
  296     goto err_get_version;
  297   if (p_name)
  298     *p_name = g_strdup (entity_text (gchild));
  299   gchild = entity_child (child, "version");
  300   if (!gchild)
  301     goto err_get_version;
  302   if (p_version)
  303     *p_version = g_strdup (entity_text (gchild));
  304 
  305   free_entity (entity);
  306   return 0;
  307 
  308 err_get_version:
  309   g_warning ("Erroneous OSP <get_version/> response.");
  310   if (s_name)
  311     g_free (*s_name);
  312   if (s_version)
  313     g_free (*s_version);
  314   if (d_name)
  315     g_free (*d_name);
  316   if (d_version)
  317     g_free (*d_version);
  318   if (p_name)
  319     g_free (*p_name);
  320   if (p_version)
  321     g_free (*p_version);
  322   free_entity (entity);
  323   return 1;
  324 }
  325 
  326 /**
  327  * @brief Get the VTs version from an OSP server.
  328  *
  329  * @param[in]   connection    Connection to an OSP server.
  330  * @param[out]  vts_version   Parsed scanner version.
  331  *
  332  * @return 0 if success, 1 if error.
  333  */
  334 int
  335 osp_get_vts_version (osp_connection_t *connection, char **vts_version)
  336 {
  337   entity_t entity, vts, version;
  338 
  339   if (!connection)
  340     return 1;
  341 
  342   if (osp_send_command (connection, &entity, "<get_version/>"))
  343     return 1;
  344 
  345   vts = entity_child (entity, "vts");
  346   if (!vts)
  347     {
  348       g_warning ("%s: element VTS missing.", __FUNCTION__);
  349       free_entity (entity);
  350       return 1;
  351     }
  352 
  353   version = entity_child (vts, "version");
  354   if (!version)
  355     {
  356       g_warning ("%s: element VERSION missing.", __FUNCTION__);
  357       free_entity (entity);
  358       return 1;
  359     }
  360 
  361   if (vts_version)
  362     *vts_version = g_strdup (entity_text (version));
  363 
  364   free_entity (entity);
  365   return 0;
  366 }
  367 
  368 /**
  369  * @brief Get all VTs from an OSP server.
  370  *
  371  * @param[in]   connection    Connection to an OSP server.
  372  * @param[out]  vts           VTs.
  373  *
  374  * @return 0 if success, 1 if error.
  375  */
  376 int
  377 osp_get_vts (osp_connection_t *connection, entity_t *vts)
  378 {
  379   if (!connection)
  380     return 1;
  381 
  382   if (vts == NULL)
  383     return 1;
  384 
  385   if (osp_send_command (connection, vts, "<get_vts/>"))
  386     return 1;
  387 
  388   return 0;
  389 }
  390 
  391 /**
  392  * @brief Get filtered set of VTs from an OSP server.
  393  *
  394  * @param[in]   connection  Connection to an OSP server.
  395  * @param[in]   opts        Struct containing the options to apply.
  396  * @param[out]  vts         VTs.
  397  *
  398  * @return 0 if success, 1 if error.
  399  */
  400 int
  401 osp_get_vts_ext (osp_connection_t *connection, osp_get_vts_opts_t opts, entity_t *vts)
  402 {
  403   if (!connection)
  404     return 1;
  405 
  406   if (vts == NULL)
  407     return 1;
  408 
  409   if (opts.filter)
  410     {
  411       if (osp_send_command (connection, vts, "<get_vts filter='%s'/>", opts.filter))
  412         return 1;
  413       return 0;
  414     }
  415 
  416   if (osp_send_command (connection, vts, "<get_vts/>"))
  417     return 1;
  418   return 0;
  419 }
  420 
  421 /**
  422  * @brief Delete a scan from an OSP server.
  423  *
  424  * @param[in]   connection  Connection to an OSP server.
  425  * @param[in]   scan_id     ID of scan to delete.
  426  *
  427  * @return 0 if success, 1 if error.
  428  */
  429 int
  430 osp_delete_scan (osp_connection_t *connection, const char *scan_id)
  431 {
  432   entity_t entity;
  433   int ret = 0;
  434   const char *status;
  435 
  436   if (!connection)
  437     return 1;
  438 
  439   ret = osp_send_command (connection, &entity, "<delete_scan scan_id='%s'/>",
  440                           scan_id);
  441   if (ret)
  442     return 1;
  443 
  444   /* Check response status. */
  445   status = entity_attribute (entity, "status");
  446   assert (status);
  447   if (strcmp (status, "200"))
  448     ret = 1;
  449 
  450   free_entity (entity);
  451   return ret;
  452 }
  453 
  454 /**
  455  * @brief Get performance graphics from an OSP server.
  456  *
  457  * @param[in]   connection  Connection to an OSP server.
  458  * @param[in]   opts        Struct containing the options to apply.
  459  * @param[out]  graph       Graphic base64 encoded.
  460  * @param[out]  error       Pointer to error, if any.
  461  *
  462  * @return 0 if success, -1 if error.
  463  */
  464 int
  465 osp_get_performance_ext (osp_connection_t *connection,
  466                          osp_get_performance_opts_t opts, char **graph,
  467                          char **error)
  468 {
  469   entity_t entity;
  470   int rc;
  471   time_t now;
  472 
  473   if (!connection)
  474     {
  475       if (error)
  476         *error = g_strdup ("Couldn't send get_performance command "
  477                            "to scanner. Not valid connection");
  478       return -1;
  479     }
  480 
  481   time (&now);
  482 
  483   if (!opts.titles || !strcmp (opts.titles, "") || opts.start < 0
  484       || opts.start > now || opts.end < 0 || opts.end > now)
  485     {
  486       if (error)
  487         *error = g_strdup ("Couldn't send get_performance command "
  488                            "to scanner. Bad or missing parameters.");
  489       return -1;
  490     }
  491 
  492   rc = osp_send_command (connection, &entity,
  493                          "<get_performance start='%d' "
  494                          "end='%d' titles='%s'/>",
  495                          opts.start, opts.end, opts.titles);
  496 
  497   if (rc)
  498     {
  499       if (error)
  500         *error = g_strdup ("Couldn't send get_performance command to scanner");
  501       return -1;
  502     }
  503 
  504   if (graph && entity_text (entity) && strcmp (entity_text (entity), "\0"))
  505     *graph = g_strdup (entity_text (entity));
  506   else
  507     {
  508       const char *text = entity_attribute (entity, "status_text");
  509 
  510       assert (text);
  511       if (error)
  512         *error = g_strdup (text);
  513       free_entity (entity);
  514       return -1;
  515     }
  516 
  517   free_entity (entity);
  518   return 0;
  519 }
  520 
  521 /**
  522  * @brief Get a scan status from an OSP server
  523  *
  524  * @param[in]   connection  Connection to an OSP server.
  525  * @param[in]   scan_id     ID of scan to get.
  526  * @param[out]  error       Pointer to error, if any.
  527  *
  528  * @return Osp scan status
  529  */
  530 osp_scan_status_t
  531 osp_get_scan_status_ext (osp_connection_t *connection,
  532                          osp_get_scan_status_opts_t opts, char **error)
  533 {
  534   entity_t entity, child;
  535   int rc;
  536   osp_scan_status_t status = OSP_SCAN_STATUS_ERROR;
  537 
  538   if (!connection)
  539     {
  540       if (error)
  541         *error = g_strdup ("Couldn't send get_scans command "
  542                            "to scanner. Not valid connection");
  543       return status;
  544     }
  545 
  546   assert (opts.scan_id);
  547   rc = osp_send_command (connection, &entity,
  548                          "<get_scans scan_id='%s'"
  549                          " details='0'"
  550                          " pop_results='0'/>",
  551                          opts.scan_id);
  552 
  553   if (rc)
  554     {
  555       if (error)
  556         *error = g_strdup ("Couldn't send get_scans command to scanner");
  557       return status;
  558     }
  559 
  560   child = entity_child (entity, "scan");
  561   if (!child)
  562     {
  563       const char *text = entity_attribute (entity, "status_text");
  564 
  565       assert (text);
  566       if (error)
  567         *error = g_strdup (text);
  568       free_entity (entity);
  569       return status;
  570     }
  571 
  572   if (!strcmp (entity_attribute (child, "status"), "init"))
  573     status = OSP_SCAN_STATUS_INIT;
  574   else if (!strcmp (entity_attribute (child, "status"), "running"))
  575     status = OSP_SCAN_STATUS_RUNNING;
  576   else if (!strcmp (entity_attribute (child, "status"), "stopped"))
  577     status = OSP_SCAN_STATUS_STOPPED;
  578   else if (!strcmp (entity_attribute (child, "status"), "finished"))
  579     status = OSP_SCAN_STATUS_FINISHED;
  580 
  581   free_entity (entity);
  582   return status;
  583 }
  584 
  585 /**
  586  * @brief Get a scan from an OSP server, optionally removing the results.
  587  *
  588  * @param[in]   connection  Connection to an OSP server.
  589  * @param[in]   scan_id     ID of scan to get.
  590  * @param[out]  report_xml  Scans report.
  591  * @param[in]   details     0 for no scan details, 1 otherwise.
  592  * @param[in]   pop_results 0 to leave results, 1 to pop results from scanner.
  593  * @param[out]  error       Pointer to error, if any.
  594  *
  595  * @return Scan progress if success, -1 if error.
  596  */
  597 int
  598 osp_get_scan_pop (osp_connection_t *connection, const char *scan_id,
  599                   char **report_xml, int details, int pop_results,
  600                   char **error)
  601 {
  602   entity_t entity, child;
  603   int progress;
  604   int rc;
  605 
  606   if (!connection)
  607     {
  608       if (error)
  609         *error = g_strdup ("Couldn't send get_scan command "
  610                            "to scanner. Not valid connection");
  611       return -1;
  612     }
  613   assert (scan_id);
  614   rc = osp_send_command (connection, &entity,
  615                          "<get_scans scan_id='%s'"
  616                          " details='%d'"
  617                          " pop_results='%d'/>",
  618                          scan_id,
  619                          pop_results ? 1 : 0,
  620                          details ? 1 : 0);
  621   if (rc)
  622     {
  623       if (error)
  624         *error = g_strdup ("Couldn't send get_scans command to scanner");
  625       return -1;
  626     }
  627 
  628   child = entity_child (entity, "scan");
  629   if (!child)
  630     {
  631       const char *text = entity_attribute (entity, "status_text");
  632 
  633       assert (text);
  634       if (error)
  635         *error = g_strdup (text);
  636       free_entity (entity);
  637       return -1;
  638     }
  639   progress = atoi (entity_attribute (child, "progress"));
  640   if (report_xml)
  641     {
  642       GString *string;
  643 
  644       string = g_string_new ("");
  645       print_entity_to_string (child, string);
  646       *report_xml = g_string_free (string, FALSE);
  647     }
  648   free_entity (entity);
  649   return progress;
  650 }
  651 
  652 /**
  653  * @brief Get a scan from an OSP server.
  654  *
  655  * @param[in]   connection  Connection to an OSP server.
  656  * @param[in]   scan_id     ID of scan to get.
  657  * @param[out]  report_xml  Scans report.
  658  * @param[in]   details     0 for no scan details, 1 otherwise.
  659  * @param[out]  error       Pointer to error, if any.
  660  *
  661  * @return Scan progress if success, -1 if error.
  662  */
  663 int
  664 osp_get_scan (osp_connection_t *connection, const char *scan_id,
  665               char **report_xml, int details, char **error)
  666 {
  667   return osp_get_scan_pop (connection, scan_id, report_xml, details, 0, error);
  668 }
  669 
  670 /**
  671  * @brief Stop a scan on an OSP server.
  672  *
  673  * @param[in]   connection  Connection to an OSP server.
  674  * @param[in]   scan_id     ID of scan to delete.
  675  * @param[out]  error       Pointer to error, if any.
  676  *
  677  * @return Scan progress if success, -1 if error.
  678  */
  679 int
  680 osp_stop_scan (osp_connection_t *connection, const char *scan_id, char **error)
  681 {
  682   entity_t entity;
  683   int rc;
  684 
  685   if (!connection)
  686     {
  687       if (error)
  688         *error = g_strdup ("Couldn't send stop_scan command "
  689                            "to scanner. Not valid connection");
  690       return -1;
  691     }
  692   assert (scan_id);
  693   rc = osp_send_command (connection, &entity, "<stop_scan scan_id='%s'/>",
  694                          scan_id);
  695   if (rc)
  696     {
  697       if (error)
  698         *error = g_strdup ("Couldn't send stop_scan command to scanner");
  699       return -1;
  700     }
  701 
  702   rc = atoi (entity_attribute (entity, "status"));
  703   if (rc == 200)
  704     {
  705       free_entity (entity);
  706       return 0;
  707     }
  708   else
  709     {
  710       const char *text = entity_attribute (entity, "status_text");
  711 
  712       assert (text);
  713       if (error)
  714         *error = g_strdup (text);
  715       free_entity (entity);
  716       return -1;
  717     }
  718 }
  719 
  720 /**
  721  * @brief Concatenate options as xml.
  722  *
  723  * @param[in]     key      Tag name for xml element.
  724  * @param[in]     value    Text for xml element.
  725  * @param[in,out] pstr     Parameters as xml concatenated xml elements.
  726  *
  727  */
  728 static void
  729 option_concat_as_xml (gpointer key, gpointer value, gpointer pstr)
  730 {
  731   char *options_str, *tmp, *key_escaped, *value_escaped;
  732 
  733   options_str = *(char **) pstr;
  734 
  735   key_escaped = g_markup_escape_text ((char *) key, -1);
  736   value_escaped = g_markup_escape_text ((char *) value, -1);
  737   tmp = g_strdup_printf ("%s<%s>%s</%s>", options_str ? options_str : "",
  738                          key_escaped, value_escaped, key_escaped);
  739 
  740   g_free (options_str);
  741   g_free (key_escaped);
  742   g_free (value_escaped);
  743   *(char **) pstr = tmp;
  744 }
  745 
  746 /**
  747  * @brief Start an OSP scan against a target.
  748  *
  749  * @param[in]   connection  Connection to an OSP server.
  750  * @param[in]   target      Target host to scan.
  751  * @param[in]   ports       List of ports to scan.
  752  * @param[in]   options     Table of scan options.
  753  * @param[in]   scan_id     uuid to set for scan, null otherwise.
  754  * @param[out]  error       Pointer to error, if any.
  755  *
  756  * @return 0 on success, -1 otherwise.
  757  */
  758 int
  759 osp_start_scan (osp_connection_t *connection, const char *target,
  760                 const char *ports, GHashTable *options, const char *scan_id,
  761                 char **error)
  762 {
  763   entity_t entity;
  764   char *options_str = NULL;
  765   int status;
  766   int rc;
  767 
  768   if (!connection)
  769     {
  770       if (error)
  771         *error = g_strdup ("Couldn't send start_scan command "
  772                            "to scanner. Not valid connection");
  773       return -1;
  774     }
  775 
  776   assert (target);
  777   /* Construct options string. */
  778   if (options)
  779     g_hash_table_foreach (options, option_concat_as_xml, &options_str);
  780 
  781   rc = osp_send_command (connection, &entity,
  782                          "<start_scan target='%s' ports='%s' scan_id='%s'>"
  783                          "<scanner_params>%s</scanner_params></start_scan>",
  784                          target, ports ? ports : "", scan_id ? scan_id : "",
  785                          options_str ? options_str : "");
  786   g_free (options_str);
  787   if (rc)
  788     {
  789       if (error)
  790         *error = g_strdup ("Couldn't send start_scan command to scanner");
  791       return -1;
  792     }
  793 
  794   status = atoi (entity_attribute (entity, "status"));
  795   if (status == 200)
  796     {
  797       free_entity (entity);
  798       return 0;
  799     }
  800   else
  801     {
  802       const char *text = entity_attribute (entity, "status_text");
  803 
  804       assert (text);
  805       if (error)
  806         *error = g_strdup (text);
  807       free_entity (entity);
  808       return -1;
  809     }
  810 }
  811 
  812 /**
  813  * @brief Concatenate a credential as XML.
  814  *
  815  * @param[in]     credential  Credential data.
  816  * @param[in,out] xml_string  XML string buffer to append to.
  817  *
  818  */
  819 static void
  820 credential_append_as_xml (osp_credential_t *credential,
  821                           GString *xml_string)
  822 
  823 {
  824   GHashTableIter auth_data_iter;
  825   gchar *auth_data_name, *auth_data_value;
  826 
  827   xml_string_append (xml_string,
  828                      "<credential type=\"%s\" service=\"%s\" port=\"%s\">",
  829                      credential->type ? credential->type : "",
  830                      credential->service ? credential->service : "",
  831                      credential->port ? credential->port : "");
  832 
  833   g_hash_table_iter_init (&auth_data_iter, credential->auth_data);
  834   while (g_hash_table_iter_next (&auth_data_iter,
  835                                  (gpointer*)&auth_data_name,
  836                                  (gpointer*)&auth_data_value))
  837     {
  838       xml_string_append (xml_string,
  839                          "<%s>%s</%s>",
  840                          auth_data_name,
  841                          auth_data_value,
  842                          auth_data_name);
  843     }
  844 
  845   xml_string_append (xml_string, "</credential>");
  846 }
  847 
  848 /**
  849  * @brief Concatenate a target as XML.
  850  *
  851  * @param[in]     target      Target data.
  852  * @param[in,out] xml_string  XML string buffer to append to.
  853  *
  854  */
  855 static void
  856 target_append_as_xml (osp_target_t *target, GString *xml_string)
  857 {
  858   xml_string_append (xml_string,
  859                      "<target>"
  860                      "<hosts>%s</hosts>"
  861                      "<exclude_hosts>%s</exclude_hosts>"
  862                      "<finished_hosts>%s</finished_hosts>"
  863                      "<ports>%s</ports>",
  864                      target->hosts ? target->hosts : "",
  865                      target->exclude_hosts ? target->exclude_hosts : "",
  866                      target->finished_hosts ? target->finished_hosts : "",
  867                      target->ports ? target->ports : "");
  868 
  869   if (target->credentials)
  870     {
  871       g_string_append (xml_string, "<credentials>");
  872       g_slist_foreach (target->credentials,
  873                        (GFunc) credential_append_as_xml,
  874                        xml_string);
  875       g_string_append (xml_string, "</credentials>");
  876     }
  877   xml_string_append (xml_string,
  878                      "</target>");
  879 }
  880 
  881 /**
  882  * @brief Append VT groups as XML to a string buffer.
  883  *
  884  * @param[in]     vt_group    VT group data.
  885  * @param[in,out] xml_string  XML string buffer to append to.
  886  */
  887 static void vt_group_append_as_xml (osp_vt_group_t *vt_group,
  888                                     GString *xml_string)
  889 {
  890   xml_string_append (xml_string,
  891                      "<vt_group filter=\"%s\"/>",
  892                      vt_group->filter);
  893 }
  894 
  895 /**
  896  * @brief Append VT values as XML to a string buffer.
  897  *
  898  * @param[in]     id          Identifier of the vt_value.
  899  * @param[in]     value       The value of the vt_value.
  900  * @param[in,out] xml_string  XML string buffer to append to.
  901  *
  902  */
  903 static void
  904 vt_value_append_as_xml (gpointer id, gchar *value, GString *xml_string)
  905 {
  906   xml_string_append (xml_string,
  907                      "<vt_value id=\"%s\">%s</vt_value>",
  908                      id ? id : "",
  909                      value ? value : "");
  910 }
  911 
  912 /**
  913  * @brief Append single VTs as XML to a string buffer.
  914  *
  915  * @param[in]     vt_single   Single VT data.
  916  * @param[in,out] xml_string  XML string buffer to append to.
  917  */
  918 static void vt_single_append_as_xml (osp_vt_single_t *vt_single,
  919                                     GString *xml_string)
  920 {
  921   xml_string_append (xml_string,
  922                      "<vt_single id=\"%s\">",
  923                      vt_single->vt_id);
  924   g_hash_table_foreach (vt_single->vt_values,
  925                         (GHFunc) vt_value_append_as_xml,
  926                         xml_string);
  927   xml_string_append (xml_string, "</vt_single>");
  928 }
  929 
  930 
  931 /**
  932  * @brief Start an OSP scan against a target.
  933  *
  934  * @param[in]   connection  Connection to an OSP server.
  935  * @param[in]   opts        Struct containing the options to apply.
  936  * @param[out]  error       Pointer to error, if any.
  937  *
  938  * @return 0 on success, -1 otherwise.
  939  */
  940 int
  941 osp_start_scan_ext (osp_connection_t *connection,
  942                     osp_start_scan_opts_t opts,
  943                     char **error)
  944 {
  945   gchar *scanner_params_xml = NULL;
  946   GString *xml;
  947   GSList *list_item;
  948   int list_count;
  949   int rc, status;
  950   entity_t entity;
  951   gchar *cmd;
  952   char filename[] = "/tmp/osp-cmd-XXXXXX";
  953   int fd;
  954 
  955   if (!connection)
  956     {
  957       if (error)
  958         *error = g_strdup ("Couldn't send start_scan command "
  959                            "to scanner. Not valid connection");
  960       return -1;
  961     }
  962 
  963   fd = mkstemp (filename);
  964   FILE *file = fdopen (fd, "w");
  965 
  966   xml = g_string_sized_new (10240);
  967   g_string_append (xml, "<start_scan");
  968   if (opts.parallel)
  969     xml_string_append (xml, " parallel=\"%d\"", opts.parallel);
  970   xml_string_append (xml,
  971                      " scan_id=\"%s\">",
  972                      opts.scan_id ? opts.scan_id : "");
  973 
  974   g_string_append (xml, "<targets>");
  975   g_slist_foreach (opts.targets, (GFunc) target_append_as_xml, xml);
  976   g_string_append (xml, "</targets>");
  977 
  978   g_string_append (xml, "<scanner_params>");
  979   if (opts.scanner_params)
  980     {
  981       scanner_params_xml = NULL;
  982       g_hash_table_foreach (opts.scanner_params,
  983                             (GHFunc) option_concat_as_xml,
  984                             &scanner_params_xml);
  985       if (scanner_params_xml)
  986         g_string_append (xml, scanner_params_xml);
  987       g_free (scanner_params_xml);
  988     }
  989   g_string_append (xml, "</scanner_params>");
  990 
  991   g_string_append (xml, "<vt_selection>");
  992   g_slist_foreach (opts.vt_groups, (GFunc)vt_group_append_as_xml, xml);
  993 
  994   fprintf (file, "%s", xml->str);
  995 
  996   g_string_free (xml, TRUE);
  997 
  998   xml = g_string_new ("");
  999   list_item = opts.vts;
 1000   list_count = 0;
 1001   while (list_item)
 1002     {
 1003       list_count ++;
 1004         vt_single_append_as_xml (list_item->data, xml);
 1005 
 1006       list_item = list_item->next;
 1007 
 1008       if (list_count == 1000)
 1009         {
 1010           fprintf (file, "%s", xml->str);
 1011 
 1012           g_string_free (xml, TRUE);
 1013           xml = g_string_new ("");
 1014           list_count = 0;
 1015         }
 1016     }
 1017 
 1018   g_string_append (xml, "</vt_selection>");
 1019   g_string_append (xml, "</start_scan>");
 1020 
 1021   fprintf (file, "%s", xml->str);
 1022   fflush (file);
 1023   fclose (file);
 1024   g_string_free (xml, TRUE);
 1025 
 1026   g_file_get_contents (filename, &cmd, NULL, NULL);
 1027 
 1028   rc = osp_send_command (connection, &entity, "%s", cmd);
 1029 
 1030   g_free (cmd);
 1031   unlink (filename);
 1032 
 1033   if (rc)
 1034     {
 1035       if (error)
 1036         *error = g_strdup ("Could not send start_scan command to scanner");
 1037       return -1;
 1038     }
 1039 
 1040   status = atoi (entity_attribute (entity, "status"));
 1041   if (status == 200)
 1042     {
 1043       free_entity (entity);
 1044       return 0;
 1045     }
 1046   else
 1047     {
 1048       const char *text = entity_attribute (entity, "status_text");
 1049 
 1050       assert (text);
 1051       if (error)
 1052         *error = g_strdup (text);
 1053       free_entity (entity);
 1054       return -1;
 1055     }
 1056 
 1057   if (error)
 1058     *error = NULL;
 1059   free_entity (entity);
 1060   return 0;
 1061 }
 1062 
 1063 /**
 1064  * @brief Get an OSP parameter's type from its string format.
 1065  *
 1066  * @param[in]   str     OSP parameter in string format.
 1067  *
 1068  * @return OSP parameter type.
 1069  */
 1070 static osp_param_type_t
 1071 osp_param_str_to_type (const char *str)
 1072 {
 1073   assert (str);
 1074   if (!strcmp (str, "integer"))
 1075     return OSP_PARAM_TYPE_INT;
 1076   else if (!strcmp (str, "string"))
 1077     return OSP_PARAM_TYPE_STR;
 1078   else if (!strcmp (str, "password"))
 1079     return OSP_PARAM_TYPE_PASSWORD;
 1080   else if (!strcmp (str, "file"))
 1081     return OSP_PARAM_TYPE_FILE;
 1082   else if (!strcmp (str, "boolean"))
 1083     return OSP_PARAM_TYPE_BOOLEAN;
 1084   else if (!strcmp (str, "ovaldef_file"))
 1085     return OSP_PARAM_TYPE_OVALDEF_FILE;
 1086   else if (!strcmp (str, "selection"))
 1087     return OSP_PARAM_TYPE_SELECTION;
 1088   else if (!strcmp (str, "credential_up"))
 1089     return OSP_PARAM_TYPE_CRD_UP;
 1090   assert (0);
 1091   return 0;
 1092 }
 1093 
 1094 /**
 1095  * @brief Get an OSP parameter in string format form its type.
 1096  *
 1097  * @param[in]   param     OSP parameter.
 1098  *
 1099  * @return OSP parameter in string format.
 1100  */
 1101 const char *
 1102 osp_param_type_str (const osp_param_t *param)
 1103 {
 1104   osp_param_type_t type;
 1105 
 1106   assert (param);
 1107   type = param->type;
 1108   if (type == OSP_PARAM_TYPE_INT)
 1109     return "integer";
 1110   else if (type == OSP_PARAM_TYPE_STR)
 1111     return "string";
 1112   else if (type == OSP_PARAM_TYPE_PASSWORD)
 1113     return "password";
 1114   else if (type == OSP_PARAM_TYPE_FILE)
 1115     return "file";
 1116   else if (type == OSP_PARAM_TYPE_BOOLEAN)
 1117     return "boolean";
 1118   else if (type == OSP_PARAM_TYPE_OVALDEF_FILE)
 1119     return "ovaldef_file";
 1120   else if (type == OSP_PARAM_TYPE_SELECTION)
 1121     return "selection";
 1122   else if (type == OSP_PARAM_TYPE_CRD_UP)
 1123     return "credential_up";
 1124   assert (0);
 1125   return NULL;
 1126 }
 1127 
 1128 /**
 1129  * @brief Get an OSP scanner's details.
 1130  *
 1131  * @param[in]   connection  Connection to an OSP server.
 1132  * @param[out]  desc        Scanner's description.
 1133  * @param[out]  params      Scanner's parameters.
 1134  *
 1135  * @return 0 if success, 1 if failure.
 1136  */
 1137 int
 1138 osp_get_scanner_details (osp_connection_t *connection, char **desc,
 1139                          GSList **params)
 1140 {
 1141   entity_t entity, child;
 1142   entities_t entities;
 1143 
 1144   assert (connection);
 1145 
 1146   if (osp_send_command (connection, &entity, "<get_scanner_details/>"))
 1147     return 1;
 1148   if (params)
 1149     {
 1150       child = entity_child (entity, "scanner_params");
 1151       if (!child)
 1152         {
 1153           free_entity (entity);
 1154           return 1;
 1155         }
 1156       entities = child->entities;
 1157       while (entities)
 1158         {
 1159           osp_param_t *param;
 1160 
 1161           child = entities->data;
 1162           param = osp_param_new ();
 1163           param->id = g_strdup (entity_attribute (child, "id"));
 1164           param->type =
 1165             osp_param_str_to_type (entity_attribute (child, "type"));
 1166           param->name = g_strdup (entity_text (entity_child (child, "name")));
 1167           param->desc =
 1168             g_strdup (entity_text (entity_child (child, "description")));
 1169           param->def = g_strdup (entity_text (entity_child (child, "default")));
 1170           if (entity_child (child, "mandatory"))
 1171             param->mandatory =
 1172               atoi (entity_text (entity_child (child, "mandatory")));
 1173           *params = g_slist_append (*params, param);
 1174           entities = next_entities (entities);
 1175         }
 1176     }
 1177   if (desc)
 1178     {
 1179       child = entity_child (entity, "description");
 1180       assert (child);
 1181       *desc = g_strdup (entity_text (child));
 1182     }
 1183 
 1184   free_entity (entity);
 1185   return 0;
 1186 }
 1187 
 1188 /**
 1189  * @brief Create a new OSP parameter.
 1190  *
 1191  * @return New OSP parameter.
 1192  */
 1193 osp_param_t *
 1194 osp_param_new (void)
 1195 {
 1196   return g_malloc0 (sizeof (osp_param_t));
 1197 }
 1198 
 1199 /**
 1200  * @brief Get an OSP parameter's id.
 1201  *
 1202  * @param[in]   param   OSP parameter.
 1203  *
 1204  * @return ID of OSP parameter.
 1205  */
 1206 const char *
 1207 osp_param_id (const osp_param_t *param)
 1208 {
 1209   assert (param);
 1210 
 1211   return param->id;
 1212 }
 1213 
 1214 /**
 1215  * @brief Get an OSP parameter's name.
 1216  *
 1217  * @param[in]   param   OSP parameter.
 1218  *
 1219  * @return Name of OSP parameter.
 1220  */
 1221 const char *
 1222 osp_param_name (const osp_param_t *param)
 1223 {
 1224   assert (param);
 1225 
 1226   return param->name;
 1227 }
 1228 
 1229 /**
 1230  * @brief Get an OSP parameter's description.
 1231  *
 1232  * @param[in]   param   OSP parameter.
 1233  *
 1234  * @return Description of OSP parameter.
 1235  */
 1236 const char *
 1237 osp_param_desc (const osp_param_t *param)
 1238 {
 1239   assert (param);
 1240 
 1241   return param->desc;
 1242 }
 1243 
 1244 /**
 1245  * @brief Get an OSP parameter's default value.
 1246  *
 1247  * @param[in]   param   OSP parameter.
 1248  *
 1249  * @return Default value of OSP parameter.
 1250  */
 1251 const char *
 1252 osp_param_default (const osp_param_t *param)
 1253 {
 1254   assert (param);
 1255 
 1256   return param->def;
 1257 }
 1258 
 1259 /**
 1260  * @brief Get an OSP parameter's mandatory value.
 1261  *
 1262  * @param[in]   param   OSP parameter.
 1263  *
 1264  * @return Mandatory value of OSP parameter.
 1265  */
 1266 int
 1267 osp_param_mandatory (const osp_param_t *param)
 1268 {
 1269   assert (param);
 1270 
 1271   return param->mandatory;
 1272 }
 1273 
 1274 /**
 1275  * @brief Free an OSP parameter.
 1276  *
 1277  * @param[in] param OSP parameter to destroy.
 1278  */
 1279 void
 1280 osp_param_free (osp_param_t *param)
 1281 {
 1282   if (!param)
 1283     return;
 1284   g_free (param->id);
 1285   g_free (param->name);
 1286   g_free (param->desc);
 1287   g_free (param->def);
 1288   g_free (param);
 1289 }
 1290 
 1291 
 1292 /**
 1293  * @brief Allocate and initialize a new OSP credential.
 1294  *
 1295  * @param[in]   type      The credential type.
 1296  * @param[in]   service   The service the credential is for.
 1297  * @param[in]   port      The port.
 1298  *
 1299  * @return New osp credential.
 1300  */
 1301 osp_credential_t *
 1302 osp_credential_new (const char *type, const char *service, const char *port)
 1303 {
 1304   osp_credential_t *new_credential;
 1305 
 1306   new_credential = g_malloc0 (sizeof (osp_credential_t));
 1307 
 1308   new_credential->type = type ? g_strdup (type) : NULL;
 1309   new_credential->service = service ? g_strdup (service) : NULL;
 1310   new_credential->port = port ? g_strdup (port) : NULL;
 1311   new_credential->auth_data = g_hash_table_new_full (g_str_hash,
 1312                                                      g_str_equal,
 1313                                                      g_free,
 1314                                                      g_free);
 1315 
 1316   return new_credential;
 1317 }
 1318 
 1319 /**
 1320  * @brief Free an OSP credential.
 1321  *
 1322  * @param[in]   credential  The credential to free.
 1323  */
 1324 void
 1325 osp_credential_free (osp_credential_t *credential)
 1326 {
 1327   if (!credential)
 1328     return;
 1329 
 1330   g_free (credential->type);
 1331   g_free (credential->service);
 1332   g_free (credential->port);
 1333   g_hash_table_destroy (credential->auth_data);
 1334   g_free (credential);
 1335 }
 1336 
 1337 /**
 1338  * @brief Get authentication data from an OSP credential.
 1339  *
 1340  * @param[in]  credential  The credential to get the data from.
 1341  * @param[in]  name        The name of the data item to get.
 1342  *
 1343  * @return The requested authentication data or NULL if not available.
 1344  */
 1345 const gchar *
 1346 osp_credential_get_auth_data (osp_credential_t *credential,
 1347                               const char *name)
 1348 {
 1349   if (credential == NULL || name == NULL)
 1350     return NULL;
 1351   return g_hash_table_lookup (credential->auth_data, name);
 1352 }
 1353 
 1354 /**
 1355  * @brief Get authentication data from an OSP credential.
 1356  *
 1357  * @param[in]  credential  The credential to get the data from.
 1358  * @param[in]  name        The name of the data item to get.
 1359  * @param[in]  value       The authentication data or NULL to unset.
 1360  */
 1361 void
 1362 osp_credential_set_auth_data (osp_credential_t *credential,
 1363                               const char *name,
 1364                               const char *value)
 1365 {
 1366   if (credential == NULL || name == NULL)
 1367     return;
 1368 
 1369   if (g_regex_match_simple ("^[[:alpha:]][[:alnum:]_]*$", name, 0, 0))
 1370     {
 1371       if (value)
 1372         g_hash_table_replace (credential->auth_data,
 1373                               g_strdup (name),
 1374                               g_strdup (value));
 1375       else
 1376         g_hash_table_remove (credential->auth_data,
 1377                              value);
 1378     }
 1379   else
 1380     {
 1381       g_warning ("%s: Invalid auth data name: %s", __FUNCTION__, name);
 1382     }
 1383 }
 1384 
 1385 /**
 1386  * @brief Create a new OSP target.
 1387  *
 1388  * @param[in]  hosts          The hostnames of the target.
 1389  * @param[in]  ports          The ports of the target.
 1390  * @param[in]  exclude_hosts  The excluded hosts of the target.
 1391  *
 1392  * @return The newly allocated osp_target_t.
 1393  */
 1394 osp_target_t *
 1395 osp_target_new (const char *hosts,
 1396                 const char *ports,
 1397                 const char *exclude_hosts)
 1398 {
 1399   osp_target_t *new_target;
 1400   new_target = g_malloc0 (sizeof (osp_target_t));
 1401 
 1402   new_target->exclude_hosts = exclude_hosts ? g_strdup (exclude_hosts) : NULL;
 1403   new_target->hosts = hosts ? g_strdup (hosts) : NULL;
 1404   new_target->ports = ports ? g_strdup (ports) : NULL;
 1405   new_target->finished_hosts = NULL;
 1406 
 1407   return new_target;
 1408 }
 1409 
 1410 /**
 1411  * @brief Set the finished hosts of an OSP target.
 1412  *
 1413  * @param[in]  target         The OSP target to modify.
 1414  * @param[in]  finished_hosts The hostnames to consider finished.
 1415  */
 1416 void
 1417 osp_target_set_finished_hosts (osp_target_t *target,
 1418                                const char *finished_hosts)
 1419 {
 1420   g_free (target->finished_hosts);
 1421   target->finished_hosts = finished_hosts ? g_strdup (finished_hosts) : NULL;
 1422 }
 1423 
 1424 /**
 1425  * @brief Free an OSP target, including all added credentials.
 1426  *
 1427  * @param[in]  target  The OSP target to free.
 1428  */
 1429 void
 1430 osp_target_free (osp_target_t *target)
 1431 {
 1432   if (!target)
 1433     return;
 1434 
 1435   g_slist_free_full (target->credentials,
 1436                      (GDestroyNotify) osp_credential_free);
 1437   g_free (target->exclude_hosts);
 1438   g_free (target->hosts);
 1439   g_free (target->ports);
 1440   g_free (target);
 1441 }
 1442 
 1443 /**
 1444  * @brief Add a credential to an OSP target.
 1445  *
 1446  * @param[in]  target       The OSP target to add the credential to.
 1447  * @param[in]  credential   The credential to add. Will be freed with target.
 1448  */
 1449 void
 1450 osp_target_add_credential (osp_target_t *target, osp_credential_t *credential)
 1451 {
 1452   if (!target || !credential)
 1453     return;
 1454 
 1455   target->credentials = g_slist_prepend (target->credentials, credential);
 1456 }
 1457 
 1458 /**
 1459  * @brief Create a new OSP VT group.
 1460  *
 1461  * @param[in]  filter  The filter string for the VT group.
 1462  *
 1463  * @return  The newly allocated VT group.
 1464  */
 1465 osp_vt_group_t *
 1466 osp_vt_group_new (const char *filter)
 1467 {
 1468   osp_vt_group_t *new_vt_group;
 1469   new_vt_group = g_malloc0 (sizeof (osp_vt_group_t));
 1470 
 1471   new_vt_group->filter = filter ? g_strdup (filter) : NULL;
 1472 
 1473   return new_vt_group;
 1474 }
 1475 
 1476 /**
 1477  * @brief Free a OSP VT group.
 1478  *
 1479  * @param[in]  vt_group  The VT group to free.
 1480  */
 1481 void
 1482 osp_vt_group_free (osp_vt_group_t *vt_group)
 1483 {
 1484   if (!vt_group)
 1485     return;
 1486 
 1487   g_free (vt_group->filter);
 1488   g_free (vt_group);
 1489 }
 1490 
 1491 /**
 1492  * @brief Create a new single OSP VT.
 1493  *
 1494  * @param[in]  vt_id  The id of the VT.
 1495  *
 1496  * @return  The newly allocated single VT.
 1497  */
 1498 osp_vt_single_t *
 1499 osp_vt_single_new (const char *vt_id)
 1500 {
 1501   osp_vt_single_t *new_vt_single;
 1502   new_vt_single = g_malloc0 (sizeof (osp_vt_single_t));
 1503 
 1504   new_vt_single->vt_id = vt_id ? g_strdup (vt_id) : NULL;
 1505   new_vt_single->vt_values = g_hash_table_new_full (g_str_hash, g_str_equal,
 1506                                                     g_free, g_free);
 1507 
 1508   return new_vt_single;
 1509 }
 1510 
 1511 /**
 1512  * @brief Free a single OSP VT, including all preference values.
 1513  *
 1514  * @param[in]  vt_single  The OSP VT to free.
 1515  */
 1516 void
 1517 osp_vt_single_free (osp_vt_single_t *vt_single)
 1518 {
 1519   if (!vt_single)
 1520     return;
 1521 
 1522   g_hash_table_destroy (vt_single->vt_values);
 1523 
 1524   g_free (vt_single->vt_id);
 1525   g_free (vt_single);
 1526 }
 1527 
 1528 /**
 1529  * @brief Add a preference value to an OSP VT.
 1530  * This creates a copy of the name and value.
 1531  *
 1532  * @param[in]  vt_single  The VT to add the preference to.
 1533  * @param[in]  name       The name / identifier of the preference.
 1534  * @param[in]  value      The value of the preference.
 1535  */
 1536 void
 1537 osp_vt_single_add_value (osp_vt_single_t *vt_single,
 1538                          const char *name, const char *value)
 1539 {
 1540   g_hash_table_replace (vt_single->vt_values,
 1541                         g_strdup (name),
 1542                         g_strdup (value));
 1543 }
 1544