Coverage Report

Created: 2026-07-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/syslogappender.cpp
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/net/syslogappender.h>
19
#include <log4cxx/helpers/loglog.h>
20
#include <log4cxx/helpers/stringhelper.h>
21
#include <log4cxx/helpers/datagramsocket.h>
22
#include <log4cxx/spi/loggingevent.h>
23
#include <log4cxx/level.h>
24
#include <log4cxx/helpers/transcoder.h>
25
#include <log4cxx/helpers/optionconverter.h>
26
#if !defined(LOG4CXX)
27
  #define LOG4CXX 1
28
#endif
29
#include <log4cxx/private/syslogappender_priv.h>
30
#include <algorithm>
31
32
0
#define LOG_UNDEF -1
33
34
using namespace LOG4CXX_NS;
35
using namespace LOG4CXX_NS::helpers;
36
using namespace LOG4CXX_NS::net;
37
38
namespace
39
{
40
  constexpr size_t SYSLOG_PACKET_SUFFIX_RESERVED_CHARS = 12;
41
42
  void appendPacketSuffix(LogString& item, size_t current, size_t total)
43
0
  {
44
0
    item.append(LOG4CXX_STR("("));
45
0
    StringHelper::toString(current, item);
46
0
    item.append(LOG4CXX_STR("/"));
47
0
    StringHelper::toString(total, item);
48
0
    item.append(LOG4CXX_STR(")"));
49
0
  }
50
}
51
52
namespace LOG4CXX_NS
53
{
54
namespace net
55
{
56
namespace detail
57
{
58
LOG4CXX_EXPORT std::vector<LogString> splitSyslogPackets(const LogString& msg, size_t maxMessageLength)
59
0
{
60
0
  std::vector<LogString> packets;
61
0
  auto digitCount = [](size_t value) {
62
0
    size_t digits = 1u;
63
0
    while (value >= 10u)
64
0
    {
65
0
      value /= 10u;
66
0
      ++digits;
67
0
    }
68
0
    return digits;
69
0
  };
70
0
  auto reservePackets = [&](size_t count) -> bool
71
0
  {
72
0
    if (count > packets.max_size())
73
0
    {
74
0
      LogLog::error(LOG4CXX_STR("SyslogAppender cannot reserve memory for packet splitting; message too large."));
75
0
      return false;
76
0
    }
77
78
0
    try
79
0
    {
80
0
      packets.reserve(count);
81
0
      return true;
82
0
    }
83
0
    catch (const std::length_error&)
84
0
    {
85
0
      LogLog::error(LOG4CXX_STR("SyslogAppender cannot reserve memory for packet splitting; message too large."));
86
0
      return false;
87
0
    }
88
0
  };
89
0
  auto splitByChunkSize = [&](size_t chunkSize, bool appendSuffix) -> bool
90
0
  {
91
0
    const size_t nChunks = msg.size() / chunkSize + ((msg.size() % chunkSize) != 0u ? 1u : 0u);
92
0
    if (!reservePackets(nChunks))
93
0
    {
94
0
      return false;
95
0
    }
96
97
0
    for (size_t start = 0u; start < msg.size();)
98
0
    {
99
0
      const size_t remaining = msg.size() - start;
100
0
      const size_t count = std::min(chunkSize, remaining);
101
0
      packets.push_back(msg.substr(start, count));
102
0
      start += count;
103
0
    }
104
105
0
    if (appendSuffix)
106
0
    {
107
0
      size_t current = 1u;
108
0
      for (auto& item : packets)
109
0
      {
110
0
        appendPacketSuffix(item, current, packets.size());
111
0
        ++current;
112
0
      }
113
0
    }
114
115
0
    return true;
116
0
  };
117
118
0
  if (maxMessageLength == 0u)
119
0
  {
120
0
    return packets;
121
0
  }
122
123
0
  if (msg.size() <= maxMessageLength)
124
0
  {
125
0
    packets.push_back(msg);
126
0
    return packets;
127
0
  }
128
129
0
  size_t chunkSize = maxMessageLength > SYSLOG_PACKET_SUFFIX_RESERVED_CHARS
130
0
    ? maxMessageLength - SYSLOG_PACKET_SUFFIX_RESERVED_CHARS
131
0
    : 1u;
132
133
0
  const size_t maxIterations = 10u;
134
0
  for (size_t iter = 0u; iter < maxIterations; ++iter)
135
0
  {
136
0
    const size_t nChunks = msg.size() / chunkSize + ((msg.size() % chunkSize) != 0u ? 1u : 0u);
137
0
    const size_t suffixLen = 2u * digitCount(nChunks) + 3u;
138
139
0
    if (suffixLen <= SYSLOG_PACKET_SUFFIX_RESERVED_CHARS)
140
0
    {
141
0
      splitByChunkSize(chunkSize, true);
142
0
      return packets;
143
0
    }
144
145
0
    if (suffixLen >= maxMessageLength)
146
0
    {
147
0
      LogLog::warn(LOG4CXX_STR("SyslogAppender: suffix does not fit in MaxMessageLength; omitting packet suffix."));
148
0
      splitByChunkSize(maxMessageLength, false);
149
0
      return packets;
150
0
    }
151
152
0
    size_t newChunkSize = maxMessageLength - suffixLen;
153
0
    if (newChunkSize == 0u)
154
0
    {
155
0
      newChunkSize = 1u;
156
0
    }
157
158
0
    if (newChunkSize >= chunkSize)
159
0
    {
160
0
      splitByChunkSize(chunkSize, true);
161
0
      return packets;
162
0
    }
163
164
0
    chunkSize = newChunkSize;
165
0
  }
166
167
0
  splitByChunkSize(chunkSize, true);
168
0
  return packets;
169
0
}
170
}
171
}
172
}
173
174
IMPLEMENT_LOG4CXX_OBJECT(SyslogAppender)
175
176
0
#define _priv static_cast<SyslogAppenderPriv*>(m_priv.get())
177
178
SyslogAppender::SyslogAppender()
179
0
  : AppenderSkeleton (std::make_unique<SyslogAppenderPriv>())
