Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
21 andreas 1
/*
2
 * Copyright (C) 2021 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 <vector>
20
 
21
#include <string.h>
22
#include <strings.h>
23
#include <sys/socket.h>
24
#include <sys/types.h>
25
#include <unistd.h>
26
#include <stdlib.h>
27
#include <fcntl.h>
28
#include <netinet/in.h>
29
#include <netdb.h>
30
#include <netinet/tcp.h>
31
#include <openssl/err.h>
32
#include <sys/socket.h>
33
#include <arpa/inet.h>
34
#include <signal.h>
35
#include <poll.h>
36
 
37
#include "thttpclient.h"
38
#include "base64.h"
39
#include "terror.h"
40
#include "tresources.h"
41
#include "tconfig.h"
42
 
43
#define HTTP_DIRECTION_RECEIVE  1
44
#define HTTP_DIRECTION_SEND     2
45
 
46
using std::string;
47
using std::vector;
48
using std::min;
49
 
50
int _verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
51
 
52
THTTPClient::THTTPClient()
53
{
54
    DECL_TRACER("THTTPClient::THTTPClient()");
55
    mBody.body = nullptr;
56
    mBody.len = 0;
57
    mRequest.code = 0;
58
    mRequest.direction = 0;
59
    mRequest.method = UNSUPPORTED;
60
    mRequest.path = nullptr;
61
    mRequest.status = nullptr;
62
    mRequest.version = nullptr;
63
}
64
 
65
THTTPClient::~THTTPClient()
66
{
67
    DECL_TRACER("THTTPClient::~THTTPClient()");
68
    mBody.clear();
69
    mRequest.clear();
70
}
71
 
72
#define MAX_BUFFER  65535
73
#define MAX_BLOCK   32767
74
 
75
char *THTTPClient::tcall(size_t *size, const string& URL, const string& user, const string& pw)
76
{
77
    DECL_TRACER("THTTPClient::tcall(size_t size, const string& URL, const string& user, const string& pw)");
78
 
79
    char *buffer = nullptr;
80
    size_t bufsize = 0;
81
    mUser = user;
82
    mPassword = pw;
83
 
84
    try
85
    {
86
        buffer = new char[MAX_BUFFER];
87
        bufsize = MAX_BUFFER;
88
    }
89
    catch (std::exception& e)
90
    {
91
        MSG_ERROR(e.what());
92
        return nullptr;
93
    }
94
 
95
    string request = makeRequest(URL);
96
 
97
    if (TError::isError())
98
    {
99
        delete[] buffer;
100
        return nullptr;
101
    }
102
 
103
    int fd;
104
 
105
    if ((fd = socket_connect()) == -1)
106
    {
107
        delete[] buffer;
108
        return nullptr;
109
    }
110
 
111
    int ret = 0;
112
 
113
    if ((ret = sockWrite(fd, (char *)request.c_str(), request.length())) < 0)
114
    {
115
        if (errno)
116
        {
117
            MSG_ERROR("Write error: " << strerror(errno));
118
        }
119
        else if (mURL.scheme.compare("https") == 0)
120
        {
121
            int err = SSL_get_error(mSsl, ret);
122
 
123
            switch (err)
124
            {
125
                case SSL_ERROR_ZERO_RETURN:     MSG_ERROR("The TLS/SSL peer has closed the connection for writing by sending the close_notify alert."); break;
126
                case SSL_ERROR_WANT_READ:
127
                case SSL_ERROR_WANT_WRITE:      MSG_ERROR("The operation did not complete and can be retried later."); break;
128
                case SSL_ERROR_WANT_CONNECT:
129
                case SSL_ERROR_WANT_ACCEPT:     MSG_ERROR("The operation did not complete; the same TLS/SSL I/O function should be called again later."); break;
130
                case SSL_ERROR_WANT_X509_LOOKUP:MSG_ERROR("The operation did not complete because an application callback set by SSL_CTX_set_client_cert_cb() has asked to be called again."); break;
131
                case SSL_ERROR_WANT_ASYNC:      MSG_ERROR("The operation did not complete because an asynchronous engine is still processing data."); break;
132
                case SSL_ERROR_WANT_ASYNC_JOB:  MSG_ERROR("The asynchronous job could not be started because there were no async jobs available in the pool."); break;
133
                case SSL_ERROR_WANT_CLIENT_HELLO_CB: MSG_ERROR("The operation did not complete because an application callback set by SSL_CTX_set_client_hello_cb() has asked to be called again."); break;
134
                case SSL_ERROR_SYSCALL:         MSG_ERROR("Some non-recoverable, fatal I/O error occurred."); break;
135
                case SSL_ERROR_SSL:             MSG_ERROR("A non-recoverable, fatal error in the SSL library occurred, usually a protocol error."); break;
136
 
137
                default:
138
                    MSG_ERROR("Unknown error " << err << " occured!");
139
            }
140
        }
141
        else
142
        {
143
            MSG_ERROR("Write error!");
144
        }
145
 
146
        shutdown(fd, SHUT_RDWR);
147
        close(fd);
148
        delete[] buffer;
149
 
150
        if (mURL.scheme.compare("https") == 0)
151
        {
152
            SSL_CTX_free(mCtx);
153
            mCtx = nullptr;
154
        }
155
 
156
        return nullptr;
157
    }
158
 
159
    char buf[1024];
160
    memset(buf, 0, sizeof(buf));
161
    size_t pos = 0, length = 0;
162
    struct pollfd pfd;
163
    int rlen, nfds = 1;
164
 
165
    pfd.fd = fd;
166
    pfd.events = POLLIN;
167
 
168
    try
169
    {
170
        if (poll(&pfd, nfds, 10000) != -1)  // FIXME: Make the timeout configurable.
171
        {
172
            while ((rlen = sockRead(fd, buf, sizeof(buf))) > 0)
173
            {
174
                long len = rlen;
175
 
176
                if ((pos + len) >= bufsize)
177
                {
178
                    buffer = (char *)renew(buffer, bufsize, bufsize + MAX_BLOCK);
179
                    bufsize += MAX_BLOCK;
180
                }
181
 
182
                if (len > 0)
183
                {
184
                    memcpy(buffer+pos, buf, len);
185
                    pos += len;
186
                    length += len;
187
                }
188
 
189
                memset(buf, 0, sizeof(buf));
190
            }
191
        }
192
    }
193
    catch (std::exception& e)
194
    {
195
        MSG_ERROR(e.what());
196
 
197
        if (buffer)
198
            delete[] buffer;
199
 
200
        if (mURL.scheme.compare("https") == 0)
201
        {
202
            SSL_free(mSsl);
203
            mSsl = nullptr;
204
        }
205
 
206
        shutdown(fd, SHUT_RDWR);
207
        close(fd);
208
 
209
        if (mURL.scheme.compare("https") == 0)
210
        {
211
            SSL_CTX_free(mCtx);
212
            mCtx = nullptr;
213
        }
214
 
215
        return nullptr;
216
    }
217
 
218
    int myerrno = errno;
219
 
220
    if (mURL.scheme.compare("https") == 0)
221
    {
222
        SSL_free(mSsl);
223
        mSsl = nullptr;
224
    }
225
 
226
    shutdown(fd, SHUT_RDWR);
227
    close(fd);
228
 
229
    if (mURL.scheme.compare("https") == 0)
230
    {
231
        SSL_CTX_free(mCtx);
232
        mCtx = nullptr;
233
    }
234
 
235
    if (pos == 0)
236
    {
237
        if (myerrno)
238
        {
239
            MSG_ERROR("Internal read error: " << strerror(myerrno));
240
        }
241
        else
242
        {
243
            MSG_ERROR("Internal read error: Received no data.");
244
        }
245
 
246
        if (buffer)
247
            delete[] buffer;
248
 
249
        return nullptr;
250
    }
251
 
252
    MSG_DEBUG("Read " << length << " bytes.");
253
 
254
    if (parseHeader(buffer, length) != 0)
255
    {
256
        if (buffer)
257
            delete[] buffer;
258
 
259
        return nullptr;
260
    }
261
 
262
    if (mRequest.code >= 300)
263
    {
264
        if (mRequest.status && *mRequest.status)
265
        {
266
            MSG_ERROR(mRequest.code << ": " << mRequest.status);
267
        }
268
        else
269
        {
270
            MSG_ERROR(mRequest.code << ": UNKNOWN");
271
        }
272
 
273
        return nullptr;
274
    }
275
 
276
    delete[] buffer;
277
    *size = mBody.len;
278
    return mBody.body;
279
}
280
 
281
string THTTPClient::makeURL(const string& scheme, const string& host, int port, const string& path)
282
{
283
    DECL_TRACER("THTTPClient::makeURL(const string& scheme, const string& host, int port, const string& path)");
284
 
285
    string url = scheme + "://" + host;
286
 
287
    if (port > 0)
288
        url += std::to_string(port);
289
 
290
    url += "/" + path;
291
    MSG_DEBUG("URL: " << url);
292
    return url;
293
}
294
 
295
int THTTPClient::input_timeout (int filedes, unsigned int seconds)
296
{
297
    DECL_TRACER("THTTPClient::input_timeout (int filedes, unsigned int seconds)");
298
 
299
    fd_set set;
300
    struct timeval timeout;
301
 
302
 
303
    /* Initialize the file descriptor set. */
