Coverage Report

Created: 2023-09-25 06:17

/src/znc/src/Utils.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2004-2023 ZNC, see the NOTICE file for details.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#ifdef __CYGWIN__
18
#ifndef _XOPEN_SOURCE
19
// strptime() wants this
20
#define _XOPEN_SOURCE 600
21
#endif
22
#endif
23
24
#include <znc/Utils.h>
25
#include <znc/ZNCDebug.h>
26
#include <znc/FileUtils.h>
27
#include <znc/Message.h>
28
#ifdef HAVE_LIBSSL
29
#include <openssl/ssl.h>
30
#include <openssl/bn.h>
31
#include <openssl/rsa.h>
32
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20700000L))
33
#define X509_getm_notBefore X509_get_notBefore
34
#define X509_getm_notAfter X509_get_notAfter
35
#endif
36
#endif /* HAVE_LIBSSL */
37
#include <memory>
38
#include <unistd.h>
39
#include <time.h>
40
41
#include <sys/types.h>
42
#include <sys/socket.h>
43
#include <netdb.h>
44
#include <netinet/in.h>
45
46
#ifdef HAVE_TCSETATTR
47
#include <termios.h>
48
#endif
49
50
#ifdef HAVE_ICU
51
#include <unicode/ucnv.h>
52
#include <unicode/errorcode.h>
53
#endif
54
55
// Required with GCC 4.3+ if openssl is disabled
56
#include <cstring>
57
#include <cstdlib>
58
#include <iomanip>
59
#include <chrono>
60
61
#include "cctz/time_zone.h"
62
63
using std::map;
64
using std::vector;
65
66
0
CUtils::CUtils() {}
67
0
CUtils::~CUtils() {}
68
69
#ifdef HAVE_LIBSSL
70
// Generated by "openssl dhparam 2048"
71
constexpr const char* szDefaultDH2048 =
72
    "-----BEGIN DH PARAMETERS-----\n"
73
    "MIIBCAKCAQEAtS/K3TMY8IHzcCATQSjUF3rDidjDDQmT+mLxyxRORmzMPjFIFkKH\n"
74
    "MOmxZvyCBArdaoCCEBBOzrldl/bBLn5TOeZb+MW7mpBLANTuQSOu97DDM7EzbnqC\n"
75
    "b6z3QgixZ2+UqxdmQAu4nBPLFwym6W/XPFEHpz6iHISSvjzzo4cfI0xwWTcoAvFQ\n"
76
    "r/ZU5BXSXp7XuDxSyyAqaaKUxquElf+x56QWrpNJypjzPpslg5ViAKwWQS0TnCrU\n"
77
    "sVuhFtbNlZjqW1tMSBxiWFltS1HoEaaI79MEpf1Ps25OrQl8xqqCGKkZcHlNo4oF\n"
78
    "cvUyzAEcCQYHmiYjp2hoZbSa8b690TQaAwIBAg==\n"
79
    "-----END DH PARAMETERS-----\n";
80
81
void CUtils::GenerateCert(FILE* pOut, const CString& sHost) {
82
    const int days = 365;
83
    const int years = 10;
84
85
    unsigned int uSeed = (unsigned int)time(nullptr);
86
    int serial = (rand_r(&uSeed) % 9999);
87
88
    std::unique_ptr<BIGNUM, void (*)(BIGNUM*)> pExponent(BN_new(), ::BN_free);
89
    if (!pExponent || !BN_set_word(pExponent.get(), 0x10001)) return;
90
91
    std::unique_ptr<RSA, void (*)(RSA*)> pRSA(RSA_new(), ::RSA_free);
92
    if (!pRSA ||
93
        !RSA_generate_key_ex(pRSA.get(), 2048, pExponent.get(), nullptr))
94
        return;
95
96
    std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)> pKey(EVP_PKEY_new(),
97
                                                        ::EVP_PKEY_free);
98
    if (!pKey || !EVP_PKEY_set1_RSA(pKey.get(), pRSA.get())) return;
99
100
    std::unique_ptr<X509, void (*)(X509*)> pCert(X509_new(), ::X509_free);
101
    if (!pCert) return;
102
103
    X509_set_version(pCert.get(), 2);
104
    ASN1_INTEGER_set(X509_get_serialNumber(pCert.get()), serial);
105
    X509_gmtime_adj(X509_getm_notBefore(pCert.get()), 0);
106
    X509_gmtime_adj(X509_getm_notAfter(pCert.get()),
107
                    (long)60 * 60 * 24 * days * years);
108
    X509_set_pubkey(pCert.get(), pKey.get());
109
110
    const char* pLogName = getenv("LOGNAME");
111
    const char* pHostName = nullptr;
112
113
    if (!pLogName) pLogName = "Unknown";
114
115
    if (!sHost.empty()) pHostName = sHost.c_str();