180
0
{
181
0
  this->initSyslogFacilityStr();
182
183
0
}
Unexecuted instantiation: log4cxx::net::SyslogAppender::SyslogAppender()
Unexecuted instantiation: log4cxx::net::SyslogAppender::SyslogAppender()
184
185
SyslogAppender::SyslogAppender(const LayoutPtr& layout1,
186
  int syslogFacility1)
187
0
  : AppenderSkeleton (std::make_unique<SyslogAppenderPriv>(layout1, syslogFacility1))
188
0
{
189
0
  this->initSyslogFacilityStr();
190
0
}
Unexecuted instantiation: log4cxx::net::SyslogAppender::SyslogAppender(std::__1::shared_ptr<log4cxx::Layout> const&, int)
Unexecuted instantiation: log4cxx::net::SyslogAppender::SyslogAppender(std::__1::shared_ptr<log4cxx::Layout> const&, int)
191
192
SyslogAppender::SyslogAppender(const LayoutPtr& layout1,
193
  const LogString& syslogHost1, int syslogFacility1)
194
0
  : AppenderSkeleton (std::make_unique<SyslogAppenderPriv>(layout1, syslogHost1, syslogFacility1))
195
0
{
196
0
  this->initSyslogFacilityStr();
197
0
  setSyslogHost(syslogHost1);
198
0
}
Unexecuted instantiation: log4cxx::net::SyslogAppender::SyslogAppender(std::__1::shared_ptr<log4cxx::Layout> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int)
Unexecuted instantiation: log4cxx::net::SyslogAppender::SyslogAppender(std::__1::shared_ptr<log4cxx::Layout> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int)
199
200
SyslogAppender::~SyslogAppender()
201
0
{
202
0
  _priv->setClosed();
203
0
}
204
205
/** Release any resources held by this SyslogAppender.*/
206
void SyslogAppender::close()
207
0
{
208
0
  _priv->setClosed();
209
210
0
  if (_priv->sw)
211
0
  {
212
0
    _priv->sw = nullptr;
213
0
  }
214
0
}
215
216
void SyslogAppender::initSyslogFacilityStr()
217
0
{
218
0
  _priv->facilityStr = getFacilityString(_priv->syslogFacility);
219
220
0
  if (_priv->facilityStr.empty())
221
0
  {
222
0
    LogString msg(LOG4CXX_STR("\""));
223
0
    StringHelper::toString(_priv->syslogFacility, msg);
224
0
    msg.append(LOG4CXX_STR("\" is an unknown syslog facility. Defaulting to \"USER\"."));
225
0
    LogLog::warn(msg);
226
0
    _priv->syslogFacility = LOG_USER;
227
0
    _priv->facilityStr = LOG4CXX_STR("user:");
228
0
  }
229
0
  else
230
0
  {
231
0
    _priv->facilityStr += LOG4CXX_STR(":");
232
0
  }
233
0
}
234
235
/**
236
Returns the specified syslog facility as a lower-case String,
237
e.g. "kern", "user", etc.
238
*/
239
LogString SyslogAppender::getFacilityString(
240
  int syslogFacility)
