Subversion Repositories tpanel

Rev

Rev 446 | Rev 465 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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