304
    FD_ZERO (&set);
305
    FD_SET (filedes, &set);
306
 
307
    /* Initialize the timeout data structure. */
308
    timeout.tv_sec = seconds;
309
    timeout.tv_usec = 0;
310
 
311
    /* select returns 0 if timeout, 1 if input available, -1 if error. */
312
    return TEMP_FAILURE_RETRY (select (FD_SETSIZE, &set, NULL, NULL, &timeout));
313
}
314
 
315
int THTTPClient::socket_connect()
316
{
317
    struct addrinfo *ainfo = nullptr;
318
    int sock = -1;
319
    int on = 1;
320
    bool retry = true;
321
 
322
    MSG_DEBUG("Trying to connect to host " << mURL.host << " at port " << mURL.port);
323
 
324
    if ((ainfo = lookup_host(mURL.host, mURL.port)) == nullptr)
325
        return -1;
326
 
327
    while (ainfo && retry)
328
    {
329
        sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
330
 
331
        if (sock == -1)
332
        {
333
            MSG_ERROR("Error opening socket: " << strerror(errno));
334
            ainfo = ainfo->ai_next;
335
            continue;
336
        }
337
 
338
        struct timeval tv;
339
 
340
        // FIXME: Make the timeouts configurable!
341
        tv.tv_sec = 10;
342
        tv.tv_usec = 0;
343
 
344
        if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(int)) == -1)
