Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
86 andreas 1
/*
2
 * Copyright (C) 2022 by Andreas Theofilu <andreas@theosys.at>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
88 andreas 19
#include <chrono>
20
#include <thread>
21
 
86 andreas 22
#include "tsocket.h"
23
#include "terror.h"
24
#include "tconfig.h"
89 andreas 25
#include "texcept.h"
86 andreas 26
 
27
#include <string.h>
28
#include <strings.h>
29
#include <sys/socket.h>
30
#include <sys/types.h>
31
#include <unistd.h>
32
#include <stdlib.h>
33
#include <fcntl.h>
34
#include <netinet/in.h>
35
#include <netdb.h>
36
#include <netinet/tcp.h>
37
#include <openssl/err.h>
38
#include <sys/socket.h>
39
#include <arpa/inet.h>
40
#include <signal.h>
41
#include <poll.h>
42
 
43
using std::string;
44
 
45
int _cert_callback(int preverify_ok, X509_STORE_CTX *ctx);
46
 
92 andreas 47
TSocket::TSocket()
48
{
49
    DECL_TRACER("TSocket::TSocket()")
50
}
51
 
86 andreas 52
TSocket::TSocket(const string& host, int port)
53
{
92 andreas 54
    DECL_TRACER("TSocket::TSocket(const std::string& host, int port)");
86 andreas 55
 
56
    mHost = host;
57
    mPort = port;
58
}
59
 
60
TSocket::~TSocket()
61
{
62
    DECL_TRACER("TSocket::~TSocket()");
63
 
64
    close();
65
}
66
 
67
 
68
bool TSocket::connect(bool encrypt)
69
{
88 andreas 70
    DECL_TRACER("TSocket::connect(bool encrypt)");
86 andreas 71
 
72
    struct addrinfo *ainfo = nullptr;
73
    int sock = -1;
74
    int on = 1;
75
    bool retry = true;
76
 
93 andreas 77
    MSG_INFO("Trying to connect to host " << mHost << " at port " << mPort);
86 andreas 78
 
79
    if ((ainfo = lookup_host(mHost, mPort)) == nullptr)
80
        return false;
81
 
82
    while (ainfo && retry)
83
    {
84
        sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
85
 
86
        if (sock == -1)
87
        {
93 andreas 88
            MSG_ERROR("[" << mHost << "] Error opening socket: " << strerror(errno));
86 andreas 89
            ainfo = ainfo->ai_next;
90
            continue;
91
        }
92
 
93 andreas 93
        MSG_DEBUG("[" << mHost << "] Socket successfully created.");
86 andreas 94
        struct in_addr  *addr;
95
 
96
        if (ainfo->ai_family == AF_INET)
97
        {
98
            struct sockaddr_in *ipv = (struct sockaddr_in *)ainfo->ai_addr;
99
            addr = &(ipv->sin_addr);
100
        }
101
        else
102
        {
103
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ainfo->ai_addr;
104
            addr = (struct in_addr *) &(ipv6->sin6_addr);
105
        }
106
 
107
        char buffer[100];
88 andreas 108
        // FIXME: This is the address where we connected to, but we need the
109
        //        address of the local network interface where the connection
110
        //        was initiated from.
86 andreas 111
        inet_ntop(ainfo->ai_family, addr, buffer, sizeof(buffer));
112
        mMyIP.assign(buffer);
88 andreas 113
        MSG_DEBUG("Client IP: " << mMyIP);
86 andreas 114
 
115
        struct timeval tv;
116
 
117
        // FIXME: Make the timeouts configurable!
118
        tv.tv_sec = 10;
119
        tv.tv_usec = 0;
120
 
121
        if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(int)) == -1)
122
        {
93 andreas 123
            MSG_ERROR("[" << mHost << "] Error setting socket options for address reuse: " << strerror(errno));
86 andreas 124
            ::close(sock);
125
            return false;
126
        }
127
 
128
        if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(struct timeval)) == -1)
129
        {
93 andreas 130
            MSG_ERROR("[" << mHost << "] Error setting socket options for receive: " << strerror(errno));
86 andreas 131
            ::close(sock);
132
            return false;
133
        }
134
 
88 andreas 135
        if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(struct timeval)) == -1)
136
        {
93 andreas 137
            MSG_ERROR("[" << mHost << "] Error setting socket options for send: " << strerror(errno));
88 andreas 138
            ::close(sock);
139
            return false;
140
        }
141
 
86 andreas 142
        if (::connect(sock, ainfo->ai_addr, ainfo->ai_addrlen) == -1)
143
        {
93 andreas 144
            if (errno != EINPROGRESS)
145
            {
146
                MSG_ERROR("[" << mHost << "] Connect error: " << strerror(errno));
147
                ::close(sock);
148
                mConnected = false;
149
                return false;
150
            }
151
            else
152
            {
153
                MSG_INFO("[" << mHost << "] Connection is in progress ...");
154
                mConnected = true;
155
                break;
156
            }
86 andreas 157
        }
158
        else
159
        {
93 andreas 160
            MSG_INFO("[" << mHost << "] Successfully connected.");
87 andreas 161
            mConnected = true;
86 andreas 162
            break;
163
        }
164
    }
165
 
166
    if (ainfo == nullptr)
87 andreas 167
    {
93 andreas 168
        MSG_ERROR("[" << mHost << "] No network interface to connect to target was found!");
87 andreas 169
        mConnected = false;
86 andreas 170
        return false;
87 andreas 171
    }
86 andreas 172
 
173
    if (encrypt)
174
    {
175
        int ret;
176
 
93 andreas 177
        MSG_DEBUG("[" << mHost << "] Initializing SSL connection ...");
86 andreas 178
        mCtx = initCTX();
179
 
180
        if (mCtx == NULL)
181
        {
93 andreas 182
            MSG_ERROR("[" << mHost << "] Error initializing CTX.");
86 andreas 183
            ::close(sock);
87 andreas 184
            mConnected = false;
86 andreas 185
            return false;
186
        }
187
 
188
        mSsl = SSL_new(mCtx);      /* create new SSL connection state */