116
117
    if (!pHostName) pHostName = getenv("HOSTNAME");
118
119
    if (!pHostName) pHostName = "host.unknown";
120
121
    CString sEmailAddr = pLogName;
122
    sEmailAddr += "@";
123
    sEmailAddr += pHostName;
124
125
    X509_NAME* pName = X509_get_subject_name(pCert.get());
126
    X509_NAME_add_entry_by_txt(pName, "OU", MBSTRING_ASC,
127
                               (unsigned char*)pLogName, -1, -1, 0);
128
    X509_NAME_add_entry_by_txt(pName, "CN", MBSTRING_ASC,
129
                               (unsigned char*)pHostName, -1, -1, 0);
130
    X509_NAME_add_entry_by_txt(pName, "emailAddress", MBSTRING_ASC,
131
                               (unsigned char*)sEmailAddr.c_str(), -1, -1, 0);
132
133
    X509_set_issuer_name(pCert.get(), pName);
134
135
    if (!X509_sign(pCert.get(), pKey.get(), EVP_sha256())) return;
136
137
    PEM_write_RSAPrivateKey(pOut, pRSA.get(), nullptr, nullptr, 0, nullptr,
138
                            nullptr);
139
    PEM_write_X509(pOut, pCert.get());
140
141
    fprintf(pOut, "%s", szDefaultDH2048);