345
        {
346
            MSG_ERROR("Error setting socket options for address reuse: " << strerror(errno));
347
            close(sock);
348
            return -1;
349
        }
350
#ifndef __SVR4
351
        // SO_RCVTIMEO is not supported on Solaris10!
352
        if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(struct timeval)) == -1)
353
        {
354
            MSG_ERROR("Error setting socket options for receive: " << strerror(errno));
355
            close(sock);
356
            return -1;
357
        }
358
#endif
359
 
360
        if (connect(sock, ainfo->ai_addr, ainfo->ai_addrlen) == -1)
361
        {
362
            MSG_ERROR("Connect error: " << strerror(errno));
363
            close(sock);
364
            retry = false;
365
        }
366
        else
367
        {
368
            retry = false;
369
            break;
370
        }
371
    }
372
 
373
    if (ainfo == nullptr)
374
        return -1;
375
 
376
    if (mURL.scheme.compare("https") == 0)
377
    {
378
        int ret;
379
 
380
        MSG_DEBUG("Initializing SSL connection ...");
381
        mCtx = initCTX();
382
 
383
        if (mCtx == NULL)
384
        {
385
            MSG_ERROR("Error initializing CTX.");
386
            close(sock);
387
            return -1;
388
        }
389
 
390
        mSsl = SSL_new(mCtx);      /* create new SSL connection state */
391
 
392
        if (mSsl == NULL)
393
        {
394
            log_ssl_error();
395
            SSL_CTX_free(mCtx);
396
            close(sock);
397
            return -1;
398
        }
399
 
400
        SSL_set_fd(mSsl, sock);    /* attach the socket descriptor */
401
 
402
        if (TConfig::certCheck())
403
        {
404
            SSL_set_verify(mSsl, SSL_VERIFY_PEER, _verify_callback);
405
            MSG_TRACE("Verify on peer certificate was set.");
406
        }
407
 
408
        while ((ret = SSL_connect(mSsl)) < 0)
409
        {
410
            fd_set fds;
411
            FD_ZERO(&fds);
412
            FD_SET(sock, &fds);
413
 
414
            switch (SSL_get_error(mSsl, ret))
415
            {
416
                case SSL_ERROR_WANT_READ:
417
                    select(sock + 1, &fds, NULL, NULL, NULL);
418
                    break;
419
 
420
                case SSL_ERROR_WANT_WRITE:
421
                    select(sock + 1, NULL, &fds, NULL, NULL);
422
                    break;
423
 
424
                default:
425
                    MSG_ERROR("Error getting a new SSL handle.");
426
                    SSL_CTX_free(mCtx);
427
                    close(sock);
428
                    SSL_free(mSsl);
429
                    return -1;
430
            }
431
        }
432
 
433
        if (TConfig::certCheck())
434
        {
435
            if (SSL_get_peer_certificate(mSsl))
436
            {
437
                MSG_DEBUG("Result of peer certificate verification is checked ...");
438
 
439
                if ((ret = SSL_get_verify_result(mSsl)) != X509_V_OK)
440
                {
441
                    MSG_ERROR("Error verifiying peer.");
442
                    SSL_CTX_free(mCtx);
443
                    close(sock);
444
                    SSL_free(mSsl);
445
                    return -1;
446
                }
447
 
448
                MSG_TRACE("Certificate was valid.");
449
            }
450
            else
451
                MSG_WARNING("Peer offered no or invalid certificate!");
452
        }
453
    }