189
 
190
        if (mSsl == NULL)
191
        {
192
            log_ssl_error();
193
            SSL_CTX_free(mCtx);
194
            ::close(sock);
87 andreas 195
            mConnected = false;
86 andreas 196
            return false;
197
        }
198
 
199
        SSL_set_fd(mSsl, sock);    /* attach the socket descriptor */
200
 
201
        if (TConfig::certCheck())
202
        {
203
            SSL_set_verify(mSsl, SSL_VERIFY_PEER, _cert_callback);
93 andreas 204
            MSG_TRACE("[" << mHost << "] Verify on peer certificate was set.");
86 andreas 205
        }
206
 
207
        while ((ret = SSL_connect(mSsl)) < 0)
208
        {
209
            fd_set fds;
210
            FD_ZERO(&fds);
211
            FD_SET(sock, &fds);
212
 
213
            switch (SSL_get_error(mSsl, ret))
214
            {
215
                case SSL_ERROR_WANT_READ:
216
                    select(sock + 1, &fds, NULL, NULL, NULL);
217
                break;
218
 
219
                case SSL_ERROR_WANT_WRITE:
220
                    select(sock + 1, NULL, &fds, NULL, NULL);
221
                break;
222
 
223
                default:
93 andreas 224
                    MSG_ERROR("[" << mHost << "] Error getting a new SSL handle.");
86 andreas 225
                    SSL_CTX_free(mCtx);
226
                    ::close(sock);
227
                    SSL_free(mSsl);
87 andreas 228
                    mConnected = false;
86 andreas 229
                    return false;
230
            }
231
        }
232
 
233
        if (TConfig::certCheck())