241
0
{
242
0
  switch (syslogFacility)
243
0
  {
244
0
    case LOG_KERN:
245
0
      return LOG4CXX_STR("kern");
246
247
0
    case LOG_USER:
248
0
      return LOG4CXX_STR("user");
249
250
0
    case LOG_MAIL:
251
0
      return LOG4CXX_STR("mail");
252
253
0
    case LOG_DAEMON:
254
0
      return LOG4CXX_STR("daemon");
255
256
0
    case LOG_AUTH:
257
0
      return LOG4CXX_STR("auth");
258
259
0
    case LOG_SYSLOG:
260
0
      return LOG4CXX_STR("syslog");
261
262
0
    case LOG_LPR:
263
0
      return LOG4CXX_STR("lpr");
264
265
0
    case LOG_NEWS:
266
0
      return LOG4CXX_STR("news");
267
268
0
    case LOG_UUCP:
269
0
      return LOG4CXX_STR("uucp");
270
271
0
    case LOG_CRON:
272
0
      return LOG4CXX_STR("cron");
273
0
#ifdef LOG_AUTHPRIV
274
275
0
    case LOG_AUTHPRIV:
276
0
      return LOG4CXX_STR("authpriv");
277
0
#endif
278
0
#ifdef LOG_FTP
279
280
0
    case LOG_FTP:
281
0
      return LOG4CXX_STR("ftp");
282
0
#endif
283
284
0
    case LOG_LOCAL0:
285
0
      return LOG4CXX_STR("local0");
286
287
0
    case LOG_LOCAL1:
288
0
      return LOG4CXX_STR("local1");
289
290
0
    case LOG_LOCAL2:
291
0
      return LOG4CXX_STR("local2");
292
293
0
    case LOG_LOCAL3:
294
0
      return LOG4CXX_STR("local3");
295
296
0
    case LOG_LOCAL4:
297
0
      return LOG4CXX_STR("local4");
298
299
0
    case LOG_LOCAL5:
300
0
      return LOG4CXX_STR("local5");
301
302
0
    case LOG_LOCAL6:
303
0
      return LOG4CXX_STR("local6");
304
305
0
    case LOG_LOCAL7:
306
0
      return LOG4CXX_STR("local7");
307
308
0
    default:
309
0
      return LogString();
310
0
  }
311
0
}
312
313
int SyslogAppender::getFacility(
314
  const LogString& s)
315
0
{
316
0
  if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("KERN"), LOG4CXX_STR("kern")))
317
0
  {
318
0
    return LOG_KERN;
319
0
  }
320
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("USER"), LOG4CXX_STR("user")))
321
0
  {
322
0
    return LOG_USER;
323
0
  }
324
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("MAIL"), LOG4CXX_STR("mail")))
325
0
  {
326
0
    return LOG_MAIL;
327
0
  }
328
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("DAEMON"), LOG4CXX_STR("daemon")))
329
0
  {
330
0
    return LOG_DAEMON;
331
0
  }
332
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("AUTH"), LOG4CXX_STR("auth")))
333
0
  {
334
0
    return LOG_AUTH;
335
0
  }
336
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("SYSLOG"), LOG4CXX_STR("syslog")))
337
0
  {
338
0
    return LOG_SYSLOG;
339
0
  }
340
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LPR"), LOG4CXX_STR("lpr")))
341
0
  {
342
0
    return LOG_LPR;
343
0
  }
344
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("NEWS"), LOG4CXX_STR("news")))
345
0
  {
346
0
    return LOG_NEWS;
347
0
  }
348
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("UUCP"), LOG4CXX_STR("uucp")))
349
0
  {
350
0
    return LOG_UUCP;
351
0
  }
352
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("CRON"), LOG4CXX_STR("cron")))
353
0
  {
354
0
    return LOG_CRON;
355
0
  }