454
 
455
    return sock;
456
}
457
 
458
struct addrinfo *THTTPClient::lookup_host (const string& host, int port)
459
{
460
    DECL_TRACER("THTTPClient::lookup_host (const string& host, int port)");
461
 
462
    struct addrinfo *res;
463
    struct addrinfo hints;
464
    char sport[16];
465
 
466
    memset (&hints, 0, sizeof (hints));
467
    hints.ai_family = AF_INET;
468
    hints.ai_protocol = IPPROTO_TCP;
469
    hints.ai_socktype = SOCK_STREAM;
470
    hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
471
    snprintf(sport, sizeof(sport), "%d", port);
472
 
473
    if (getaddrinfo (host.c_str(), sport, &hints, &res) != 0)
474
    {
475
        MSG_ERROR("Getaddrinfo: " << strerror(errno));
476
        return nullptr;
477
    }
478
 
479
    return res;
480
}
481
 
482
int THTTPClient::sockWrite(int fd, char *buffer, size_t len)
483
{
484
    DECL_TRACER("THTTPClient::sockWrite(int fd, char *buffer, size_t len)");
485
 
486
    int proto = 0;
487
 
488
    if (buffer == nullptr || (mURL.scheme.compare("https") == 0 && mSsl == nullptr))
489
        return -1;
490
 
491
    if (mURL.scheme.compare("http") == 0)
492
        proto = 0;
493
    else if (mURL.scheme.compare("https") == 0)
494
        proto = 1;
495
    else
496
    {
497
        MSG_WARNING("Not supported protocol " << mURL.scheme << "!");
498
        proto = -1;
499
    }
500
 
501
    switch (proto)
502
    {
503
        case 0: return write(fd, buffer, len);
504
        case 1: return SSL_write(mSsl, buffer, len);
505
    }
506
 
507
    return -1;
508
}
509
 