142
}
143
#endif /* HAVE_LIBSSL */
144
145
0
CString CUtils::GetIP(unsigned long addr) {
146
0
    char szBuf[16];
147
0
    memset((char*)szBuf, 0, 16);
148
149
0
    if (addr >= (1 << 24)) {
150
0
        unsigned long ip[4];
151
0
        ip[0] = addr >> 24 & 255;
152
0
        ip[1] = addr >> 16 & 255;
153
0
        ip[2] = addr >> 8 & 255;
154
0
        ip[3] = addr & 255;
155
0
        sprintf(szBuf, "%lu.%lu.%lu.%lu", ip[0], ip[1], ip[2], ip[3]);
156
0
    }
157
158
0
    return szBuf;
159
0
}
160
161
0
unsigned long CUtils::GetLongIP(const CString& sIP) {
162
0
    unsigned long ret;
163
0
    char ip[4][4];
164
0
    unsigned int i;
165
166
0
    i = sscanf(sIP.c_str(), "%3[0-9].%3[0-9].%3[0-9].%3[0-9]", ip[0], ip[1],
167
0
               ip[2], ip[3]);
168
0
    if (i != 4) return 0;
169
170
    // Beware that atoi("200") << 24 would overflow and turn negative!
171
0
    ret = atol(ip[0]) << 24;
172
0
    ret += atol(ip[1]) << 16;
173
0
    ret += atol(ip[2]) << 8;
174
0
    ret += atol(ip[3]) << 0;
175
176
0
    return ret;
177
0
}
178
179
// If you change this here and in GetSaltedHashPass(),
180
// don't forget CUser::HASH_DEFAULT!
181
// TODO refactor this
182
const CString CUtils::sDefaultHash = "sha256";
183
0
CString CUtils::GetSaltedHashPass(CString& sSalt) {
184
0
    sSalt = GetSalt();
185
186
0
    while (true) {
187
0
        CString pass1;
188
0
        do {
189
0
            pass1 = CUtils::GetPass("Enter password");
190
0
        } while (pass1.empty());
191
192
0
        CString pass2 = CUtils::GetPass("Confirm password");
193
194
0
        if (!pass1.Equals(pass2, CString::CaseSensitive)) {
195
0
            CUtils::PrintError("The supplied passwords did not match");
196
0
        } else {
197
            // Construct the salted pass
198
0
            return SaltedSHA256Hash(pass1, sSalt);
199
0
        }
200
0
    }
201
0
}
202
203
0
CString CUtils::GetSalt() { return CString::RandomString(20); }
204
205
0
CString CUtils::SaltedMD5Hash(const CString& sPass, const CString& sSalt) {
206
0
    return CString(sPass + sSalt).MD5();
207
0
}
208
209
0
CString CUtils::SaltedSHA256Hash(const CString& sPass, const CString& sSalt) {
210
0
    return CString(sPass + sSalt).SHA256();
211
0
}
212
213
0
CString CUtils::GetPass(const CString& sPrompt) {
214
0
#ifdef HAVE_TCSETATTR
215
    // Disable echo
216
0
    struct termios t;
217
0
    tcgetattr(1, &t);
218
0
    struct termios t2 = t;
219
0
    t2.c_lflag &= ~ECHO;
220
0
    tcsetattr(1, TCSANOW, &t2);
221
    // Read pass
222
0
    CString r;
223
0
    GetInput(sPrompt, r);
224
    // Restore echo and go to new line
225
0
    tcsetattr(1, TCSANOW, &t);
226
0
    fprintf(stdout, "\n");
227
0
    fflush(stdout);
228
0
    return r;
229
#else
230
    PrintPrompt(sPrompt);
231
#ifdef HAVE_GETPASSPHRASE
232
    return getpassphrase("");
233
#else
234
    return getpass("");
235
#endif
236
#endif
237
0
}
238
239
0
bool CUtils::GetBoolInput(const CString& sPrompt, bool bDefault) {
240
0
    return CUtils::GetBoolInput(sPrompt, &bDefault);
241
0
}
242
243
0
bool CUtils::GetBoolInput(const CString& sPrompt, bool* pbDefault) {
244
0
    CString sRet, sDefault;
245
246
0
    if (pbDefault) {
247
0
        sDefault = (*pbDefault) ? "yes" : "no";
248
0
    }
249
250
0
    while (true) {
251
0
        GetInput(sPrompt, sRet, sDefault, "yes/no");
252
253
0
        if (sRet.Equals("y") || sRet.Equals("yes")) {
254
0
            return true;
255
0
        } else if (sRet.Equals("n") || sRet.Equals("no")) {
256
0
            return false;
257
0
        }
258
0
    }
259
0
}
260
261
bool CUtils::GetNumInput(const CString& sPrompt, unsigned int& uRet,
262
                         unsigned int uMin, unsigned int uMax,
263
0
                         unsigned int uDefault) {
264
0
    if (uMin > uMax) {
265
0
        return false;
266
0
    }
267
268
0
    CString sDefault = (uDefault != (unsigned int)~0) ? CString(uDefault) : "";
269
0
    CString sNum, sHint;
270
271
0
    if (uMax != (unsigned int)~0) {
272
0
        sHint = CString(uMin) + " to " + CString(uMax);
273
0
    } else if (uMin > 0) {
274
0
        sHint = CString(uMin) + " and up";
275
0
    }
276
277
0
    while (true) {
278
0
        GetInput(sPrompt, sNum, sDefault, sHint);
279
0
        if (sNum.empty()) {
280
0
            return false;
281
0
        }
282
283
0
        uRet = sNum.ToUInt();
284
285
0
        if ((uRet >= uMin && uRet <= uMax)) {
286
0
            break;
287
0
        }
288
289
0
        CUtils::PrintError("Number must be " + sHint);
290
0
    }
291
292
0
    return true;
293
0
}
294
295
bool CUtils::GetInput(const CString& sPrompt, CString& sRet,
296
0
                      const CString& sDefault, const CString& sHint) {
297
0
    CString sExtra;
298
0
    CString sInput;
299
0
    sExtra += (!sHint.empty()) ? (" (" + sHint + ")") : "";
300
0
    sExtra += (!sDefault.empty()) ? (" [" + sDefault + "]") : "";
301
302
0
    PrintPrompt(sPrompt + sExtra);
303
0
    char szBuf[1024];
304
0
    memset(szBuf, 0, 1024);
305
0
    if (fgets(szBuf, 1024, stdin) == nullptr) {
306
        // Reading failed (Error? EOF?)
307
0
        PrintError("Error while reading from stdin. Exiting...");
308
0
        exit(-1);
309
0
    }
310
0
    sInput = szBuf;
311
312
0
    sInput.TrimSuffix("\n");
313
314
0
    if (sInput.empty()) {
315
0
        sRet = sDefault;
316
0
    } else {
317
0
        sRet = sInput;
318
0
    }
319
320
0
    return !sRet.empty();
321
0
}
322
323
0
#define BOLD "\033[1m"
324
#define NORM "\033[22m"
325
326
#define RED "\033[31m"
327
#define GRN "\033[32m"
328
#define YEL "\033[33m"
329
#define BLU "\033[34m"
330
#define DFL "\033[39m"
331
332
0
void CUtils::PrintError(const CString& sMessage) {
333
0
    if (CDebug::StdoutIsTTY())
334
0
        fprintf(stdout, BOLD BLU "[" RED " ** " BLU "]" DFL NORM " %s\n",
335
0
                sMessage.c_str());
336
0
    else
337
0
        fprintf(stdout, "%s\n", sMessage.c_str());
338
0
    fflush(stdout);
339
0
}
340
341
0
void CUtils::PrintPrompt(const CString& sMessage) {
342
0
    if (CDebug::StdoutIsTTY())
343
0
        fprintf(stdout, BOLD BLU "[" YEL " ?? " BLU "]" DFL NORM " %s: ",
344
0
                sMessage.c_str());
345
0
    else
346
0
        fprintf(stdout, "[ ?? ] %s: ", sMessage.c_str());
347
0
    fflush(stdout);
348
0
}
349
350
0
void CUtils::PrintMessage(const CString& sMessage, bool bStrong) {
351
0
    if (CDebug::StdoutIsTTY()) {
352
0
        if (bStrong)
353
0
            fprintf(stdout,
354
0
                    BOLD BLU "[" YEL " ** " BLU "]" DFL BOLD " %s" NORM "\n",
355
0
                    sMessage.c_str());
356
0
        else
357
0
            fprintf(stdout, BOLD BLU "[" YEL " ** " BLU "]" DFL NORM " %s\n",
358
0
                    sMessage.c_str());
359
0
    } else
360
0
        fprintf(stdout, "%s\n", sMessage.c_str());
361
362
0
    fflush(stdout);
363
0
}
364
365
0
void CUtils::PrintAction(const CString& sMessage) {
366
0
    if (CDebug::StdoutIsTTY())
367
0
        fprintf(stdout, BOLD BLU "[ .. " BLU "]" DFL NORM " %s...\n",
368
0
                sMessage.c_str());
369
0
    else
370
0
        fprintf(stdout, "%s... ", sMessage.c_str());
371
0
    fflush(stdout);
372
0
}
373
374
0
void CUtils::PrintStatus(bool bSuccess, const CString& sMessage) {
375
0
    if (CDebug::StdoutIsTTY()) {
376
0
        if (bSuccess) {
377
0
            if (!sMessage.empty())
378
0
                fprintf(stdout,
379
0
                        BOLD BLU "[" GRN " >> " BLU "]" DFL NORM " %s\n",
380
0
                        sMessage.c_str());
381
0
        } else {
382
0
            fprintf(stdout, BOLD BLU "[" RED " !! " BLU "]" DFL NORM BOLD RED
383
0
                                     " %s" DFL NORM "\n",
384
0
                    sMessage.empty() ? "failed" : sMessage.c_str());
385
0
        }
386
0
    } else {
387
0
        if (bSuccess) {
388
0
            fprintf(stdout, "%s\n", sMessage.c_str());
389
0
        } else {
390
0
            if (!sMessage.empty()) {
391
0
                fprintf(stdout, "[ %s ]", sMessage.c_str());
392
0
            }
393
394
0
            fprintf(stdout, "\n");
395
0
        }
396
0
    }
397
398
0
    fflush(stdout);
399
0
}
400
401
1.90k
timeval CUtils::GetTime() {
402
1.90k
#ifdef HAVE_CLOCK_GETTIME
403
1.90k
    timespec ts;
404
1.90k
    if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
405
1.90k
        return { ts.tv_sec, static_cast<suseconds_t>(ts.tv_nsec / 1000) };
406
1.90k
    }