234
        {
235
            if (SSL_get_peer_certificate(mSsl))
236
            {
93 andreas 237
                MSG_DEBUG("[" << mHost << "] Result of peer certificate verification is checked ...");
86 andreas 238
 
93 andreas 239
                if (SSL_get_verify_result(mSsl) != X509_V_OK)
86 andreas 240
                {
93 andreas 241
                    MSG_ERROR("[" << mHost << "] Error verifiying peer.");
86 andreas 242
                    SSL_CTX_free(mCtx);
243
                    ::close(sock);
244
                    SSL_free(mSsl);
87 andreas 245
                    mConnected = false;
86 andreas 246
                    return false;
247
                }
248
 
93 andreas 249
                MSG_TRACE("[" << mHost << "] Certificate was valid.");
86 andreas 250
            }
251
            else
93 andreas 252
                MSG_WARNING("[" << mHost << "] Peer offered no or invalid certificate!");
86 andreas 253
        }
254
 
255
        mEncrypted = true;
256
    }
257
 
258
    mSockfd = sock;
259
    return true;
260
}
261
 
262
bool TSocket::connect(const string& host, int port, bool encrypt)
263
{
88 andreas 264
    DECL_TRACER("TSocket::connect(const string&, int port, bool encrypt)");
86 andreas 265
 
266
    if (host.empty() || port < 1)
267
    {
88 andreas 268
        MSG_ERROR("CONNECT: Invalid credentials! (HOST: " << (host.empty() ? "<none>" : host) << ", PORT: " << port << ")");
86 andreas 269
        return false;
270
    }
271
 
272
    if (mConnected && host == mHost && port == mPort)
87 andreas 273
    {
93 andreas 274
        MSG_INFO("[" << mHost << "] Already connected.");
86 andreas 275
        return true;
87 andreas 276
    }
86 andreas 277
 
278
    if (mConnected)
89 andreas 279
        close();
86 andreas 280
 
281
    mHost = host;
282
    mPort = port;
283
    return connect(encrypt);
284
}
285
 
93 andreas 286
bool TSocket::isSockValid()
287
{
288
    DECL_TRACER("TSocket::isSockValid()");
289
 
290
    if (!mConnected || mSockfd <= 0)
291
        return false;
292
 
293
    int optval;
294
    socklen_t optlen = sizeof(optval);
295
 
296
    int res = getsockopt(mSockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen);
297
 
298
    if (optval == 0 && res == 0)
299
        return true;
300
 
301
    if (res != 0)
302
    {
303
        MSG_ERROR("[" << mHost << "] Network error: " << strerror(errno));
304
    }
305
 
306
    return false;
307
}
308
 
112 andreas 309
size_t TSocket::receive(char* buffer, size_t size, bool doPoll)
86 andreas 310
{
311
    DECL_TRACER("TSocket::receive(char* buffer, size_t size)");
312
 
313
    int proto = 0;
88 andreas 314
    bool retry = false;
315
    std::chrono::system_clock::time_point end = std::chrono::system_clock::now() + std::chrono::seconds(10);
86 andreas 316
 
92 andreas 317
    if (!mConnected || buffer == nullptr || (mEncrypted && mSsl == nullptr))
94 andreas 318
        return npos;
86 andreas 319
 
320
    if (!mEncrypted)
321
        proto = 0;
322
    else
323
        proto = 1;
324
 
87 andreas 325
    struct pollfd pfd;
88 andreas 326
    int nfds = 1;       // Only one entry in structure
87 andreas 327
 
328
    pfd.fd = mSockfd;
329
    pfd.events = POLLIN;
330
 
88 andreas 331
    int s = 0;
332
 
333
    do
86 andreas 334
    {
112 andreas 335
        if (doPoll)
336
            s = poll(&pfd, nfds, 10000);    // FIXME: Make the timeout configurable.
337
        else
338
            s = 1;
88 andreas 339
 
340
        if (s < 0)
87 andreas 341
        {
93 andreas 342
            close();
343
            XCEPTNETWORK("[" + mHost + "] Poll error on read: " + strerror(errno));
88 andreas 344
        }
345
 
346
        if (s == 0)
347
        {
348
            if (std::chrono::system_clock::now() < end)
97 andreas 349
            {
88 andreas 350
                retry = true;
97 andreas 351
                MSG_DEBUG("looping ...");
352
            }
88 andreas 353
            else
93 andreas 354
            {
355
                errno = ETIMEDOUT;
88 andreas 356
                retry = false;
93 andreas 357
            }
88 andreas 358
        }
359
        else
360
        {
87 andreas 361
            switch (proto)
362
            {
363
                case 0: return read(mSockfd, buffer, size);
364
                case 1: return SSL_read(mSsl, buffer, size);
365
            }
366
        }
86 andreas 367
    }
88 andreas 368
    while (retry);
369
 
94 andreas 370
    return npos;
88 andreas 371
}
372
 