510
int THTTPClient::sockRead(int fd, char *buffer, size_t len)
511
{
512
    DECL_TRACER("THTTPClient::sockRead(int fd, char *buffer, size_t len)");
513
 
514
    int proto = 0;
515
 
516
    if (buffer == NULL || (mURL.scheme.compare("https") == 0 && mSsl == NULL))
517
        return -1;
518
 
519
    if (mURL.scheme.compare("http") == 0)
520
        proto = 0;
521
    else if (mURL.scheme.compare("https") == 0)
522
        proto = 1;
523
 
524
    switch (proto)
525
    {
526
        case 0: return read(fd, buffer, len);
527
        case 1: return SSL_read(mSsl, buffer, len);
528
    }
529
 
530
    return -1;
531
}
532
 
533
URL_t& THTTPClient::parseURL(const string& URL)
534
{
535
    DECL_TRACER("THTTPClient::parseURL(const string& URL, const string& user, const string& pw)");
536
 
537
    if (URL.empty())
538
    {
539
        MSG_ERROR("Invalid empty URL!");
540
        TError::setError();
541
        mURL.clear();
542
        return mURL;
543
    }
544
 
545
    size_t pos = URL.find("://");
546
 
547
    if (pos == string::npos)
548
    {
549
        MSG_ERROR("Invalid URL: " << URL);
550
        TError::setError();
551
        mURL.clear();
552
        return mURL;
553
    }
554
 
555
    mURL.scheme = URL.substr(0, pos);
556
    pos += 3;
557
    string part = URL.substr(pos);
558
    pos = part.find("/");
559
    string host;
560
 
561
    if (pos == string::npos)
562
    {
563
        host = part;
564
        part.clear();
565
    }
566
    else
567
    {
568
        host = part.substr(0, pos);
569
        part = part.substr(pos + 1);
570
    }
571
 
572
    pos = host.find(":");
573
 
574
    if (pos != string::npos)
575
    {
576
        string sport = host.substr(pos + 1);
577
        mURL.host = host.substr(0, pos);
578
        mURL.port = atoi(sport.c_str());
579
    }
580
    else
581
        mURL.host = host;
582
 
583
    if (!part.empty())
584
        mURL.path = part;
585
 
586
    pos = host.find("@");
587
 
588
    if (pos != string::npos)
589
    {
590
        mURL.user = host.substr(0, pos);
591
        mURL.host = host.substr(pos + 1);
592
    }
593
 
594
    if (mURL.port == 0)
595
    {
596
        if (mURL.scheme.compare("https") == 0)
597
            mURL.port = 443;
598
        else
599
            mURL.port = 80;
600
    }
601
 
602
    MSG_DEBUG("URL components: Scheme: " << mURL.scheme << ", Host: " << mURL.host << ", Port: " << mURL.port << ", Path: " << mURL.path << ", User: " << mURL.user << ", Password: " << ((mURL.pw.empty()) ? "" : "****"));
603
    return mURL;
604
}
605
 