407
0
#endif
408
409
0
    struct timeval tv;
410
0
    if (gettimeofday(&tv, nullptr) == 0) {
411
0
        return tv;
412
0
    }
413
414
    // Last resort, no microseconds
415
0
    return { time(nullptr), 0 };
416
0
}
417
418
0
unsigned long long CUtils::GetMillTime() {
419
0
    std::chrono::time_point<std::chrono::steady_clock> time = std::chrono::steady_clock::now();
420
0
    return std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch()).count();
421
0
}
422
423
0
CString CUtils::CTime(time_t t, const CString& sTimezone) {
424
0
    return FormatTime(t, "%c", sTimezone);
425
0
}
426
427
CString CUtils::FormatTime(time_t t, const CString& sFormat,
428
0
                           const CString& sTimezone) {
429
0
    cctz::time_zone tz;
430
0
    if (sTimezone.empty()) {
431
0
        tz = cctz::local_time_zone();
432
0
    } else if (sTimezone.StartsWith("GMT")) {
433
0
        int offset = CString(sTimezone.substr(3)).ToInt();
434
0
        tz = cctz::fixed_time_zone(cctz::seconds(offset * 60 * 60));
435
0
    } else {
436
0
        cctz::load_time_zone(sTimezone, &tz);
437
0
    }
438
439
0
    return cctz::format(sFormat, std::chrono::system_clock::from_time_t(t), tz);
440
0
}
441
442
CString CUtils::FormatTime(const timeval& tv, const CString& sFormat,
443
0
                           const CString& sTimezone) {
444
    // Parse additional format specifiers before passing them to
445
    // strftime, since the way strftime treats unknown format
446
    // specifiers is undefined.
447
    // TODO: consider using cctz's %E#f instead.
448
0
    CString sFormat2;
449
450
    // Make sure %% is parsed correctly, i.e. %%f is passed through to
451
    // strftime to become %f, and not 123.
452
0
    bool bInFormat = false;
453
0
    int iDigits;
454
0
    CString::size_type uLastCopied = 0, uFormatStart;
455
456
0
    for (CString::size_type i = 0; i < sFormat.length(); i++) {
457
0
        if (!bInFormat) {
458
0
            if (sFormat[i] == '%') {
459
0
                uFormatStart = i;
460
0
                bInFormat = true;
461
0
                iDigits = 3;
462
0
            }
463
0
        } else {
464
0
            switch (sFormat[i]) {
465
0
                case '0': case '1': case '2': case '3': case '4':
466
0
                case '5': case '6': case '7': case '8': case '9':
467
0
                    iDigits = sFormat[i] - '0';
468
0
                    break;
469
0
                case 'f': {
470
0
                    int iVal = tv.tv_usec;
471
0
                    int iDigitDelta = iDigits - 6; // tv_user is in 10^-6 seconds
472
0
                    for (; iDigitDelta > 0; iDigitDelta--)
473
0
                        iVal *= 10;
474
0
                    for (; iDigitDelta < 0; iDigitDelta++)
475
0
                        iVal /= 10;
476
0
                    sFormat2 += sFormat.substr(uLastCopied,
477
0
                        uFormatStart - uLastCopied);
478
0
                    CString sVal = CString(iVal);
479
0
                    sFormat2 += CString(iDigits - sVal.length(), '0');
480
0
                    sFormat2 += sVal;
481
0
                    uLastCopied = i + 1;
482
0
                    bInFormat = false;
483
0
                    break;
484
0
                }
485
0
                default:
486
0
                    bInFormat = false;
487
0
            }
488
0
        }
489
0
    }
490
491
0
    if (uLastCopied) {
492
0
        sFormat2 += sFormat.substr(uLastCopied);
493
0
        return FormatTime(tv.tv_sec, sFormat2, sTimezone);
494
0
    } else {
495
        // If there are no extended format specifiers, avoid doing any
496
        // memory allocations entirely.
497
0
        return FormatTime(tv.tv_sec, sFormat, sTimezone);
498
0
    }
499
0
}
500
501
0
CString CUtils::FormatServerTime(const timeval& tv) {
502
0
    using namespace std::chrono;
503
0
    system_clock::time_point time{duration_cast<system_clock::duration>(
504
0
        seconds(tv.tv_sec) + microseconds(tv.tv_usec))};
505
0
    return cctz::format("%Y-%m-%dT%H:%M:%E3SZ", time, cctz::utc_time_zone());
506
0
}
507
508
0
timeval CUtils::ParseServerTime(const CString& sTime) {
509
0
    using namespace std::chrono;
510
0
    system_clock::time_point tp;
511
0
    cctz::parse("%Y-%m-%dT%H:%M:%E*SZ", sTime, cctz::utc_time_zone(), &tp);
512
0
    struct timeval tv;
513
0
    memset(&tv, 0, sizeof(tv));
514
0
    microseconds usec = duration_cast<microseconds>(tp.time_since_epoch());
515
0
    tv.tv_sec = usec.count() / 1000000;
516
0
    tv.tv_usec = usec.count() % 1000000;
517
0
    return tv;
518
0
}
519
520
namespace {
521
void FillTimezones(const CString& sPath, SCString& result,
522
0
                   const CString& sPrefix) {
523
0
    CDir Dir;
524
0
    Dir.Fill(sPath);
525
0
    for (CFile* pFile : Dir) {
526
0
        CString sName = pFile->GetShortName();
527
0
        CString sFile = pFile->GetLongName();
528
0
        if (sName == "posix" || sName == "right")
529
0
            continue;  // these 2 dirs contain the same filenames
530
0
        if (sName.EndsWith(".tab") || sName == "posixrules" ||
531
0
            sName == "localtime")
532
0
            continue;
533
0
        if (pFile->IsDir()) {
534
0
            if (sName == "Etc") {
535
0
                FillTimezones(sFile, result, sPrefix);
536
0
            } else {
537
0
                FillTimezones(sFile, result, sPrefix + sName + "/");
538
0
            }
539
0
        } else {
540
0
            result.insert(sPrefix + sName);
541
0
        }
542
0
    }
543
0
}
544
}  // namespace
545
546
0
SCString CUtils::GetTimezones() {
547
0
    static SCString result;
548
0
    if (result.empty()) {
549
0
        FillTimezones("/usr/share/zoneinfo", result, "");
550
0
    }
551
0
    return result;
552
0
}
553
554
0
SCString CUtils::GetEncodings() {
555
0
    static SCString ssResult;
556
#ifdef HAVE_ICU
557
    if (ssResult.empty()) {
558
        for (int i = 0; i < ucnv_countAvailable(); ++i) {
559
            const char* pConvName = ucnv_getAvailableName(i);
560
            ssResult.insert(pConvName);
561
            icu::ErrorCode e;
562
            for (int st = 0; st < ucnv_countStandards(); ++st) {
563
                const char* pStdName = ucnv_getStandard(st, e);
564
                icu::LocalUEnumerationPointer ue(
565
                    ucnv_openStandardNames(pConvName, pStdName, e));
566
                while (const char* pStdConvNameEnum =
567
                           uenum_next(ue.getAlias(), nullptr, e)) {
568
                    ssResult.insert(pStdConvNameEnum);
569
                }
570
            }
571
        }
572
    }
573
#endif
574
0
    return ssResult;
575
0
}
576
577
0
bool CUtils::CheckCIDR(const CString& sIP, const CString& sRange) {
578
0
    if (sIP.WildCmp(sRange)) {
579
0
        return true;
580
0
    }
581
0
    auto deleter = [](addrinfo* ai) { freeaddrinfo(ai); };
582
    // Try to split the string into an IP and routing prefix
583
0
    VCString vsSplitCIDR;
584
0
    if (sRange.Split("/", vsSplitCIDR, false) != 2) return false;
585
0
    const CString sRoutingPrefix = vsSplitCIDR.back();
586
0
    const int iRoutingPrefix = sRoutingPrefix.ToInt();
587
0
    if (iRoutingPrefix < 0 || iRoutingPrefix > 128) return false;
588
589
    // If iRoutingPrefix is 0, it could be due to ToInt() failing, so
590
    // sRoutingPrefix needs to be all zeroes
591
0
    if (iRoutingPrefix == 0 && sRoutingPrefix != "0") return false;
592
593
    // Convert each IP from a numeric string to an addrinfo
594
0
    addrinfo aiHints;
595
0
    memset(&aiHints, 0, sizeof(addrinfo));
596
0
    aiHints.ai_flags = AI_NUMERICHOST;
597
598
0
    addrinfo* aiHostC;
599
0
    int iIsHostValid = getaddrinfo(sIP.c_str(), nullptr, &aiHints, &aiHostC);
600
0
    if (iIsHostValid != 0) return false;
601
0
    std::unique_ptr<addrinfo, decltype(deleter)> aiHost(aiHostC, deleter);
602
603
0
    aiHints.ai_family = aiHost->ai_family;  // Host and range must be in
604
                                            // the same address family
605
606
0
    addrinfo* aiRangeC;
607
0
    int iIsRangeValid =
608
0
        getaddrinfo(vsSplitCIDR.front().c_str(), nullptr, &aiHints, &aiRangeC);
609
0
    if (iIsRangeValid != 0) {
610
0
        return false;
611
0
    }
612
0
    std::unique_ptr<addrinfo, decltype(deleter)> aiRange(aiRangeC, deleter);
613
614
    // "/0" allows all IPv[4|6] addresses
615
0
    if (iRoutingPrefix == 0) {
616
0
        return true;
617
0
    }
618
619
    // If both IPs are valid and of the same type, make a bit field mask
620
    // from the routing prefix, AND it to the host and range, and see if
621
    // they match
622
0
    if (aiHost->ai_family == AF_INET) {
623
0
        if (iRoutingPrefix > 32) {
624
0
            return false;
625
0
        }
626
627
0
        const sockaddr_in* saHost =
628
0
            reinterpret_cast<const sockaddr_in*>(aiHost->ai_addr);
629
0
        const sockaddr_in* saRange =
630
0
            reinterpret_cast<const sockaddr_in*>(aiRange->ai_addr);
631
632
        // Make IPv4 bitmask
633
0
        const in_addr_t inBitmask = htonl((~0u) << (32 - iRoutingPrefix));
634
635
        // Compare masked IPv4s
636
0
        return ((inBitmask & saHost->sin_addr.s_addr) ==
637
0
                (inBitmask & saRange->sin_addr.s_addr));
638
0
    } else if (aiHost->ai_family == AF_INET6) {
639
        // Make IPv6 bitmask
640
0
        in6_addr in6aBitmask;
641
0
        memset(&in6aBitmask, 0, sizeof(in6aBitmask));
642
643
0
        for (int i = 0, iBitsLeft = iRoutingPrefix; iBitsLeft > 0;
644
0
             ++i, iBitsLeft -= 8) {
645
0
            if (iBitsLeft >= 8) {
646
0
                in6aBitmask.s6_addr[i] = (uint8_t)(~0u);
647
0
            } else {
648
0
                in6aBitmask.s6_addr[i] = (uint8_t)(~0u) << (8 - iBitsLeft);
649
0
            }
650
0
        }
651
652
        // Compare masked IPv6s
653
0
        const sockaddr_in6* sa6Host =
654
0
            reinterpret_cast<const sockaddr_in6*>(aiHost->ai_addr);
655
0
        const sockaddr_in6* sa6Range =
656
0
            reinterpret_cast<const sockaddr_in6*>(aiRange->ai_addr);
657
658
0
        for (int i = 0; i < 16; ++i) {
659
0
            if ((in6aBitmask.s6_addr[i] & sa6Host->sin6_addr.s6_addr[i]) !=
660
0
                (in6aBitmask.s6_addr[i] & sa6Range->sin6_addr.s6_addr[i])) {
661
0
                return false;
662
0
            }
663
0
        }
664
0
        return true;
665
0
    } else {
666
0
        return false;
667
0
    }
668
0
}
669
670
0
MCString CUtils::GetMessageTags(const CString& sLine) {
671
0
    if (sLine.StartsWith("@")) {
672
0
        return CMessage(sLine).GetTags();
673
0
    }
674
0
    return MCString::EmptyMap;
675
0
}
676
677
0
void CUtils::SetMessageTags(CString& sLine, const MCString& mssTags) {
678
0
    CMessage Message(sLine);
679
0
    Message.SetTags(mssTags);
680
0
    sLine = Message.ToString();
681
0
}
682
683
0
bool CTable::AddColumn(const CString& sName) {
684
0
    if (eStyle == ListStyle && m_vsHeaders.size() >= 2)
685
0
        return false;
686
0
    for (const CString& sHeader : m_vsHeaders) {
687
0
        if (sHeader.Equals(sName)) {
688
0
            return false;
689
0
        }
690
0
    }
691
692
0
    m_vsHeaders.push_back(sName);
693
0
    m_msuWidths[sName] = sName.size();
694
695
0
    return true;
696
0
}
697
698
0
bool CTable::SetStyle(EStyle eNewStyle) {
699
0
    switch (eNewStyle) {
700
0
    case GridStyle:
701
0
        break;
702
0
    case ListStyle:
703
0
        if (m_vsHeaders.size() > 2) return false;
704
0
        break;
705
0
    }
706
707
0
    eStyle = eNewStyle;
708
0
    return true;
709
0
}
710
711
0
CTable::size_type CTable::AddRow() {
712
    // Don't add a row if no headers are defined
713
0
    if (m_vsHeaders.empty()) {
714
0
        return (size_type)-1;
715
0
    }
716
717
    // Add a vector with enough space for each column
718
0
    push_back(vector<CString>(m_vsHeaders.size()));
719
0
    return size() - 1;
720
0
}
721
722
bool CTable::SetCell(const CString& sColumn, const CString& sValue,
723
0
                     size_type uRowIdx) {
724
0
    if (uRowIdx == (size_type)~0) {
725
0
        if (empty()) {
726
0
            return false;
727
0
        }
728
729
0
        uRowIdx = size() - 1;
730
0
    }
731
732
0
    unsigned int uColIdx = GetColumnIndex(sColumn);
733
734
0
    if (uColIdx == (unsigned int)-1) return false;
735
736
0
    (*this)[uRowIdx][uColIdx] = sValue;
737
738
0
    if (m_msuWidths[sColumn] < sValue.size())
739
0
        m_msuWidths[sColumn] = sValue.size();
740
741
0
    return true;
742
0
}
743
744
0
bool CTable::GetLine(unsigned int uIdx, CString& sLine) const {
745
0
    std::stringstream ssRet;
746
747
0
    if (empty()) {
748
0
        return false;
749
0
    }
750
751
0
    if (eStyle == ListStyle) {
752
0
        if (m_vsHeaders.size() > 2) return false; // definition list mode can only do up to two columns
753
0
        if (uIdx >= size()) return false;
754
755
0
        const std::vector<CString>& mRow = (*this)[uIdx];
756
0
        ssRet << "\x02" << mRow[0] << "\x0f"; //bold first column
757
0
        if (m_vsHeaders.size() >= 2 && mRow[1] != "") {
758
0
            ssRet << ": " << mRow[1];
759
0
        }
760
761
0
        sLine = ssRet.str();
762
0
        return true;
763
0
    }
764
765
0
    if (uIdx == 1) {
766
0
        ssRet.fill(' ');
767
0
        ssRet << "| ";
768
769
0
        for (unsigned int a = 0; a < m_vsHeaders.size(); a++) {
770
0
            ssRet.width(GetColumnWidth(a));
771
0
            ssRet << std::left << m_vsHeaders[a];
772
0
            ssRet << ((a == m_vsHeaders.size() - 1) ? " |" : " | ");
773
0
        }
774
775
0
        sLine = ssRet.str();
776
0
        return true;
777
0
    } else if ((uIdx == 0) || (uIdx == 2) || (uIdx == (size() + 3))) {
778
0
        ssRet.fill('-');
779
0
        ssRet << "+-";
780
781
0
        for (unsigned int a = 0; a < m_vsHeaders.size(); a++) {
782
0
            ssRet.width(GetColumnWidth(a));
783
0
            ssRet << std::left << "-";
784
0
            ssRet << ((a == m_vsHeaders.size() - 1) ? "-+" : "-+-");
785
0
        }
786
787
0
        sLine = ssRet.str();
788
0
        return true;
789
0
    } else {
790
0
        uIdx -= 3;
791
792
0
        if (uIdx < size()) {
793
0
            const std::vector<CString>& mRow = (*this)[uIdx];
794
0
            ssRet.fill(' ');
795
0
            ssRet << "| ";
796
797
0
            for (unsigned int c = 0; c < m_vsHeaders.size(); c++) {
798
0
                ssRet.width(GetColumnWidth(c));
799
0
                ssRet << std::left << mRow[c];
800
0
                ssRet << ((c == m_vsHeaders.size() - 1) ? " |" : " | ");
801
0
            }
802
803
0
            sLine = ssRet.str();
804
0
            return true;
805
0
        }
806
0
    }
807
808
0
    return false;
809
0
}
810
811
0
unsigned int CTable::GetColumnIndex(const CString& sName) const {
812
0
    for (unsigned int i = 0; i < m_vsHeaders.size(); i++) {
813
0
        if (m_vsHeaders[i] == sName) return i;
814
0
    }
815
816
0
    DEBUG("CTable::GetColumnIndex(" + sName + ") failed");
817
818
0
    return (unsigned int)-1;
819
0
}
820
821
0
CString::size_type CTable::GetColumnWidth(unsigned int uIdx) const {
822
0
    if (uIdx >= m_vsHeaders.size()) {
823
0
        return 0;
824
0
    }
825
826
0
    const CString& sColName = m_vsHeaders[uIdx];
827
0
    std::map<CString, CString::size_type>::const_iterator it =
828
0
        m_msuWidths.find(sColName);
829
830
0
    if (it == m_msuWidths.end()) {
831
        // AddColumn() and SetCell() should make sure that we get a value :/
832
0
        return 0;
833
0
    }
834
0
    return it->second;
835
0
}
836
837
0
void CTable::Clear() {
838
0
    clear();
839
0
    m_vsHeaders.clear();
840
0
    m_msuWidths.clear();
841
0
}
842
843
#ifdef HAVE_LIBSSL
844
CBlowfish::CBlowfish(const CString& sPassword, int iEncrypt,
845
                     const CString& sIvec)