356
357
0
#ifdef LOG_AUTHPRIV
358
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("AUTHPRIV"), LOG4CXX_STR("authpriv")))
359
0
  {
360
0
    return LOG_AUTHPRIV;
361
0
  }
362
363
0
#endif
364
0
#ifdef LOG_FTP
365
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("FTP"), LOG4CXX_STR("ftp")))
366
0
  {
367
0
    return LOG_FTP;
368
0
  }
369
370
0
#endif
371
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL0"), LOG4CXX_STR("local0")))
372
0
  {
373
0
    return LOG_LOCAL0;
374
0
  }
375
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL1"), LOG4CXX_STR("local1")))
376
0
  {
377
0
    return LOG_LOCAL1;
378
0
  }
379
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL2"), LOG4CXX_STR("local2")))
380
0
  {
381
0
    return LOG_LOCAL2;
382
0
  }
383
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL3"), LOG4CXX_STR("local3")))
384
0
  {
385
0
    return LOG_LOCAL3;
386
0
  }
387
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL4"), LOG4CXX_STR("local4")))
388
0
  {
389
0
    return LOG_LOCAL4;
390
0
  }
391
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL5"), LOG4CXX_STR("local5")))
392
0
  {
393
0
    return LOG_LOCAL5;
394
0
  }
395
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL6"), LOG4CXX_STR("local6")))
396
0
  {
397
0
    return LOG_LOCAL6;
398
0
  }
399
0
  else if (StringHelper::equalsIgnoreCase(s, LOG4CXX_STR("LOCAL7"), LOG4CXX_STR("local7")))
400
0
  {
401
0
    return LOG_LOCAL7;
402
0
  }
403
0
  else
404
0
  {
405
0
    return LOG_UNDEF;
406
0
  }
407
0
}
408
409
void SyslogAppender::append( LOG4CXX_APPEND_FORMAL_PARAMETERS )
410
0
{
411
0
  if  (!isAsSevereAsThreshold(event->getLevel()))
412
0
  {
413
0
    return;
414
0
  }
415
416
0
  LogString msg;
417
0
  std::string encoded;
418
0
  _priv->layout->format(msg, event);
419
420
0
  Transcoder::encode(msg, encoded);
421
0
  auto packets = detail::splitSyslogPackets(msg, static_cast<size_t>(_priv->maxMessageLength));
422
0
  if (packets.empty() && !msg.empty())
423
0
  {
424
0
    return;
425
0
  }
426
427
  // On the local host, we can directly use the system function 'syslog'
428
  // if it is available
429
0
#if LOG4CXX_HAVE_SYSLOG
430
431
0
  if (_priv->sw == 0)
432
0
  {
433
0
    for (auto const& item : packets)
434
0
    {
435
      // use of "%s" to avoid a security hole
436
0
      LOG4CXX_ENCODE_CHAR(itemStr, item);
437
0
      ::syslog(_priv->syslogFacility | event->getLevel()->getSyslogEquivalent(),
438
0
        "%s", itemStr.c_str());
439
0
    }
440
441
0
    return;
442
0
  }
443
444
0
#endif
445
446
  // We must not attempt to append if sw is null.
447
0
  if (_priv->sw == 0)
448
0
  {
449
0
    _priv->errorHandler->error(LOG4CXX_STR("No syslog host is set for SyslogAppedender named \"") +
450
0
      _priv->name + LOG4CXX_STR("\"."));
451
0
    return;
452
0
  }
453
454
0
  for (auto const& item : packets)
455
0
  {
456
0
    LogString sbuf(1, 0x3C /* '<' */);
457
0
    StringHelper::toString((_priv->syslogFacility | event->getLevel()->getSyslogEquivalent()), sbuf);
458
0
    sbuf.append(1, (logchar) 0x3E /* '>' */);
459
460
0
    if (_priv->facilityPrinting)
461
0
    {
462
0
      sbuf.append(_priv->facilityStr);
463
0
    }
464
465
0
    sbuf.append(item);
466
0
    _priv->sw->write(sbuf);
467
0
  }
468
0
}
469
470
#if LOG4CXX_ABI_VERSION <= 15
471
void SyslogAppender::activateOptions(Pool&)
472
0
{
473
0
}
474
#endif
475
476
void SyslogAppender::setOption(const LogString& option, const LogString& value)
477
0
{
478
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SYSLOGHOST"), LOG4CXX_STR("sysloghost")))
479
0
  {
480
0
    setSyslogHost(value);
481
0
  }
482
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FACILITY"), LOG4CXX_STR("facility")))
483
0
  {
484
0
    setFacility(value);
485
0
  }
486
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("MAXMESSAGELENGTH"), LOG4CXX_STR("maxmessagelength")))
487
0
  {
488
0
    setMaxMessageLength(OptionConverter::toInt(value, 1024));
489
0
  }