373
size_t TSocket::readAbsolut(char *buffer, size_t size)
374
{
375
    DECL_TRACER("TSocket::readAbsolut(char *buffer, size_t size)");
376
 
92 andreas 377
    if (!mConnected || buffer == nullptr || !size)
94 andreas 378
        return npos;
88 andreas 379
 
380
    size_t rest = size;
381
    char *buf = new char[size + 1];
382
    char *p = buffer;
89 andreas 383
    std::chrono::system_clock::time_point end = std::chrono::system_clock::now() + std::chrono::seconds(10);
88 andreas 384
 
385
    while (rest && mConnected)
87 andreas 386
    {
88 andreas 387
        size_t rec = receive(buf, rest);
388
 
94 andreas 389
        if (rec != npos && rec > 0)
88 andreas 390
        {
391
            rest -= rec;
392
            memmove(p, buf, rec);
393
            p += rec;
89 andreas 394
            end = std::chrono::system_clock::now() + std::chrono::seconds(10);
88 andreas 395
        }
89 andreas 396
        else if (std::chrono::system_clock::now() >= end)
397
        {
93 andreas 398
            string message = "[" + mHost + "] Read: ";
88 andreas 399
 
89 andreas 400
            if (!mEncrypted)
93 andreas 401
            {
402
                if (errno)
403
                    message.append(strerror(errno));
404
                else
405
                    message.append("Timeout on reading");
406
            }
89 andreas 407
            else
408
            {
409
                log_ssl_error();
410
                message.append("Read error!");
411
            }
412
 
413
            close();
93 andreas 414
            delete[] buf;
415
            buf = nullptr;
416
#ifdef __ANDROID__
417
            MSG_ERROR(message);
418
            return -1;
419
#else
89 andreas 420
            XCEPTNETWORK(message);
93 andreas 421
#endif
89 andreas 422
        }
423
 
88 andreas 424
        if (rest)
425
            std::this_thread::sleep_for(std::chrono::microseconds(1000));
87 andreas 426
    }
86 andreas 427
 
93 andreas 428
    if (buf)
429
        delete[] buf;
430
 
89 andreas 431
    return (size - rest);
86 andreas 432
}
433
 
434
size_t TSocket::send(char* buffer, size_t size)
435
{
436
    DECL_TRACER("TSocket::send(char* buffer, size_t size)");
437
 
438
    int proto = 0;
93 andreas 439
    bool retry = false;
440
    std::chrono::system_clock::time_point end = std::chrono::system_clock::now() + std::chrono::seconds(10);
86 andreas 441
 
88 andreas 442
    if (!mConnected || buffer == nullptr || (mEncrypted && mSsl == nullptr))
94 andreas 443
        return npos;
86 andreas 444
 
445
    if (!mEncrypted)
446
        proto = 0;
447
    else
448
        proto = 1;
449
 
93 andreas 450
    struct pollfd pfd;
451
    int nfds = 1;       // Only one entry in structure
452
 
453
    pfd.fd = mSockfd;
454
    pfd.events = POLLOUT;
455
 
456
    int s = 0;
457
 
458
    do
86 andreas 459
    {
93 andreas 460
        s = poll(&pfd, nfds, 10000);    // FIXME: Make the timeout configurable.
461
 
462
        if (s < 0)
463
        {
464
            close();
465
            XCEPTNETWORK("[" + mHost + "] Poll error on write: " + strerror(errno));
466
        }
467
 
468
        if (s == 0)
469
        {
470
            if (std::chrono::system_clock::now() < end)
471
                retry = true;
472
            else
473
            {
474
                retry = false;
475
                errno = ETIMEDOUT;
476
            }
477
        }
478
        else
479
        {
480
            switch (proto)
481
            {
482
                case 0: return write(mSockfd, buffer, size);
483
                case 1: return SSL_write(mSsl, buffer, size);
484
            }
485
        }
86 andreas 486
    }
93 andreas 487
    while (retry);
86 andreas 488
 
94 andreas 489
    return npos;
86 andreas 490
}
491
 