606
int THTTPClient::parseHeader(const char *buffer, size_t len)
607
{
608
    DECL_TRACER("THTTPClient::parseHeader(const char *buffer, size_t len)");
609
 
610
    if (buffer == nullptr || len == 0)
611
    {
612
        MSG_DEBUG("Empty receive buffer!");
613
        return -1;
614
    }
615
 
616
    int blen = 0, receive = 0, code = 0;
617
    char *raw = (char *)buffer;
618
    char *path, *version, *status;
619
    int direction = HTTP_DIRECTION_SEND;
620
    METHOD_t method;
621
    mHeader.clear();
622
    mBody.clear();
623
    mRequest.clear();
624
 
625
    // Method
626
    size_t meth_len = strcspn(raw, " ");
627
 
628
    if (meth_len >= len)
629
    {
630
        MSG_ERROR("Buffer contains no valid HTTP response!");
631
        return -1;
632
    }
633
 
634
    if (memcmp(raw, "GET", 3) == 0)
635
        method = GET;
636
    else if (memcmp(raw, "PUT", 3) == 0)
637
        method = PUT;
638
    else if (memcmp(raw, "POST", 4) == 0)
639
        method = POST;
640
    else if (memcmp(raw, "HEAD", 4) == 0)
641
        method = HEAD;
642
    else
643
    {
644
        method = UNSUPPORTED;
645
        direction = HTTP_DIRECTION_RECEIVE;
646
        receive = 1;
647
        MSG_DEBUG("Detected a receive buffer");
648
    }
649
 
650
    mRequest.method = method;
651
    mRequest.direction = direction;
652
    raw += meth_len + 1; // move past <SP>
653
    status = path = version = nullptr;
654
 
655
    if (!receive)
656
    {
657
        // Request-URI
658
        size_t path_len = strcspn(raw, " ");
659
 
660
        try
661
        {
662
            path = new char[path_len+1];
663
            memcpy(path, raw, path_len);
664
            path[path_len] = '\0';
665
            raw += path_len + 1; // move past <SP>
666
 
667
            // HTTP-Version
668
            size_t ver_len = strcspn(raw, "\r\n");
669
            version = new char[ver_len + 1];
670
            memcpy(version, raw, ver_len);
671
            version[ver_len] = '\0';
672
            raw += ver_len + 2; // move past <CR><LF>
673
            mRequest.path = path;
674
            mRequest.version = version;
675
        }
676
        catch (std::exception& e)
677
        {
678
            MSG_ERROR("Error allocating memory: " << e.what());
679
 
680
            if (path)
681
                delete[] path;
682
 
683
            if (version)
684
                delete[] version;
685
 
686
            mRequest.path = nullptr;
687
            mRequest.version = nullptr;
688
            return -1;
689
        }
690
    }
691
    else
692
    {
693
        char scode[16];
694
        memset(scode, 0, sizeof(scode));
695
        size_t code_len = strcspn(raw, " ");
696
        strncpy(scode, raw, min(code_len, sizeof(scode)));
697
        scode[sizeof(scode)-1] = 0;
698
        code = atoi(scode);
699
 
700
        MSG_DEBUG("Received code " << code);
701
 
702
        if (strstr(buffer, "\r\n\r\n") == NULL)
703
        {
704
            MSG_ERROR("Received no content!");
705
            return -1;
706
        }
707
 
708
        if (code_len >= len)
709
        {
710
            MSG_ERROR("Buffer contains no valid HTTP response!");
711
            return -1;
712
        }
713
 
714
        raw += code_len + 1;
715
        size_t stat_len = strcspn(raw, "\r\n");
716
 
717
        try
718
        {
719
            status = new char[stat_len + 1];
720
            memcpy(status, raw, stat_len);
721
            status[stat_len] = '\0';
722
            raw += stat_len + 2;
723
            mRequest.status = status;
724
        }
725
        catch (std::exception& e)
726
        {
727
            MSG_ERROR("Error allocating memory: " << e.what());
728
            return -1;
729
        }
730
    }
731
 
732
    char *end = strstr(raw, "\r\n\r\n");
733
 
734
    while (raw < end)
735
    {
736
        HTTPHEAD_t head;
737
        char hv0[1024];
738
        // name
739
        size_t name_len = strcspn(raw, ":");
740
 
741
        size_t mylen = min(name_len, sizeof(hv0));
742
        memcpy(hv0, raw, mylen);
743
        hv0[mylen] = '\0';
744
        head.name = hv0;
745
        raw += name_len + 1; // move past :
746
 
747
        while (*raw == ' ')
748
            raw++;
749
 
750
        // value
751
        size_t value_len = strcspn(raw, "\r\n");
752
        mylen = min(value_len, sizeof(hv0));
753
        memcpy(hv0, raw, mylen);
754
        hv0[mylen] = '\0';
755
        head.content = hv0;
756
        raw += value_len + 2; // move past <CR><LF>
757
 
758
        if (head.name.compare("Content-Length") == 0)
759
            blen = atoi(head.content.c_str());
760
 
761
        // next
762
        mHeader.push_back(head);
763
    }
764
 
765
    if (blen == 0)
766
    {
767
        size_t head_len = strcspn(buffer, "\r\n\r\n");
768
 
769
        if (head_len < len)
770
            blen = len - head_len + 4;
771
    }
772
 
773
    MSG_DEBUG("Content length: " << blen);
774
 
775
    if (blen > 0)
776
    {
777
        raw = end + 4;
778
 
779
        try
780
        {
781
            mBody.body = new char[blen +1];
782
            memcpy(mBody.body, raw, blen);
783
            mBody.body[blen] = '\0';
784
            mBody.len = blen;
785
        }
786
        catch(std::exception& e)
787
        {
788
            MSG_ERROR(e.what());
789
 
790
            if (status)
791
                delete[] status;
792
 
793
            if (path)
794
                delete[] path;
795
 
796
            if (version)
797
                delete[] version;
798
 
799
            mRequest.status = nullptr;
800
            mRequest.path = nullptr;
801
            mRequest.version = nullptr;
802
            return -1;
803
        }
804
    }
805
 
806
    return 0;
807
}
808
 
