LCOV - code coverage report
Current view: top level - src/client - main.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 0 233 0.0 %
Date: 2019-03-02 00:16:58 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #include "../burp.h"
       2             : #include "../conffile.h"
       3             : #include "../action.h"
       4             : #include "../asfd.h"
       5             : #include "../async.h"
       6             : #include "../cmd.h"
       7             : #include "../cntr.h"
       8             : #include "../fsops.h"
       9             : #include "../handy.h"
      10             : #include "../iobuf.h"
      11             : #include "../log.h"
      12             : #include "../run_script.h"
      13             : #include "auth.h"
      14             : #include "backup.h"
      15             : #include "ca.h"
      16             : #include "delete.h"
      17             : #include "extra_comms.h"
      18             : #include "list.h"
      19             : #include "monitor.h"
      20             : #include "find_logic.h"
      21             : #include "monitor/status_client_ncurses.h"
      22             : #include "protocol2/restore.h"
      23             : #include "restore.h"
      24             : #include "main.h"
      25             : 
      26             : #ifndef HAVE_WIN32
      27             : #include <sys/utsname.h>
      28             : #endif
      29             : 
      30             : // These will also be used as the exit codes of the program and are therefore
      31             : // unsigned integers.
      32             : // Remember to update the man page if you update these.
      33             : enum cliret
      34             : {
      35             :         CLIENT_OK=0,
      36             :         CLIENT_ERROR=1,
      37             :         CLIENT_RESTORE_WARNINGS=2,
      38             :         CLIENT_SERVER_TIMER_NOT_MET=3,
      39             :         CLIENT_COULD_NOT_CONNECT=4,
      40             :         // This one happens after a successful certificate signing request so
      41             :         // that it connects again straight away with the new key/certificate.
      42             :         CLIENT_RECONNECT=100
      43             : };
      44             : 
      45             : struct tchk
      46             : {
      47             :         int resume;
      48             :         enum cliret ret;
      49             : };
      50             : 
      51           0 : static enum asl_ret maybe_check_timer_func(struct asfd *asfd,
      52             :         struct conf **confs, void *param)
      53             : {
      54           0 :         int complen=0;
      55           0 :         struct tchk *tchk=(struct tchk *)param;
      56             : 
      57           0 :         if(!strcmp(asfd->rbuf->buf, "timer conditions not met"))
      58             :         {
      59           0 :                 logp("Timer conditions on the server were not met\n");
      60           0 :                 tchk->ret=CLIENT_SERVER_TIMER_NOT_MET;
      61           0 :                 return ASL_END_OK;
      62             :         }
      63           0 :         else if(!strcmp(asfd->rbuf->buf, "timer conditions met"))
      64             :         {
      65             :                 // Only happens on 'timer check only'.
      66           0 :                 logp("Timer conditions on the server were met\n");
      67           0 :                 tchk->ret=CLIENT_OK;
      68           0 :                 return ASL_END_OK;
      69             :         }
      70             : 
      71           0 :         if(!strncmp_w(asfd->rbuf->buf, "ok"))
      72             :                 complen=3;
      73           0 :         else if(!strncmp_w(asfd->rbuf->buf, "resume"))
      74             :         {
      75           0 :                 complen=7;
      76           0 :                 tchk->resume=1;
      77           0 :                 logp("server wants to resume previous backup.\n");
      78             :         }
      79             :         else
      80             :         {
      81           0 :                 iobuf_log_unexpected(asfd->rbuf, __func__);
      82           0 :                 return ASL_END_ERROR;
      83             :         }
      84             : 
      85             :         // The server now tells us the compression level in the OK response.
      86           0 :         if(strlen(asfd->rbuf->buf)>3)
      87           0 :                 set_int(confs[OPT_COMPRESSION], atoi(asfd->rbuf->buf+complen));
      88           0 :         logp("Compression level: %d\n",
      89             :                 get_int(confs[OPT_COMPRESSION]));
      90             : 
      91           0 :         return ASL_END_OK;
      92             : }
      93             : 
      94           0 : static enum cliret maybe_check_timer(struct asfd *asfd,
      95             :         const char *phase1str, struct conf **confs, int *resume)
      96             : {
      97             :         struct tchk tchk;
      98           0 :         memset(&tchk, 0, sizeof(tchk));
      99           0 :         if(asfd->write_str(asfd, CMD_GEN, phase1str))
     100             :                 return CLIENT_ERROR;
     101             : 
     102           0 :         if(asfd->simple_loop(asfd, confs, &tchk,
     103             :                 __func__, maybe_check_timer_func)) return CLIENT_ERROR;
     104           0 :         *resume=tchk.resume;
     105           0 :         return tchk.ret;
     106             : }
     107             : 
     108           0 : static enum cliret backup_wrapper(struct asfd *asfd,
     109             :         enum action action, const char *phase1str,
     110             :         const char *incexc, struct conf **confs)
     111             : {
     112           0 :         int resume=0;
     113           0 :         enum cliret ret=CLIENT_OK;
     114           0 :         const char *b_script_pre=get_string(confs[OPT_B_SCRIPT_PRE]);
     115           0 :         const char *b_script_post=get_string(confs[OPT_B_SCRIPT_POST]);
     116             : 
     117             :         // Set bulk packets quality of service flags on backup.
     118           0 :         if(incexc)
     119             :         {
     120           0 :                 logp("Server is overriding the configuration\n");
     121           0 :                 logp("with the following settings:\n");
     122           0 :                 if(log_incexcs_buf(incexc)) goto error;
     123             :         }
     124           0 :         if(!get_strlist(confs[OPT_STARTDIR]))
     125             :         {
     126           0 :                 logp("Found no include paths!\n");
     127           0 :                 goto error;
     128             :         }
     129             : 
     130           0 :         switch(maybe_check_timer(asfd, phase1str, confs, &resume))
     131             :         {
     132             :                 case CLIENT_OK:
     133           0 :                         if(action==ACTION_TIMER_CHECK) goto end;
     134             :                         break;
     135             :                 case CLIENT_SERVER_TIMER_NOT_MET:
     136             :                         goto timer_not_met;
     137             :                 default:
     138             :                         goto error;
     139             :         }
     140             : 
     141           0 :         if(b_script_pre)
     142             :         {
     143           0 :                 int a=0;
     144             :                 const char *args[12];
     145           0 :                 args[a++]=b_script_pre;
     146           0 :                 if(get_int(confs[OPT_B_SCRIPT_RESERVED_ARGS]))
     147             :                 {
     148           0 :                         args[a++]="pre";
     149           0 :                         args[a++]="reserved2";
     150           0 :                         args[a++]="reserved3";
     151           0 :                         args[a++]="reserved4";
     152           0 :                         args[a++]="reserved5";
     153             :                 }
     154           0 :                 args[a++]=NULL;
     155           0 :                 if(run_script(asfd,
     156             :                         args, get_strlist(confs[OPT_B_SCRIPT_PRE_ARG]),
     157             :                         confs, 1, 1, 1))
     158           0 :                                  ret=CLIENT_ERROR;
     159             : 
     160           0 :                 if(get_int(confs[OPT_GLOB_AFTER_SCRIPT_PRE]))
     161             :                 {
     162           0 :                         if(reeval_glob(confs))
     163           0 :                                 ret=CLIENT_ERROR;
     164             :                 }
     165             :         }
     166             : 
     167           0 :         if(ret==CLIENT_OK && do_backup_client(asfd,
     168           0 :                 confs, action, resume)) ret=CLIENT_ERROR;
     169             : 
     170           0 :         if((ret==CLIENT_OK || get_int(confs[OPT_B_SCRIPT_POST_RUN_ON_FAIL]))
     171           0 :           && b_script_post)
     172             :         {
     173           0 :                 int a=0;
     174             :                 const char *args[12];
     175           0 :                 args[a++]=b_script_post;
     176           0 :                 if(get_int(confs[OPT_B_SCRIPT_RESERVED_ARGS]))
     177             :                 {
     178           0 :                         args[a++]="post";
     179             :                         // Tell post script whether the restore failed.
     180           0 :                         args[a++]=ret?"1":"0";
     181           0 :                         args[a++]="reserved3";
     182           0 :                         args[a++]="reserved4";
     183           0 :                         args[a++]="reserved5";
     184             :                 }
     185           0 :                 args[a++]=NULL;
     186             :                 // At this point, the server may have closed the connection,
     187             :                 // so cannot log remotely.
     188           0 :                 if(run_script(asfd,
     189             :                         args, get_strlist(confs[OPT_B_SCRIPT_POST_ARG]),
     190             :                         confs, 1, 1, /*log_remote*/ 0))
     191           0 :                                 ret=CLIENT_ERROR;
     192             :         }
     193             : 
     194           0 :         if(ret==CLIENT_OK) logp("backup finished ok\n");
     195             : 
     196             : end:
     197             :         // The include_logic/exclude_logic cache may have been populated
     198             :         // during backup so we clean it here
     199           0 :         free_logic_cache();
     200           0 :         return ret;
     201             : error:
     202           0 :         logp("error in backup\n");
     203           0 :         return CLIENT_ERROR;
     204             : timer_not_met:
     205             :         return CLIENT_SERVER_TIMER_NOT_MET;
     206             : }
     207             : 
     208             : static int s_server_session_id_context=1;
     209             : 
     210           0 : static int ssl_setup(int *rfd, SSL **ssl, SSL_CTX **ctx,
     211             :         enum action action, struct conf **confs)
     212             : {
     213           0 :         int port=-1;
     214           0 :         char portstr[8]="";
     215           0 :         BIO *sbio=NULL;
     216           0 :         ssl_load_globals();
     217           0 :         if(!(*ctx=ssl_initialise_ctx(confs)))
     218             :         {
     219           0 :                 logp("error initialising ssl ctx\n");
     220           0 :                 return -1;
     221             :         }
     222             : 
     223           0 :         SSL_CTX_set_session_id_context(*ctx,
     224             :                 (const uint8_t *)&s_server_session_id_context,
     225             :                 sizeof(s_server_session_id_context));
     226             : 
     227           0 :         switch(action)
     228             :         {
     229             :                 case ACTION_BACKUP:
     230             :                 case ACTION_BACKUP_TIMED:
     231             :                 case ACTION_TIMER_CHECK:
     232           0 :                         port=get_int(confs[OPT_PORT_BACKUP]);
     233           0 :                         break;
     234             :                 case ACTION_RESTORE:
     235           0 :                         port=get_int(confs[OPT_PORT_RESTORE]);
     236           0 :                         break;
     237             :                 case ACTION_VERIFY:
     238           0 :                         port=get_int(confs[OPT_PORT_VERIFY]);
     239           0 :                         break;
     240             :                 case ACTION_LIST:
     241             :                 case ACTION_LIST_LONG:
     242             :                 case ACTION_DIFF:
     243             :                 case ACTION_DIFF_LONG:
     244           0 :                         port=get_int(confs[OPT_PORT_LIST]);
     245           0 :                         break;
     246             :                 case ACTION_DELETE:
     247           0 :                         port=get_int(confs[OPT_PORT_DELETE]);
     248           0 :                         break;
     249             :                 case ACTION_MONITOR:
     250             :                 {
     251             :                         struct strlist *s;
     252           0 :                         if(!(s=get_strlist(confs[OPT_STATUS_PORT])))
     253             :                         {
     254           0 :                                 logp("%s not set\n",
     255           0 :                                         confs[OPT_STATUS_PORT]->field);
     256           0 :                                 return -1;
     257             :                         }
     258           0 :                         port=atoi(s->path);
     259           0 :                         break;
     260             :                 }
     261             :                 case ACTION_CHAMP_CHOOSER:
     262             :                 case ACTION_ESTIMATE:
     263             :                 case ACTION_STATUS:
     264             :                 case ACTION_STATUS_SNAPSHOT:
     265           0 :                         logp("Unexpected action in %s: %d\n",
     266             :                                 __func__, action);
     267           0 :                         return -1;
     268             :         }
     269             : 
     270           0 :         snprintf(portstr, sizeof(portstr), "%d", port);
     271           0 :         if((*rfd=init_client_socket(get_string(confs[OPT_SERVER]), portstr))<0)
     272             :                 return -1;
     273             : 
     274           0 :         if(!(*ssl=SSL_new(*ctx))
     275           0 :           || !(sbio=BIO_new_socket(*rfd, BIO_NOCLOSE)))
     276             :         {
     277           0 :                 logp_ssl_err("Problem joining SSL to the socket\n");
     278           0 :                 return -1;
     279             :         }
     280           0 :         SSL_set_bio(*ssl, sbio, sbio);
     281           0 :         if(SSL_connect(*ssl)<=0)
     282             :         {
     283           0 :                 logp_ssl_err("SSL connect error\n");
     284           0 :                 return -1;
     285             :         }
     286             :         return 0;
     287             : }
     288             : 
     289           0 : static enum cliret initial_comms(struct async *as,
     290             :         enum action *action, char **incexc, struct conf **confs)
     291             : {
     292             :         struct asfd *asfd;
     293           0 :         char *server_version=NULL;
     294           0 :         enum cliret ret=CLIENT_OK;
     295           0 :         asfd=as->asfd;
     296             : 
     297           0 :         if(authorise_client(asfd, &server_version,
     298           0 :           get_string(confs[OPT_CNAME]),
     299           0 :           get_string(confs[OPT_PASSWORD]),
     300             :           get_cntr(confs)))
     301             :                 goto error;
     302             : 
     303           0 :         if(server_version)
     304             :         {
     305           0 :                 logp("Server version: %s\n", server_version);
     306             :                 // Servers before 1.3.2 did not tell us their versions.
     307             :                 // 1.3.2 and above can do the automatic CA stuff that
     308             :                 // follows.
     309           0 :                 switch(ca_client_setup(asfd, confs))
     310             :                 {
     311             :                         case 0:
     312             :                                 break; // All OK.
     313             :                         case 1:
     314             :                                 // Certificate signed successfully.
     315             :                                 // Everything is OK, but we will reconnect now,
     316             :                                 // in order to use the new keys/certificates.
     317             :                                 goto reconnect;
     318             :                         default:
     319           0 :                                 logp("Error with cert signing request\n");
     320           0 :                                 goto error;
     321             :                 }
     322             :         }
     323             : 
     324           0 :         if(ssl_check_cert(asfd->ssl, NULL, confs))
     325             :         {
     326           0 :                 logp("check cert failed\n");
     327           0 :                 goto error;
     328             :         }
     329             : 
     330           0 :         if(extra_comms_client(as, confs, action, incexc))
     331             :         {
     332           0 :                 logp("extra comms failed\n");
     333           0 :                 goto error;
     334             :         }
     335             : 
     336             :         ret=CLIENT_OK; goto end;
     337             : error:
     338             :         ret=CLIENT_ERROR; goto end;
     339             : reconnect:
     340             :         ret=CLIENT_RECONNECT; goto end;
     341             : end:
     342           0 :         free_w(&server_version);
     343           0 :         return ret;
     344             : }
     345             : 
     346           0 : static enum cliret restore_wrapper(struct asfd *asfd, enum action action,
     347             :         int vss_restore, struct conf **confs)
     348             : {
     349           0 :         enum cliret ret=CLIENT_OK;
     350           0 :         const char *r_script_pre=get_string(confs[OPT_R_SCRIPT_PRE]);
     351           0 :         const char *r_script_post=get_string(confs[OPT_R_SCRIPT_POST]);
     352             : 
     353           0 :         if(r_script_pre)
     354             :         {
     355           0 :                 int a=0;
     356             :                 const char *args[12];
     357           0 :                 args[a++]=r_script_pre;
     358           0 :                 if(get_int(confs[OPT_R_SCRIPT_RESERVED_ARGS]))
     359             :                 {
     360           0 :                         args[a++]="pre";
     361           0 :                         args[a++]="reserved2";
     362           0 :                         args[a++]="reserved3";
     363           0 :                         args[a++]="reserved4";
     364           0 :                         args[a++]="reserved5";
     365             :                 }
     366           0 :                 args[a++]=NULL;
     367           0 :                 if(run_script(asfd,
     368             :                         args, get_strlist(confs[OPT_R_SCRIPT_PRE_ARG]),
     369             :                         confs, 1, 1, 1))
     370           0 :                                 ret=CLIENT_ERROR;
     371             :         }
     372           0 :         if(ret==CLIENT_OK)
     373             :         {
     374           0 :                 if(do_restore_client(asfd, confs,
     375           0 :                         action, vss_restore)) ret=CLIENT_ERROR;
     376             :         }
     377           0 :         if((ret==CLIENT_OK || get_int(confs[OPT_R_SCRIPT_POST_RUN_ON_FAIL]))
     378           0 :           && r_script_post)
     379             :         {
     380           0 :                 int a=0;
     381             :                 const char *args[12];
     382           0 :                 args[a++]=r_script_post;
     383           0 :                 if(get_int(confs[OPT_R_SCRIPT_RESERVED_ARGS]))
     384             :                 {
     385           0 :                         args[a++]="post";
     386             :                         // Tell post script whether the restore failed.
     387           0 :                         args[a++]=ret?"1":"0";
     388           0 :                         args[a++]="reserved3";
     389           0 :                         args[a++]="reserved4";
     390           0 :                         args[a++]="reserved5";
     391             :                 }
     392           0 :                 args[a++]=NULL;
     393           0 :                 if(run_script(asfd,
     394             :                         args, get_strlist(confs[OPT_R_SCRIPT_POST_ARG]),
     395             :                         confs, 1, 1, /*log_remote*/ 0))
     396           0 :                                 ret=CLIENT_ERROR;
     397             :         }
     398             : 
     399             :         // Return non-zero if there were warnings,
     400             :         // so that the test script can easily check.
     401           0 :         if(ret==CLIENT_OK && get_cntr(confs)->ent[CMD_WARNING]->count)
     402           0 :                 ret=CLIENT_RESTORE_WARNINGS;
     403             : 
     404           0 :         return ret;
     405             : }
     406             : 
     407           0 : static enum cliret do_client(struct conf **confs,
     408             :         enum action action, int vss_restore)
     409             : {
     410           0 :         enum cliret ret=CLIENT_OK;
     411           0 :         int rfd=-1;
     412           0 :         SSL *ssl=NULL;
     413           0 :         SSL_CTX *ctx=NULL;
     414           0 :         struct cntr *cntr=NULL;
     415           0 :         char *incexc=NULL;
     416           0 :         enum action act=action;
     417           0 :         struct async *as=NULL;
     418           0 :         struct asfd *asfd=NULL;
     419             : 
     420             : //      as->settimers(0, 100);
     421             : 
     422             : //      logp("begin client\n");
     423             : //      logp("action %d\n", action);
     424             : 
     425             :         // Status monitor forks a child process instead of connecting to
     426             :         // the server directly.
     427           0 :         if(action==ACTION_STATUS
     428           0 :           || action==ACTION_STATUS_SNAPSHOT)
     429             :         {
     430             : #ifdef HAVE_WIN32
     431             :                 logp("Status mode not implemented on Windows.\n");
     432             :                 goto error;
     433             : #endif
     434           0 :                 if(status_client_ncurses_init(act)
     435           0 :                   || status_client_ncurses(confs)) ret=CLIENT_ERROR;
     436             :                 goto end;
     437             :         }
     438             : 
     439           0 :         if(!(cntr=cntr_alloc())
     440           0 :           || cntr_init(cntr, get_string(confs[OPT_CNAME]), getpid()))
     441             :                 goto error;
     442           0 :         set_cntr(confs[OPT_CNTR], cntr);
     443             : 
     444           0 :         if(act!=ACTION_ESTIMATE)
     445             :         {
     446           0 :                 if(ssl_setup(&rfd, &ssl, &ctx, action, confs))
     447             :                         goto could_not_connect;
     448             : 
     449           0 :                 if(!(as=async_alloc())
     450           0 :                   || as->init(as, act==ACTION_ESTIMATE)
     451           0 :                   || !(asfd=setup_asfd_ssl(as, "main socket", &rfd, ssl)))
     452             :                         goto end;
     453           0 :                 asfd->set_timeout(asfd, get_int(confs[OPT_NETWORK_TIMEOUT]));
     454           0 :                 asfd->ratelimit=get_float(confs[OPT_RATELIMIT]);
     455             : 
     456             :                 // Set quality of service bits on backup packets.
     457           0 :                 if(act==ACTION_BACKUP
     458           0 :                                 || act==ACTION_BACKUP_TIMED
     459           0 :                                 || act==ACTION_TIMER_CHECK)
     460           0 :                         as->asfd->set_bulk_packets(as->asfd);
     461             : 
     462           0 :                 if((ret=initial_comms(as, &act, &incexc, confs)))
     463             :                         goto end;
     464             :         }
     465             : 
     466           0 :         rfd=-1;
     467           0 :         switch(act)
     468             :         {
     469             :                 case ACTION_BACKUP:
     470           0 :                         ret=backup_wrapper(asfd, act, "backupphase1",
     471             :                           incexc, confs);
     472           0 :                         break;
     473             :                 case ACTION_BACKUP_TIMED:
     474           0 :                         ret=backup_wrapper(asfd, act, "backupphase1timed",
     475             :                           incexc, confs);
     476           0 :                         break;
     477             :                 case ACTION_TIMER_CHECK:
     478           0 :                         ret=backup_wrapper(asfd, act, "backupphase1timedcheck",
     479             :                           incexc, confs);
     480           0 :                         break;
     481             :                 case ACTION_RESTORE:
     482             :                 case ACTION_VERIFY:
     483           0 :                         ret=restore_wrapper(asfd, act, vss_restore, confs);
     484           0 :                         break;
     485             :                 case ACTION_ESTIMATE:
     486           0 :                         if(do_backup_client(asfd, confs, act, 0))
     487             :                                 goto error;
     488             :                         break;
     489             :                 case ACTION_DELETE:
     490           0 :                         if(do_delete_client(asfd, confs))
     491             :                                 goto error;
     492             :                         break;
     493             :                 case ACTION_MONITOR:
     494           0 :                         if(do_monitor_client(asfd))
     495             :                                 goto error;
     496             :                         break;
     497             :                 case ACTION_DIFF:
     498             :                 case ACTION_DIFF_LONG:
     499             : /*
     500             :                         if(!strcmp(get_string(confs[OPT_BACKUP2]), "n"))
     501             :                                 // Do a phase1 scan and diff that.
     502             :                                 ret=backup_wrapper(asfd, act,
     503             :                                         "backupphase1diff", incexc, confs);
     504             :                         else
     505             : */
     506             :                         // Diff two backups that already exist.
     507             :                         // Fall through, the list code is all we need
     508             :                         // for simple diffs on the client side.
     509             :                 case ACTION_LIST:
     510             :                 case ACTION_LIST_LONG:
     511             :                 default:
     512           0 :                         if(do_list_client(asfd, act, confs)) goto error;
     513             :                         break;
     514             :         }
     515             : 
     516           0 :         if(asfd_flush_asio(asfd))
     517           0 :                 ret=CLIENT_ERROR;
     518             : 
     519             :         goto end;
     520             : error:
     521             :         ret=CLIENT_ERROR; goto end;
     522             : could_not_connect:
     523             :         ret=CLIENT_COULD_NOT_CONNECT;
     524             : end:
     525           0 :         close_fd(&rfd);
     526           0 :         async_free(&as);
     527           0 :         asfd_free(&asfd);
     528           0 :         if(ctx) ssl_destroy_ctx(ctx);
     529           0 :         free_w(&incexc);
     530           0 :         set_cntr(confs[OPT_CNTR], NULL);
     531           0 :         cntr_free(&cntr);
     532             : 
     533             :         //logp("end client\n");
     534           0 :         return ret;
     535             : }
     536             : 
     537           0 : int client(struct conf **confs, enum action action, int vss_restore)
     538             : {
     539           0 :         enum cliret ret=CLIENT_OK;
     540             : 
     541           0 :         if(!get_int(confs[OPT_ENABLED]))
     542             :         {
     543           0 :                 logp("Client not enabled\n");
     544           0 :                 return ret;
     545             :         }
     546             : 
     547             : #ifdef HAVE_WIN32
     548             :         // prevent sleep when idle
     549             :         SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
     550             : #endif
     551             : 
     552           0 :         switch((ret=do_client(confs, action, vss_restore)))
     553             :         {
     554             :                 case CLIENT_RECONNECT:
     555           0 :                         logp("Re-opening connection to server\n");
     556           0 :                         sleep(5);
     557           0 :                         ret=do_client(confs, action, vss_restore);
     558             :                 default:
     559             :                         break;
     560             :         }
     561             : 
     562             : #ifdef HAVE_WIN32
     563             :         // allow sleep when idle
     564             :         SetThreadExecutionState(ES_CONTINUOUS);
     565             : #endif
     566             : 
     567             :         // See enum cliret for return codes.
     568           0 :         return (int)ret;
     569             : }

Generated by: LCOV version 1.13