492
bool TSocket::close()
493
{
494
    DECL_TRACER("TSocket::close()");
495
 
89 andreas 496
    bool status = true;
497
 
86 andreas 498
    if (!mConnected)
499
        return true;
500
 
87 andreas 501
    if (mEncrypted && mSsl)
86 andreas 502
    {
87 andreas 503
        SSL_free(mSsl);
504
        mSsl = nullptr;
86 andreas 505
    }
506
 
93 andreas 507
    if (!isSockValid())
508
    {
509
        mConnected = false;
510
 
511
        if (mEncrypted && mCtx)
512
        {
513
            SSL_CTX_free(mCtx);
514
            mCtx = nullptr;
515
        }
516
 
517
        mSockfd = -1;
518
        mEncrypted = false;
519
        return false;
520
    }
521
 
87 andreas 522
    if (shutdown(mSockfd, SHUT_RDWR) != 0)
523
    {
93 andreas 524
        MSG_ERROR("[" << mHost << "] Error shutting down connection: " << strerror(errno));
89 andreas 525
        status = false;
87 andreas 526
    }
527
 
528
    mConnected = false;
529
 
86 andreas 530
    if (::close(mSockfd) != 0)
531
    {
93 andreas 532
        MSG_ERROR("[" << mHost << "] Error closing a socket: " << strerror(errno));
89 andreas 533
        status = false;
86 andreas 534
    }
535
 
88 andreas 536
    mSockfd = -1;
537
 
87 andreas 538
    if (mEncrypted && mCtx)
539
    {
540
        SSL_CTX_free(mCtx);
541
        mCtx = nullptr;
542
    }
543
 
86 andreas 544
    mEncrypted = false;
89 andreas 545
    return status;
86 andreas 546
}
547
 
548
struct addrinfo *TSocket::lookup_host (const string& host, int port)
549
{
88 andreas 550
    DECL_TRACER("TSocket::lookup_host (const string& host, int port)");
86 andreas 551
 
552
    struct addrinfo *res;
553
    struct addrinfo hints;
554
    char sport[16];
555
 
556
    memset (&hints, 0, sizeof (hints));
557
    hints.ai_family = AF_INET;
558
    hints.ai_protocol = IPPROTO_TCP;
559
    hints.ai_socktype = SOCK_STREAM;
560
    hints.ai_flags = AI_CANONNAME;
561
    snprintf(sport, sizeof(sport), "%d", port);
562
    int ret = 0;
563
 
564
    if ((ret = getaddrinfo (host.c_str(), sport, &hints, &res)) != 0)
565
    {
93 andreas 566
        MSG_ERROR("[" << mHost << "] Getaddrinfo: " << gai_strerror(ret));
86 andreas 567
        return nullptr;
568
    }
569
 
570
    return res;
571
}
572
 
573
void TSocket::initSSL()
574
{
88 andreas 575
    DECL_TRACER("TSocket::initSSL()");
86 andreas 576
 
577
    if (mSSLInitialized)
578
        return;
579
 
580
    SSL_library_init();
581
    ERR_load_BIO_strings();
582
    ERR_load_crypto_strings();
583
    SSL_load_error_strings();
584
    mSSLInitialized = true;
585
}
586
 