809
string THTTPClient::getHeadParameter(const string& name)
810
{
811
    DECL_TRACER("THTTPClient::getHeadParameter(const string& name)");
812
 
813
    if (mHeader.empty())
814
        return string();
815
 
816
    vector<HTTPHEAD_t>::iterator iter;
817
 
818
    for (iter = mHeader.begin(); iter != mHeader.end(); iter++)
819
    {
820
        if (iter->name.compare(name) == 0)
821
            return iter->content;
822
    }
823
 
824
    return string();
825
}
826
 
827
char *THTTPClient::getContent(const char* buffer)
828
{
829
    DECL_TRACER("THTTPClient::getContent(const char* buffer)");
830
 
831
    char *ctnt = strstr((char *)buffer, "\r\n\r\n");
832
    return ctnt;
833
}
834
 
835
void THTTPClient::initSSL()
836
{
837
    DECL_TRACER("THTTPClient::initSSL()");
838
 
839
    if (mSSLInitialized)
840
        return;
841
 
842
    SSL_library_init();
843
    ERR_load_BIO_strings();
844
    ERR_load_crypto_strings();
845
    SSL_load_error_strings();
846
    mSSLInitialized = true;
847
}
848
 
849
string THTTPClient::makeRequest(const string& url)
850
{
851
    DECL_TRACER("THTTPClient::makeRequest(const string& url)");
852
 
853
    URL_t uparts = parseURL(url);
854
    string request = "GET " + uparts.path + " HTTP/1.1\r\n";
855
    request += "Host: " + uparts.host;
856
 
857
    if (uparts.port > 0 && uparts.port != 80 && uparts.port != 443)
858
        request += ":" + std::to_string(uparts.port);
859
 
860
    request += "\r\n";
861
 
862
    if (!mUser.empty())
863
    {
864
        string clearname = mUser + ":" + mPassword;
865
        string enc = Base64::encode((BYTE *)clearname.c_str(), clearname.size());
866
        request += "Authorization: Basic " + enc + "\r\n";
867
    }
868
 
869
    request += "User-Agent: tpanel/" + std::to_string(V_MAJOR) + "." + std::to_string(V_MINOR) + "\r\n";
870
    request += "Accept: image/*\r\n";
871
    request += "\r\n";
872
    MSG_DEBUG("Requesting: " << std::endl << request << "------------------------------------------");
873
    return request;
874
}
875
 