490
0
  else
491
0
  {
492
0
    AppenderSkeleton::setOption(option, value);
493
0
  }
494
0
}
495
496
void SyslogAppender::setSyslogHost(const LogString& syslogHost1)
497
0
{
498
0
  if (_priv->sw != 0)
499
0
  {
500
0
    _priv->sw = nullptr;
501
0
  }
502
503
0
  LogString slHost = syslogHost1;
504
0
  int slHostPort = -1;
505
506
0
  LogString::size_type colonPos = 0;
507
0
  colonPos = slHost.rfind(':');
508
509
0
  if (colonPos != LogString::npos)
510
0
  {
511
0
    slHostPort = StringHelper::toInt(slHost.substr(colonPos + 1));
512
    // Erase the :port part of the host name
513
0
    slHost.erase( colonPos );
514
0
  }
515
516
  // On the local host, we can directly use the system function 'syslog'
517
  // if it is available (cf. append)
518
0
#if LOG4CXX_HAVE_SYSLOG
519
520
0
  if (syslogHost1 != LOG4CXX_STR("localhost") && syslogHost1 != LOG4CXX_STR("127.0.0.1")
521
0
    && !syslogHost1.empty())
522
0
#endif
523
0
  {
524
0
    if (slHostPort >= 0)
525
0
    {
526
0
      _priv->sw = std::make_unique<SyslogWriter>(slHost, slHostPort);
527
0
    }
528
0
    else
529
0
    {
530
0
      _priv->sw = std::make_unique<SyslogWriter>(slHost);
531
0
    }
532
0
  }
533
534
0
  _priv->syslogHost = slHost;
535
0
  _priv->syslogHostPort = slHostPort;
536
0
}
537
538
539
void SyslogAppender::setFacility(const LogString& facilityName)
540
0
{
541
0
  if (facilityName.empty())
542
0
  {
543
0
    return;
544
0
  }
545
546
0
  _priv->syslogFacility = getFacility(facilityName);
547
548
0
  if (_priv->syslogFacility == LOG_UNDEF)
549
0
  {
550
0
    LogLog::warn(LOG4CXX_STR("[") + facilityName +
551
0
      LOG4CXX_STR("] is an unknown syslog facility. Defaulting to [USER]."));
552
0
    _priv->syslogFacility = LOG_USER;
553
0
  }
554
555
0
  this->initSyslogFacilityStr();
556
0
}
557
558
const LogString& SyslogAppender::getSyslogHost() const
559
0
{
560
0
  return _priv->syslogHost;
561
0
}
562
563
LogString SyslogAppender::getFacility() const
564
0
{
565
0
  return getFacilityString(_priv->syslogFacility);
566
0
}
567
568
void SyslogAppender::setFacilityPrinting(bool facilityPrinting1)
569
0
{
570
0
  _priv->facilityPrinting = facilityPrinting1;
571
0
}
572
573
bool SyslogAppender::getFacilityPrinting() const
574
0
{
575
0
  return _priv->facilityPrinting;
576
0
}
577
578
void SyslogAppender::setMaxMessageLength(int maxMessageLength1)
579
0
{
580
  // append() reserves 12 characters per chunk for an "(x/y)" sequence suffix.
581
  // A value at or below the suffix size produces a zero-length chunk (causing
582
  // an infinite split loop) or an iterator computed before msg.begin() (UB).
583
0
  static const int MIN_MAX_MESSAGE_LENGTH = 13;
584
0
  if (maxMessageLength1 < MIN_MAX_MESSAGE_LENGTH)
585
0
  {
586
0
    LogLog::warn(LOG4CXX_STR("SyslogAppender MaxMessageLength is too small. Using the default value."));
587
0
    maxMessageLength1 = 1024;
588
0
  }
589
0
  _priv->maxMessageLength = maxMessageLength1;
590
0
}
591
592
int SyslogAppender::getMaxMessageLength() const
593
0
{
594
0
  return _priv->maxMessageLength;
595
0
}