587
SSL_CTX *TSocket::initCTX()
588
{
88 andreas 589
    DECL_TRACER("TSocket::initCTX()");
86 andreas 590
 
591
    SSL_CTX *ctx;
88 andreas 592
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
86 andreas 593
    const SSL_METHOD *method = TLS_client_method();
88 andreas 594
#else
86 andreas 595
    const SSL_METHOD *method = TLSv1_2_client_method();
88 andreas 596
#endif
86 andreas 597
    ctx = SSL_CTX_new(method);   /* Create new context */
598
 
599
    if ( ctx == NULL )
600
    {
601
        log_ssl_error();
602
        return NULL;
603
    }
604
 
605
    char *cert_check = getenv("CERT_CHECK");
606
 
607
    if (cert_check && strcmp(cert_check, "ON") == 0)
608
    {
609
        char *cert_path = getenv("CERT_PATH");
610
        char *cert_chain = getenv("CERT_CHAIN");
611
        char *cert_file = getenv("CERT_FILE");
612
        char *cert_type = getenv("CERT_TYPE");
613
 
614
        if (cert_path == NULL)
615
        {
616
            MSG_WARNING("Missing environment variable \"CERT_PATH\" defining the path to the cerificates.");
617
            return ctx;
618
        }
619
 
620
        if (cert_chain == NULL)
621
        {
622
            MSG_WARNING("Certificate check is enabled but no certificate chain file is set! No certificate verification was made.");
623
            return ctx;
624
        }
625
 
626
        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, _cert_callback);
627
 
628
        if (cert_type == NULL)
629
            cert_type = (char *)"PEM";
630
 
631
        if (SSL_CTX_load_verify_locations(ctx, cert_chain, cert_path) != 1)
632
        {
633
            MSG_ERROR("Error with certificate " << cert_path << "/" << cert_chain);
634
            log_ssl_error();
635
            SSL_CTX_free(ctx);
636
            return NULL;
637
        }
638
 
639
        int type = SSL_FILETYPE_PEM;
640
 
641
        if (strcmp(cert_type, "ASN1") == 0)
642
            type = SSL_FILETYPE_ASN1;
643
 
644
        if (cert_file && SSL_CTX_use_certificate_file(ctx, cert_file, type) != 1)
645
        {
646
            MSG_ERROR("Error with certificate " << cert_file);
647
            log_ssl_error();
648
            SSL_CTX_free(ctx);
649
            return NULL;
650
        }
651
    }
652
 
653
    return ctx;
654
}
655
 
656
void TSocket::log_ssl_error()
657
{
88 andreas 658
    DECL_TRACER("TSocket::log_ssl_error()");
86 andreas 659
    unsigned long int err;
660
    char errstr[512];
661
 
662
    while ((err = ERR_get_error()) != 0)
663
    {
664
        ERR_error_string_n(err, &errstr[0], sizeof(errstr));
665
        MSG_ERROR(errstr);
666
    }
667
}
668
 
669
/*
670
 * Callback fuction for SSL connections.
671
 */
672
int _cert_callback(int preverify_ok, X509_STORE_CTX *ctx)
673
{
674
    DECL_TRACER("_cert_callback(int preverify_ok, X509_STORE_CTX *ctx)");
675
 
676
    char    buf[256];
677
    X509   *err_cert;
678
    int     err, depth;
679
 
680
    err_cert = X509_STORE_CTX_get_current_cert(ctx);
681
    err = X509_STORE_CTX_get_error(ctx);
682
    depth = X509_STORE_CTX_get_error_depth(ctx);
683
 
684
    /*
685
     * Retrieve the pointer to the SSL of the connection currently treated
686
     * and the application specific data stored into the SSL object.
687
     */
88 andreas 688
    X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
86 andreas 689
    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
690
 
691
    if (!preverify_ok)
692
    {
693
        MSG_WARNING("verify error:num=" << err << ":" << X509_verify_cert_error_string(err) << ":depth=" << depth << ":" << buf);
694
    }
695
 
696
    /*
697
     * At this point, err contains the last verification error. We can use
698
     * it for something special
699
     */
700
    if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT))
701
    {
702
        X509_NAME_oneline(X509_get_issuer_name(err_cert), buf, 256);
703
        MSG_WARNING("issuer= " << buf);
704
    }
705
 
706
    return preverify_ok;
707
}