876
SSL_CTX *THTTPClient::initCTX()
877
{
878
    DECL_TRACER("THTTPClient::initCTX()");
879
 
880
    SSL_CTX *ctx;
881
    #if OPENSSL_VERSION_NUMBER >= 0x1010000fL
882
    const SSL_METHOD *method = TLS_client_method();
883
    #else
884
    const SSL_METHOD *method = TLSv1_2_client_method();
885
    #endif
886
    ctx = SSL_CTX_new(method);   /* Create new context */
887
 
888
    if ( ctx == NULL )
889
    {
890
        log_ssl_error();
891
        return NULL;
892
    }
893
 
894
    char *cert_check = getenv("CERT_CHECK");
895
 
896
    if (cert_check && strcmp(cert_check, "ON") == 0)
897
    {
898
        char *cert_path = getenv("CERT_PATH");
899
        char *cert_chain = getenv("CERT_CHAIN");
900
        char *cert_file = getenv("CERT_FILE");
901
        char *cert_type = getenv("CERT_TYPE");
902
 
903
        if (cert_path == NULL)
904
        {
905
            MSG_WARNING("Missing environment variable \"CERT_PATH\" defining the path to the cerificates.");
906
            return ctx;
907
        }
908
 
909
        if (cert_chain == NULL)
910
        {
911
            MSG_WARNING("Certificate check is enabled but no certificate chain file is set! No certificate verification was made.");
912
            return ctx;
913
        }
914
 
915
        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, _verify_callback);
916
 
917
        if (cert_type == NULL)
918
            cert_type = (char *)"PEM";
919
 
920
        if (SSL_CTX_load_verify_locations(ctx, cert_chain, cert_path) != 1)
921
        {
922
            MSG_ERROR("Error with certificate " << cert_path << "/" << cert_chain);
923
            log_ssl_error();
924
            SSL_CTX_free(ctx);
925
            return NULL;
926
        }
927
 
928
        int type = SSL_FILETYPE_PEM;
929
 
930
        if (strcmp(cert_type, "ASN1") == 0)
931
            type = SSL_FILETYPE_ASN1;
932
 
933
        if (cert_file && SSL_CTX_use_certificate_file(ctx, cert_file, type) != 1)
934
        {
935
            MSG_ERROR("Error with certificate " << cert_file);
936
            log_ssl_error();
937
            SSL_CTX_free(ctx);
938
            return NULL;
939
        }
940
    }
941
 
942
    return ctx;
943
}
944
 
945
void THTTPClient::log_ssl_error()
946
{
947
    DECL_TRACER("THTTPClient::log_ssl_error()");
948
    unsigned long int err;
949
    char errstr[512];
950
 
951
    while ((err = ERR_get_error()) != 0)
952
    {
953
        ERR_error_string_n(err, &errstr[0], sizeof(errstr));
954
        MSG_ERROR(errstr);
955
    }
956
}
957
 
958
string THTTPClient::getSocketError(int err, const string data)
959
{
960
    DECL_TRACER("getSocketError(int err, const string data)");
961
 
962
    switch(err)
963
    {
964
        // h_errno
965
        case HOST_NOT_FOUND: return string("Host ") + data + " not found!";
966
        case NO_DATA:        return string("The requested name ") + data + " does not have an IP address!";
967
        case NO_RECOVERY:    return string("A nonrecoverable name server error occurred.");
968
        case TRY_AGAIN:      return string("A temporary error occurred on an authoritative name server.  Try again later.");
969
 
970
        default:
971
            return string("Unkown error");
972
    }
973
 
974
    return string();
975
}
976
 
977
/*
978
 * Callback fuction for SSL connections.
979
 */
980
int _verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
981
{
982
    DECL_TRACER("_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)");
983
    char    buf[256];
984
    X509   *err_cert;
985
    int     err, depth;
986
    SSL    *ssl;
987
 
988
    err_cert = X509_STORE_CTX_get_current_cert(ctx);
989
    err = X509_STORE_CTX_get_error(ctx);
990
    depth = X509_STORE_CTX_get_error_depth(ctx);
991
 
992
    /*
993
     * Retrieve the pointer to the SSL of the connection currently treated
994
     * and the application specific data stored into the SSL object.
995
     */
996
    ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
997
    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
998
 
999
    if (!preverify_ok)
1000
    {
1001
        MSG_WARNING("verify error:num=" << err << ":" << X509_verify_cert_error_string(err) << ":depth=" << depth << ":" << buf);
1002
    }
1003
 
1004
    /*
1005
     * At this point, err contains the last verification error. We can use
1006
     * it for something special
1007
     */
1008
    if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT))
1009
    {
1010
        X509_NAME_oneline(X509_get_issuer_name(err_cert), buf, 256);
1011
        MSG_WARNING("issuer= " << buf);
1012
    }
1013
 
1014
    return preverify_ok;
1015
}