846
    : m_ivec((unsigned char*)calloc(sizeof(unsigned char), 8)),
847
      m_bkey(),
848
      m_iEncrypt(iEncrypt),
849
      m_num(0) {
850
    if (sIvec.length() >= 8) {
851
        memcpy(m_ivec, sIvec.data(), 8);
852
    }
853
854
    BF_set_key(&m_bkey, (unsigned int)sPassword.length(),
855
               (unsigned char*)sPassword.data());
856
}
857
858
CBlowfish::~CBlowfish() { free(m_ivec); }
859
860
//! output must be freed
861
unsigned char* CBlowfish::MD5(const unsigned char* input, unsigned int ilen) {
862
    unsigned char* output = (unsigned char*)malloc(MD5_DIGEST_LENGTH);
863
    ::MD5(input, ilen, output);
864
    return output;
865
}
866
867
//! returns an md5 of the CString (not hex encoded)
868
CString CBlowfish::MD5(const CString& sInput, bool bHexEncode) {
869
    CString sRet;
870
    unsigned char* data =
871
        MD5((const unsigned char*)sInput.data(), (unsigned int)sInput.length());
872
873
    if (!bHexEncode) {
874
        sRet.append((const char*)data, MD5_DIGEST_LENGTH);
875
    } else {
876
        for (int a = 0; a < MD5_DIGEST_LENGTH; a++) {
877
            sRet += g_HexDigits[data[a] >> 4];
878
            sRet += g_HexDigits[data[a] & 0xf];
879
        }
880
    }
881
882
    free(data);
883
    return sRet;
884
}
885
886
//! output must be the same size as input
887
void CBlowfish::Crypt(unsigned char* input, unsigned char* output,
888
                      unsigned int uBytes) {
889
    BF_cfb64_encrypt(input, output, uBytes, &m_bkey, m_ivec, &m_num,
890
                     m_iEncrypt);
891
}
892
893
//! must free result
894
unsigned char* CBlowfish::Crypt(unsigned char* input, unsigned int uBytes) {
895
    unsigned char* buff = (unsigned char*)malloc(uBytes);
896
    Crypt(input, buff, uBytes);
897
    return buff;
898
}
899
900
CString CBlowfish::Crypt(const CString& sData) {
901
    unsigned char* buff =
902
        Crypt((unsigned char*)sData.data(), (unsigned int)sData.length());
903
    CString sOutput;
904
    sOutput.append((const char*)buff, sData.length());
905
    free(buff);
906
    return sOutput;
907
}
908
909
#endif  // HAVE_LIBSSL